| @@ -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> | |||
| ); | |||
| @@ -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" | |||
| } | |||
| @@ -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" | |||
| @@ -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; | |||
| } | |||
| } | |||