Browse Source

Book card interaction smooth 2

Sviluppo_Carrello_Immagini
Lorenzo Pollutri 1 month ago
parent
commit
7234bdb7a2
4 changed files with 397 additions and 176 deletions
  1. +71
    -165
      components/PublicGrid.tsx
  2. +281
    -11
      package-lock.json
  3. +1
    -0
      package.json
  4. +44
    -0
      types/page-flip.d.ts

+ 71
- 165
components/PublicGrid.tsx View File

@@ -445,10 +445,11 @@ function playFlipSound(ctx: AudioContext | null) {
}

function FlipBook({ pages, onClose }: { pages: string[]; onClose: () => void }) {
const [spread, setSpread] = useState(0);
const [flipping, setFlipping] = useState<'forward' | 'backward' | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
const flipRef = useRef<import('page-flip').PageFlip | null>(null);
const audioRef = useRef<AudioContext | null>(null);
const dragStartX = useRef<number | null>(null);
const [currentPage, setCurrentPage] = useState(0);
const [pageCount, setPageCount] = useState(pages.length);

const getCtx = () => {
if (!audioRef.current) {
@@ -459,29 +460,65 @@ function FlipBook({ pages, onClose }: { pages: string[]; onClose: () => void })
return audioRef.current;
};

const totalSpreads = Math.max(1, Math.ceil(pages.length / 2));
const getPage = (i: number) => (i >= 0 && i < pages.length ? pages[i] : '');

const leftIdx = spread * 2;
const rightIdx = spread * 2 + 1;
const nextLeftIdx = (spread + 1) * 2;
const nextRightIdx = (spread + 1) * 2 + 1;
const prevLeftIdx = (spread - 1) * 2;
const prevRightIdx = (spread - 1) * 2 + 1;

const goNext = useCallback(() => {
if (flipping !== null || spread >= totalSpreads - 1) return;
playFlipSound(getCtx());
setFlipping('forward');
window.setTimeout(() => { setSpread(s => s + 1); setFlipping(null); }, 750);
}, [flipping, spread, totalSpreads]);

const goPrev = useCallback(() => {
if (flipping !== null || spread <= 0) return;
playFlipSound(getCtx());
setFlipping('backward');
window.setTimeout(() => { setSpread(s => s - 1); setFlipping(null); }, 750);
}, [flipping, spread]);
useEffect(() => {
if (!containerRef.current || pages.length === 0) return;
let cancelled = false;

// page-flip's destroy() removes its own block from the DOM. Give it a child
// div we own so React's managed container stays intact across StrictMode cycles.
const block = document.createElement('div');
block.style.width = '100%';
block.style.height = '100%';
containerRef.current.appendChild(block);

import('page-flip').then(({ PageFlip }) => {
if (cancelled) return;
const flip = new PageFlip(block, {
width: 550,
height: 733,
size: 'stretch',
minWidth: 315,
maxWidth: 1400,
minHeight: 420,
maxHeight: 900,
drawShadow: true,
flippingTime: 800,
usePortrait: true,
maxShadowOpacity: 0.5,
showCover: true,
mobileScrollSupport: false,
useMouseEvents: true,
swipeDistance: 30,
showPageCorners: true,
disableFlipByClick: false,
});

flip.on('flip', (e) => {
const newPage = typeof e.data === 'number' ? e.data : 0;
setCurrentPage(newPage);
playFlipSound(getCtx());
});
flip.on('init', () => {
setPageCount(flip.getPageCount());
});

flip.loadFromImages(pages);
flipRef.current = flip;
}).catch(() => {});

return () => {
cancelled = true;
if (flipRef.current) {
try { flipRef.current.destroy(); } catch {}
flipRef.current = null;
}
try { block.remove(); } catch {}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const goNext = useCallback(() => flipRef.current?.flipNext(), []);
const goPrev = useCallback(() => flipRef.current?.flipPrev(), []);

useEffect(() => {
const onKey = (e: KeyboardEvent) => {
@@ -493,43 +530,8 @@ function FlipBook({ pages, onClose }: { pages: string[]; onClose: () => void })
return () => window.removeEventListener('keydown', onKey);
}, [onClose, goNext, goPrev]);

const onPointerDown = (e: React.PointerEvent) => { dragStartX.current = e.clientX; };
const onPointerUp = (e: React.PointerEvent) => {
if (dragStartX.current === null) return;
const dx = e.clientX - dragStartX.current;
if (Math.abs(dx) > 50) (dx < 0 ? goNext() : goPrev());
dragStartX.current = null;
};
const onPointerCancel = () => { dragStartX.current = null; };

// Pages visible underneath the flipping overlay
const visibleLeft = flipping === 'backward' ? getPage(prevLeftIdx) : getPage(leftIdx);
const visibleRight = flipping === 'forward' ? getPage(nextRightIdx) : getPage(rightIdx);

const currentPageNum = Math.min(leftIdx + 1, pages.length);
const lastPageNum = Math.min(rightIdx + 1, pages.length);
const indicatorLabel = currentPageNum === lastPageNum
? `${currentPageNum} / ${pages.length}`
: `${currentPageNum}-${lastPageNum} / ${pages.length}`;

const pageBoxClass = 'absolute inset-0 bg-white overflow-hidden';
const pageImgClass = 'w-full h-full object-contain';

return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/95 select-none"
onPointerDown={onPointerDown}
onPointerUp={onPointerUp}
onPointerCancel={onPointerCancel}
style={{ perspective: '2500px', touchAction: 'pan-y' }}
>
<style dangerouslySetInnerHTML={{ __html: `
@keyframes flipBookForward { from { transform: rotateY(0deg); } to { transform: rotateY(-180deg); } }
@keyframes flipBookBackward { from { transform: rotateY(0deg); } to { transform: rotateY(180deg); } }
@keyframes flipBookShadowIn { 0% { opacity: 0; } 100% { opacity: 1; } }
@keyframes flipBookShadowOut { 0% { opacity: 1; } 100% { opacity: 0; } }
`}} />

<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/95 select-none">
<button
onClick={onClose}
className="absolute top-4 right-4 bg-white/10 hover:bg-white/25 text-white w-11 h-11 flex items-center justify-center rounded-full text-xl z-30 transition-colors"
@@ -538,122 +540,26 @@ function FlipBook({ pages, onClose }: { pages: string[]; onClose: () => void })
>✕</button>

<div
className="relative shadow-2xl"
style={{
width: 'min(95vw, 1400px)',
height: 'min(85vh, 900px)',
transformStyle: 'preserve-3d',
}}
>
{/* Static left page */}
{visibleLeft ? (
<div className="absolute left-0 top-0 w-1/2 h-full bg-white overflow-hidden">
<img src={visibleLeft} className={pageImgClass} alt="" draggable={false} />
</div>
) : (
<div
className="absolute left-0 top-0 w-1/2 h-full overflow-hidden"
style={{
background: 'linear-gradient(135deg, #4a3826 0%, #2e2114 55%, #1a1108 100%)',
boxShadow: 'inset 0 0 80px rgba(0,0,0,0.55)',
}}
aria-hidden
/>
)}
{/* Static right page */}
{visibleRight ? (
<div className="absolute right-0 top-0 w-1/2 h-full bg-white overflow-hidden">
<img src={visibleRight} className={pageImgClass} alt="" draggable={false} />
</div>
) : (
<div
className="absolute right-0 top-0 w-1/2 h-full overflow-hidden"
style={{
background: 'linear-gradient(225deg, #4a3826 0%, #2e2114 55%, #1a1108 100%)',
boxShadow: 'inset 0 0 80px rgba(0,0,0,0.55)',
}}
aria-hidden
/>
)}
{/* Spine */}
<div className="absolute left-1/2 top-0 -translate-x-1/2 w-2 h-full bg-gradient-to-r from-black/40 via-black/20 to-black/40 z-10 pointer-events-none" />

{/* Flipping overlay — forward (right page rotates left) */}
{flipping === 'forward' && (
<div
className="absolute right-0 top-0 w-1/2 h-full"
style={{
transformOrigin: 'left center',
transformStyle: 'preserve-3d',
animation: 'flipBookForward 750ms cubic-bezier(0.4, 0.0, 0.35, 1) forwards',
filter: 'drop-shadow(0 10px 18px rgba(0,0,0,0.55))',
zIndex: 20,
}}
>
<div className={pageBoxClass} style={{ backfaceVisibility: 'hidden' }}>
{getPage(rightIdx) && <img src={getPage(rightIdx)} className={pageImgClass} alt="" draggable={false} />}
<div className="absolute inset-0 pointer-events-none" style={{
background: 'linear-gradient(90deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 65%)',
animation: 'flipBookShadowIn 750ms ease-out forwards',
}} />
</div>
<div className={pageBoxClass} style={{ backfaceVisibility: 'hidden', transform: 'rotateY(180deg)' }}>
{getPage(nextLeftIdx) && <img src={getPage(nextLeftIdx)} className={pageImgClass} alt="" draggable={false} />}
<div className="absolute inset-0 pointer-events-none" style={{
background: 'linear-gradient(270deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 65%)',
animation: 'flipBookShadowOut 750ms ease-in forwards',
}} />
</div>
</div>
)}

{/* Flipping overlay — backward (left page rotates right) */}
{flipping === 'backward' && (
<div
className="absolute left-0 top-0 w-1/2 h-full"
style={{
transformOrigin: 'right center',
transformStyle: 'preserve-3d',
animation: 'flipBookBackward 750ms cubic-bezier(0.4, 0.0, 0.35, 1) forwards',
filter: 'drop-shadow(0 10px 18px rgba(0,0,0,0.55))',
zIndex: 20,
}}
>
<div className={pageBoxClass} style={{ backfaceVisibility: 'hidden' }}>
{getPage(leftIdx) && <img src={getPage(leftIdx)} className={pageImgClass} alt="" draggable={false} />}
<div className="absolute inset-0 pointer-events-none" style={{
background: 'linear-gradient(270deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 65%)',
animation: 'flipBookShadowIn 750ms ease-out forwards',
}} />
</div>
<div className={pageBoxClass} style={{ backfaceVisibility: 'hidden', transform: 'rotateY(180deg)' }}>
{getPage(prevRightIdx) && <img src={getPage(prevRightIdx)} className={pageImgClass} alt="" draggable={false} />}
<div className="absolute inset-0 pointer-events-none" style={{
background: 'linear-gradient(90deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 65%)',
animation: 'flipBookShadowOut 750ms ease-in forwards',
}} />
</div>
</div>
)}
</div>
ref={containerRef}
style={{ width: 'min(95vw, 1400px)', height: 'min(85vh, 900px)' }}
className="shadow-2xl"
/>

<button
onClick={goPrev}
disabled={spread === 0 || flipping !== null}
className="absolute left-4 top-1/2 -translate-y-1/2 bg-white/10 hover:bg-white/25 disabled:opacity-25 disabled:cursor-not-allowed text-white w-14 h-14 flex items-center justify-center rounded-full text-3xl z-30 transition-colors"
className="absolute left-4 top-1/2 -translate-y-1/2 bg-white/10 hover:bg-white/25 text-white w-14 h-14 flex items-center justify-center rounded-full text-3xl z-30 transition-colors"
title="Pagina precedente"
aria-label="Pagina precedente"
>‹</button>
<button
onClick={goNext}
disabled={spread >= totalSpreads - 1 || flipping !== null}
className="absolute right-4 top-1/2 -translate-y-1/2 bg-white/10 hover:bg-white/25 disabled:opacity-25 disabled:cursor-not-allowed text-white w-14 h-14 flex items-center justify-center rounded-full text-3xl z-30 transition-colors"
className="absolute right-4 top-1/2 -translate-y-1/2 bg-white/10 hover:bg-white/25 text-white w-14 h-14 flex items-center justify-center rounded-full text-3xl z-30 transition-colors"
title="Pagina successiva"
aria-label="Pagina successiva"
>›</button>

<div className="absolute bottom-5 left-1/2 -translate-x-1/2 bg-white/15 text-white px-4 py-1.5 rounded-full text-sm font-medium z-30">
{pages.length === 0 ? 'Nessuna pagina' : indicatorLabel}
{pageCount === 0 ? 'Nessuna pagina' : `${currentPage + 1} / ${pageCount}`}
</div>
</div>
);


+ 281
- 11
package-lock.json View File

@@ -7,8 +7,12 @@
"": {
"name": "captive-portal-cms",
"version": "0.1.0",
"hasInstallScript": true,
"dependencies": {
"geist": "^1.4.2",
"next": "16.2.4",
"page-flip": "^2.0.7",
"pdfjs-dist": "^4.7.76",
"react": "19.2.4",
"react-dom": "19.2.4"
},
@@ -67,7 +71,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1022,6 +1025,256 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@napi-rs/canvas": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz",
"integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==",
"license": "MIT",
"optional": true,
"workspaces": [
"e2e/*"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
},
"optionalDependencies": {
"@napi-rs/canvas-android-arm64": "0.1.100",
"@napi-rs/canvas-darwin-arm64": "0.1.100",
"@napi-rs/canvas-darwin-x64": "0.1.100",
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100",
"@napi-rs/canvas-linux-arm64-gnu": "0.1.100",
"@napi-rs/canvas-linux-arm64-musl": "0.1.100",
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.100",
"@napi-rs/canvas-linux-x64-gnu": "0.1.100",
"@napi-rs/canvas-linux-x64-musl": "0.1.100",
"@napi-rs/canvas-win32-arm64-msvc": "0.1.100",
"@napi-rs/canvas-win32-x64-msvc": "0.1.100"
}
},
"node_modules/@napi-rs/canvas-android-arm64": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz",
"integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-darwin-arm64": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz",
"integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-darwin-x64": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz",
"integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz",
"integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz",
"integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz",
"integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz",
"integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz",
"integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-linux-x64-musl": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz",
"integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-win32-arm64-msvc": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz",
"integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
"version": "0.1.100",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz",
"integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
@@ -1562,7 +1815,6 @@
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1622,7 +1874,6 @@
"integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.58.2",
"@typescript-eslint/types": "8.58.2",
@@ -2148,7 +2399,6 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2492,7 +2742,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782",
@@ -3060,7 +3309,6 @@
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -3643,6 +3891,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/geist": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/geist/-/geist-1.7.0.tgz",
"integrity": "sha512-ZaoiZwkSf0DwwB1ncdLKp+ggAldqxl5L1+SXaNIBGkPAqcu+xjVJLxlf3/S8vLt9UHx1xu5fz3lbzKCj5iOVdQ==",
"license": "SIL OPEN FONT LICENSE",
"peerDependencies": {
"next": ">=13.2.0"
}
},
"node_modules/generator-function": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
@@ -5263,6 +5520,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/page-flip": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/page-flip/-/page-flip-2.0.7.tgz",
"integrity": "sha512-96lQFUUz7r/LZzEUZJ3yBIMEKU9+m8HMFDzTvTdD6P7Ag/wXINjp9n0W7b4wanwnDbQETo4uNUoL3zMqpFxwGA==",
"license": "MIT"
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -5303,6 +5566,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/pdfjs-dist": {
"version": "4.10.38",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz",
"integrity": "sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=20"
},
"optionalDependencies": {
"@napi-rs/canvas": "^0.1.65"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -5419,7 +5694,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5429,7 +5703,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -6121,7 +6394,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -6284,7 +6556,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -6560,7 +6831,6 @@
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}


+ 1
- 0
package.json View File

@@ -12,6 +12,7 @@
"dependencies": {
"geist": "^1.4.2",
"next": "16.2.4",
"page-flip": "^2.0.7",
"pdfjs-dist": "^4.7.76",
"react": "19.2.4",
"react-dom": "19.2.4"


+ 44
- 0
types/page-flip.d.ts View File

@@ -0,0 +1,44 @@
declare module 'page-flip' {
export interface FlipEvent {
data: unknown;
object: PageFlip;
}

export interface PageFlipSettings {
startPage?: number;
size?: 'fixed' | 'stretch';
width?: number;
height?: number;
minWidth?: number;
maxWidth?: number;
minHeight?: number;
maxHeight?: number;
drawShadow?: boolean;
flippingTime?: number;
usePortrait?: boolean;
startZIndex?: number;
autoSize?: boolean;
maxShadowOpacity?: number;
showCover?: boolean;
mobileScrollSupport?: boolean;
clickEventForward?: boolean;
useMouseEvents?: boolean;
swipeDistance?: number;
showPageCorners?: boolean;
disableFlipByClick?: boolean;
}

export class PageFlip {
constructor(element: HTMLElement, settings: PageFlipSettings);
loadFromImages(urls: string[]): void;
loadFromHtml(elements: HTMLElement[] | NodeListOf<HTMLElement>): void;
flipNext(): void;
flipPrev(): void;
destroy(): void;
update(): void;
getCurrentPageIndex(): number;
getPageCount(): number;
on(event: 'flip' | 'init' | 'update' | 'changeOrientation' | 'changeState', cb: (e: FlipEvent) => void): void;
off(event: string, cb: (e: FlipEvent) => void): void;
}
}

Loading…
Cancel
Save