From df85042df90aee0b09eb11a3cd5bbc98169cb765 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Sun, 30 Jul 2023 17:56:18 +0200 Subject: [PATCH] clickable APNGs --- resources/js/components/common/PngDialog.js | 27 +++++++++ resources/js/components/common/PngPlayer.js | 1 + resources/js/components/common/RawHTML.js | 61 +++++++++++++++------ resources/sass/common.scss | 18 ++++++ 4 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 resources/js/components/common/PngDialog.js diff --git a/resources/js/components/common/PngDialog.js b/resources/js/components/common/PngDialog.js new file mode 100644 index 0000000..dbd030f --- /dev/null +++ b/resources/js/components/common/PngDialog.js @@ -0,0 +1,27 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Modal } from 'react-bootstrap'; + +import PngPlayer from './PngPlayer'; + +const PngDialog = ({ onHide, show, src, title }) => + {title ? + + + {title} + + + : null} + + + +; + +PngDialog.propTypes = { + onHide: PropTypes.func, + show: PropTypes.bool, + src: PropTypes.string, + title: PropTypes.string, +}; + +export default PngDialog; diff --git a/resources/js/components/common/PngPlayer.js b/resources/js/components/common/PngPlayer.js index 4bdb41a..d1d5da3 100644 --- a/resources/js/components/common/PngPlayer.js +++ b/resources/js/components/common/PngPlayer.js @@ -24,6 +24,7 @@ const PngPlayer = ({ src }) => { const [player, setPlayer] = React.useState(null); React.useEffect(() => { + if (!src) return; setError(null); setLoading(true); const ctrl = new AbortController(); diff --git a/resources/js/components/common/RawHTML.js b/resources/js/components/common/RawHTML.js index fb0b51e..dde6d53 100644 --- a/resources/js/components/common/RawHTML.js +++ b/resources/js/components/common/RawHTML.js @@ -2,8 +2,23 @@ import PropTypes from 'prop-types'; import React from 'react'; import { useNavigate } from 'react-router-dom'; +import PngDialog from './PngDialog'; + +const isApng = el => el.nodeName === 'IMG' && el.getAttribute('type') === 'image/apng'; + +const isLink = el => el.nodeName === 'A'; + +const canClick = el => { + if (isLink(el)) return true; + if (isApng(el)) return true; + return false; +}; + const RawHTML = ({ html }) => { const navigate = useNavigate(); + const [apng, setApng] = React.useState(null); + const [show, setShow] = React.useState(false); + const [title, setTitle] = React.useState(null); const onClick = e => { if (e.defaultPrevented) return; @@ -11,34 +26,46 @@ const RawHTML = ({ html }) => { if (e.button !== 0) return; let el = e.target; - while (el && el.nodeName !== 'A') { + while (el && !canClick(el)) { el = el.parentNode; } if (!el) return; - if (el.target && el.target !== '_self') return; - if (el.attributes.download) return; - if (el.rel && /(?:^|\s+)external(?:\s+|$)/.test(el.rel)) return; + if (isLink(el)) { + if (el.target && el.target !== '_self') return; + if (el.attributes.download) return; + if (el.rel && /(?:^|\s+)external(?:\s+|$)/.test(el.rel)) return; + + const href = el.getAttribute('href'); - const href = el.getAttribute('href'); + if (href.startsWith('#')) return; + if (href.startsWith('http')) return; + if (href.startsWith('mailto')) return; + if (href.startsWith('tel')) return; - if (href.startsWith('#')) return; - if (href.startsWith('http')) return; - if (href.startsWith('mailto')) return; - if (href.startsWith('tel')) return; + el.blur(); + e.preventDefault(); - el.blur(); - e.preventDefault(); + setTimeout(() => { + // scroll to top on location change + scrollTo({ top: 0, behavior: 'smooth' }); + }, 50); - setTimeout(() => { - // scroll to top on location change - scrollTo({ top: 0, behavior: 'smooth' }); - }, 50); + navigate(href); + return; + } - navigate(href); + if (isApng(el)) { + setApng(el.getAttribute('src')); + setShow(true); + setTitle(el.getAttribute('alt')); + } }; - return
; + return <> +
+ setShow(false)} show={show} src={apng} title={title} /> + ; }; RawHTML.propTypes = { diff --git a/resources/sass/common.scss b/resources/sass/common.scss index 3e88649..4d603f0 100644 --- a/resources/sass/common.scss +++ b/resources/sass/common.scss @@ -23,6 +23,18 @@ h1 { max-width: none !important; } +.pic-end { + float: right; + margin-left: 1rem; + margin-bottom: 1rem; +} + +.pic-start { + float: left; + margin-right: 1rem; + margin-bottom: 1rem; +} + .png-player { display: flex; flex-direction: column; @@ -60,6 +72,12 @@ h1 { } } +.raw-html { + img[type="image/apng"] { + cursor: pointer; + } +} + .snes-button-a, .snes-button-b, .snes-button-x, -- 2.39.2