Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./use-cursor-visibility";
export * from "./use-image-navigation";
export * from "./use-image-scale";
export * from "./use-prevent-scroll";
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { useState, useRef, useCallback } from "react";

export const useCursorVisibility = (isScaleFocus: boolean) => {
const [isCursorVisible, setIsisCursorVisible] = useState(true);
export const useCursorVisibility = () => {
const [isCursorVisible, setIsCursorVisible] = useState(true);
const cursorTimeOutRef = useRef<NodeJS.Timeout>();

const handleMoveMouse = useCallback(() => {
setIsisCursorVisible(true);
setIsCursorVisible(true);

clearTimeout(cursorTimeOutRef.current);

if (cursorTimeOutRef.current) {
clearTimeout(cursorTimeOutRef.current);
}

if (!isScaleFocus) {
cursorTimeOutRef.current = setTimeout(() => {
setIsisCursorVisible(false);
}, 2000);
}
}, [isScaleFocus]);
cursorTimeOutRef.current = setTimeout(() => {
setIsCursorVisible(false);
}, 2000);
}, []);

return {
isCursorVisible,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useEffect } from "react";

import { getGapStyles, getGapWidth } from "../../lib";

export const usePreventScroll = (isOpened: boolean) => {
useEffect(() => {
const styleElement = document.createElement("style");

if (isOpened) {
document.body.setAttribute("data-scroll-locked", "true");
const gap = getGapWidth();

const scrollLockedStyles = getGapStyles(gap);
styleElement.textContent = scrollLockedStyles;
document.head.appendChild(styleElement);
}

return () => {
document.body.removeAttribute("data-scroll-locked");
if (styleElement.parentNode) {
styleElement.parentNode.removeChild(styleElement);
}
};
}, [isOpened]);
};
17 changes: 11 additions & 6 deletions packages/core/src/lib/components/image/image-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
useCursorVisibility,
useImageNavigation,
useImageScale,
usePreventScroll,
} from "./hooks/image-viewer";

import { getCursorStyle } from "./lib";
Expand Down Expand Up @@ -51,8 +52,7 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
handleScaleDown,
} = useImageScale();

const { isCursorVisible, handleMoveMouse } =
useCursorVisibility(isScaleFocus);
const { isCursorVisible, handleMoveMouse } = useCursorVisibility();

useEffect(() => {
if (currentImageIndex || isOpened) {
Expand Down Expand Up @@ -106,6 +106,8 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
[urls, setCurrentImageIndex, setIsOpened],
);

usePreventScroll(isOpened);

return (
<>
<button
Expand All @@ -120,7 +122,7 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
{isOpened && (
<motion.div
role="dialog"
className={`notion-image-viewer-container ${isCursorVisible ? "notion-visible-cursor" : "notion-hide-cursor"}`}
className={`notion-image-viewer-container`}
aria-modal="true"
onMouseMove={handleMoveMouse}
initial={{ opacity: 0 }}
Expand All @@ -131,22 +133,25 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
<button
className="notion-image-viewer-overlay"
onClick={() => setIsOpened(false)}
style={{
cursor: isCursorVisible ? "default" : "none",
}}
/>
<motion.img
key={urls[currentImageIndex]}
ref={imageRef}
className={`notion-image-viewer-container-image ${isCursorVisible ? "notion-visible-cursor" : "notion-hide-cursor"}`}
className={`notion-image-viewer-container-image`}
src={urls[currentImageIndex]}
alt="posting image"
style={{
transform: `scale(${scale})`,
transformOrigin: `${scaleOriginX * 100}% ${scaleOriginY * 100}%`,
cursor: getCursorStyle(scale),
cursor: isCursorVisible ? getCursorStyle(scale) : "none",
}}
onClick={handleZoomInOut}
/>

{isCursorVisible && (
{(isCursorVisible || isScaleFocus) && (
<ImageViewerTools
url={urls[currentImageIndex]}
currentImageIndex={currentImageIndex}
Expand Down
56 changes: 56 additions & 0 deletions packages/core/src/lib/components/image/lib/get-gap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
interface GapOffset {
left: number;
top: number;
right: number;
gap: number;
}

export const zeroGap = {
left: 0,
top: 0,
right: 0,
gap: 0,
};

const parse = (x: string | null) => parseInt(x || "", 10) || 0;

export const getOffset = (): number[] => {
const cs = window.getComputedStyle(document.body);

const left = cs["marginLeft"];
const top = cs["marginTop"];
const right = cs["marginRight"];

return [parse(left), parse(top), parse(right)];
};

export const getGapWidth = (): GapOffset => {
if (typeof window === "undefined") {
return zeroGap;
}

const offsets = getOffset();
const documentWidth = document.documentElement.clientWidth;
const windowWidth = window.innerWidth;

return {
left: offsets[0],
top: offsets[1],
right: offsets[2],
gap: Math.max(0, windowWidth - documentWidth + offsets[2] - offsets[0]),
};
};

export const getGapStyles = ({ left, top, right, gap }: GapOffset) => `
body[data-scroll-locked] {
padding-left: ${left}px;
padding-top: ${top}px;
padding-right: ${right}px;
margin-right: ${gap}px !important;
overflow: hidden !important;
overscroll-behavior: contain;
margin-left: 0;
margin-top: 0;
position: relative !important;
}
`;
1 change: 1 addition & 0 deletions packages/core/src/lib/components/image/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./download-image-file";
export * from "./get-gap";
export * from "./get-image-url-or-null";
export * from "./get-cursor-style";
export * from "./scale-round";
Loading