feat(ImgViewer): Add the ability to click on an image to view it full screen

This commit is contained in:
haitaoo
2023-02-23 14:53:47 +08:00
parent 3fe58c784a
commit b046c7f292
6 changed files with 88 additions and 3 deletions
+2
View File
@@ -9,6 +9,7 @@ import useUserModal from './useUserModal';
import useChangePasswordModal from './useChangePasswordModal';
import usePageTags from './usePageTags';
import usePromptWithUnload from './usePrompt';
import useImgViewer from './useImgViewer';
export {
useTagModal,
@@ -22,4 +23,5 @@ export {
useChangePasswordModal,
usePageTags,
usePromptWithUnload,
useImgViewer,
};
+74
View File
@@ -0,0 +1,74 @@
import { useLayoutEffect, useState, MouseEvent } from 'react';
import { Modal } from 'react-bootstrap';
import ReactDOM from 'react-dom/client';
const div = document.createElement('div');
const root = ReactDOM.createRoot(div);
const useImgViewer = () => {
const [visible, setVisible] = useState(false);
const [imgSrc, setImgSrc] = useState('');
const onClose = () => {
setVisible(false);
setImgSrc('');
};
const checkIfInLink = (target) => {
let ret = false;
let el = target.parentElement;
while (el) {
if (el.nodeName.toLowerCase() === 'a') {
ret = true;
break;
}
el = el.parentElement;
}
return ret;
};
const checkClickForImgView = (evt: MouseEvent<HTMLElement>) => {
const { target } = evt;
// @ts-ignore
if (target.nodeName.toLowerCase() !== 'img') {
return;
}
const img = target as HTMLImageElement;
if (!img.naturalWidth || !img.naturalHeight) {
return;
}
const src = img.currentSrc || img.src;
if (src && checkIfInLink(img) === false) {
setImgSrc(src);
setVisible(true);
}
};
useLayoutEffect(() => {
root.render(
<Modal
show={visible}
fullscreen
centered
scrollable
contentClassName="bg-transparent"
onHide={onClose}>
<Modal.Body>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions */}
<img
className="cursor-zoom-out img-fluid position-absolute top-50 start-50 translate-middle"
src={imgSrc}
alt={imgSrc}
onClick={onClose}
/>
</Modal.Body>
</Modal>,
);
});
return {
onClose,
checkClickForImgView,
};
};
export default useImgViewer;
+1 -1
View File
@@ -6,7 +6,7 @@ import {
import { useTranslation } from 'react-i18next';
// https://gist.github.com/chaance/2f3c14ec2351a175024f62fd6ba64aa6
// The link above is an example of implementing usePromt with useBlocer.
// The link above is an example of implementing usePrompt with useBlocker.
interface PromptProps {
when: boolean;
beforeUnload?: boolean;
+4
View File
@@ -120,6 +120,10 @@ a {
cursor: pointer;
}
.cursor-zoom-out {
cursor: zoom-out;
}
.resize-none {
resize: none;
}
+6 -1
View File
@@ -14,12 +14,14 @@ import {
PageTags,
} from '@/components';
import { LoginToContinueModal } from '@/components/Modal';
import { useImgViewer } from '@/hooks';
const Layout: FC = () => {
const { msg: toastMsg, variant, clear: toastClear } = toastStore();
const closeToast = () => {
toastClear();
};
const imgViewer = useImgViewer();
const { show: showLoginToContinueModal } = loginToContinueStore();
return (
<HelmetProvider>
@@ -30,7 +32,10 @@ const Layout: FC = () => {
revalidateOnFocus: false,
}}>
<Header />
<div className="position-relative page-wrap">
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
<div
className="position-relative page-wrap"
onClick={imgViewer.checkClickForImgView}>
<Outlet />
</div>
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
@@ -18,7 +18,7 @@ const Index: FC<Props> = ({ visible, introduction, data }) => {
<h5 className="mb-3">{t('about_me')}</h5>
{introduction ? (
<div
className="mb-4 text-break"
className="mb-4 text-break fmt"
dangerouslySetInnerHTML={{ __html: introduction }}
/>
) : (