| @@ -445,10 +445,11 @@ function playFlipSound(ctx: AudioContext | null) { | |||||
| } | } | ||||
| function FlipBook({ pages, onClose }: { pages: string[]; onClose: () => void }) { | 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 audioRef = useRef<AudioContext | null>(null); | ||||
| const dragStartX = useRef<number | null>(null); | |||||
| const [currentPage, setCurrentPage] = useState(0); | |||||
| const [pageCount, setPageCount] = useState(pages.length); | |||||
| const getCtx = () => { | const getCtx = () => { | ||||
| if (!audioRef.current) { | if (!audioRef.current) { | ||||
| @@ -459,29 +460,65 @@ function FlipBook({ pages, onClose }: { pages: string[]; onClose: () => void }) | |||||
| return audioRef.current; | 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(() => { | useEffect(() => { | ||||
| const onKey = (e: KeyboardEvent) => { | const onKey = (e: KeyboardEvent) => { | ||||
| @@ -493,43 +530,8 @@ function FlipBook({ pages, onClose }: { pages: string[]; onClose: () => void }) | |||||
| return () => window.removeEventListener('keydown', onKey); | return () => window.removeEventListener('keydown', onKey); | ||||
| }, [onClose, goNext, goPrev]); | }, [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 ( | 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 | <button | ||||
| onClick={onClose} | 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" | 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> | >✕</button> | ||||
| <div | <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 | <button | ||||
| onClick={goPrev} | 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" | title="Pagina precedente" | ||||
| aria-label="Pagina precedente" | aria-label="Pagina precedente" | ||||
| >‹</button> | >‹</button> | ||||
| <button | <button | ||||
| onClick={goNext} | 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" | title="Pagina successiva" | ||||
| aria-label="Pagina successiva" | aria-label="Pagina successiva" | ||||
| >›</button> | >›</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"> | <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> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -7,8 +7,12 @@ | |||||
| "": { | "": { | ||||
| "name": "captive-portal-cms", | "name": "captive-portal-cms", | ||||
| "version": "0.1.0", | "version": "0.1.0", | ||||
| "hasInstallScript": true, | |||||
| "dependencies": { | "dependencies": { | ||||
| "geist": "^1.4.2", | |||||
| "next": "16.2.4", | "next": "16.2.4", | ||||
| "page-flip": "^2.0.7", | |||||
| "pdfjs-dist": "^4.7.76", | |||||
| "react": "19.2.4", | "react": "19.2.4", | ||||
| "react-dom": "19.2.4" | "react-dom": "19.2.4" | ||||
| }, | }, | ||||
| @@ -67,7 +71,6 @@ | |||||
| "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", | "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "dependencies": { | "dependencies": { | ||||
| "@babel/code-frame": "^7.29.0", | "@babel/code-frame": "^7.29.0", | ||||
| "@babel/generator": "^7.29.0", | "@babel/generator": "^7.29.0", | ||||
| @@ -1022,6 +1025,256 @@ | |||||
| "@jridgewell/sourcemap-codec": "^1.4.14" | "@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": { | "node_modules/@napi-rs/wasm-runtime": { | ||||
| "version": "0.2.12", | "version": "0.2.12", | ||||
| "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", | "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==", | "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "dependencies": { | "dependencies": { | ||||
| "csstype": "^3.2.2" | "csstype": "^3.2.2" | ||||
| } | } | ||||
| @@ -1622,7 +1874,6 @@ | |||||
| "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", | "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "dependencies": { | "dependencies": { | ||||
| "@typescript-eslint/scope-manager": "8.58.2", | "@typescript-eslint/scope-manager": "8.58.2", | ||||
| "@typescript-eslint/types": "8.58.2", | "@typescript-eslint/types": "8.58.2", | ||||
| @@ -2148,7 +2399,6 @@ | |||||
| "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", | "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "bin": { | "bin": { | ||||
| "acorn": "bin/acorn" | "acorn": "bin/acorn" | ||||
| }, | }, | ||||
| @@ -2492,7 +2742,6 @@ | |||||
| } | } | ||||
| ], | ], | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "dependencies": { | "dependencies": { | ||||
| "baseline-browser-mapping": "^2.10.12", | "baseline-browser-mapping": "^2.10.12", | ||||
| "caniuse-lite": "^1.0.30001782", | "caniuse-lite": "^1.0.30001782", | ||||
| @@ -3060,7 +3309,6 @@ | |||||
| "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", | "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "dependencies": { | "dependencies": { | ||||
| "@eslint-community/eslint-utils": "^4.8.0", | "@eslint-community/eslint-utils": "^4.8.0", | ||||
| "@eslint-community/regexpp": "^4.12.1", | "@eslint-community/regexpp": "^4.12.1", | ||||
| @@ -3643,6 +3891,15 @@ | |||||
| "url": "https://github.com/sponsors/ljharb" | "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": { | "node_modules/generator-function": { | ||||
| "version": "2.0.1", | "version": "2.0.1", | ||||
| "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", | "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", | ||||
| @@ -5263,6 +5520,12 @@ | |||||
| "url": "https://github.com/sponsors/sindresorhus" | "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": { | "node_modules/parent-module": { | ||||
| "version": "1.0.1", | "version": "1.0.1", | ||||
| "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", | ||||
| @@ -5303,6 +5566,18 @@ | |||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT" | "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": { | "node_modules/picocolors": { | ||||
| "version": "1.1.1", | "version": "1.1.1", | ||||
| "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", | "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", | "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", | ||||
| "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", | "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "engines": { | "engines": { | ||||
| "node": ">=0.10.0" | "node": ">=0.10.0" | ||||
| } | } | ||||
| @@ -5429,7 +5703,6 @@ | |||||
| "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", | ||||
| "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", | "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "dependencies": { | "dependencies": { | ||||
| "scheduler": "^0.27.0" | "scheduler": "^0.27.0" | ||||
| }, | }, | ||||
| @@ -6121,7 +6394,6 @@ | |||||
| "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", | "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "engines": { | "engines": { | ||||
| "node": ">=12" | "node": ">=12" | ||||
| }, | }, | ||||
| @@ -6284,7 +6556,6 @@ | |||||
| "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", | "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "Apache-2.0", | "license": "Apache-2.0", | ||||
| "peer": true, | |||||
| "bin": { | "bin": { | ||||
| "tsc": "bin/tsc", | "tsc": "bin/tsc", | ||||
| "tsserver": "bin/tsserver" | "tsserver": "bin/tsserver" | ||||
| @@ -6560,7 +6831,6 @@ | |||||
| "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", | "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", | ||||
| "dev": true, | "dev": true, | ||||
| "license": "MIT", | "license": "MIT", | ||||
| "peer": true, | |||||
| "funding": { | "funding": { | ||||
| "url": "https://github.com/sponsors/colinhacks" | "url": "https://github.com/sponsors/colinhacks" | ||||
| } | } | ||||
| @@ -12,6 +12,7 @@ | |||||
| "dependencies": { | "dependencies": { | ||||
| "geist": "^1.4.2", | "geist": "^1.4.2", | ||||
| "next": "16.2.4", | "next": "16.2.4", | ||||
| "page-flip": "^2.0.7", | |||||
| "pdfjs-dist": "^4.7.76", | "pdfjs-dist": "^4.7.76", | ||||
| "react": "19.2.4", | "react": "19.2.4", | ||||
| "react-dom": "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; | |||||
| } | |||||
| } | |||||