ui
This commit is contained in:
@@ -17,6 +17,8 @@ const ensureDocumentRoot = () => {
|
|||||||
return document.body;
|
return document.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CLICK_DELAY_MS = 240;
|
||||||
|
|
||||||
const PreviewZoomOverlay = ({
|
const PreviewZoomOverlay = ({
|
||||||
open = false,
|
open = false,
|
||||||
display = null,
|
display = null,
|
||||||
@@ -34,6 +36,7 @@ const PreviewZoomOverlay = ({
|
|||||||
const previouslyFocusedRef = useRef(null);
|
const previouslyFocusedRef = useRef(null);
|
||||||
const visibilityTimerRef = useRef(null);
|
const visibilityTimerRef = useRef(null);
|
||||||
const displayTimerRef = useRef(null);
|
const displayTimerRef = useRef(null);
|
||||||
|
const clickTimerRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (display?.url) {
|
if (display?.url) {
|
||||||
@@ -86,12 +89,20 @@ const PreviewZoomOverlay = ({
|
|||||||
if (displayTimerRef.current) {
|
if (displayTimerRef.current) {
|
||||||
cancelAnimationFrame(displayTimerRef.current);
|
cancelAnimationFrame(displayTimerRef.current);
|
||||||
}
|
}
|
||||||
|
if (clickTimerRef.current) {
|
||||||
|
clearTimeout(clickTimerRef.current);
|
||||||
|
clickTimerRef.current = null;
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsNativeScale(false);
|
setIsNativeScale(false);
|
||||||
setNaturalSize({ width: null, height: null });
|
setNaturalSize({ width: null, height: null });
|
||||||
focusRef.current = null;
|
focusRef.current = null;
|
||||||
|
if (clickTimerRef.current) {
|
||||||
|
clearTimeout(clickTimerRef.current);
|
||||||
|
clickTimerRef.current = null;
|
||||||
|
}
|
||||||
const scrollEl = scrollRef.current;
|
const scrollEl = scrollRef.current;
|
||||||
if (scrollEl) {
|
if (scrollEl) {
|
||||||
scrollEl.scrollLeft = 0;
|
scrollEl.scrollLeft = 0;
|
||||||
@@ -198,6 +209,45 @@ const PreviewZoomOverlay = ({
|
|||||||
|
|
||||||
const effectiveDisplay = activeDisplay;
|
const effectiveDisplay = activeDisplay;
|
||||||
const navVisible = Boolean(effectiveDisplay?.canGoPrev || effectiveDisplay?.canGoNext);
|
const navVisible = Boolean(effectiveDisplay?.canGoPrev || effectiveDisplay?.canGoNext);
|
||||||
|
|
||||||
|
const toggleZoomAtPoint = (clientX, clientY) => {
|
||||||
|
const img = imageRef.current;
|
||||||
|
setIsNativeScale((current) => {
|
||||||
|
if (!current && img) {
|
||||||
|
const rect = img.getBoundingClientRect();
|
||||||
|
const xRatio = rect.width > 0 ? (clientX - rect.left) / rect.width : 0.5;
|
||||||
|
const yRatio = rect.height > 0 ? (clientY - rect.top) / rect.height : 0.5;
|
||||||
|
focusRef.current = {
|
||||||
|
xRatio: clamp(xRatio, 0, 1),
|
||||||
|
yRatio: clamp(yRatio, 0, 1),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
focusRef.current = null;
|
||||||
|
}
|
||||||
|
return !current;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImagePointerDown = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (event.button && event.button !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.pointerType === 'touch' && event.isPrimary === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (clickTimerRef.current) {
|
||||||
|
clearTimeout(clickTimerRef.current);
|
||||||
|
clickTimerRef.current = null;
|
||||||
|
toggleZoomAtPoint(event.clientX, event.clientY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickTimerRef.current = setTimeout(() => {
|
||||||
|
clickTimerRef.current = null;
|
||||||
|
onClose();
|
||||||
|
}, CLICK_DELAY_MS);
|
||||||
|
};
|
||||||
const stageClassName = [
|
const stageClassName = [
|
||||||
'preview-zoom__stage',
|
'preview-zoom__stage',
|
||||||
]
|
]
|
||||||
@@ -225,11 +275,13 @@ const PreviewZoomOverlay = ({
|
|||||||
height: naturalSize.height ? `${naturalSize.height}px` : 'auto',
|
height: naturalSize.height ? `${naturalSize.height}px` : 'auto',
|
||||||
maxWidth: 'none',
|
maxWidth: 'none',
|
||||||
maxHeight: 'none',
|
maxHeight: 'none',
|
||||||
|
touchAction: 'manipulation',
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
cursor: 'zoom-in',
|
cursor: 'zoom-in',
|
||||||
maxWidth: '95vw',
|
maxWidth: '95vw',
|
||||||
maxHeight: '95vh',
|
maxHeight: '95vh',
|
||||||
|
touchAction: 'manipulation',
|
||||||
};
|
};
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
@@ -263,26 +315,7 @@ const PreviewZoomOverlay = ({
|
|||||||
height: event.currentTarget.naturalHeight || null,
|
height: event.currentTarget.naturalHeight || null,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onClick={(event) => {
|
onPointerDown={handleImagePointerDown}
|
||||||
event.stopPropagation();
|
|
||||||
if (!isNativeScale) {
|
|
||||||
const img = imageRef.current;
|
|
||||||
if (img) {
|
|
||||||
const rect = img.getBoundingClientRect();
|
|
||||||
const xRatio = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0.5;
|
|
||||||
const yRatio = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0.5;
|
|
||||||
focusRef.current = {
|
|
||||||
xRatio: clamp(xRatio, 0, 1),
|
|
||||||
yRatio: clamp(yRatio, 0, 1),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
focusRef.current = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
focusRef.current = null;
|
|
||||||
}
|
|
||||||
setIsNativeScale((current) => !current);
|
|
||||||
}}
|
|
||||||
style={imageStyle}
|
style={imageStyle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2482,7 +2482,7 @@ button.danger:hover:not([disabled]) {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: var(--detail-panel-width);
|
width: min(100vw, var(--detail-panel-width));
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user