@embedpdf/plugin-thumbnail 1.4.1 → 2.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +316 -84
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/actions.d.ts +43 -0
  6. package/dist/lib/index.d.ts +5 -2
  7. package/dist/lib/reducer.d.ts +6 -0
  8. package/dist/lib/thumbnail-plugin.d.ts +19 -17
  9. package/dist/lib/types.d.ts +38 -2
  10. package/dist/preact/index.cjs +1 -1
  11. package/dist/preact/index.cjs.map +1 -1
  12. package/dist/preact/index.js +39 -18
  13. package/dist/preact/index.js.map +1 -1
  14. package/dist/react/index.cjs +1 -1
  15. package/dist/react/index.cjs.map +1 -1
  16. package/dist/react/index.js +39 -18
  17. package/dist/react/index.js.map +1 -1
  18. package/dist/shared/components/thumbnail-img.d.ts +5 -1
  19. package/dist/shared/components/thumbnails-pane.d.ts +6 -7
  20. package/dist/shared-preact/components/thumbnail-img.d.ts +5 -1
  21. package/dist/shared-preact/components/thumbnails-pane.d.ts +6 -7
  22. package/dist/shared-react/components/thumbnail-img.d.ts +5 -1
  23. package/dist/shared-react/components/thumbnails-pane.d.ts +6 -7
  24. package/dist/svelte/components/ThumbImg.svelte.d.ts +4 -0
  25. package/dist/svelte/components/ThumbnailsPane.svelte.d.ts +4 -0
  26. package/dist/svelte/index.cjs +1 -1
  27. package/dist/svelte/index.cjs.map +1 -1
  28. package/dist/svelte/index.js +44 -25
  29. package/dist/svelte/index.js.map +1 -1
  30. package/dist/vue/components/thumbnail-img.vue.d.ts +8 -3
  31. package/dist/vue/components/thumbnails-pane.vue.d.ts +9 -2
  32. package/dist/vue/index.cjs +1 -1
  33. package/dist/vue/index.cjs.map +1 -1
  34. package/dist/vue/index.js +135 -79
  35. package/dist/vue/index.js.map +1 -1
  36. package/package.json +8 -8
@@ -1,7 +1,10 @@
1
1
  import { PluginPackage } from '@embedpdf/core';
2
- import { ThumbnailPluginConfig } from './types';
2
+ import { ThumbnailPluginConfig, ThumbnailState } from './types';
3
3
  import { ThumbnailPlugin } from './thumbnail-plugin';
4
- export declare const ThumbnailPluginPackage: PluginPackage<ThumbnailPlugin, ThumbnailPluginConfig>;
4
+ import { ThumbnailAction } from './actions';
5
+ export declare const ThumbnailPluginPackage: PluginPackage<ThumbnailPlugin, ThumbnailPluginConfig, ThumbnailState, ThumbnailAction>;
5
6
  export * from './thumbnail-plugin';
6
7
  export * from './types';
7
8
  export * from './manifest';
9
+ export * from './actions';
10
+ export * from './reducer';
@@ -0,0 +1,6 @@
1
+ import { Reducer } from '@embedpdf/core';
2
+ import { ThumbnailAction } from './actions';
3
+ import { ThumbnailState, ThumbnailDocumentState } from './types';
4
+ export declare const initialDocumentState: ThumbnailDocumentState;
5
+ export declare const initialState: ThumbnailState;
6
+ export declare const thumbnailReducer: Reducer<ThumbnailState, ThumbnailAction>;
@@ -1,27 +1,29 @@
1
- import { BasePlugin, PluginRegistry, Unsubscribe } from '@embedpdf/core';
2
- import { ScrollToOptions, ThumbnailPluginConfig, WindowState, ThumbnailCapability } from './types';
3
- export declare class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {
1
+ import { BasePlugin, PluginRegistry } from '@embedpdf/core';
2
+ import { ThumbnailPluginConfig, ThumbnailCapability, ThumbnailState } from './types';
3
+ import { ThumbnailAction } from './actions';
4
+ export declare class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability, ThumbnailState, ThumbnailAction> {
4
5
  cfg: ThumbnailPluginConfig;
5
6
  static readonly id: "thumbnail";
6
7
  private renderCapability;
7
8
  private scrollCapability;
8
- private thumbs;
9
- private window;
10
- private viewportH;
11
- private scrollY;
12
- private readonly emitWindow;
13
- private readonly refreshPages$;
14
- private readonly taskCache;
15
- private canAutoScroll;
9
+ private readonly taskCaches;
10
+ private readonly canAutoScroll;
11
+ private readonly window$;
16
12
  private readonly scrollTo$;
13
+ private readonly refreshPages$;
17
14
  constructor(id: string, registry: PluginRegistry, cfg: ThumbnailPluginConfig);
18
- initialize(): Promise<void>;
19
- onRefreshPages(fn: (pages: number[]) => void): Unsubscribe;
20
- onWindow(cb: (w: WindowState) => void): Unsubscribe;
21
- onScrollTo(cb: (o: ScrollToOptions) => void): Unsubscribe;
22
- private setWindowState;
15
+ protected onDocumentLoadingStarted(documentId: string): void;
16
+ protected onDocumentLoaded(documentId: string): void;
17
+ protected onDocumentClosed(documentId: string): void;
18
+ protected onRotationChanged(documentId: string): void;
23
19
  protected buildCapability(): ThumbnailCapability;
24
- updateWindow(scrollY: number, viewportH: number): void;
20
+ private createThumbnailScope;
21
+ private getDocumentState;
22
+ private calculateWindowState;
23
+ updateWindow(scrollY: number, viewportH: number, documentId?: string): void;
24
+ private getWindow;
25
25
  private scrollToThumb;
26
26
  private renderThumb;
27
+ initialize(): Promise<void>;
28
+ destroy(): Promise<void>;
27
29
  }
@@ -1,4 +1,4 @@
1
- import { BasePluginConfig } from '@embedpdf/core';
1
+ import { BasePluginConfig, EventHook } from '@embedpdf/core';
2
2
  import { PdfErrorReason, Task } from '@embedpdf/models';
3
3
  import { ScrollBehavior } from '@embedpdf/plugin-scroll';
4
4
  export interface ThumbnailPluginConfig extends BasePluginConfig {
@@ -42,8 +42,44 @@ export interface WindowState {
42
42
  items: ThumbMeta[];
43
43
  totalHeight: number;
44
44
  }
45
+ export interface ThumbnailDocumentState {
46
+ thumbs: ThumbMeta[];
47
+ window: WindowState | null;
48
+ viewportH: number;
49
+ scrollY: number;
50
+ }
51
+ export interface ThumbnailState {
52
+ documents: Record<string, ThumbnailDocumentState>;
53
+ activeDocumentId: string | null;
54
+ }
55
+ export interface WindowChangeEvent {
56
+ documentId: string;
57
+ window: WindowState | null;
58
+ }
59
+ export interface ScrollToEvent {
60
+ documentId: string;
61
+ options: ScrollToOptions;
62
+ }
63
+ export interface RefreshPagesEvent {
64
+ documentId: string;
65
+ pages: number[];
66
+ }
67
+ export interface ThumbnailScope {
68
+ scrollToThumb(pageIdx: number): void;
69
+ renderThumb(pageIdx: number, dpr: number): Task<Blob, PdfErrorReason>;
70
+ updateWindow(scrollY: number, viewportH: number): void;
71
+ getWindow(): WindowState | null;
72
+ onWindow: EventHook<WindowState | null>;
73
+ onScrollTo: EventHook<ScrollToOptions>;
74
+ onRefreshPages: EventHook<number[]>;
75
+ }
45
76
  export interface ThumbnailCapability {
46
77
  scrollToThumb(pageIdx: number): void;
47
- /** lazily render one thumb */
48
78
  renderThumb(pageIdx: number, dpr: number): Task<Blob, PdfErrorReason>;
79
+ updateWindow(scrollY: number, viewportH: number): void;
80
+ getWindow(): WindowState | null;
81
+ forDocument(documentId: string): ThumbnailScope;
82
+ onWindow: EventHook<WindowChangeEvent>;
83
+ onScrollTo: EventHook<ScrollToEvent>;
84
+ onRefreshPages: EventHook<RefreshPagesEvent>;
49
85
  }
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/preact"),t=require("@embedpdf/plugin-thumbnail"),r=require("preact/jsx-runtime");require("preact");const n=require("preact/hooks"),o=require("@embedpdf/models"),l=()=>e.usePlugin(t.ThumbnailPlugin.id),i=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...u}){const{provides:s}=i(),{plugin:c}=l(),[d,a]=n.useState(),p=n.useRef(null),[f,g]=n.useState(0);return n.useEffect((()=>{if(c)return c.onRefreshPages((t=>{t.includes(e.pageIndex)&&g((e=>e+1))}))}),[c]),n.useEffect((()=>{const t=null==s?void 0:s.renderThumb(e.pageIndex,window.devicePixelRatio);return null==t||t.wait((e=>{const t=URL.createObjectURL(e);p.current=t,a(t)}),o.ignore),()=>{p.current?(URL.revokeObjectURL(p.current),p.current=null):null==t||t.abort({code:o.PdfErrorCode.Cancelled,message:"canceled render task"})}}),[s,e.pageIndex,f]),d?r.jsx("img",{src:d,onLoad:()=>{p.current&&(URL.revokeObjectURL(p.current),p.current=null)},style:t,...u}):null},exports.ThumbnailsPane=function({style:e,scrollOptions:t,selectedPage:o,...i}){const{plugin:u}=l(),s=n.useRef(null),[c,d]=n.useState(null);n.useEffect((()=>null==u?void 0:u.onWindow(d)),[u]),n.useEffect((()=>{const e=s.current;if(!e)return;const t=()=>null==u?void 0:u.updateWindow(e.scrollTop,e.clientHeight);return e.addEventListener("scroll",t),()=>e.removeEventListener("scroll",t)}),[u]),n.useEffect((()=>{const e=s.current;if(!e||!u)return;const t=new ResizeObserver((()=>{u.updateWindow(e.scrollTop,e.clientHeight)}));return t.observe(e),()=>t.disconnect()}),[u]),n.useEffect((()=>{const e=s.current;e&&u&&u.updateWindow(e.scrollTop,e.clientHeight)}),[c,u]),n.useEffect((()=>{const e=s.current;if(e&&u&&c)return u.onScrollTo((({top:t,behavior:r})=>{e.scrollTo({top:t,behavior:r})}))}),[u,!!c]);const a=(null==u?void 0:u.cfg.paddingY)??0;return r.jsx("div",{ref:s,style:{overflowY:"auto",position:"relative",paddingTop:a,paddingBottom:a,height:"100%",...e},...i,children:r.jsx("div",{style:{height:(null==c?void 0:c.totalHeight)??0,position:"relative"},children:null==c?void 0:c.items.map((e=>i.children(e)))})})},exports.useThumbnailCapability=i,exports.useThumbnailPlugin=l,Object.keys(t).forEach((e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})}));
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/preact"),t=require("@embedpdf/plugin-thumbnail"),r=require("preact/jsx-runtime");require("preact");const n=require("preact/hooks"),o=require("@embedpdf/models"),u=()=>e.usePlugin(t.ThumbnailPlugin.id),i=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({documentId:e,meta:t,style:l,...c}){const{provides:s}=i(),{plugin:d}=u(),[a,p]=n.useState(),f=n.useRef(null),[m,g]=n.useState(0);return n.useEffect(()=>{if(!d)return;return d.provides().forDocument(e).onRefreshPages(e=>{e.includes(t.pageIndex)&&g(e=>e+1)})},[d,e,t.pageIndex]),n.useEffect(()=>{const r=null==s?void 0:s.forDocument(e),n=null==r?void 0:r.renderThumb(t.pageIndex,window.devicePixelRatio);return null==n||n.wait(e=>{const t=URL.createObjectURL(e);f.current=t,p(t)},o.ignore),()=>{f.current?(URL.revokeObjectURL(f.current),f.current=null):null==n||n.abort({code:o.PdfErrorCode.Cancelled,message:"canceled render task"})}},[s,e,t.pageIndex,m]),a?r.jsx("img",{src:a,onLoad:()=>{f.current&&(URL.revokeObjectURL(f.current),f.current=null)},style:l,...c}):null},exports.ThumbnailsPane=function({documentId:e,style:t,children:o,...i}){const{plugin:l}=u(),c=n.useRef(null),[s,d]=n.useState({window:null,docId:null}),a=s.docId===e?s.window:null;n.useEffect(()=>{if(!l)return;const t=l.provides().forDocument(e),r=t.getWindow();r&&d({window:r,docId:e});const n=t.onWindow(t=>{d({window:t,docId:e})});return()=>{n(),d({window:null,docId:null})}},[l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l)return;const r=l.provides().forDocument(e),n=()=>r.updateWindow(t.scrollTop,t.clientHeight);return t.addEventListener("scroll",n),()=>t.removeEventListener("scroll",n)},[l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l)return;const r=l.provides().forDocument(e),n=new ResizeObserver(()=>{r.updateWindow(t.scrollTop,t.clientHeight)});return n.observe(t),()=>n.disconnect()},[l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l)return;l.provides().forDocument(e).updateWindow(t.scrollTop,t.clientHeight)},[a,l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l||!a)return;return l.provides().forDocument(e).onScrollTo(({top:e,behavior:r})=>{t.scrollTo({top:e,behavior:r})})},[l,e,!!a]);const p=(null==l?void 0:l.cfg.paddingY)??0;return r.jsx("div",{ref:c,style:{overflowY:"auto",position:"relative",paddingTop:p,paddingBottom:p,height:"100%",...t},...i,children:r.jsx("div",{style:{height:(null==a?void 0:a.totalHeight)??0,position:"relative"},children:null==a?void 0:a.items.map(e=>o(e))})})},exports.useThumbnailCapability=i,exports.useThumbnailPlugin=u,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnail-img.tsx","../../src/shared/components/thumbnails-pane.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties, Fragment } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n return thumbnailPlugin.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin]);\n\n useEffect(() => {\n const task = thumbs?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { ThumbMeta, WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n /** @deprecated use scrollToThumb via capability or rely on autoScroll */\n selectedPage?: number;\n /** @deprecated behavior is now controlled by ThumbnailPluginConfig.scrollBehavior */\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n const [window, setWindow] = useState<WindowState | null>(null);\n\n // 1) subscribe once to window updates\n useEffect(() => thumbnailPlugin?.onWindow(setWindow), [thumbnailPlugin]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n const onScroll = () => thumbnailPlugin?.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin]);\n\n // 2.5) keep plugin in sync when viewport resizes (e.g., menu opens/closes)\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const resizeObserver = new ResizeObserver(() => {\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n // push initial metrics\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin]);\n\n // 4) let plugin drive scroll – only after window is set, and only once\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n return thumbnailPlugin.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, !!window]); // Note: !!window to prevent re-subscription on window updates\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => props.children(m))}\n </div>\n </div>\n );\n}\n"],"names":["useThumbnailPlugin","usePlugin","ThumbnailPlugin","id","useThumbnailCapability","useCapability","meta","style","props","provides","thumbs","plugin","thumbnailPlugin","url","setUrl","useState","urlRef","useRef","refreshTick","setRefreshTick","useEffect","onRefreshPages","pages","includes","pageIndex","tick","task","renderThumb","window","devicePixelRatio","wait","blob","objectUrl","URL","createObjectURL","current","ignore","revokeObjectURL","abort","code","PdfErrorCode","Cancelled","message","jsx","src","onLoad","scrollOptions","selectedPage","viewportRef","setWindow","onWindow","vp","onScroll","updateWindow","scrollTop","clientHeight","addEventListener","removeEventListener","resizeObserver","ResizeObserver","observe","disconnect","onScrollTo","top","behavior","scrollTo","paddingY","cfg","jsxRuntime","ref","overflowY","position","paddingTop","paddingBottom","height","children","totalHeight","items","map","m"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnail-img.tsx","../../src/shared/components/thumbnails-pane.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n /**\n * The ID of the document that this thumbnail belongs to\n */\n documentId: string;\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ documentId, meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin, documentId, meta.pageIndex]);\n\n useEffect(() => {\n const scope = thumbs?.forDocument(documentId);\n const task = scope?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, documentId, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n /**\n * The ID of the document that this thumbnail pane displays\n */\n documentId: string;\n style?: CSSProperties;\n children: (m: any) => ReactNode;\n};\n\nexport function ThumbnailsPane({ documentId, style, children, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n // Store window data along with the documentId it came from\n const [windowData, setWindowData] = useState<{\n window: WindowState | null;\n docId: string | null;\n }>({ window: null, docId: null });\n\n // Only use the window if it matches the current documentId\n const window = windowData.docId === documentId ? windowData.window : null;\n\n // 1) subscribe to window updates for this document\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n\n // Get initial window state immediately on mount\n const initialWindow = scope.getWindow();\n\n if (initialWindow) {\n setWindowData({ window: initialWindow, docId: documentId });\n }\n\n // Subscribe to future updates\n const unsubscribe = scope.onWindow((newWindow) => {\n setWindowData({ window: newWindow, docId: documentId });\n });\n\n // Clear state when documentId changes or component unmounts\n return () => {\n unsubscribe();\n setWindowData({ window: null, docId: null });\n };\n }, [thumbnailPlugin, documentId]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const onScroll = () => scope.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin, documentId]);\n\n // 2.5) keep plugin in sync when viewport resizes\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const resizeObserver = new ResizeObserver(() => {\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin, documentId]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin, documentId]);\n\n // 4) let plugin drive scroll\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, documentId, !!window]);\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => children(m))}\n </div>\n </div>\n );\n}\n"],"names":["useThumbnailPlugin","usePlugin","ThumbnailPlugin","id","useThumbnailCapability","useCapability","documentId","meta","style","props","provides","thumbs","plugin","thumbnailPlugin","url","setUrl","useState","urlRef","useRef","refreshTick","setRefreshTick","useEffect","forDocument","onRefreshPages","pages","includes","pageIndex","tick","scope","task","renderThumb","window","devicePixelRatio","wait","blob","objectUrl","URL","createObjectURL","current","ignore","revokeObjectURL","abort","code","PdfErrorCode","Cancelled","message","jsx","src","onLoad","children","viewportRef","windowData","setWindowData","docId","initialWindow","getWindow","unsubscribe","onWindow","newWindow","vp","onScroll","updateWindow","scrollTop","clientHeight","addEventListener","removeEventListener","resizeObserver","ResizeObserver","observe","disconnect","onScrollTo","top","behavior","scrollTo","paddingY","cfg","ref","overflowY","position","paddingTop","paddingBottom","height","totalHeight","items","map","m"],"mappings":"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"}
@@ -7,39 +7,58 @@ import { useRef, useState, useEffect } from "preact/hooks";
7
7
  import { ignore, PdfErrorCode } from "@embedpdf/models";
8
8
  const useThumbnailPlugin = () => usePlugin(ThumbnailPlugin.id);
9
9
  const useThumbnailCapability = () => useCapability(ThumbnailPlugin.id);
10
- function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }) {
10
+ function ThumbnailsPane({ documentId, style, children, ...props }) {
11
11
  const { plugin: thumbnailPlugin } = useThumbnailPlugin();
12
12
  const viewportRef = useRef(null);
13
- const [window2, setWindow] = useState(null);
14
- useEffect(() => thumbnailPlugin == null ? void 0 : thumbnailPlugin.onWindow(setWindow), [thumbnailPlugin]);
13
+ const [windowData, setWindowData] = useState({ window: null, docId: null });
14
+ const window2 = windowData.docId === documentId ? windowData.window : null;
15
+ useEffect(() => {
16
+ if (!thumbnailPlugin) return;
17
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
18
+ const initialWindow = scope.getWindow();
19
+ if (initialWindow) {
20
+ setWindowData({ window: initialWindow, docId: documentId });
21
+ }
22
+ const unsubscribe = scope.onWindow((newWindow) => {
23
+ setWindowData({ window: newWindow, docId: documentId });
24
+ });
25
+ return () => {
26
+ unsubscribe();
27
+ setWindowData({ window: null, docId: null });
28
+ };
29
+ }, [thumbnailPlugin, documentId]);
15
30
  useEffect(() => {
16
31
  const vp = viewportRef.current;
17
- if (!vp) return;
18
- const onScroll = () => thumbnailPlugin == null ? void 0 : thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
32
+ if (!vp || !thumbnailPlugin) return;
33
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
34
+ const onScroll = () => scope.updateWindow(vp.scrollTop, vp.clientHeight);
19
35
  vp.addEventListener("scroll", onScroll);
20
36
  return () => vp.removeEventListener("scroll", onScroll);
21
- }, [thumbnailPlugin]);
37
+ }, [thumbnailPlugin, documentId]);
22
38
  useEffect(() => {
23
39
  const vp = viewportRef.current;
24
40
  if (!vp || !thumbnailPlugin) return;
41
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
25
42
  const resizeObserver = new ResizeObserver(() => {
26
- thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
43
+ scope.updateWindow(vp.scrollTop, vp.clientHeight);
27
44
  });
28
45
  resizeObserver.observe(vp);
29
46
  return () => resizeObserver.disconnect();
30
- }, [thumbnailPlugin]);
47
+ }, [thumbnailPlugin, documentId]);
31
48
  useEffect(() => {
32
49
  const vp = viewportRef.current;
33
50
  if (!vp || !thumbnailPlugin) return;
34
- thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
35
- }, [window2, thumbnailPlugin]);
51
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
52
+ scope.updateWindow(vp.scrollTop, vp.clientHeight);
53
+ }, [window2, thumbnailPlugin, documentId]);
36
54
  useEffect(() => {
37
55
  const vp = viewportRef.current;
38
56
  if (!vp || !thumbnailPlugin || !window2) return;
39
- return thumbnailPlugin.onScrollTo(({ top, behavior }) => {
57
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
58
+ return scope.onScrollTo(({ top, behavior }) => {
40
59
  vp.scrollTo({ top, behavior });
41
60
  });
42
- }, [thumbnailPlugin, !!window2]);
61
+ }, [thumbnailPlugin, documentId, !!window2]);
43
62
  const paddingY = (thumbnailPlugin == null ? void 0 : thumbnailPlugin.cfg.paddingY) ?? 0;
44
63
  return /* @__PURE__ */ jsx(
45
64
  "div",
@@ -54,11 +73,11 @@ function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }) {
54
73
  ...style
55
74
  },
56
75
  ...props,
57
- children: /* @__PURE__ */ jsx("div", { style: { height: (window2 == null ? void 0 : window2.totalHeight) ?? 0, position: "relative" }, children: window2 == null ? void 0 : window2.items.map((m) => props.children(m)) })
76
+ children: /* @__PURE__ */ jsx("div", { style: { height: (window2 == null ? void 0 : window2.totalHeight) ?? 0, position: "relative" }, children: window2 == null ? void 0 : window2.items.map((m) => children(m)) })
58
77
  }
59
78
  );
60
79
  }
61
- function ThumbImg({ meta, style, ...props }) {
80
+ function ThumbImg({ documentId, meta, style, ...props }) {
62
81
  const { provides: thumbs } = useThumbnailCapability();
63
82
  const { plugin: thumbnailPlugin } = useThumbnailPlugin();
64
83
  const [url, setUrl] = useState();
@@ -66,14 +85,16 @@ function ThumbImg({ meta, style, ...props }) {
66
85
  const [refreshTick, setRefreshTick] = useState(0);
67
86
  useEffect(() => {
68
87
  if (!thumbnailPlugin) return;
69
- return thumbnailPlugin.onRefreshPages((pages) => {
88
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
89
+ return scope.onRefreshPages((pages) => {
70
90
  if (pages.includes(meta.pageIndex)) {
71
91
  setRefreshTick((tick) => tick + 1);
72
92
  }
73
93
  });
74
- }, [thumbnailPlugin]);
94
+ }, [thumbnailPlugin, documentId, meta.pageIndex]);
75
95
  useEffect(() => {
76
- const task = thumbs == null ? void 0 : thumbs.renderThumb(meta.pageIndex, window.devicePixelRatio);
96
+ const scope = thumbs == null ? void 0 : thumbs.forDocument(documentId);
97
+ const task = scope == null ? void 0 : scope.renderThumb(meta.pageIndex, window.devicePixelRatio);
77
98
  task == null ? void 0 : task.wait((blob) => {
78
99
  const objectUrl = URL.createObjectURL(blob);
79
100
  urlRef.current = objectUrl;
@@ -90,7 +111,7 @@ function ThumbImg({ meta, style, ...props }) {
90
111
  });
91
112
  }
92
113
  };
93
- }, [thumbs, meta.pageIndex, refreshTick]);
114
+ }, [thumbs, documentId, meta.pageIndex, refreshTick]);
94
115
  const handleImageLoad = () => {
95
116
  if (urlRef.current) {
96
117
  URL.revokeObjectURL(urlRef.current);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnails-pane.tsx","../../src/shared/components/thumbnail-img.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { ThumbMeta, WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n /** @deprecated use scrollToThumb via capability or rely on autoScroll */\n selectedPage?: number;\n /** @deprecated behavior is now controlled by ThumbnailPluginConfig.scrollBehavior */\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n const [window, setWindow] = useState<WindowState | null>(null);\n\n // 1) subscribe once to window updates\n useEffect(() => thumbnailPlugin?.onWindow(setWindow), [thumbnailPlugin]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n const onScroll = () => thumbnailPlugin?.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin]);\n\n // 2.5) keep plugin in sync when viewport resizes (e.g., menu opens/closes)\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const resizeObserver = new ResizeObserver(() => {\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n // push initial metrics\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin]);\n\n // 4) let plugin drive scroll – only after window is set, and only once\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n return thumbnailPlugin.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, !!window]); // Note: !!window to prevent re-subscription on window updates\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => props.children(m))}\n </div>\n </div>\n );\n}\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties, Fragment } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n return thumbnailPlugin.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin]);\n\n useEffect(() => {\n const task = thumbs?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n"],"names":["window"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnails-pane.tsx","../../src/shared/components/thumbnail-img.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n /**\n * The ID of the document that this thumbnail pane displays\n */\n documentId: string;\n style?: CSSProperties;\n children: (m: any) => ReactNode;\n};\n\nexport function ThumbnailsPane({ documentId, style, children, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n // Store window data along with the documentId it came from\n const [windowData, setWindowData] = useState<{\n window: WindowState | null;\n docId: string | null;\n }>({ window: null, docId: null });\n\n // Only use the window if it matches the current documentId\n const window = windowData.docId === documentId ? windowData.window : null;\n\n // 1) subscribe to window updates for this document\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n\n // Get initial window state immediately on mount\n const initialWindow = scope.getWindow();\n\n if (initialWindow) {\n setWindowData({ window: initialWindow, docId: documentId });\n }\n\n // Subscribe to future updates\n const unsubscribe = scope.onWindow((newWindow) => {\n setWindowData({ window: newWindow, docId: documentId });\n });\n\n // Clear state when documentId changes or component unmounts\n return () => {\n unsubscribe();\n setWindowData({ window: null, docId: null });\n };\n }, [thumbnailPlugin, documentId]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const onScroll = () => scope.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin, documentId]);\n\n // 2.5) keep plugin in sync when viewport resizes\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const resizeObserver = new ResizeObserver(() => {\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin, documentId]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin, documentId]);\n\n // 4) let plugin drive scroll\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, documentId, !!window]);\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => children(m))}\n </div>\n </div>\n );\n}\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n /**\n * The ID of the document that this thumbnail belongs to\n */\n documentId: string;\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ documentId, meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin, documentId, meta.pageIndex]);\n\n useEffect(() => {\n const scope = thumbs?.forDocument(documentId);\n const task = scope?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, documentId, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n"],"names":["window"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/react"),t=require("@embedpdf/plugin-thumbnail"),r=require("react/jsx-runtime"),n=require("react"),o=require("@embedpdf/models"),l=()=>e.usePlugin(t.ThumbnailPlugin.id),i=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...u}){const{provides:s}=i(),{plugin:c}=l(),[d,a]=n.useState(),p=n.useRef(null),[f,g]=n.useState(0);return n.useEffect((()=>{if(c)return c.onRefreshPages((t=>{t.includes(e.pageIndex)&&g((e=>e+1))}))}),[c]),n.useEffect((()=>{const t=null==s?void 0:s.renderThumb(e.pageIndex,window.devicePixelRatio);return null==t||t.wait((e=>{const t=URL.createObjectURL(e);p.current=t,a(t)}),o.ignore),()=>{p.current?(URL.revokeObjectURL(p.current),p.current=null):null==t||t.abort({code:o.PdfErrorCode.Cancelled,message:"canceled render task"})}}),[s,e.pageIndex,f]),d?r.jsx("img",{src:d,onLoad:()=>{p.current&&(URL.revokeObjectURL(p.current),p.current=null)},style:t,...u}):null},exports.ThumbnailsPane=function({style:e,scrollOptions:t,selectedPage:o,...i}){const{plugin:u}=l(),s=n.useRef(null),[c,d]=n.useState(null);n.useEffect((()=>null==u?void 0:u.onWindow(d)),[u]),n.useEffect((()=>{const e=s.current;if(!e)return;const t=()=>null==u?void 0:u.updateWindow(e.scrollTop,e.clientHeight);return e.addEventListener("scroll",t),()=>e.removeEventListener("scroll",t)}),[u]),n.useEffect((()=>{const e=s.current;if(!e||!u)return;const t=new ResizeObserver((()=>{u.updateWindow(e.scrollTop,e.clientHeight)}));return t.observe(e),()=>t.disconnect()}),[u]),n.useEffect((()=>{const e=s.current;e&&u&&u.updateWindow(e.scrollTop,e.clientHeight)}),[c,u]),n.useEffect((()=>{const e=s.current;if(e&&u&&c)return u.onScrollTo((({top:t,behavior:r})=>{e.scrollTo({top:t,behavior:r})}))}),[u,!!c]);const a=(null==u?void 0:u.cfg.paddingY)??0;return r.jsx("div",{ref:s,style:{overflowY:"auto",position:"relative",paddingTop:a,paddingBottom:a,height:"100%",...e},...i,children:r.jsx("div",{style:{height:(null==c?void 0:c.totalHeight)??0,position:"relative"},children:null==c?void 0:c.items.map((e=>i.children(e)))})})},exports.useThumbnailCapability=i,exports.useThumbnailPlugin=l,Object.keys(t).forEach((e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})}));
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/react"),t=require("@embedpdf/plugin-thumbnail"),r=require("react/jsx-runtime"),n=require("react"),o=require("@embedpdf/models"),u=()=>e.usePlugin(t.ThumbnailPlugin.id),i=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({documentId:e,meta:t,style:l,...c}){const{provides:d}=i(),{plugin:s}=u(),[a,p]=n.useState(),f=n.useRef(null),[m,g]=n.useState(0);return n.useEffect(()=>{if(!s)return;return s.provides().forDocument(e).onRefreshPages(e=>{e.includes(t.pageIndex)&&g(e=>e+1)})},[s,e,t.pageIndex]),n.useEffect(()=>{const r=null==d?void 0:d.forDocument(e),n=null==r?void 0:r.renderThumb(t.pageIndex,window.devicePixelRatio);return null==n||n.wait(e=>{const t=URL.createObjectURL(e);f.current=t,p(t)},o.ignore),()=>{f.current?(URL.revokeObjectURL(f.current),f.current=null):null==n||n.abort({code:o.PdfErrorCode.Cancelled,message:"canceled render task"})}},[d,e,t.pageIndex,m]),a?r.jsx("img",{src:a,onLoad:()=>{f.current&&(URL.revokeObjectURL(f.current),f.current=null)},style:l,...c}):null},exports.ThumbnailsPane=function({documentId:e,style:t,children:o,...i}){const{plugin:l}=u(),c=n.useRef(null),[d,s]=n.useState({window:null,docId:null}),a=d.docId===e?d.window:null;n.useEffect(()=>{if(!l)return;const t=l.provides().forDocument(e),r=t.getWindow();r&&s({window:r,docId:e});const n=t.onWindow(t=>{s({window:t,docId:e})});return()=>{n(),s({window:null,docId:null})}},[l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l)return;const r=l.provides().forDocument(e),n=()=>r.updateWindow(t.scrollTop,t.clientHeight);return t.addEventListener("scroll",n),()=>t.removeEventListener("scroll",n)},[l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l)return;const r=l.provides().forDocument(e),n=new ResizeObserver(()=>{r.updateWindow(t.scrollTop,t.clientHeight)});return n.observe(t),()=>n.disconnect()},[l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l)return;l.provides().forDocument(e).updateWindow(t.scrollTop,t.clientHeight)},[a,l,e]),n.useEffect(()=>{const t=c.current;if(!t||!l||!a)return;return l.provides().forDocument(e).onScrollTo(({top:e,behavior:r})=>{t.scrollTo({top:e,behavior:r})})},[l,e,!!a]);const p=(null==l?void 0:l.cfg.paddingY)??0;return r.jsx("div",{ref:c,style:{overflowY:"auto",position:"relative",paddingTop:p,paddingBottom:p,height:"100%",...t},...i,children:r.jsx("div",{style:{height:(null==a?void 0:a.totalHeight)??0,position:"relative"},children:null==a?void 0:a.items.map(e=>o(e))})})},exports.useThumbnailCapability=i,exports.useThumbnailPlugin=u,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnail-img.tsx","../../src/shared/components/thumbnails-pane.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties, Fragment } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n return thumbnailPlugin.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin]);\n\n useEffect(() => {\n const task = thumbs?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { ThumbMeta, WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n /** @deprecated use scrollToThumb via capability or rely on autoScroll */\n selectedPage?: number;\n /** @deprecated behavior is now controlled by ThumbnailPluginConfig.scrollBehavior */\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n const [window, setWindow] = useState<WindowState | null>(null);\n\n // 1) subscribe once to window updates\n useEffect(() => thumbnailPlugin?.onWindow(setWindow), [thumbnailPlugin]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n const onScroll = () => thumbnailPlugin?.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin]);\n\n // 2.5) keep plugin in sync when viewport resizes (e.g., menu opens/closes)\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const resizeObserver = new ResizeObserver(() => {\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n // push initial metrics\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin]);\n\n // 4) let plugin drive scroll – only after window is set, and only once\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n return thumbnailPlugin.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, !!window]); // Note: !!window to prevent re-subscription on window updates\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => props.children(m))}\n </div>\n </div>\n );\n}\n"],"names":["useThumbnailPlugin","usePlugin","ThumbnailPlugin","id","useThumbnailCapability","useCapability","meta","style","props","provides","thumbs","plugin","thumbnailPlugin","url","setUrl","useState","urlRef","useRef","refreshTick","setRefreshTick","useEffect","onRefreshPages","pages","includes","pageIndex","tick","task","renderThumb","window","devicePixelRatio","wait","blob","objectUrl","URL","createObjectURL","current","ignore","revokeObjectURL","abort","code","PdfErrorCode","Cancelled","message","jsx","src","onLoad","scrollOptions","selectedPage","viewportRef","setWindow","onWindow","vp","onScroll","updateWindow","scrollTop","clientHeight","addEventListener","removeEventListener","resizeObserver","ResizeObserver","observe","disconnect","onScrollTo","top","behavior","scrollTo","paddingY","cfg","jsxRuntime","ref","overflowY","position","paddingTop","paddingBottom","height","children","totalHeight","items","map","m"],"mappings":"gPAGaA,EAAqB,IAAMC,YAA2BC,EAAAA,gBAAgBC,IACtEC,EAAyB,IAAMC,gBAA+BH,EAAAA,gBAAgBC,qBCMpF,UAAkBG,KAAEA,EAAAC,MAAMA,KAAUC,IACzC,MAAQC,SAAUC,GAAWN,KACrBO,OAAQC,GAAoBZ,KAC7Ba,EAAKC,GAAUC,aAChBC,EAASC,SAAsB,OAC9BC,EAAaC,GAAkBJ,EAAAA,SAAS,GAuCxC,OArCPK,EAAAA,WAAU,KACR,GAAKR,EACE,OAAAA,EAAgBS,gBAAgBC,IACjCA,EAAMC,SAASjB,EAAKkB,YACPL,GAACM,GAASA,EAAO,GAAC,GAEpC,GACA,CAACb,IAEJQ,EAAAA,WAAU,KACR,MAAMM,EAAO,MAAAhB,OAAA,EAAAA,EAAQiB,YAAYrB,EAAKkB,UAAWI,OAAOC,kBAOxD,OANM,MAAAH,GAAAA,EAAAI,MAAMC,IACJ,MAAAC,EAAYC,IAAIC,gBAAgBH,GACtCf,EAAOmB,QAAUH,EACjBlB,EAAOkB,EAAS,GACfI,UAEI,KACDpB,EAAOmB,SACLF,IAAAI,gBAAgBrB,EAAOmB,SAC3BnB,EAAOmB,QAAU,MAEjB,MAAAT,GAAAA,EAAMY,MAAM,CACVC,KAAMC,EAAaA,aAAAC,UACnBC,QAAS,wBACV,CAEL,GACC,CAAChC,EAAQJ,EAAKkB,UAAWN,IASrBL,EAAO8B,EAAAA,IAAA,MAAA,CAAIC,IAAK/B,EAAKgC,OAPJ,KAClB7B,EAAOmB,UACLF,IAAAI,gBAAgBrB,EAAOmB,SAC3BnB,EAAOmB,QAAU,KAAA,EAIgC5B,WAAkBC,IAAY,IACrF,yBC1CO,UAAwBD,MAAEA,EAAAuC,cAAOA,eAAeC,KAAiBvC,IACtE,MAAQG,OAAQC,GAAoBZ,IAC9BgD,EAAc/B,SAAuB,OAEpCW,EAAQqB,GAAalC,EAAAA,SAA6B,MAGzDK,EAAAA,WAAU,IAAM,MAAAR,OAAA,EAAAA,EAAiBsC,SAASD,IAAY,CAACrC,IAGvDQ,EAAAA,WAAU,KACR,MAAM+B,EAAKH,EAAYb,QACvB,IAAKgB,EAAI,OACT,MAAMC,EAAW,IAAM,MAAAxC,OAAA,EAAAA,EAAiByC,aAAaF,EAAGG,UAAWH,EAAGI,cAEtE,OADGJ,EAAAK,iBAAiB,SAAUJ,GACvB,IAAMD,EAAGM,oBAAoB,SAAUL,EAAQ,GACrD,CAACxC,IAGJQ,EAAAA,WAAU,KACR,MAAM+B,EAAKH,EAAYb,QACnB,IAACgB,IAAOvC,EAAiB,OAEvB,MAAA8C,EAAiB,IAAIC,gBAAe,KACxC/C,EAAgByC,aAAaF,EAAGG,UAAWH,EAAGI,aAAY,IAIrD,OAFPG,EAAeE,QAAQT,GAEhB,IAAMO,EAAeG,YAAW,GACtC,CAACjD,IAGJQ,EAAAA,WAAU,KACR,MAAM+B,EAAKH,EAAYb,QAClBgB,GAAOvC,GAGZA,EAAgByC,aAAaF,EAAGG,UAAWH,EAAGI,aAAY,GACzD,CAAC3B,EAAQhB,IAGZQ,EAAAA,WAAU,KACR,MAAM+B,EAAKH,EAAYb,QACvB,GAAKgB,GAAOvC,GAAoBgB,EAEhC,OAAOhB,EAAgBkD,YAAW,EAAGC,MAAKC,eACxCb,EAAGc,SAAS,CAAEF,MAAKC,YAAU,GAC9B,GACA,CAACpD,IAAmBgB,IAEjB,MAAAsC,GAA4B,MAAjBtD,OAAiB,EAAAA,EAAAuD,IAAID,WAAY,EAGhD,OAAAE,EAAAzB,IAAC,MAAA,CACC0B,IAAKrB,EACLzC,MAAO,CACL+D,UAAW,OACXC,SAAU,WACVC,WAAYN,EACZO,cAAeP,EACfQ,OAAQ,UACLnE,MAEDC,EAEJmE,SAAAhC,EAAAA,IAAC,OAAIpC,MAAO,CAAEmE,QAAQ,MAAA9C,OAAA,EAAAA,EAAQgD,cAAe,EAAGL,SAAU,YACvDI,SAAQ,MAAA/C,OAAAA,EAAAA,EAAAiD,MAAMC,KAAKC,GAAMvE,EAAMmE,SAASI,QAIjD"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnail-img.tsx","../../src/shared/components/thumbnails-pane.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n /**\n * The ID of the document that this thumbnail belongs to\n */\n documentId: string;\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ documentId, meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin, documentId, meta.pageIndex]);\n\n useEffect(() => {\n const scope = thumbs?.forDocument(documentId);\n const task = scope?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, documentId, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n /**\n * The ID of the document that this thumbnail pane displays\n */\n documentId: string;\n style?: CSSProperties;\n children: (m: any) => ReactNode;\n};\n\nexport function ThumbnailsPane({ documentId, style, children, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n // Store window data along with the documentId it came from\n const [windowData, setWindowData] = useState<{\n window: WindowState | null;\n docId: string | null;\n }>({ window: null, docId: null });\n\n // Only use the window if it matches the current documentId\n const window = windowData.docId === documentId ? windowData.window : null;\n\n // 1) subscribe to window updates for this document\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n\n // Get initial window state immediately on mount\n const initialWindow = scope.getWindow();\n\n if (initialWindow) {\n setWindowData({ window: initialWindow, docId: documentId });\n }\n\n // Subscribe to future updates\n const unsubscribe = scope.onWindow((newWindow) => {\n setWindowData({ window: newWindow, docId: documentId });\n });\n\n // Clear state when documentId changes or component unmounts\n return () => {\n unsubscribe();\n setWindowData({ window: null, docId: null });\n };\n }, [thumbnailPlugin, documentId]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const onScroll = () => scope.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin, documentId]);\n\n // 2.5) keep plugin in sync when viewport resizes\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const resizeObserver = new ResizeObserver(() => {\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin, documentId]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin, documentId]);\n\n // 4) let plugin drive scroll\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, documentId, !!window]);\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => children(m))}\n </div>\n </div>\n );\n}\n"],"names":["useThumbnailPlugin","usePlugin","ThumbnailPlugin","id","useThumbnailCapability","useCapability","documentId","meta","style","props","provides","thumbs","plugin","thumbnailPlugin","url","setUrl","useState","urlRef","useRef","refreshTick","setRefreshTick","useEffect","forDocument","onRefreshPages","pages","includes","pageIndex","tick","scope","task","renderThumb","window","devicePixelRatio","wait","blob","objectUrl","URL","createObjectURL","current","ignore","revokeObjectURL","abort","code","PdfErrorCode","Cancelled","message","jsx","src","onLoad","children","viewportRef","windowData","setWindowData","docId","initialWindow","getWindow","unsubscribe","onWindow","newWindow","vp","onScroll","updateWindow","scrollTop","clientHeight","addEventListener","removeEventListener","resizeObserver","ResizeObserver","observe","disconnect","onScrollTo","top","behavior","scrollTo","paddingY","cfg","ref","overflowY","position","paddingTop","paddingBottom","height","totalHeight","items","map","m"],"mappings":"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"}
@@ -6,39 +6,58 @@ import { useRef, useState, useEffect } from "react";
6
6
  import { ignore, PdfErrorCode } from "@embedpdf/models";
7
7
  const useThumbnailPlugin = () => usePlugin(ThumbnailPlugin.id);
8
8
  const useThumbnailCapability = () => useCapability(ThumbnailPlugin.id);
9
- function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }) {
9
+ function ThumbnailsPane({ documentId, style, children, ...props }) {
10
10
  const { plugin: thumbnailPlugin } = useThumbnailPlugin();
11
11
  const viewportRef = useRef(null);
12
- const [window2, setWindow] = useState(null);
13
- useEffect(() => thumbnailPlugin == null ? void 0 : thumbnailPlugin.onWindow(setWindow), [thumbnailPlugin]);
12
+ const [windowData, setWindowData] = useState({ window: null, docId: null });
13
+ const window2 = windowData.docId === documentId ? windowData.window : null;
14
+ useEffect(() => {
15
+ if (!thumbnailPlugin) return;
16
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
17
+ const initialWindow = scope.getWindow();
18
+ if (initialWindow) {
19
+ setWindowData({ window: initialWindow, docId: documentId });
20
+ }
21
+ const unsubscribe = scope.onWindow((newWindow) => {
22
+ setWindowData({ window: newWindow, docId: documentId });
23
+ });
24
+ return () => {
25
+ unsubscribe();
26
+ setWindowData({ window: null, docId: null });
27
+ };
28
+ }, [thumbnailPlugin, documentId]);
14
29
  useEffect(() => {
15
30
  const vp = viewportRef.current;
16
- if (!vp) return;
17
- const onScroll = () => thumbnailPlugin == null ? void 0 : thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
31
+ if (!vp || !thumbnailPlugin) return;
32
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
33
+ const onScroll = () => scope.updateWindow(vp.scrollTop, vp.clientHeight);
18
34
  vp.addEventListener("scroll", onScroll);
19
35
  return () => vp.removeEventListener("scroll", onScroll);
20
- }, [thumbnailPlugin]);
36
+ }, [thumbnailPlugin, documentId]);
21
37
  useEffect(() => {
22
38
  const vp = viewportRef.current;
23
39
  if (!vp || !thumbnailPlugin) return;
40
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
24
41
  const resizeObserver = new ResizeObserver(() => {
25
- thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
42
+ scope.updateWindow(vp.scrollTop, vp.clientHeight);
26
43
  });
27
44
  resizeObserver.observe(vp);
28
45
  return () => resizeObserver.disconnect();
29
- }, [thumbnailPlugin]);
46
+ }, [thumbnailPlugin, documentId]);
30
47
  useEffect(() => {
31
48
  const vp = viewportRef.current;
32
49
  if (!vp || !thumbnailPlugin) return;
33
- thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
34
- }, [window2, thumbnailPlugin]);
50
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
51
+ scope.updateWindow(vp.scrollTop, vp.clientHeight);
52
+ }, [window2, thumbnailPlugin, documentId]);
35
53
  useEffect(() => {
36
54
  const vp = viewportRef.current;
37
55
  if (!vp || !thumbnailPlugin || !window2) return;
38
- return thumbnailPlugin.onScrollTo(({ top, behavior }) => {
56
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
57
+ return scope.onScrollTo(({ top, behavior }) => {
39
58
  vp.scrollTo({ top, behavior });
40
59
  });
41
- }, [thumbnailPlugin, !!window2]);
60
+ }, [thumbnailPlugin, documentId, !!window2]);
42
61
  const paddingY = (thumbnailPlugin == null ? void 0 : thumbnailPlugin.cfg.paddingY) ?? 0;
43
62
  return /* @__PURE__ */ jsx(
44
63
  "div",
@@ -53,11 +72,11 @@ function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }) {
53
72
  ...style
54
73
  },
55
74
  ...props,
56
- children: /* @__PURE__ */ jsx("div", { style: { height: (window2 == null ? void 0 : window2.totalHeight) ?? 0, position: "relative" }, children: window2 == null ? void 0 : window2.items.map((m) => props.children(m)) })
75
+ children: /* @__PURE__ */ jsx("div", { style: { height: (window2 == null ? void 0 : window2.totalHeight) ?? 0, position: "relative" }, children: window2 == null ? void 0 : window2.items.map((m) => children(m)) })
57
76
  }
58
77
  );
59
78
  }
60
- function ThumbImg({ meta, style, ...props }) {
79
+ function ThumbImg({ documentId, meta, style, ...props }) {
61
80
  const { provides: thumbs } = useThumbnailCapability();
62
81
  const { plugin: thumbnailPlugin } = useThumbnailPlugin();
63
82
  const [url, setUrl] = useState();
@@ -65,14 +84,16 @@ function ThumbImg({ meta, style, ...props }) {
65
84
  const [refreshTick, setRefreshTick] = useState(0);
66
85
  useEffect(() => {
67
86
  if (!thumbnailPlugin) return;
68
- return thumbnailPlugin.onRefreshPages((pages) => {
87
+ const scope = thumbnailPlugin.provides().forDocument(documentId);
88
+ return scope.onRefreshPages((pages) => {
69
89
  if (pages.includes(meta.pageIndex)) {
70
90
  setRefreshTick((tick) => tick + 1);
71
91
  }
72
92
  });
73
- }, [thumbnailPlugin]);
93
+ }, [thumbnailPlugin, documentId, meta.pageIndex]);
74
94
  useEffect(() => {
75
- const task = thumbs == null ? void 0 : thumbs.renderThumb(meta.pageIndex, window.devicePixelRatio);
95
+ const scope = thumbs == null ? void 0 : thumbs.forDocument(documentId);
96
+ const task = scope == null ? void 0 : scope.renderThumb(meta.pageIndex, window.devicePixelRatio);
76
97
  task == null ? void 0 : task.wait((blob) => {
77
98
  const objectUrl = URL.createObjectURL(blob);
78
99
  urlRef.current = objectUrl;
@@ -89,7 +110,7 @@ function ThumbImg({ meta, style, ...props }) {
89
110
  });
90
111
  }
91
112
  };
92
- }, [thumbs, meta.pageIndex, refreshTick]);
113
+ }, [thumbs, documentId, meta.pageIndex, refreshTick]);
93
114
  const handleImageLoad = () => {
94
115
  if (urlRef.current) {
95
116
  URL.revokeObjectURL(urlRef.current);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnails-pane.tsx","../../src/shared/components/thumbnail-img.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { ThumbMeta, WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n /** @deprecated use scrollToThumb via capability or rely on autoScroll */\n selectedPage?: number;\n /** @deprecated behavior is now controlled by ThumbnailPluginConfig.scrollBehavior */\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n const [window, setWindow] = useState<WindowState | null>(null);\n\n // 1) subscribe once to window updates\n useEffect(() => thumbnailPlugin?.onWindow(setWindow), [thumbnailPlugin]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n const onScroll = () => thumbnailPlugin?.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin]);\n\n // 2.5) keep plugin in sync when viewport resizes (e.g., menu opens/closes)\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const resizeObserver = new ResizeObserver(() => {\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n // push initial metrics\n thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin]);\n\n // 4) let plugin drive scroll – only after window is set, and only once\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n return thumbnailPlugin.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, !!window]); // Note: !!window to prevent re-subscription on window updates\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => props.children(m))}\n </div>\n </div>\n );\n}\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties, Fragment } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n return thumbnailPlugin.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin]);\n\n useEffect(() => {\n const task = thumbs?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n"],"names":["window"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-thumbnail.ts","../../src/shared/components/thumbnails-pane.tsx","../../src/shared/components/thumbnail-img.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","import { useEffect, useRef, useState, HTMLAttributes, CSSProperties, ReactNode } from '@framework';\nimport { WindowState } from '@embedpdf/plugin-thumbnail';\nimport { useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n /**\n * The ID of the document that this thumbnail pane displays\n */\n documentId: string;\n style?: CSSProperties;\n children: (m: any) => ReactNode;\n};\n\nexport function ThumbnailsPane({ documentId, style, children, ...props }: ThumbnailsProps) {\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const viewportRef = useRef<HTMLDivElement>(null);\n\n // Store window data along with the documentId it came from\n const [windowData, setWindowData] = useState<{\n window: WindowState | null;\n docId: string | null;\n }>({ window: null, docId: null });\n\n // Only use the window if it matches the current documentId\n const window = windowData.docId === documentId ? windowData.window : null;\n\n // 1) subscribe to window updates for this document\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n\n // Get initial window state immediately on mount\n const initialWindow = scope.getWindow();\n\n if (initialWindow) {\n setWindowData({ window: initialWindow, docId: documentId });\n }\n\n // Subscribe to future updates\n const unsubscribe = scope.onWindow((newWindow) => {\n setWindowData({ window: newWindow, docId: documentId });\n });\n\n // Clear state when documentId changes or component unmounts\n return () => {\n unsubscribe();\n setWindowData({ window: null, docId: null });\n };\n }, [thumbnailPlugin, documentId]);\n\n // 2) keep plugin in sync while the user scrolls\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const onScroll = () => scope.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbnailPlugin, documentId]);\n\n // 2.5) keep plugin in sync when viewport resizes\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n const resizeObserver = new ResizeObserver(() => {\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n });\n resizeObserver.observe(vp);\n\n return () => resizeObserver.disconnect();\n }, [thumbnailPlugin, documentId]);\n\n // 3) kick-start after document change\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n scope.updateWindow(vp.scrollTop, vp.clientHeight);\n }, [window, thumbnailPlugin, documentId]);\n\n // 4) let plugin drive scroll\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbnailPlugin || !window) return;\n\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onScrollTo(({ top, behavior }) => {\n vp.scrollTo({ top, behavior });\n });\n }, [thumbnailPlugin, documentId, !!window]);\n\n const paddingY = thumbnailPlugin?.cfg.paddingY ?? 0;\n\n return (\n <div\n ref={viewportRef}\n style={{\n overflowY: 'auto',\n position: 'relative',\n paddingTop: paddingY,\n paddingBottom: paddingY,\n height: '100%',\n ...style,\n }}\n {...props}\n >\n <div style={{ height: window?.totalHeight ?? 0, position: 'relative' }}>\n {window?.items.map((m) => children(m))}\n </div>\n </div>\n );\n}\n","import { useEffect, useState, useRef, HTMLAttributes, CSSProperties } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\n\ntype ThumbnailImgProps = Omit<HTMLAttributes<HTMLImageElement>, 'style'> & {\n /**\n * The ID of the document that this thumbnail belongs to\n */\n documentId: string;\n style?: CSSProperties;\n meta: ThumbMeta;\n};\n\nexport function ThumbImg({ documentId, meta, style, ...props }: ThumbnailImgProps) {\n const { provides: thumbs } = useThumbnailCapability();\n const { plugin: thumbnailPlugin } = useThumbnailPlugin();\n const [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\n const [refreshTick, setRefreshTick] = useState(0);\n\n useEffect(() => {\n if (!thumbnailPlugin) return;\n const scope = thumbnailPlugin.provides().forDocument(documentId);\n return scope.onRefreshPages((pages) => {\n if (pages.includes(meta.pageIndex)) {\n setRefreshTick((tick) => tick + 1);\n }\n });\n }, [thumbnailPlugin, documentId, meta.pageIndex]);\n\n useEffect(() => {\n const scope = thumbs?.forDocument(documentId);\n const task = scope?.renderThumb(meta.pageIndex, window.devicePixelRatio);\n task?.wait((blob) => {\n const objectUrl = URL.createObjectURL(blob);\n urlRef.current = objectUrl;\n setUrl(objectUrl);\n }, ignore);\n\n return () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n } else {\n task?.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n }\n };\n }, [thumbs, documentId, meta.pageIndex, refreshTick]);\n\n const handleImageLoad = () => {\n if (urlRef.current) {\n URL.revokeObjectURL(urlRef.current);\n urlRef.current = null;\n }\n };\n\n return url ? <img src={url} onLoad={handleImageLoad} style={style} {...props} /> : null;\n}\n"],"names":["window"],"mappings":"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"}