mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
import { X } from 'lucide-react';
|
|
import { useEffect, useState, type ReactElement } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
import { useTranslation } from '../../i18n';
|
|
import { lockBodyScroll } from '../../utils/bodyScrollLock';
|
|
|
|
function sheetRoot(): HTMLElement {
|
|
return document.getElementById('m-sheet-root') ?? document.body;
|
|
}
|
|
|
|
/**
|
|
* One image full screen, for a picture shown small in a list (a chat
|
|
* attachment). A tap on the image zooms in, and the zoomed image pans with the
|
|
* finger as any scrolled page does; a tap beside it or the X closes the view,
|
|
* as Escape does with a keyboard. The page's own pinch zoom is off in the app,
|
|
* so the zoom is the viewer's.
|
|
*/
|
|
export default function MImageViewer({
|
|
src,
|
|
alt,
|
|
onClose,
|
|
}: {
|
|
src: string;
|
|
alt: string;
|
|
onClose: () => void;
|
|
}): ReactElement {
|
|
const { t } = useTranslation();
|
|
const [zoomed, setZoomed] = useState(false);
|
|
|
|
useEffect(() => lockBodyScroll(), []);
|
|
useEffect(() => {
|
|
const onKey = (e: KeyboardEvent) => {
|
|
if (e.key === 'Escape') onClose();
|
|
};
|
|
window.addEventListener('keydown', onKey);
|
|
return () => window.removeEventListener('keydown', onKey);
|
|
}, [onClose]);
|
|
|
|
return createPortal(
|
|
<div
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={alt}
|
|
className="m-root fixed inset-0 z-[var(--z-overlay)] bg-[color:var(--m-viewer-bg)]"
|
|
onClick={onClose}
|
|
>
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
aria-label={t('collab.chat.closeImage')}
|
|
className="absolute end-4 top-[calc(var(--m-safe-top,12px)+4px)] z-10 flex h-10 w-10 items-center justify-center rounded-full bg-[color:var(--m-viewer-btn)] text-[color:var(--m-viewer-ink)]"
|
|
>
|
|
<X size={20} strokeWidth={2} />
|
|
</button>
|
|
<div
|
|
className={`h-full w-full ${zoomed ? 'overflow-auto' : 'flex items-center justify-center overflow-hidden p-4'}`}
|
|
>
|
|
<img
|
|
src={src}
|
|
alt={alt}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setZoomed((z) => !z);
|
|
}}
|
|
className={
|
|
zoomed ? 'block w-[250%] max-w-none cursor-zoom-out' : 'max-h-full max-w-full cursor-zoom-in object-contain'
|
|
}
|
|
/>
|
|
</div>
|
|
</div>,
|
|
sheetRoot()
|
|
);
|
|
}
|