]> git.localhorst.tv Git - alttp.git/blob - resources/js/components/common/RawHTML.js
clickable APNGs
[alttp.git] / resources / js / components / common / RawHTML.js
1 import PropTypes from 'prop-types';
2 import React from 'react';
3 import { useNavigate } from 'react-router-dom';
4
5 import PngDialog from './PngDialog';
6
7 const isApng = el => el.nodeName === 'IMG' && el.getAttribute('type') === 'image/apng';
8
9 const isLink = el => el.nodeName === 'A';
10
11 const canClick = el => {
12         if (isLink(el)) return true;
13         if (isApng(el)) return true;
14         return false;
15 };
16
17 const RawHTML = ({ html }) => {
18         const navigate = useNavigate();
19         const [apng, setApng] = React.useState(null);
20         const [show, setShow] = React.useState(false);
21         const [title, setTitle] = React.useState(null);
22
23         const onClick = e => {
24                 if (e.defaultPrevented) return;
25                 if (e.metaKey || e.ctrlKey || e.shiftKey) return;
26                 if (e.button !== 0) return;
27
28                 let el = e.target;
29                 while (el && !canClick(el)) {
30                         el = el.parentNode;
31                 }
32                 if (!el) return;
33
34                 if (isLink(el)) {
35                         if (el.target && el.target !== '_self') return;
36                         if (el.attributes.download) return;
37                         if (el.rel && /(?:^|\s+)external(?:\s+|$)/.test(el.rel)) return;
38
39                         const href = el.getAttribute('href');
40
41                         if (href.startsWith('#')) return;
42                         if (href.startsWith('http')) return;
43                         if (href.startsWith('mailto')) return;
44                         if (href.startsWith('tel')) return;
45
46                         el.blur();
47                         e.preventDefault();
48
49                         setTimeout(() => {
50                                 // scroll to top on location change
51                                 scrollTo({ top: 0, behavior: 'smooth' });
52                         }, 50);
53
54                         navigate(href);
55                         return;
56                 }
57
58                 if (isApng(el)) {
59                         setApng(el.getAttribute('src'));
60                         setShow(true);
61                         setTitle(el.getAttribute('alt'));
62                 }
63         };
64
65         return <>
66                 <div className="raw-html" onClick={onClick} dangerouslySetInnerHTML={{ __html: html }} />
67                 <PngDialog onHide={() => setShow(false)} show={show} src={apng} title={title} />
68         </>;
69 };
70
71 RawHTML.propTypes = {
72         html: PropTypes.string,
73 };
74
75 export default RawHTML;