@embedpdf/plugin-thumbnail 1.3.3 → 1.3.5
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.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +77 -17
- package/dist/index.js.map +1 -1
- package/dist/lib/thumbnail-plugin.d.ts +10 -2
- package/dist/lib/types.d.ts +14 -4
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +13 -29
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +13 -29
- package/dist/react/index.js.map +1 -1
- package/dist/shared-preact/components/thumbnails-pane.d.ts +3 -1
- package/dist/shared-react/components/thumbnails-pane.d.ts +3 -1
- package/dist/vue/components/index.d.ts +2 -0
- package/dist/vue/components/thumbnail-img.vue.d.ts +6 -0
- package/dist/vue/components/thumbnails-pane.vue.d.ts +14 -0
- package/dist/vue/hooks/index.d.ts +1 -0
- package/dist/vue/hooks/use-thumbnail.d.ts +3 -0
- package/dist/vue/index.cjs +2 -0
- package/dist/vue/index.cjs.map +1 -0
- package/dist/vue/index.d.ts +3 -0
- package/dist/vue/index.js +140 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +15 -7
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@embedpdf/core"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@embedpdf/core"),i=require("@embedpdf/models"),e="thumbnail",s={id:e,name:"Thumbnail Plugin",version:"1.0.0",provides:["thumbnail"],requires:["render"],optional:["scroll"],defaultConfig:{enabled:!0,width:150,gap:10,buffer:3,labelHeight:16,autoScroll:!0,scrollBehavior:"smooth",imagePadding:0}},o=class extends t.BasePlugin{constructor(i,e,s){var o;super(i,e),this.cfg=s,this.scrollCapability=null,this.thumbs=[],this.window=null,this.viewportH=0,this.scrollY=0,this.emitWindow=t.createBehaviorEmitter(),this.refreshPages$=t.createEmitter(),this.taskCache=new Map,this.canAutoScroll=!0,this.scrollTo$=t.createBehaviorEmitter(),this.renderCapability=this.registry.getPlugin("render").provides(),this.scrollCapability=(null==(o=this.registry.getPlugin("scroll"))?void 0:o.provides())??null,this.coreStore.onAction(t.SET_DOCUMENT,((t,i)=>{this.taskCache.clear(),this.setWindowState(i)})),this.coreStore.onAction(t.REFRESH_PAGES,(t=>{this.refreshPages$.emit(t.payload);for(const i of t.payload)this.taskCache.delete(i)})),this.scrollCapability&&!1!==this.cfg.autoScroll&&(this.scrollCapability.onPageChangeState((({isChanging:t,targetPage:i})=>{this.canAutoScroll=!t,t||this.scrollToThumb(i-1)})),this.scrollCapability.onPageChange((({pageNumber:t})=>{this.canAutoScroll&&this.scrollToThumb(t-1)})))}async initialize(){}onRefreshPages(t){return this.refreshPages$.on(t)}onWindow(t){return this.emitWindow.on(t)}onScrollTo(t){return this.scrollTo$.on(t)}setWindowState(t){const i=t.core;if(!i.document)return;const e=this.cfg.width??120,s=this.cfg.labelHeight??16,o=this.cfg.gap??8,r=this.cfg.imagePadding??0,h=Math.max(1,e-2*r);let a=0;this.thumbs=i.document.pages.map((t=>{const i=t.size.height/t.size.width,e=Math.round(h*i),n=r+e+r+s,l={pageIndex:t.index,width:h,height:e,wrapperHeight:n,top:a,labelHeight:s,padding:r};return a+=n+o,l})),this.window={start:-1,end:-1,items:[],totalHeight:a-o},this.emitWindow.emit(this.window)}buildCapability(){return{renderThumb:(t,i)=>this.renderThumb(t,i),scrollToThumb:t=>this.scrollToThumb(t)}}updateWindow(t,i){const e=this.cfg.buffer??3;this.scrollY=t,this.viewportH=i;let s=0,o=this.thumbs.length-1,r=0;for(;s<=o;){const i=s+o>>1,e=this.thumbs[i];e.top+e.wrapperHeight<t?s=i+1:(r=i,o=i-1)}let h=r;const a=t+i;for(;h+1<this.thumbs.length&&this.thumbs[h].top<a;)h++;h=Math.min(this.thumbs.length-1,h+e);const n=Math.max(0,r-e);this.window&&n===this.window.start&&h===this.window.end||(this.window={start:n,end:h,items:this.thumbs.slice(n,h+1),totalHeight:this.window.totalHeight},this.emitWindow.emit(this.window))}scrollToThumb(t){if(!this.window)return;const i=this.thumbs[t];if(!i)return;const e=this.cfg.scrollBehavior??"smooth";if(this.viewportH<=0){const t=Math.max(0,i.top-i.wrapperHeight);return void this.scrollTo$.emit({top:t,behavior:e})}const s=i.top,o=i.top+i.wrapperHeight,r=s<this.scrollY+8,h=o>this.scrollY+this.viewportH-8;r?this.scrollTo$.emit({top:s,behavior:e}):h&&this.scrollTo$.emit({top:Math.max(0,o-this.viewportH),behavior:e})}renderThumb(t,e){if(this.taskCache.has(t))return this.taskCache.get(t);const s=this.coreState.core.document.pages[t],o=this.cfg.width??120,r=this.cfg.imagePadding??0,h=Math.max(1,o-2*r)/s.size.width,a=this.renderCapability.renderPageRect({pageIndex:t,rect:{origin:{x:0,y:0},size:s.size},options:{scaleFactor:h,dpr:e}});return this.taskCache.set(t,a),a.wait(i.ignore,(()=>this.taskCache.delete(t))),a}};o.id="thumbnail";let r=o;const h={manifest:s,create:(t,i)=>new r(e,t,i),reducer:()=>{},initialState:{}};exports.THUMBNAIL_PLUGIN_ID=e,exports.ThumbnailPlugin=r,exports.ThumbnailPluginPackage=h,exports.manifest=s;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/lib/manifest.ts","../src/lib/thumbnail-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { ThumbnailPluginConfig } from './types';\n\nexport const THUMBNAIL_PLUGIN_ID = 'thumbnail';\n\nexport const manifest: PluginManifest<ThumbnailPluginConfig> = {\n id: THUMBNAIL_PLUGIN_ID,\n name: 'Thumbnail Plugin',\n version: '1.0.0',\n provides: ['thumbnail'],\n requires: ['render'],\n optional: [],\n defaultConfig: {\n enabled: true,\n width: 150,\n gap: 10,\n buffer: 3,\n labelHeight: 16,\n },\n};\n","import {\n BasePlugin,\n CoreState,\n createBehaviorEmitter,\n createEmitter,\n PluginRegistry,\n REFRESH_PAGES,\n SET_DOCUMENT,\n StoreState,\n Unsubscribe,\n} from '@embedpdf/core';\nimport { ThumbMeta, ThumbnailPluginConfig, WindowState } from './types';\nimport { ThumbnailCapability } from './types';\nimport { ignore, PdfErrorReason, Task } from '@embedpdf/models';\nimport { RenderCapability, RenderPlugin } from '@embedpdf/plugin-render';\n\nexport class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {\n static readonly id = 'thumbnail' as const;\n\n private renderCapability: RenderCapability;\n private thumbs: ThumbMeta[] = [];\n private window: WindowState | null = null;\n private readonly emitWindow = createBehaviorEmitter<WindowState>();\n private readonly refreshPages$ = createEmitter<number[]>();\n private readonly taskCache = new Map<number, Task<Blob, PdfErrorReason>>();\n\n constructor(\n id: string,\n registry: PluginRegistry,\n private cfg: ThumbnailPluginConfig,\n ) {\n super(id, registry);\n\n this.renderCapability = this.registry.getPlugin<RenderPlugin>('render')!.provides();\n\n this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {\n this.taskCache.clear();\n this.setWindowState(state);\n });\n\n this.coreStore.onAction(REFRESH_PAGES, (action) => {\n this.refreshPages$.emit(action.payload);\n for (const pageIdx of action.payload) {\n this.taskCache.delete(pageIdx);\n }\n });\n }\n\n /* ------------ init ------------------------------------------------ */\n async initialize(): Promise<void> {}\n\n public onRefreshPages(fn: (pages: number[]) => void): Unsubscribe {\n return this.refreshPages$.on(fn);\n }\n\n private setWindowState(state: StoreState<CoreState>) {\n const core = state.core;\n\n if (!core.document) return;\n\n const W = this.cfg.width ?? 120;\n const L = this.cfg.labelHeight ?? 16; // label\n const GAP = this.cfg.gap ?? 8;\n\n let offset = 0;\n this.thumbs = core.document.pages.map((p) => {\n const ratio = p.size.height / p.size.width;\n const thumbH = Math.round(W * ratio);\n const wrapH = thumbH + L; // no GAP here\n\n const meta: ThumbMeta = {\n pageIndex: p.index,\n width: W,\n height: thumbH,\n wrapperHeight: wrapH,\n top: offset,\n labelHeight: L,\n };\n offset += wrapH + GAP; // GAP added *after* wrapper\n return meta;\n });\n\n this.window = {\n start: -1,\n end: -1,\n items: [],\n totalHeight: offset - GAP, // last item has no gap below\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ capability ----------------------------------------- */\n protected buildCapability(): ThumbnailCapability {\n return {\n onWindow: this.emitWindow.on,\n setViewport: (y, h) => this.updateWindow(y, h),\n renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),\n };\n }\n\n /* ------------ windowing math ------------------------------------- */\n private updateWindow(scrollY: number, viewportH: number) {\n const BUF = this.cfg.buffer ?? 3;\n\n /* -------- find first visible wrapper ---------- */\n let low = 0,\n high = this.thumbs.length - 1,\n first = 0;\n while (low <= high) {\n const mid = (low + high) >> 1;\n const m = this.thumbs[mid];\n if (m.top + m.wrapperHeight < scrollY) low = mid + 1;\n else {\n first = mid;\n high = mid - 1;\n }\n }\n\n /* -------- find last visible + buffer ---------- */\n let last = first;\n const limit = scrollY + viewportH;\n while (last + 1 < this.thumbs.length && this.thumbs[last].top < limit) last++;\n last = Math.min(this.thumbs.length - 1, last + BUF);\n\n const start = Math.max(0, first - BUF);\n if (this.window && start === this.window.start && last === this.window.end) return;\n\n this.window = {\n start,\n end: last,\n items: this.thumbs.slice(start, last + 1),\n totalHeight: this.window!.totalHeight,\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ thumbnail raster ----------------------------------- */\n private renderThumb(idx: number, dpr: number) {\n if (this.taskCache.has(idx)) return this.taskCache.get(idx)!;\n\n const core = this.coreState.core;\n const page = core.document!.pages[idx];\n const scale = (this.cfg.width ?? 120) / page.size.width;\n\n const task = this.renderCapability.renderPageRect({\n pageIndex: idx,\n rect: { origin: { x: 0, y: 0 }, size: page.size },\n options: {\n scaleFactor: scale,\n dpr,\n },\n });\n\n this.taskCache.set(idx, task);\n\n task.wait(ignore, () => {\n this.taskCache.delete(idx);\n });\n\n return task;\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, THUMBNAIL_PLUGIN_ID } from './manifest';\nimport { ThumbnailPluginConfig } from './types';\nimport { ThumbnailPlugin } from './thumbnail-plugin';\n\nexport const ThumbnailPluginPackage: PluginPackage<ThumbnailPlugin, ThumbnailPluginConfig> = {\n manifest,\n create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),\n reducer: () => {},\n initialState: {},\n};\n\nexport * from './thumbnail-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"names":["THUMBNAIL_PLUGIN_ID","manifest","id","name","version","provides","requires","optional","defaultConfig","enabled","width","gap","buffer","labelHeight","_ThumbnailPlugin","BasePlugin","constructor","registry","cfg","super","this","thumbs","window","emitWindow","createBehaviorEmitter","refreshPages$","createEmitter","taskCache","Map","renderCapability","getPlugin","coreStore","onAction","SET_DOCUMENT","_action","state","clear","setWindowState","REFRESH_PAGES","action","emit","payload","pageIdx","delete","initialize","onRefreshPages","fn","on","core","document","W","L","GAP","offset","pages","map","p","ratio","size","height","thumbH","Math","round","wrapH","meta","pageIndex","index","wrapperHeight","top","start","end","items","totalHeight","buildCapability","onWindow","setViewport","y","h","updateWindow","renderThumb","idx","dpr","scrollY","viewportH","BUF","low","high","length","first","mid","m","last","limit","min","max","slice","has","get","page","coreState","scale","task","renderPageRect","rect","origin","x","options","scaleFactor","set","wait","ignore","ThumbnailPlugin","ThumbnailPluginPackage","create","config","reducer","initialState"],"mappings":"gJAGaA,EAAsB,YAEtBC,EAAkD,CAC7DC,GAAIF,EACJG,KAAM,mBACNC,QAAS,QACTC,SAAU,CAAC,aACXC,SAAU,CAAC,UACXC,SAAU,GACVC,cAAe,CACbC,SAAS,EACTC,MAAO,IACPC,IAAK,GACLC,OAAQ,EACRC,YAAa,KCDJC,EAAN,cAA8BC,EAAAA,WAUnC,WAAAC,CACEd,EACAe,EACQC,GAERC,MAAMjB,EAAIe,GAFFG,KAAAF,IAAAA,EATVE,KAAQC,OAAsB,GAC9BD,KAAQE,OAA6B,KACpBF,KAAAG,WAAaC,0BACbJ,KAAAK,cAAgBC,kBAChBN,KAAAO,cAAgBC,IAS/BR,KAAKS,iBAAmBT,KAAKH,SAASa,UAAwB,UAAWzB,WAEzEe,KAAKW,UAAUC,SAASC,EAAcA,cAAA,CAACC,EAASC,KAC9Cf,KAAKO,UAAUS,QACfhB,KAAKiB,eAAeF,EAAK,IAG3Bf,KAAKW,UAAUC,SAASM,EAAeA,eAACC,IACjCnB,KAAAK,cAAce,KAAKD,EAAOE,SACpB,IAAA,MAAAC,KAAWH,EAAOE,QACtBrB,KAAAO,UAAUgB,OAAOD,EAAO,GAEhC,CAIH,gBAAME,GAA4B,CAE3B,cAAAC,CAAeC,GACb,OAAA1B,KAAKK,cAAcsB,GAAGD,EAAE,CAGzB,cAAAT,CAAeF,GACrB,MAAMa,EAAOb,EAAMa,KAEf,IAACA,EAAKC,SAAU,OAEd,MAAAC,EAAI9B,KAAKF,IAAIR,OAAS,IACtByC,EAAI/B,KAAKF,IAAIL,aAAe,GAC5BuC,EAAMhC,KAAKF,IAAIP,KAAO,EAE5B,IAAI0C,EAAS,EACbjC,KAAKC,OAAS2B,EAAKC,SAASK,MAAMC,KAAKC,IACrC,MAAMC,EAAQD,EAAEE,KAAKC,OAASH,EAAEE,KAAKhD,MAC/BkD,EAASC,KAAKC,MAAMZ,EAAIO,GACxBM,EAAQH,EAAST,EAEjBa,EAAkB,CACtBC,UAAWT,EAAEU,MACbxD,MAAOwC,EACPS,OAAQC,EACRO,cAAeJ,EACfK,IAAKf,EACLxC,YAAasC,GAGR,OADPE,GAAUU,EAAQX,EACXY,CAAA,IAGT5C,KAAKE,OAAS,CACZ+C,OAAO,EACPC,KAAK,EACLC,MAAO,GACPC,YAAanB,EAASD,GAEnBhC,KAAAG,WAAWiB,KAAKpB,KAAKE,OAAM,CAIxB,eAAAmD,GACD,MAAA,CACLC,SAAUtD,KAAKG,WAAWwB,GAC1B4B,YAAa,CAACC,EAAGC,IAAMzD,KAAK0D,aAAaF,EAAGC,GAC5CE,YAAa,CAACC,EAAKC,IAAQ7D,KAAK2D,YAAYC,EAAKC,GACnD,CAIM,YAAAH,CAAaI,EAAiBC,GAC9B,MAAAC,EAAMhE,KAAKF,IAAIN,QAAU,EAG/B,IAAIyE,EAAM,EACRC,EAAOlE,KAAKC,OAAOkE,OAAS,EAC5BC,EAAQ,EACV,KAAOH,GAAOC,GAAM,CACZ,MAAAG,EAAOJ,EAAMC,GAAS,EACtBI,EAAItE,KAAKC,OAAOoE,GAClBC,EAAEtB,IAAMsB,EAAEvB,cAAgBe,IAAeO,EAAM,GAEzCD,EAAAC,EACRH,EAAOG,EAAM,EACf,CAIF,IAAIE,EAAOH,EACX,MAAMI,EAAQV,EAAUC,EACjB,KAAAQ,EAAO,EAAIvE,KAAKC,OAAOkE,QAAUnE,KAAKC,OAAOsE,GAAMvB,IAAMwB,GAAOD,IACvEA,EAAO9B,KAAKgC,IAAIzE,KAAKC,OAAOkE,OAAS,EAAGI,EAAOP,GAE/C,MAAMf,EAAQR,KAAKiC,IAAI,EAAGN,EAAQJ,GAC9BhE,KAAKE,QAAU+C,IAAUjD,KAAKE,OAAO+C,OAASsB,IAASvE,KAAKE,OAAOgD,MAEvElD,KAAKE,OAAS,CACZ+C,QACAC,IAAKqB,EACLpB,MAAOnD,KAAKC,OAAO0E,MAAM1B,EAAOsB,EAAO,GACvCnB,YAAapD,KAAKE,OAAQkD,aAEvBpD,KAAAG,WAAWiB,KAAKpB,KAAKE,QAAM,CAI1B,WAAAyD,CAAYC,EAAaC,GAC3B,GAAA7D,KAAKO,UAAUqE,IAAIhB,GAAa,OAAA5D,KAAKO,UAAUsE,IAAIjB,GAEjDhC,MACAkD,EADO9E,KAAK+E,UAAUnD,KACVC,SAAUK,MAAM0B,GAC5BoB,GAAShF,KAAKF,IAAIR,OAAS,KAAOwF,EAAKxC,KAAKhD,MAE5C2F,EAAOjF,KAAKS,iBAAiByE,eAAe,CAChDrC,UAAWe,EACXuB,KAAM,CAAEC,OAAQ,CAAEC,EAAG,EAAG7B,EAAG,GAAKlB,KAAMwC,EAAKxC,MAC3CgD,QAAS,CACPC,YAAaP,EACbnB,SAUG,OANF7D,KAAAO,UAAUiF,IAAI5B,EAAKqB,GAEnBA,EAAAQ,KAAKC,EAAAA,QAAQ,KACX1F,KAAAO,UAAUgB,OAAOqC,EAAG,IAGpBqB,CAAA,GA9ITvF,EAAgBZ,GAAK,YADhB,IAAM6G,EAANjG,ECXA,MAAMkG,EAAgF,CAC3F/G,WACAgH,OAAQ,CAAChG,EAAUiG,IAAW,IAAIH,EAAgB/G,EAAqBiB,EAAUiG,GACjFC,QAAS,OACTC,aAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/lib/manifest.ts","../src/lib/thumbnail-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { ThumbnailPluginConfig } from './types';\n\nexport const THUMBNAIL_PLUGIN_ID = 'thumbnail';\n\nexport const manifest: PluginManifest<ThumbnailPluginConfig> = {\n id: THUMBNAIL_PLUGIN_ID,\n name: 'Thumbnail Plugin',\n version: '1.0.0',\n provides: ['thumbnail'],\n requires: ['render'],\n optional: ['scroll'],\n defaultConfig: {\n enabled: true,\n width: 150,\n gap: 10,\n buffer: 3,\n labelHeight: 16,\n autoScroll: true,\n scrollBehavior: 'smooth',\n imagePadding: 0,\n },\n};\n","import {\n BasePlugin,\n CoreState,\n createBehaviorEmitter,\n createEmitter,\n PluginRegistry,\n REFRESH_PAGES,\n SET_DOCUMENT,\n StoreState,\n Unsubscribe,\n} from '@embedpdf/core';\nimport { ScrollToOptions, ThumbMeta, ThumbnailPluginConfig, WindowState } from './types';\nimport { ThumbnailCapability } from './types';\nimport { ignore, PdfErrorReason, Task } from '@embedpdf/models';\nimport { RenderCapability, RenderPlugin } from '@embedpdf/plugin-render';\nimport { ScrollCapability, ScrollPlugin } from '@embedpdf/plugin-scroll';\n\nexport class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {\n static readonly id = 'thumbnail' as const;\n\n private renderCapability: RenderCapability;\n private scrollCapability: ScrollCapability | null = null;\n private thumbs: ThumbMeta[] = [];\n private window: WindowState | null = null;\n\n // track viewport metrics for scroll decisions\n private viewportH: number = 0;\n private scrollY: number = 0;\n\n private readonly emitWindow = createBehaviorEmitter<WindowState>();\n private readonly refreshPages$ = createEmitter<number[]>();\n private readonly taskCache = new Map<number, Task<Blob, PdfErrorReason>>();\n private canAutoScroll = true;\n // ask pane to scroll to a specific top\n private readonly scrollTo$ = createBehaviorEmitter<ScrollToOptions>();\n\n constructor(\n id: string,\n registry: PluginRegistry,\n private cfg: ThumbnailPluginConfig,\n ) {\n super(id, registry);\n\n this.renderCapability = this.registry.getPlugin<RenderPlugin>('render')!.provides();\n this.scrollCapability = this.registry.getPlugin<ScrollPlugin>('scroll')?.provides() ?? null;\n\n this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {\n this.taskCache.clear();\n this.setWindowState(state);\n });\n\n this.coreStore.onAction(REFRESH_PAGES, (action) => {\n this.refreshPages$.emit(action.payload);\n for (const pageIdx of action.payload) {\n this.taskCache.delete(pageIdx);\n }\n });\n\n // auto-scroll thumbnails when the main scroller's current page changes\n if (this.scrollCapability && this.cfg.autoScroll !== false) {\n this.scrollCapability.onPageChangeState(({ isChanging, targetPage }) => {\n this.canAutoScroll = !isChanging;\n if (!isChanging) {\n this.scrollToThumb(targetPage - 1);\n }\n });\n this.scrollCapability.onPageChange(({ pageNumber }) => {\n if (this.canAutoScroll) {\n this.scrollToThumb(pageNumber - 1);\n }\n });\n }\n }\n\n /* ------------ init ------------------------------------------------ */\n async initialize(): Promise<void> {}\n\n public onRefreshPages(fn: (pages: number[]) => void): Unsubscribe {\n return this.refreshPages$.on(fn);\n }\n\n public onWindow(cb: (w: WindowState) => void): Unsubscribe {\n return this.emitWindow.on(cb);\n }\n\n public onScrollTo(cb: (o: ScrollToOptions) => void): Unsubscribe {\n return this.scrollTo$.on(cb);\n }\n\n private setWindowState(state: StoreState<CoreState>) {\n const core = state.core;\n if (!core.document) return;\n\n const OUTER_W = this.cfg.width ?? 120;\n const L = this.cfg.labelHeight ?? 16;\n const GAP = this.cfg.gap ?? 8;\n const P = this.cfg.imagePadding ?? 0;\n\n // Inner bitmap width cannot go below 1px\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n let offset = 0;\n this.thumbs = core.document.pages.map((p) => {\n const ratio = p.size.height / p.size.width;\n const imgH = Math.round(INNER_W * ratio);\n const wrapH = P + imgH + P + L; // padding + image + padding + label\n\n const meta: ThumbMeta = {\n pageIndex: p.index,\n width: INNER_W, // bitmap width (for <img> size)\n height: imgH, // bitmap height (for <img> size)\n wrapperHeight: wrapH, // full row height used by virtualizer\n top: offset, // top of the row\n labelHeight: L,\n padding: P, // NEW\n };\n offset += wrapH + GAP;\n return meta;\n });\n\n this.window = {\n start: -1,\n end: -1,\n items: [],\n totalHeight: offset - GAP,\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ capability ----------------------------------------- */\n protected buildCapability(): ThumbnailCapability {\n return {\n renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),\n scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx),\n };\n }\n\n /* ------------ windowing & viewport state ------------------------- */\n public updateWindow(scrollY: number, viewportH: number) {\n const BUF = this.cfg.buffer ?? 3;\n\n // remember latest viewport metrics for scroll decisions\n this.scrollY = scrollY;\n this.viewportH = viewportH;\n\n /* find first visible */\n let low = 0,\n high = this.thumbs.length - 1,\n first = 0;\n while (low <= high) {\n const mid = (low + high) >> 1;\n const m = this.thumbs[mid];\n if (m.top + m.wrapperHeight < scrollY) low = mid + 1;\n else {\n first = mid;\n high = mid - 1;\n }\n }\n\n /* find last visible + buffer */\n let last = first;\n const limit = scrollY + viewportH;\n while (last + 1 < this.thumbs.length && this.thumbs[last].top < limit) last++;\n last = Math.min(this.thumbs.length - 1, last + BUF);\n\n const start = Math.max(0, first - BUF);\n if (this.window && start === this.window.start && last === this.window.end) return;\n\n this.window = {\n start,\n end: last,\n items: this.thumbs.slice(start, last + 1),\n totalHeight: this.window!.totalHeight,\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ scroll helper now in plugin ------------------------ */\n private scrollToThumb(pageIdx: number) {\n if (!this.window) return;\n const item = this.thumbs[pageIdx];\n if (!item) return;\n\n const behavior = this.cfg.scrollBehavior ?? 'smooth';\n\n if (this.viewportH <= 0) {\n // Center the thumbnail in the viewport\n const top = Math.max(0, item.top - item.wrapperHeight);\n this.scrollTo$.emit({ top, behavior });\n return;\n }\n\n const margin = 8;\n const top = item.top;\n const bottom = item.top + item.wrapperHeight;\n\n const needsUp = top < this.scrollY + margin;\n const needsDown = bottom > this.scrollY + this.viewportH - margin;\n\n if (needsUp) {\n this.scrollTo$.emit({ top, behavior });\n } else if (needsDown) {\n this.scrollTo$.emit({ top: Math.max(0, bottom - this.viewportH), behavior });\n }\n }\n\n /* ------------ thumbnail raster ----------------------------------- */\n private renderThumb(idx: number, dpr: number) {\n if (this.taskCache.has(idx)) return this.taskCache.get(idx)!;\n\n const core = this.coreState.core;\n const page = core.document!.pages[idx];\n\n const OUTER_W = this.cfg.width ?? 120;\n const P = this.cfg.imagePadding ?? 0;\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n const scale = INNER_W / page.size.width; // scale to inner width (excludes padding)\n\n const task = this.renderCapability.renderPageRect({\n pageIndex: idx,\n rect: { origin: { x: 0, y: 0 }, size: page.size },\n options: {\n scaleFactor: scale,\n dpr,\n },\n });\n\n this.taskCache.set(idx, task);\n task.wait(ignore, () => this.taskCache.delete(idx));\n return task;\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, THUMBNAIL_PLUGIN_ID } from './manifest';\nimport { ThumbnailPluginConfig } from './types';\nimport { ThumbnailPlugin } from './thumbnail-plugin';\n\nexport const ThumbnailPluginPackage: PluginPackage<ThumbnailPlugin, ThumbnailPluginConfig> = {\n manifest,\n create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),\n reducer: () => {},\n initialState: {},\n};\n\nexport * from './thumbnail-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"names":["THUMBNAIL_PLUGIN_ID","manifest","id","name","version","provides","requires","optional","defaultConfig","enabled","width","gap","buffer","labelHeight","autoScroll","scrollBehavior","imagePadding","_ThumbnailPlugin","BasePlugin","constructor","registry","cfg","super","this","scrollCapability","thumbs","window","viewportH","scrollY","emitWindow","createBehaviorEmitter","refreshPages$","createEmitter","taskCache","Map","canAutoScroll","scrollTo$","renderCapability","getPlugin","_a","coreStore","onAction","SET_DOCUMENT","_action","state","clear","setWindowState","REFRESH_PAGES","action","emit","payload","pageIdx","delete","onPageChangeState","isChanging","targetPage","scrollToThumb","onPageChange","pageNumber","initialize","onRefreshPages","fn","on","onWindow","cb","onScrollTo","core","document","OUTER_W","L","GAP","P","INNER_W","Math","max","offset","pages","map","p","ratio","size","height","imgH","round","wrapH","meta","pageIndex","index","wrapperHeight","top","padding","start","end","items","totalHeight","buildCapability","renderThumb","idx","dpr","updateWindow","BUF","low","high","length","first","mid","m","last","limit","min","slice","item","behavior","bottom","needsUp","needsDown","has","get","page","coreState","scale","task","renderPageRect","rect","origin","x","y","options","scaleFactor","set","wait","ignore","ThumbnailPlugin","ThumbnailPluginPackage","create","config","reducer","initialState"],"mappings":"gJAGaA,EAAsB,YAEtBC,EAAkD,CAC7DC,GAAIF,EACJG,KAAM,mBACNC,QAAS,QACTC,SAAU,CAAC,aACXC,SAAU,CAAC,UACXC,SAAU,CAAC,UACXC,cAAe,CACbC,SAAS,EACTC,MAAO,IACPC,IAAK,GACLC,OAAQ,EACRC,YAAa,GACbC,YAAY,EACZC,eAAgB,SAChBC,aAAc,ICHLC,EAAN,cAA8BC,EAAAA,WAmBnC,WAAAC,CACEjB,EACAkB,EACQC,SAERC,MAAMpB,EAAIkB,GAFFG,KAAAF,IAAAA,EAlBVE,KAAQC,iBAA4C,KACpDD,KAAQE,OAAsB,GAC9BF,KAAQG,OAA6B,KAGrCH,KAAQI,UAAoB,EAC5BJ,KAAQK,QAAkB,EAETL,KAAAM,WAAaC,0BACbP,KAAAQ,cAAgBC,kBAChBT,KAAAU,cAAgBC,IACjCX,KAAQY,eAAgB,EAEPZ,KAAAa,UAAYN,0BAS3BP,KAAKc,iBAAmBd,KAAKH,SAASkB,UAAwB,UAAWjC,WACzEkB,KAAKC,kBAAmB,OAAAe,EAAKhB,KAAAH,SAASkB,UAAwB,oBAAWjC,aAAc,KAEvFkB,KAAKiB,UAAUC,SAASC,EAAcA,cAAA,CAACC,EAASC,KAC9CrB,KAAKU,UAAUY,QACftB,KAAKuB,eAAeF,EAAK,IAG3BrB,KAAKiB,UAAUC,SAASM,EAAeA,eAACC,IACjCzB,KAAAQ,cAAckB,KAAKD,EAAOE,SACpB,IAAA,MAAAC,KAAWH,EAAOE,QACtB3B,KAAAU,UAAUmB,OAAOD,EAAO,IAK7B5B,KAAKC,mBAA4C,IAAxBD,KAAKF,IAAIP,aACpCS,KAAKC,iBAAiB6B,mBAAkB,EAAGC,aAAYC,iBACrDhC,KAAKY,eAAiBmB,EACjBA,GACE/B,KAAAiC,cAAcD,EAAa,EAAC,IAGrChC,KAAKC,iBAAiBiC,cAAa,EAAGC,iBAChCnC,KAAKY,eACFZ,KAAAiC,cAAcE,EAAa,EAAC,IAGvC,CAIF,gBAAMC,GAA4B,CAE3B,cAAAC,CAAeC,GACb,OAAAtC,KAAKQ,cAAc+B,GAAGD,EAAE,CAG1B,QAAAE,CAASC,GACP,OAAAzC,KAAKM,WAAWiC,GAAGE,EAAE,CAGvB,UAAAC,CAAWD,GACT,OAAAzC,KAAKa,UAAU0B,GAAGE,EAAE,CAGrB,cAAAlB,CAAeF,GACrB,MAAMsB,EAAOtB,EAAMsB,KACf,IAACA,EAAKC,SAAU,OAEd,MAAAC,EAAU7C,KAAKF,IAAIX,OAAS,IAC5B2D,EAAI9C,KAAKF,IAAIR,aAAe,GAC5ByD,EAAM/C,KAAKF,IAAIV,KAAO,EACtB4D,EAAIhD,KAAKF,IAAIL,cAAgB,EAG7BwD,EAAUC,KAAKC,IAAI,EAAGN,EAAU,EAAIG,GAE1C,IAAII,EAAS,EACbpD,KAAKE,OAASyC,EAAKC,SAASS,MAAMC,KAAKC,IACrC,MAAMC,EAAQD,EAAEE,KAAKC,OAASH,EAAEE,KAAKtE,MAC/BwE,EAAOT,KAAKU,MAAMX,EAAUO,GAC5BK,EAAQb,EAAIW,EAAOX,EAAIF,EAEvBgB,EAAkB,CACtBC,UAAWR,EAAES,MACb7E,MAAO8D,EACPS,OAAQC,EACRM,cAAeJ,EACfK,IAAKd,EACL9D,YAAawD,EACbqB,QAASnB,GAGJ,OADPI,GAAUS,EAAQd,EACXe,CAAA,IAGT9D,KAAKG,OAAS,CACZiE,OAAO,EACPC,KAAK,EACLC,MAAO,GACPC,YAAanB,EAASL,GAEnB/C,KAAAM,WAAWoB,KAAK1B,KAAKG,OAAM,CAIxB,eAAAqE,GACD,MAAA,CACLC,YAAa,CAACC,EAAKC,IAAQ3E,KAAKyE,YAAYC,EAAKC,GACjD1C,cAAgBL,GAAY5B,KAAKiC,cAAcL,GACjD,CAIK,YAAAgD,CAAavE,EAAiBD,GAC7B,MAAAyE,EAAM7E,KAAKF,IAAIT,QAAU,EAG/BW,KAAKK,QAAUA,EACfL,KAAKI,UAAYA,EAGjB,IAAI0E,EAAM,EACRC,EAAO/E,KAAKE,OAAO8E,OAAS,EAC5BC,EAAQ,EACV,KAAOH,GAAOC,GAAM,CACZ,MAAAG,EAAOJ,EAAMC,GAAS,EACtBI,EAAInF,KAAKE,OAAOgF,GAClBC,EAAEjB,IAAMiB,EAAElB,cAAgB5D,IAAe6E,EAAM,GAEzCD,EAAAC,EACRH,EAAOG,EAAM,EACf,CAIF,IAAIE,EAAOH,EACX,MAAMI,EAAQhF,EAAUD,EACjB,KAAAgF,EAAO,EAAIpF,KAAKE,OAAO8E,QAAUhF,KAAKE,OAAOkF,GAAMlB,IAAMmB,GAAOD,IACvEA,EAAOlC,KAAKoC,IAAItF,KAAKE,OAAO8E,OAAS,EAAGI,EAAOP,GAE/C,MAAMT,EAAQlB,KAAKC,IAAI,EAAG8B,EAAQJ,GAC9B7E,KAAKG,QAAUiE,IAAUpE,KAAKG,OAAOiE,OAASgB,IAASpF,KAAKG,OAAOkE,MAEvErE,KAAKG,OAAS,CACZiE,QACAC,IAAKe,EACLd,MAAOtE,KAAKE,OAAOqF,MAAMnB,EAAOgB,EAAO,GACvCb,YAAavE,KAAKG,OAAQoE,aAEvBvE,KAAAM,WAAWoB,KAAK1B,KAAKG,QAAM,CAI1B,aAAA8B,CAAcL,GAChB,IAAC5B,KAAKG,OAAQ,OACZ,MAAAqF,EAAOxF,KAAKE,OAAO0B,GACzB,IAAK4D,EAAM,OAEL,MAAAC,EAAWzF,KAAKF,IAAIN,gBAAkB,SAExC,GAAAQ,KAAKI,WAAa,EAAG,CAEvB,MAAM8D,EAAMhB,KAAKC,IAAI,EAAGqC,EAAKtB,IAAMsB,EAAKvB,eAExC,YADAjE,KAAKa,UAAUa,KAAK,CAAEwC,IAAAA,EAAKuB,YAC3B,CAGF,MACMvB,EAAMsB,EAAKtB,IACXwB,EAASF,EAAKtB,IAAMsB,EAAKvB,cAEzB0B,EAAUzB,EAAMlE,KAAKK,QAJZ,EAKTuF,EAAYF,EAAS1F,KAAKK,QAAUL,KAAKI,UALhC,EAOXuF,EACF3F,KAAKa,UAAUa,KAAK,CAAEwC,MAAKuB,aAClBG,GACT5F,KAAKa,UAAUa,KAAK,CAAEwC,IAAKhB,KAAKC,IAAI,EAAGuC,EAAS1F,KAAKI,WAAYqF,YACnE,CAIM,WAAAhB,CAAYC,EAAaC,GAC3B,GAAA3E,KAAKU,UAAUmF,IAAInB,GAAa,OAAA1E,KAAKU,UAAUoF,IAAIpB,GAEjD/B,MACAoD,EADO/F,KAAKgG,UAAUrD,KACVC,SAAUS,MAAMqB,GAE5B7B,EAAU7C,KAAKF,IAAIX,OAAS,IAC5B6D,EAAIhD,KAAKF,IAAIL,cAAgB,EAG7BwG,EAFU/C,KAAKC,IAAI,EAAGN,EAAU,EAAIG,GAElB+C,EAAKtC,KAAKtE,MAE5B+G,EAAOlG,KAAKc,iBAAiBqF,eAAe,CAChDpC,UAAWW,EACX0B,KAAM,CAAEC,OAAQ,CAAEC,EAAG,EAAGC,EAAG,GAAK9C,KAAMsC,EAAKtC,MAC3C+C,QAAS,CACPC,YAAaR,EACbtB,SAMG,OAFF3E,KAAAU,UAAUgG,IAAIhC,EAAKwB,GACnBA,EAAAS,KAAKC,UAAQ,IAAM5G,KAAKU,UAAUmB,OAAO6C,KACvCwB,CAAA,GApNTxG,EAAgBf,GAAK,YADhB,IAAMkI,EAANnH,ECZA,MAAMoH,EAAgF,CAC3FpI,WACAqI,OAAQ,CAAClH,EAAUmH,IAAW,IAAIH,EAAgBpI,EAAqBoB,EAAUmH,GACjFC,QAAS,OACTC,aAAc,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -7,25 +7,35 @@ const manifest = {
|
|
|
7
7
|
version: "1.0.0",
|
|
8
8
|
provides: ["thumbnail"],
|
|
9
9
|
requires: ["render"],
|
|
10
|
-
optional: [],
|
|
10
|
+
optional: ["scroll"],
|
|
11
11
|
defaultConfig: {
|
|
12
12
|
enabled: true,
|
|
13
13
|
width: 150,
|
|
14
14
|
gap: 10,
|
|
15
15
|
buffer: 3,
|
|
16
|
-
labelHeight: 16
|
|
16
|
+
labelHeight: 16,
|
|
17
|
+
autoScroll: true,
|
|
18
|
+
scrollBehavior: "smooth",
|
|
19
|
+
imagePadding: 0
|
|
17
20
|
}
|
|
18
21
|
};
|
|
19
22
|
const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
20
23
|
constructor(id, registry, cfg) {
|
|
24
|
+
var _a;
|
|
21
25
|
super(id, registry);
|
|
22
26
|
this.cfg = cfg;
|
|
27
|
+
this.scrollCapability = null;
|
|
23
28
|
this.thumbs = [];
|
|
24
29
|
this.window = null;
|
|
30
|
+
this.viewportH = 0;
|
|
31
|
+
this.scrollY = 0;
|
|
25
32
|
this.emitWindow = createBehaviorEmitter();
|
|
26
33
|
this.refreshPages$ = createEmitter();
|
|
27
34
|
this.taskCache = /* @__PURE__ */ new Map();
|
|
35
|
+
this.canAutoScroll = true;
|
|
36
|
+
this.scrollTo$ = createBehaviorEmitter();
|
|
28
37
|
this.renderCapability = this.registry.getPlugin("render").provides();
|
|
38
|
+
this.scrollCapability = ((_a = this.registry.getPlugin("scroll")) == null ? void 0 : _a.provides()) ?? null;
|
|
29
39
|
this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {
|
|
30
40
|
this.taskCache.clear();
|
|
31
41
|
this.setWindowState(state);
|
|
@@ -36,6 +46,19 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
36
46
|
this.taskCache.delete(pageIdx);
|
|
37
47
|
}
|
|
38
48
|
});
|
|
49
|
+
if (this.scrollCapability && this.cfg.autoScroll !== false) {
|
|
50
|
+
this.scrollCapability.onPageChangeState(({ isChanging, targetPage }) => {
|
|
51
|
+
this.canAutoScroll = !isChanging;
|
|
52
|
+
if (!isChanging) {
|
|
53
|
+
this.scrollToThumb(targetPage - 1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.scrollCapability.onPageChange(({ pageNumber }) => {
|
|
57
|
+
if (this.canAutoScroll) {
|
|
58
|
+
this.scrollToThumb(pageNumber - 1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
39
62
|
}
|
|
40
63
|
/* ------------ init ------------------------------------------------ */
|
|
41
64
|
async initialize() {
|
|
@@ -43,24 +66,38 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
43
66
|
onRefreshPages(fn) {
|
|
44
67
|
return this.refreshPages$.on(fn);
|
|
45
68
|
}
|
|
69
|
+
onWindow(cb) {
|
|
70
|
+
return this.emitWindow.on(cb);
|
|
71
|
+
}
|
|
72
|
+
onScrollTo(cb) {
|
|
73
|
+
return this.scrollTo$.on(cb);
|
|
74
|
+
}
|
|
46
75
|
setWindowState(state) {
|
|
47
76
|
const core = state.core;
|
|
48
77
|
if (!core.document) return;
|
|
49
|
-
const
|
|
78
|
+
const OUTER_W = this.cfg.width ?? 120;
|
|
50
79
|
const L = this.cfg.labelHeight ?? 16;
|
|
51
80
|
const GAP = this.cfg.gap ?? 8;
|
|
81
|
+
const P = this.cfg.imagePadding ?? 0;
|
|
82
|
+
const INNER_W = Math.max(1, OUTER_W - 2 * P);
|
|
52
83
|
let offset = 0;
|
|
53
84
|
this.thumbs = core.document.pages.map((p) => {
|
|
54
85
|
const ratio = p.size.height / p.size.width;
|
|
55
|
-
const
|
|
56
|
-
const wrapH =
|
|
86
|
+
const imgH = Math.round(INNER_W * ratio);
|
|
87
|
+
const wrapH = P + imgH + P + L;
|
|
57
88
|
const meta = {
|
|
58
89
|
pageIndex: p.index,
|
|
59
|
-
width:
|
|
60
|
-
|
|
90
|
+
width: INNER_W,
|
|
91
|
+
// bitmap width (for <img> size)
|
|
92
|
+
height: imgH,
|
|
93
|
+
// bitmap height (for <img> size)
|
|
61
94
|
wrapperHeight: wrapH,
|
|
95
|
+
// full row height used by virtualizer
|
|
62
96
|
top: offset,
|
|
63
|
-
|
|
97
|
+
// top of the row
|
|
98
|
+
labelHeight: L,
|
|
99
|
+
padding: P
|
|
100
|
+
// NEW
|
|
64
101
|
};
|
|
65
102
|
offset += wrapH + GAP;
|
|
66
103
|
return meta;
|
|
@@ -70,21 +107,21 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
70
107
|
end: -1,
|
|
71
108
|
items: [],
|
|
72
109
|
totalHeight: offset - GAP
|
|
73
|
-
// last item has no gap below
|
|
74
110
|
};
|
|
75
111
|
this.emitWindow.emit(this.window);
|
|
76
112
|
}
|
|
77
113
|
/* ------------ capability ----------------------------------------- */
|
|
78
114
|
buildCapability() {
|
|
79
115
|
return {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
renderThumb: (idx, dpr) => this.renderThumb(idx, dpr)
|
|
116
|
+
renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),
|
|
117
|
+
scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx)
|
|
83
118
|
};
|
|
84
119
|
}
|
|
85
|
-
/* ------------ windowing
|
|
120
|
+
/* ------------ windowing & viewport state ------------------------- */
|
|
86
121
|
updateWindow(scrollY, viewportH) {
|
|
87
122
|
const BUF = this.cfg.buffer ?? 3;
|
|
123
|
+
this.scrollY = scrollY;
|
|
124
|
+
this.viewportH = viewportH;
|
|
88
125
|
let low = 0, high = this.thumbs.length - 1, first = 0;
|
|
89
126
|
while (low <= high) {
|
|
90
127
|
const mid = low + high >> 1;
|
|
@@ -109,12 +146,37 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
109
146
|
};
|
|
110
147
|
this.emitWindow.emit(this.window);
|
|
111
148
|
}
|
|
149
|
+
/* ------------ scroll helper now in plugin ------------------------ */
|
|
150
|
+
scrollToThumb(pageIdx) {
|
|
151
|
+
if (!this.window) return;
|
|
152
|
+
const item = this.thumbs[pageIdx];
|
|
153
|
+
if (!item) return;
|
|
154
|
+
const behavior = this.cfg.scrollBehavior ?? "smooth";
|
|
155
|
+
if (this.viewportH <= 0) {
|
|
156
|
+
const top2 = Math.max(0, item.top - item.wrapperHeight);
|
|
157
|
+
this.scrollTo$.emit({ top: top2, behavior });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const margin = 8;
|
|
161
|
+
const top = item.top;
|
|
162
|
+
const bottom = item.top + item.wrapperHeight;
|
|
163
|
+
const needsUp = top < this.scrollY + margin;
|
|
164
|
+
const needsDown = bottom > this.scrollY + this.viewportH - margin;
|
|
165
|
+
if (needsUp) {
|
|
166
|
+
this.scrollTo$.emit({ top, behavior });
|
|
167
|
+
} else if (needsDown) {
|
|
168
|
+
this.scrollTo$.emit({ top: Math.max(0, bottom - this.viewportH), behavior });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
112
171
|
/* ------------ thumbnail raster ----------------------------------- */
|
|
113
172
|
renderThumb(idx, dpr) {
|
|
114
173
|
if (this.taskCache.has(idx)) return this.taskCache.get(idx);
|
|
115
174
|
const core = this.coreState.core;
|
|
116
175
|
const page = core.document.pages[idx];
|
|
117
|
-
const
|
|
176
|
+
const OUTER_W = this.cfg.width ?? 120;
|
|
177
|
+
const P = this.cfg.imagePadding ?? 0;
|
|
178
|
+
const INNER_W = Math.max(1, OUTER_W - 2 * P);
|
|
179
|
+
const scale = INNER_W / page.size.width;
|
|
118
180
|
const task = this.renderCapability.renderPageRect({
|
|
119
181
|
pageIndex: idx,
|
|
120
182
|
rect: { origin: { x: 0, y: 0 }, size: page.size },
|
|
@@ -124,9 +186,7 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
124
186
|
}
|
|
125
187
|
});
|
|
126
188
|
this.taskCache.set(idx, task);
|
|
127
|
-
task.wait(ignore, () =>
|
|
128
|
-
this.taskCache.delete(idx);
|
|
129
|
-
});
|
|
189
|
+
task.wait(ignore, () => this.taskCache.delete(idx));
|
|
130
190
|
return task;
|
|
131
191
|
}
|
|
132
192
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/lib/manifest.ts","../src/lib/thumbnail-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { ThumbnailPluginConfig } from './types';\n\nexport const THUMBNAIL_PLUGIN_ID = 'thumbnail';\n\nexport const manifest: PluginManifest<ThumbnailPluginConfig> = {\n id: THUMBNAIL_PLUGIN_ID,\n name: 'Thumbnail Plugin',\n version: '1.0.0',\n provides: ['thumbnail'],\n requires: ['render'],\n optional: [],\n defaultConfig: {\n enabled: true,\n width: 150,\n gap: 10,\n buffer: 3,\n labelHeight: 16,\n },\n};\n","import {\n BasePlugin,\n CoreState,\n createBehaviorEmitter,\n createEmitter,\n PluginRegistry,\n REFRESH_PAGES,\n SET_DOCUMENT,\n StoreState,\n Unsubscribe,\n} from '@embedpdf/core';\nimport { ThumbMeta, ThumbnailPluginConfig, WindowState } from './types';\nimport { ThumbnailCapability } from './types';\nimport { ignore, PdfErrorReason, Task } from '@embedpdf/models';\nimport { RenderCapability, RenderPlugin } from '@embedpdf/plugin-render';\n\nexport class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {\n static readonly id = 'thumbnail' as const;\n\n private renderCapability: RenderCapability;\n private thumbs: ThumbMeta[] = [];\n private window: WindowState | null = null;\n private readonly emitWindow = createBehaviorEmitter<WindowState>();\n private readonly refreshPages$ = createEmitter<number[]>();\n private readonly taskCache = new Map<number, Task<Blob, PdfErrorReason>>();\n\n constructor(\n id: string,\n registry: PluginRegistry,\n private cfg: ThumbnailPluginConfig,\n ) {\n super(id, registry);\n\n this.renderCapability = this.registry.getPlugin<RenderPlugin>('render')!.provides();\n\n this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {\n this.taskCache.clear();\n this.setWindowState(state);\n });\n\n this.coreStore.onAction(REFRESH_PAGES, (action) => {\n this.refreshPages$.emit(action.payload);\n for (const pageIdx of action.payload) {\n this.taskCache.delete(pageIdx);\n }\n });\n }\n\n /* ------------ init ------------------------------------------------ */\n async initialize(): Promise<void> {}\n\n public onRefreshPages(fn: (pages: number[]) => void): Unsubscribe {\n return this.refreshPages$.on(fn);\n }\n\n private setWindowState(state: StoreState<CoreState>) {\n const core = state.core;\n\n if (!core.document) return;\n\n const W = this.cfg.width ?? 120;\n const L = this.cfg.labelHeight ?? 16; // label\n const GAP = this.cfg.gap ?? 8;\n\n let offset = 0;\n this.thumbs = core.document.pages.map((p) => {\n const ratio = p.size.height / p.size.width;\n const thumbH = Math.round(W * ratio);\n const wrapH = thumbH + L; // no GAP here\n\n const meta: ThumbMeta = {\n pageIndex: p.index,\n width: W,\n height: thumbH,\n wrapperHeight: wrapH,\n top: offset,\n labelHeight: L,\n };\n offset += wrapH + GAP; // GAP added *after* wrapper\n return meta;\n });\n\n this.window = {\n start: -1,\n end: -1,\n items: [],\n totalHeight: offset - GAP, // last item has no gap below\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ capability ----------------------------------------- */\n protected buildCapability(): ThumbnailCapability {\n return {\n onWindow: this.emitWindow.on,\n setViewport: (y, h) => this.updateWindow(y, h),\n renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),\n };\n }\n\n /* ------------ windowing math ------------------------------------- */\n private updateWindow(scrollY: number, viewportH: number) {\n const BUF = this.cfg.buffer ?? 3;\n\n /* -------- find first visible wrapper ---------- */\n let low = 0,\n high = this.thumbs.length - 1,\n first = 0;\n while (low <= high) {\n const mid = (low + high) >> 1;\n const m = this.thumbs[mid];\n if (m.top + m.wrapperHeight < scrollY) low = mid + 1;\n else {\n first = mid;\n high = mid - 1;\n }\n }\n\n /* -------- find last visible + buffer ---------- */\n let last = first;\n const limit = scrollY + viewportH;\n while (last + 1 < this.thumbs.length && this.thumbs[last].top < limit) last++;\n last = Math.min(this.thumbs.length - 1, last + BUF);\n\n const start = Math.max(0, first - BUF);\n if (this.window && start === this.window.start && last === this.window.end) return;\n\n this.window = {\n start,\n end: last,\n items: this.thumbs.slice(start, last + 1),\n totalHeight: this.window!.totalHeight,\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ thumbnail raster ----------------------------------- */\n private renderThumb(idx: number, dpr: number) {\n if (this.taskCache.has(idx)) return this.taskCache.get(idx)!;\n\n const core = this.coreState.core;\n const page = core.document!.pages[idx];\n const scale = (this.cfg.width ?? 120) / page.size.width;\n\n const task = this.renderCapability.renderPageRect({\n pageIndex: idx,\n rect: { origin: { x: 0, y: 0 }, size: page.size },\n options: {\n scaleFactor: scale,\n dpr,\n },\n });\n\n this.taskCache.set(idx, task);\n\n task.wait(ignore, () => {\n this.taskCache.delete(idx);\n });\n\n return task;\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, THUMBNAIL_PLUGIN_ID } from './manifest';\nimport { ThumbnailPluginConfig } from './types';\nimport { ThumbnailPlugin } from './thumbnail-plugin';\n\nexport const ThumbnailPluginPackage: PluginPackage<ThumbnailPlugin, ThumbnailPluginConfig> = {\n manifest,\n create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),\n reducer: () => {},\n initialState: {},\n};\n\nexport * from './thumbnail-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/lib/manifest.ts","../src/lib/thumbnail-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { ThumbnailPluginConfig } from './types';\n\nexport const THUMBNAIL_PLUGIN_ID = 'thumbnail';\n\nexport const manifest: PluginManifest<ThumbnailPluginConfig> = {\n id: THUMBNAIL_PLUGIN_ID,\n name: 'Thumbnail Plugin',\n version: '1.0.0',\n provides: ['thumbnail'],\n requires: ['render'],\n optional: ['scroll'],\n defaultConfig: {\n enabled: true,\n width: 150,\n gap: 10,\n buffer: 3,\n labelHeight: 16,\n autoScroll: true,\n scrollBehavior: 'smooth',\n imagePadding: 0,\n },\n};\n","import {\n BasePlugin,\n CoreState,\n createBehaviorEmitter,\n createEmitter,\n PluginRegistry,\n REFRESH_PAGES,\n SET_DOCUMENT,\n StoreState,\n Unsubscribe,\n} from '@embedpdf/core';\nimport { ScrollToOptions, ThumbMeta, ThumbnailPluginConfig, WindowState } from './types';\nimport { ThumbnailCapability } from './types';\nimport { ignore, PdfErrorReason, Task } from '@embedpdf/models';\nimport { RenderCapability, RenderPlugin } from '@embedpdf/plugin-render';\nimport { ScrollCapability, ScrollPlugin } from '@embedpdf/plugin-scroll';\n\nexport class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {\n static readonly id = 'thumbnail' as const;\n\n private renderCapability: RenderCapability;\n private scrollCapability: ScrollCapability | null = null;\n private thumbs: ThumbMeta[] = [];\n private window: WindowState | null = null;\n\n // track viewport metrics for scroll decisions\n private viewportH: number = 0;\n private scrollY: number = 0;\n\n private readonly emitWindow = createBehaviorEmitter<WindowState>();\n private readonly refreshPages$ = createEmitter<number[]>();\n private readonly taskCache = new Map<number, Task<Blob, PdfErrorReason>>();\n private canAutoScroll = true;\n // ask pane to scroll to a specific top\n private readonly scrollTo$ = createBehaviorEmitter<ScrollToOptions>();\n\n constructor(\n id: string,\n registry: PluginRegistry,\n private cfg: ThumbnailPluginConfig,\n ) {\n super(id, registry);\n\n this.renderCapability = this.registry.getPlugin<RenderPlugin>('render')!.provides();\n this.scrollCapability = this.registry.getPlugin<ScrollPlugin>('scroll')?.provides() ?? null;\n\n this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {\n this.taskCache.clear();\n this.setWindowState(state);\n });\n\n this.coreStore.onAction(REFRESH_PAGES, (action) => {\n this.refreshPages$.emit(action.payload);\n for (const pageIdx of action.payload) {\n this.taskCache.delete(pageIdx);\n }\n });\n\n // auto-scroll thumbnails when the main scroller's current page changes\n if (this.scrollCapability && this.cfg.autoScroll !== false) {\n this.scrollCapability.onPageChangeState(({ isChanging, targetPage }) => {\n this.canAutoScroll = !isChanging;\n if (!isChanging) {\n this.scrollToThumb(targetPage - 1);\n }\n });\n this.scrollCapability.onPageChange(({ pageNumber }) => {\n if (this.canAutoScroll) {\n this.scrollToThumb(pageNumber - 1);\n }\n });\n }\n }\n\n /* ------------ init ------------------------------------------------ */\n async initialize(): Promise<void> {}\n\n public onRefreshPages(fn: (pages: number[]) => void): Unsubscribe {\n return this.refreshPages$.on(fn);\n }\n\n public onWindow(cb: (w: WindowState) => void): Unsubscribe {\n return this.emitWindow.on(cb);\n }\n\n public onScrollTo(cb: (o: ScrollToOptions) => void): Unsubscribe {\n return this.scrollTo$.on(cb);\n }\n\n private setWindowState(state: StoreState<CoreState>) {\n const core = state.core;\n if (!core.document) return;\n\n const OUTER_W = this.cfg.width ?? 120;\n const L = this.cfg.labelHeight ?? 16;\n const GAP = this.cfg.gap ?? 8;\n const P = this.cfg.imagePadding ?? 0;\n\n // Inner bitmap width cannot go below 1px\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n let offset = 0;\n this.thumbs = core.document.pages.map((p) => {\n const ratio = p.size.height / p.size.width;\n const imgH = Math.round(INNER_W * ratio);\n const wrapH = P + imgH + P + L; // padding + image + padding + label\n\n const meta: ThumbMeta = {\n pageIndex: p.index,\n width: INNER_W, // bitmap width (for <img> size)\n height: imgH, // bitmap height (for <img> size)\n wrapperHeight: wrapH, // full row height used by virtualizer\n top: offset, // top of the row\n labelHeight: L,\n padding: P, // NEW\n };\n offset += wrapH + GAP;\n return meta;\n });\n\n this.window = {\n start: -1,\n end: -1,\n items: [],\n totalHeight: offset - GAP,\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ capability ----------------------------------------- */\n protected buildCapability(): ThumbnailCapability {\n return {\n renderThumb: (idx, dpr) => this.renderThumb(idx, dpr),\n scrollToThumb: (pageIdx) => this.scrollToThumb(pageIdx),\n };\n }\n\n /* ------------ windowing & viewport state ------------------------- */\n public updateWindow(scrollY: number, viewportH: number) {\n const BUF = this.cfg.buffer ?? 3;\n\n // remember latest viewport metrics for scroll decisions\n this.scrollY = scrollY;\n this.viewportH = viewportH;\n\n /* find first visible */\n let low = 0,\n high = this.thumbs.length - 1,\n first = 0;\n while (low <= high) {\n const mid = (low + high) >> 1;\n const m = this.thumbs[mid];\n if (m.top + m.wrapperHeight < scrollY) low = mid + 1;\n else {\n first = mid;\n high = mid - 1;\n }\n }\n\n /* find last visible + buffer */\n let last = first;\n const limit = scrollY + viewportH;\n while (last + 1 < this.thumbs.length && this.thumbs[last].top < limit) last++;\n last = Math.min(this.thumbs.length - 1, last + BUF);\n\n const start = Math.max(0, first - BUF);\n if (this.window && start === this.window.start && last === this.window.end) return;\n\n this.window = {\n start,\n end: last,\n items: this.thumbs.slice(start, last + 1),\n totalHeight: this.window!.totalHeight,\n };\n this.emitWindow.emit(this.window);\n }\n\n /* ------------ scroll helper now in plugin ------------------------ */\n private scrollToThumb(pageIdx: number) {\n if (!this.window) return;\n const item = this.thumbs[pageIdx];\n if (!item) return;\n\n const behavior = this.cfg.scrollBehavior ?? 'smooth';\n\n if (this.viewportH <= 0) {\n // Center the thumbnail in the viewport\n const top = Math.max(0, item.top - item.wrapperHeight);\n this.scrollTo$.emit({ top, behavior });\n return;\n }\n\n const margin = 8;\n const top = item.top;\n const bottom = item.top + item.wrapperHeight;\n\n const needsUp = top < this.scrollY + margin;\n const needsDown = bottom > this.scrollY + this.viewportH - margin;\n\n if (needsUp) {\n this.scrollTo$.emit({ top, behavior });\n } else if (needsDown) {\n this.scrollTo$.emit({ top: Math.max(0, bottom - this.viewportH), behavior });\n }\n }\n\n /* ------------ thumbnail raster ----------------------------------- */\n private renderThumb(idx: number, dpr: number) {\n if (this.taskCache.has(idx)) return this.taskCache.get(idx)!;\n\n const core = this.coreState.core;\n const page = core.document!.pages[idx];\n\n const OUTER_W = this.cfg.width ?? 120;\n const P = this.cfg.imagePadding ?? 0;\n const INNER_W = Math.max(1, OUTER_W - 2 * P);\n\n const scale = INNER_W / page.size.width; // scale to inner width (excludes padding)\n\n const task = this.renderCapability.renderPageRect({\n pageIndex: idx,\n rect: { origin: { x: 0, y: 0 }, size: page.size },\n options: {\n scaleFactor: scale,\n dpr,\n },\n });\n\n this.taskCache.set(idx, task);\n task.wait(ignore, () => this.taskCache.delete(idx));\n return task;\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, THUMBNAIL_PLUGIN_ID } from './manifest';\nimport { ThumbnailPluginConfig } from './types';\nimport { ThumbnailPlugin } from './thumbnail-plugin';\n\nexport const ThumbnailPluginPackage: PluginPackage<ThumbnailPlugin, ThumbnailPluginConfig> = {\n manifest,\n create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),\n reducer: () => {},\n initialState: {},\n};\n\nexport * from './thumbnail-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"names":["top"],"mappings":"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"}
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import { BasePlugin, PluginRegistry, Unsubscribe } from '@embedpdf/core';
|
|
2
|
-
import { ThumbnailPluginConfig, ThumbnailCapability } from './types';
|
|
2
|
+
import { ScrollToOptions, ThumbnailPluginConfig, WindowState, ThumbnailCapability } from './types';
|
|
3
3
|
export declare class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {
|
|
4
4
|
private cfg;
|
|
5
5
|
static readonly id: "thumbnail";
|
|
6
6
|
private renderCapability;
|
|
7
|
+
private scrollCapability;
|
|
7
8
|
private thumbs;
|
|
8
9
|
private window;
|
|
10
|
+
private viewportH;
|
|
11
|
+
private scrollY;
|
|
9
12
|
private readonly emitWindow;
|
|
10
13
|
private readonly refreshPages$;
|
|
11
14
|
private readonly taskCache;
|
|
15
|
+
private canAutoScroll;
|
|
16
|
+
private readonly scrollTo$;
|
|
12
17
|
constructor(id: string, registry: PluginRegistry, cfg: ThumbnailPluginConfig);
|
|
13
18
|
initialize(): Promise<void>;
|
|
14
19
|
onRefreshPages(fn: (pages: number[]) => void): Unsubscribe;
|
|
20
|
+
onWindow(cb: (w: WindowState) => void): Unsubscribe;
|
|
21
|
+
onScrollTo(cb: (o: ScrollToOptions) => void): Unsubscribe;
|
|
15
22
|
private setWindowState;
|
|
16
23
|
protected buildCapability(): ThumbnailCapability;
|
|
17
|
-
|
|
24
|
+
updateWindow(scrollY: number, viewportH: number): void;
|
|
25
|
+
private scrollToThumb;
|
|
18
26
|
private renderThumb;
|
|
19
27
|
}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
import { BasePluginConfig } from '@embedpdf/core';
|
|
2
2
|
import { PdfErrorReason, Task } from '@embedpdf/models';
|
|
3
|
+
import { ScrollBehavior } from '@embedpdf/plugin-scroll';
|
|
3
4
|
export interface ThumbnailPluginConfig extends BasePluginConfig {
|
|
4
5
|
width?: number;
|
|
5
6
|
gap?: number;
|
|
6
7
|
buffer?: number;
|
|
7
8
|
labelHeight?: number;
|
|
9
|
+
autoScroll?: boolean;
|
|
10
|
+
scrollBehavior?: ScrollBehavior;
|
|
11
|
+
imagePadding?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ScrollToOptions {
|
|
14
|
+
top: number;
|
|
15
|
+
behavior?: ScrollBehavior;
|
|
8
16
|
}
|
|
9
17
|
export interface ThumbMeta {
|
|
10
18
|
pageIndex: number;
|
|
19
|
+
/** Inner bitmap size (excludes padding). */
|
|
11
20
|
width: number;
|
|
12
21
|
height: number;
|
|
22
|
+
/** Total row height (padding*2 + image height + labelHeight). */
|
|
13
23
|
wrapperHeight: number;
|
|
24
|
+
/** Top offset of the entire row (including padding + label). */
|
|
14
25
|
top: number;
|
|
15
26
|
labelHeight: number;
|
|
27
|
+
/** Padding applied around the image (px). */
|
|
28
|
+
padding?: number;
|
|
16
29
|
}
|
|
17
30
|
export interface WindowState {
|
|
18
31
|
start: number;
|
|
@@ -21,10 +34,7 @@ export interface WindowState {
|
|
|
21
34
|
totalHeight: number;
|
|
22
35
|
}
|
|
23
36
|
export interface ThumbnailCapability {
|
|
24
|
-
|
|
25
|
-
setViewport(offsetY: number, viewportH: number): void;
|
|
26
|
-
/** listen to window changes */
|
|
27
|
-
onWindow(cb: (w: WindowState) => void): () => void;
|
|
37
|
+
scrollToThumb(pageIdx: number): void;
|
|
28
38
|
/** lazily render one thumb */
|
|
29
39
|
renderThumb(pageIdx: number, dpr: number): Task<Blob, PdfErrorReason>;
|
|
30
40
|
}
|
package/dist/preact/index.cjs
CHANGED
|
@@ -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"),l=require("@embedpdf/models"),o=()=>e.usePlugin(t.ThumbnailPlugin.id),
|
|
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"),l=require("@embedpdf/models"),o=()=>e.usePlugin(t.ThumbnailPlugin.id),u=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...i}){const{provides:s}=u(),{plugin:c}=o(),[a,d]=n.useState(),p=n.useRef(null),[f,b]=n.useState(0);return n.useEffect((()=>{if(c)return c.onRefreshPages((t=>{t.includes(e.pageIndex)&&b((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,d(t)}),l.ignore),()=>{p.current?(URL.revokeObjectURL(p.current),p.current=null):null==t||t.abort({code:l.PdfErrorCode.Cancelled,message:"canceled render task"})}}),[s,e.pageIndex,f]),a?r.jsx("img",{src:a,onLoad:()=>{p.current&&(URL.revokeObjectURL(p.current),p.current=null)},style:t,...i}):null},exports.ThumbnailsPane=function({style:e,scrollOptions:t,selectedPage:l,...u}){const{plugin:i}=o(),s=n.useRef(null),[c,a]=n.useState(null);return n.useEffect((()=>null==i?void 0:i.onWindow(a)),[i]),n.useEffect((()=>{const e=s.current;if(!e)return;const t=()=>null==i?void 0:i.updateWindow(e.scrollTop,e.clientHeight);return e.addEventListener("scroll",t),()=>e.removeEventListener("scroll",t)}),[i]),n.useEffect((()=>{const e=s.current;e&&i&&i.updateWindow(e.scrollTop,e.clientHeight)}),[c,i]),n.useEffect((()=>{const e=s.current;if(e&&i&&c)return i.onScrollTo((({top:t,behavior:r})=>{e.scrollTo({top:t,behavior:r})}))}),[i,!!c]),r.jsx("div",{ref:s,style:{overflowY:"auto",position:"relative",...e},...u,children:r.jsx("div",{style:{height:(null==c?void 0:c.totalHeight)??0,position:"relative"},children:null==c?void 0:c.items.map((e=>u.children(e)))})})},exports.useThumbnailCapability=u,exports.useThumbnailPlugin=o,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 {
|
|
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 // 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 return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\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","onScrollTo","top","behavior","scrollTo","ref","overflowY","position","children","height","totalHeight","items","map","m"],"mappings":"iRAGaA,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,aAGzDK,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,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,EAAgB8C,YAAW,EAAGC,MAAKC,eACxCT,EAAGU,SAAS,CAAEF,MAAKC,YAAU,GAC9B,GACA,CAAChD,IAAmBgB,UAGpB,MAAI,CAAAkC,IAAKd,EAAazC,MAAO,CAAEwD,UAAW,OAAQC,SAAU,cAAezD,MAAaC,EACvFyD,eAAC,MAAI,CAAA1D,MAAO,CAAE2D,QAAQ,MAAAtC,SAAAA,EAAQuC,cAAe,EAAGH,SAAU,YACvDC,SAAQ,MAAArC,OAAA,EAAAA,EAAAwC,MAAMC,KAAKC,GAAM9D,EAAMyD,SAASK,QAIjD"}
|
package/dist/preact/index.js
CHANGED
|
@@ -7,46 +7,30 @@ 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({
|
|
11
|
-
|
|
12
|
-
selectedPage,
|
|
13
|
-
scrollOptions = { behavior: "smooth", block: "nearest", inline: "nearest" },
|
|
14
|
-
...props
|
|
15
|
-
}) {
|
|
16
|
-
const { provides: thumbs } = useThumbnailCapability();
|
|
10
|
+
function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }) {
|
|
11
|
+
const { plugin: thumbnailPlugin } = useThumbnailPlugin();
|
|
17
12
|
const viewportRef = useRef(null);
|
|
18
13
|
const [window2, setWindow] = useState(null);
|
|
19
|
-
useEffect(() =>
|
|
14
|
+
useEffect(() => thumbnailPlugin == null ? void 0 : thumbnailPlugin.onWindow(setWindow), [thumbnailPlugin]);
|
|
20
15
|
useEffect(() => {
|
|
21
16
|
const vp = viewportRef.current;
|
|
22
17
|
if (!vp) return;
|
|
23
|
-
const onScroll = () =>
|
|
18
|
+
const onScroll = () => thumbnailPlugin == null ? void 0 : thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
|
|
24
19
|
vp.addEventListener("scroll", onScroll);
|
|
25
20
|
return () => vp.removeEventListener("scroll", onScroll);
|
|
26
|
-
}, [
|
|
21
|
+
}, [thumbnailPlugin]);
|
|
27
22
|
useEffect(() => {
|
|
28
23
|
const vp = viewportRef.current;
|
|
29
|
-
if (!vp || !
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
}, [window2, thumbs]);
|
|
24
|
+
if (!vp || !thumbnailPlugin) return;
|
|
25
|
+
thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
|
|
26
|
+
}, [window2, thumbnailPlugin]);
|
|
34
27
|
useEffect(() => {
|
|
35
|
-
if (!selectedPage || !window2) return;
|
|
36
|
-
const item = window2.items.find((it) => it.pageIndex + 1 === selectedPage);
|
|
37
|
-
if (!item) return;
|
|
38
28
|
const vp = viewportRef.current;
|
|
39
|
-
if (!vp) return;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
vp.scrollTo({
|
|
45
|
-
top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,
|
|
46
|
-
...scrollOptions
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}, [selectedPage, window2, scrollOptions]);
|
|
29
|
+
if (!vp || !thumbnailPlugin || !window2) return;
|
|
30
|
+
return thumbnailPlugin.onScrollTo(({ top, behavior }) => {
|
|
31
|
+
vp.scrollTo({ top, behavior });
|
|
32
|
+
});
|
|
33
|
+
}, [thumbnailPlugin, !!window2]);
|
|
50
34
|
return /* @__PURE__ */ jsx("div", { ref: viewportRef, style: { overflowY: "auto", position: "relative", ...style }, ...props, 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)) }) });
|
|
51
35
|
}
|
|
52
36
|
function ThumbImg({ meta, style, ...props }) {
|
package/dist/preact/index.js.map
CHANGED
|
@@ -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 {
|
|
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 // 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 return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\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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"}
|
package/dist/react/index.cjs
CHANGED
|
@@ -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"),l=require("@embedpdf/models"),
|
|
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"),l=require("@embedpdf/models"),o=()=>e.usePlugin(t.ThumbnailPlugin.id),u=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...i}){const{provides:s}=u(),{plugin:c}=o(),[a,d]=n.useState(),p=n.useRef(null),[f,b]=n.useState(0);return n.useEffect((()=>{if(c)return c.onRefreshPages((t=>{t.includes(e.pageIndex)&&b((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,d(t)}),l.ignore),()=>{p.current?(URL.revokeObjectURL(p.current),p.current=null):null==t||t.abort({code:l.PdfErrorCode.Cancelled,message:"canceled render task"})}}),[s,e.pageIndex,f]),a?r.jsx("img",{src:a,onLoad:()=>{p.current&&(URL.revokeObjectURL(p.current),p.current=null)},style:t,...i}):null},exports.ThumbnailsPane=function({style:e,scrollOptions:t,selectedPage:l,...u}){const{plugin:i}=o(),s=n.useRef(null),[c,a]=n.useState(null);return n.useEffect((()=>null==i?void 0:i.onWindow(a)),[i]),n.useEffect((()=>{const e=s.current;if(!e)return;const t=()=>null==i?void 0:i.updateWindow(e.scrollTop,e.clientHeight);return e.addEventListener("scroll",t),()=>e.removeEventListener("scroll",t)}),[i]),n.useEffect((()=>{const e=s.current;e&&i&&i.updateWindow(e.scrollTop,e.clientHeight)}),[c,i]),n.useEffect((()=>{const e=s.current;if(e&&i&&c)return i.onScrollTo((({top:t,behavior:r})=>{e.scrollTo({top:t,behavior:r})}))}),[i,!!c]),r.jsx("div",{ref:s,style:{overflowY:"auto",position:"relative",...e},...u,children:r.jsx("div",{style:{height:(null==c?void 0:c.totalHeight)??0,position:"relative"},children:null==c?void 0:c.items.map((e=>u.children(e)))})})},exports.useThumbnailCapability=u,exports.useThumbnailPlugin=o,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
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -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 {
|
|
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 // 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 return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\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","onScrollTo","top","behavior","scrollTo","ref","overflowY","position","children","height","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,aAGzDK,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,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,EAAgB8C,YAAW,EAAGC,MAAKC,eACxCT,EAAGU,SAAS,CAAEF,MAAKC,YAAU,GAC9B,GACA,CAAChD,IAAmBgB,UAGpB,MAAI,CAAAkC,IAAKd,EAAazC,MAAO,CAAEwD,UAAW,OAAQC,SAAU,cAAezD,MAAaC,EACvFyD,eAAC,MAAI,CAAA1D,MAAO,CAAE2D,QAAQ,MAAAtC,SAAAA,EAAQuC,cAAe,EAAGH,SAAU,YACvDC,SAAQ,MAAArC,OAAA,EAAAA,EAAAwC,MAAMC,KAAKC,GAAM9D,EAAMyD,SAASK,QAIjD"}
|
package/dist/react/index.js
CHANGED
|
@@ -6,46 +6,30 @@ 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({
|
|
10
|
-
|
|
11
|
-
selectedPage,
|
|
12
|
-
scrollOptions = { behavior: "smooth", block: "nearest", inline: "nearest" },
|
|
13
|
-
...props
|
|
14
|
-
}) {
|
|
15
|
-
const { provides: thumbs } = useThumbnailCapability();
|
|
9
|
+
function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }) {
|
|
10
|
+
const { plugin: thumbnailPlugin } = useThumbnailPlugin();
|
|
16
11
|
const viewportRef = useRef(null);
|
|
17
12
|
const [window2, setWindow] = useState(null);
|
|
18
|
-
useEffect(() =>
|
|
13
|
+
useEffect(() => thumbnailPlugin == null ? void 0 : thumbnailPlugin.onWindow(setWindow), [thumbnailPlugin]);
|
|
19
14
|
useEffect(() => {
|
|
20
15
|
const vp = viewportRef.current;
|
|
21
16
|
if (!vp) return;
|
|
22
|
-
const onScroll = () =>
|
|
17
|
+
const onScroll = () => thumbnailPlugin == null ? void 0 : thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
|
|
23
18
|
vp.addEventListener("scroll", onScroll);
|
|
24
19
|
return () => vp.removeEventListener("scroll", onScroll);
|
|
25
|
-
}, [
|
|
20
|
+
}, [thumbnailPlugin]);
|
|
26
21
|
useEffect(() => {
|
|
27
22
|
const vp = viewportRef.current;
|
|
28
|
-
if (!vp || !
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
}, [window2, thumbs]);
|
|
23
|
+
if (!vp || !thumbnailPlugin) return;
|
|
24
|
+
thumbnailPlugin.updateWindow(vp.scrollTop, vp.clientHeight);
|
|
25
|
+
}, [window2, thumbnailPlugin]);
|
|
33
26
|
useEffect(() => {
|
|
34
|
-
if (!selectedPage || !window2) return;
|
|
35
|
-
const item = window2.items.find((it) => it.pageIndex + 1 === selectedPage);
|
|
36
|
-
if (!item) return;
|
|
37
27
|
const vp = viewportRef.current;
|
|
38
|
-
if (!vp) return;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
vp.scrollTo({
|
|
44
|
-
top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,
|
|
45
|
-
...scrollOptions
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}, [selectedPage, window2, scrollOptions]);
|
|
28
|
+
if (!vp || !thumbnailPlugin || !window2) return;
|
|
29
|
+
return thumbnailPlugin.onScrollTo(({ top, behavior }) => {
|
|
30
|
+
vp.scrollTo({ top, behavior });
|
|
31
|
+
});
|
|
32
|
+
}, [thumbnailPlugin, !!window2]);
|
|
49
33
|
return /* @__PURE__ */ jsx("div", { ref: viewportRef, style: { overflowY: "auto", position: "relative", ...style }, ...props, 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)) }) });
|
|
50
34
|
}
|
|
51
35
|
function ThumbImg({ meta, style, ...props }) {
|
package/dist/react/index.js.map
CHANGED
|
@@ -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 {
|
|
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 // 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 return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\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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"}
|
|
@@ -3,8 +3,10 @@ import { ThumbMeta } from '../../lib/index.ts';
|
|
|
3
3
|
type ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {
|
|
4
4
|
style?: CSSProperties;
|
|
5
5
|
children: (m: ThumbMeta) => ReactNode;
|
|
6
|
+
/** @deprecated use scrollToThumb via capability or rely on autoScroll */
|
|
6
7
|
selectedPage?: number;
|
|
8
|
+
/** @deprecated behavior is now controlled by ThumbnailPluginConfig.scrollBehavior */
|
|
7
9
|
scrollOptions?: ScrollIntoViewOptions;
|
|
8
10
|
};
|
|
9
|
-
export declare function ThumbnailsPane({ style,
|
|
11
|
+
export declare function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }: ThumbnailsProps): import("preact").JSX.Element;
|
|
10
12
|
export {};
|
|
@@ -3,8 +3,10 @@ import { ThumbMeta } from '../../lib/index.ts';
|
|
|
3
3
|
type ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {
|
|
4
4
|
style?: CSSProperties;
|
|
5
5
|
children: (m: ThumbMeta) => ReactNode;
|
|
6
|
+
/** @deprecated use scrollToThumb via capability or rely on autoScroll */
|
|
6
7
|
selectedPage?: number;
|
|
8
|
+
/** @deprecated behavior is now controlled by ThumbnailPluginConfig.scrollBehavior */
|
|
7
9
|
scrollOptions?: ScrollIntoViewOptions;
|
|
8
10
|
};
|
|
9
|
-
export declare function ThumbnailsPane({ style,
|
|
11
|
+
export declare function ThumbnailsPane({ style, scrollOptions, selectedPage, ...props }: ThumbnailsProps): import("react/jsx-runtime").JSX.Element;
|
|
10
12
|
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ThumbMeta } from '../../lib/index.ts';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
meta: ThumbMeta;
|
|
4
|
+
};
|
|
5
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare var __VLS_1: {
|
|
2
|
+
meta: any;
|
|
3
|
+
};
|
|
4
|
+
type __VLS_Slots = {} & {
|
|
5
|
+
default?: (props: typeof __VLS_1) => any;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_component: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
8
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
9
|
+
export default _default;
|
|
10
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
11
|
+
new (): {
|
|
12
|
+
$slots: S;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './use-thumbnail';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ThumbnailPlugin } from '../../lib/index.ts';
|
|
2
|
+
export declare const useThumbnailPlugin: () => import('@embedpdf/core/vue').PluginState<ThumbnailPlugin>;
|
|
3
|
+
export declare const useThumbnailCapability: () => import('@embedpdf/core/vue').CapabilityState<Readonly<import('../../lib/index.ts').ThumbnailCapability>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/vue"),l=require("@embedpdf/plugin-thumbnail"),n=require("vue"),t=require("@embedpdf/models"),o=()=>e.usePlugin(l.ThumbnailPlugin.id),u=()=>e.useCapability(l.ThumbnailPlugin.id),r=n.defineComponent({__name:"thumbnails-pane",setup(e){const l=n.useAttrs(),{plugin:t}=o(),u=n.ref(null),r=n.ref(null);let a=null,i=null;return n.watchEffect((e=>{t.value&&(null==a||a(),a=t.value.onWindow((e=>r.value=e)),e((()=>null==a?void 0:a())))})),n.onMounted((()=>{const e=u.value;if(!e||!t.value)return;const l=()=>t.value.updateWindow(e.scrollTop,e.clientHeight);e.addEventListener("scroll",l),t.value.updateWindow(e.scrollTop,e.clientHeight),n.onBeforeUnmount((()=>{e.removeEventListener("scroll",l)}))})),n.watchEffect((e=>{const l=u.value;l&&t.value&&r.value&&(i=t.value.onScrollTo((({top:e,behavior:t})=>{n.nextTick((()=>{l.scrollTo({top:e,behavior:t})}))})),e((()=>null==i?void 0:i())))})),n.onBeforeUnmount((()=>{null==a||a(),null==i||i()})),(e,t)=>{var o,a;return n.openBlock(),n.createElementBlock("div",n.mergeProps({ref_key:"viewportRef",ref:u,style:{overflowY:"auto",position:"relative"}},n.unref(l)),[n.createElementVNode("div",{style:n.normalizeStyle({height:((null==(o=r.value)?void 0:o.totalHeight)??0)+"px",position:"relative"})},[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList((null==(a=r.value)?void 0:a.items)??[],(l=>n.renderSlot(e.$slots,"default",{key:l.pageIndex,meta:l}))),128))],4)],16)}}}),a=["src"],i=n.defineComponent({__name:"thumbnail-img",props:{meta:{}},setup(e){const l=e,r=n.useAttrs(),{provides:i}=u(),{plugin:s}=o(),c=n.ref(null);let d=null;const p=n.ref(0);let m=null;function v(){d&&(URL.revokeObjectURL(d),d=null)}n.watchEffect((e=>{s.value&&(null==m||m(),m=s.value.onRefreshPages((e=>{e.includes(l.meta.pageIndex)&&p.value++})),e((()=>null==m?void 0:m())))}));let f=null;return n.watch((()=>[l.meta.pageIndex,p.value,!!i.value]),(()=>{null==f||f(),function(){if(!i.value)return;const e=i.value.renderThumb(l.meta.pageIndex,window.devicePixelRatio);f=()=>e.abort({code:t.PdfErrorCode.Cancelled,message:"canceled render task"}),e.wait((e=>{v();const l=URL.createObjectURL(e);d=l,c.value=l}),t.ignore)}()}),{immediate:!0}),n.onBeforeUnmount((()=>{null==f||f(),v()})),(e,l)=>c.value?(n.openBlock(),n.createElementBlock("img",n.mergeProps({key:0,src:c.value},n.unref(r),{onLoad:v}),null,16,a)):n.createCommentVNode("",!0)}});exports.ThumbImg=i,exports.ThumbnailsPane=r,exports.useThumbnailCapability=u,exports.useThumbnailPlugin=o,Object.keys(l).forEach((e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>l[e]})}));
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/vue/hooks/use-thumbnail.ts","../../src/vue/components/thumbnails-pane.vue","../../src/vue/components/thumbnail-img.vue"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount, ref, watchEffect, nextTick, useAttrs } from 'vue';\nimport { useThumbnailPlugin } from '../hooks';\nimport type { WindowState } from '@embedpdf/plugin-thumbnail';\n\nconst attrs = useAttrs();\n\nconst { plugin: thumbnailPlugin } = useThumbnailPlugin();\nconst viewportRef = ref<HTMLDivElement | null>(null);\nconst windowState = ref<WindowState | null>(null);\n\nlet offWindow: (() => void) | null = null;\nlet offScrollTo: (() => void) | null = null;\n\nwatchEffect((onCleanup) => {\n if (!thumbnailPlugin.value) return;\n offWindow?.();\n offWindow = thumbnailPlugin.value.onWindow((w) => (windowState.value = w));\n onCleanup(() => offWindow?.());\n});\n\n// Setup scroll listener on mount\nonMounted(() => {\n const vp = viewportRef.value;\n if (!vp || !thumbnailPlugin.value) return;\n\n const onScroll = () => thumbnailPlugin.value!.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n\n // initial push\n thumbnailPlugin.value.updateWindow(vp.scrollTop, vp.clientHeight);\n\n onBeforeUnmount(() => {\n vp.removeEventListener('scroll', onScroll);\n });\n});\n\n// Setup scrollTo subscription only after window is ready\nwatchEffect((onCleanup) => {\n const vp = viewportRef.value;\n if (!vp || !thumbnailPlugin.value || !windowState.value) return;\n\n offScrollTo = thumbnailPlugin.value.onScrollTo(({ top, behavior }) => {\n // Wait for Vue to finish rendering the content before scrolling\n nextTick(() => {\n vp.scrollTo({ top, behavior });\n });\n });\n\n onCleanup(() => offScrollTo?.());\n});\n\nonBeforeUnmount(() => {\n offWindow?.();\n offScrollTo?.();\n});\n</script>\n\n<template>\n <div ref=\"viewportRef\" :style=\"{ overflowY: 'auto', position: 'relative' }\" v-bind=\"attrs\">\n <div :style=\"{ height: (windowState?.totalHeight ?? 0) + 'px', position: 'relative' }\">\n <!-- ✅ Use a template v-for to render the default scoped slot -->\n <template v-for=\"m in windowState?.items ?? []\" :key=\"m.pageIndex\">\n <slot :meta=\"m\" />\n </template>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount, ref, watch, watchEffect, useAttrs } from 'vue';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\nimport type { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\n\nconst props = defineProps<{ meta: ThumbMeta }>();\nconst attrs = useAttrs();\n\nconst { provides: thumbs } = useThumbnailCapability();\nconst { plugin: thumbnailPlugin } = useThumbnailPlugin();\n\nconst url = ref<string | null>(null);\nlet urlToRevoke: string | null = null;\nconst refreshTick = ref(0);\n\nlet offRefresh: (() => void) | null = null;\n\nwatchEffect((onCleanup) => {\n if (!thumbnailPlugin.value) return;\n offRefresh?.();\n offRefresh = thumbnailPlugin.value.onRefreshPages((pages) => {\n if (pages.includes(props.meta.pageIndex)) {\n refreshTick.value++;\n }\n });\n onCleanup(() => offRefresh?.());\n});\n\nfunction revoke() {\n if (urlToRevoke) {\n URL.revokeObjectURL(urlToRevoke);\n urlToRevoke = null;\n }\n}\n\nlet abortTask: (() => void) | null = null;\n\nfunction load() {\n if (!thumbs.value) return; // wait until capability exists\n\n const task = thumbs.value.renderThumb(props.meta.pageIndex, window.devicePixelRatio);\n abortTask = () =>\n task.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n\n task.wait((blob) => {\n revoke();\n const objectUrl = URL.createObjectURL(blob);\n urlToRevoke = objectUrl;\n url.value = objectUrl;\n }, ignore);\n}\n\n/* 🔧 Re-run when:\n - page changes,\n - the plugin tells us to refresh,\n - OR the capability becomes available later.\n*/\nwatch(\n () => [props.meta.pageIndex, refreshTick.value, !!thumbs.value],\n () => {\n abortTask?.();\n load();\n },\n { immediate: true },\n);\n\nonBeforeUnmount(() => {\n abortTask?.();\n revoke();\n});\n</script>\n\n<template>\n <img v-if=\"url\" :src=\"url\" v-bind=\"attrs\" @load=\"revoke\" />\n</template>\n"],"names":["useThumbnailPlugin","usePlugin","ThumbnailPlugin","id","useThumbnailCapability","useCapability","attrs","useAttrs","plugin","thumbnailPlugin","viewportRef","ref","windowState","offWindow","offScrollTo","vue$1","watchEffect","onCleanup","value","onWindow","w","onMounted","vp","onScroll","updateWindow","scrollTop","clientHeight","addEventListener","onBeforeUnmount","removeEventListener","onScrollTo","top","behavior","nextTick","scrollTo","_openBlock","_createElementBlock","_mergeProps","style","overflowY","position","_unref","unref","_createElementVNode","_normalizeStyle","normalizeStyle","height","_a","totalHeight","createElementBlock","_Fragment","Fragment","_renderList","_b","items","m","_renderSlot","_ctx","$slots","key","pageIndex","meta","props","__props","provides","thumbs","url","urlToRevoke","refreshTick","offRefresh","revoke","URL","revokeObjectURL","onRefreshPages","pages","includes","abortTask","watch","task","renderThumb","window","devicePixelRatio","abort","code","PdfErrorCode","Cancelled","message","wait","blob","objectUrl","createObjectURL","ignore","load","immediate","src","onLoad","_hoisted_1"],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { usePlugin, useCapability } from "@embedpdf/core/vue";
|
|
2
|
+
import { ThumbnailPlugin } from "@embedpdf/plugin-thumbnail";
|
|
3
|
+
export * from "@embedpdf/plugin-thumbnail";
|
|
4
|
+
import { defineComponent, useAttrs, ref, watchEffect, onMounted, onBeforeUnmount, nextTick, createElementBlock, openBlock, mergeProps, unref, createElementVNode, normalizeStyle, Fragment, renderList, renderSlot, watch, createCommentVNode } from "vue";
|
|
5
|
+
import { ignore, PdfErrorCode } from "@embedpdf/models";
|
|
6
|
+
const useThumbnailPlugin = () => usePlugin(ThumbnailPlugin.id);
|
|
7
|
+
const useThumbnailCapability = () => useCapability(ThumbnailPlugin.id);
|
|
8
|
+
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
9
|
+
__name: "thumbnails-pane",
|
|
10
|
+
setup(__props) {
|
|
11
|
+
const attrs = useAttrs();
|
|
12
|
+
const { plugin: thumbnailPlugin } = useThumbnailPlugin();
|
|
13
|
+
const viewportRef = ref(null);
|
|
14
|
+
const windowState = ref(null);
|
|
15
|
+
let offWindow = null;
|
|
16
|
+
let offScrollTo = null;
|
|
17
|
+
watchEffect((onCleanup) => {
|
|
18
|
+
if (!thumbnailPlugin.value) return;
|
|
19
|
+
offWindow == null ? void 0 : offWindow();
|
|
20
|
+
offWindow = thumbnailPlugin.value.onWindow((w) => windowState.value = w);
|
|
21
|
+
onCleanup(() => offWindow == null ? void 0 : offWindow());
|
|
22
|
+
});
|
|
23
|
+
onMounted(() => {
|
|
24
|
+
const vp = viewportRef.value;
|
|
25
|
+
if (!vp || !thumbnailPlugin.value) return;
|
|
26
|
+
const onScroll = () => thumbnailPlugin.value.updateWindow(vp.scrollTop, vp.clientHeight);
|
|
27
|
+
vp.addEventListener("scroll", onScroll);
|
|
28
|
+
thumbnailPlugin.value.updateWindow(vp.scrollTop, vp.clientHeight);
|
|
29
|
+
onBeforeUnmount(() => {
|
|
30
|
+
vp.removeEventListener("scroll", onScroll);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
watchEffect((onCleanup) => {
|
|
34
|
+
const vp = viewportRef.value;
|
|
35
|
+
if (!vp || !thumbnailPlugin.value || !windowState.value) return;
|
|
36
|
+
offScrollTo = thumbnailPlugin.value.onScrollTo(({ top, behavior }) => {
|
|
37
|
+
nextTick(() => {
|
|
38
|
+
vp.scrollTo({ top, behavior });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
onCleanup(() => offScrollTo == null ? void 0 : offScrollTo());
|
|
42
|
+
});
|
|
43
|
+
onBeforeUnmount(() => {
|
|
44
|
+
offWindow == null ? void 0 : offWindow();
|
|
45
|
+
offScrollTo == null ? void 0 : offScrollTo();
|
|
46
|
+
});
|
|
47
|
+
return (_ctx, _cache) => {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
return openBlock(), createElementBlock("div", mergeProps({
|
|
50
|
+
ref_key: "viewportRef",
|
|
51
|
+
ref: viewportRef,
|
|
52
|
+
style: { overflowY: "auto", position: "relative" }
|
|
53
|
+
}, unref(attrs)), [
|
|
54
|
+
createElementVNode("div", {
|
|
55
|
+
style: normalizeStyle({ height: (((_a = windowState.value) == null ? void 0 : _a.totalHeight) ?? 0) + "px", position: "relative" })
|
|
56
|
+
}, [
|
|
57
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(((_b = windowState.value) == null ? void 0 : _b.items) ?? [], (m) => {
|
|
58
|
+
return renderSlot(_ctx.$slots, "default", {
|
|
59
|
+
key: m.pageIndex,
|
|
60
|
+
meta: m
|
|
61
|
+
});
|
|
62
|
+
}), 128))
|
|
63
|
+
], 4)
|
|
64
|
+
], 16);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const _hoisted_1 = ["src"];
|
|
69
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
70
|
+
__name: "thumbnail-img",
|
|
71
|
+
props: {
|
|
72
|
+
meta: {}
|
|
73
|
+
},
|
|
74
|
+
setup(__props) {
|
|
75
|
+
const props = __props;
|
|
76
|
+
const attrs = useAttrs();
|
|
77
|
+
const { provides: thumbs } = useThumbnailCapability();
|
|
78
|
+
const { plugin: thumbnailPlugin } = useThumbnailPlugin();
|
|
79
|
+
const url = ref(null);
|
|
80
|
+
let urlToRevoke = null;
|
|
81
|
+
const refreshTick = ref(0);
|
|
82
|
+
let offRefresh = null;
|
|
83
|
+
watchEffect((onCleanup) => {
|
|
84
|
+
if (!thumbnailPlugin.value) return;
|
|
85
|
+
offRefresh == null ? void 0 : offRefresh();
|
|
86
|
+
offRefresh = thumbnailPlugin.value.onRefreshPages((pages) => {
|
|
87
|
+
if (pages.includes(props.meta.pageIndex)) {
|
|
88
|
+
refreshTick.value++;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
onCleanup(() => offRefresh == null ? void 0 : offRefresh());
|
|
92
|
+
});
|
|
93
|
+
function revoke() {
|
|
94
|
+
if (urlToRevoke) {
|
|
95
|
+
URL.revokeObjectURL(urlToRevoke);
|
|
96
|
+
urlToRevoke = null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let abortTask = null;
|
|
100
|
+
function load() {
|
|
101
|
+
if (!thumbs.value) return;
|
|
102
|
+
const task = thumbs.value.renderThumb(props.meta.pageIndex, window.devicePixelRatio);
|
|
103
|
+
abortTask = () => task.abort({
|
|
104
|
+
code: PdfErrorCode.Cancelled,
|
|
105
|
+
message: "canceled render task"
|
|
106
|
+
});
|
|
107
|
+
task.wait((blob) => {
|
|
108
|
+
revoke();
|
|
109
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
110
|
+
urlToRevoke = objectUrl;
|
|
111
|
+
url.value = objectUrl;
|
|
112
|
+
}, ignore);
|
|
113
|
+
}
|
|
114
|
+
watch(
|
|
115
|
+
() => [props.meta.pageIndex, refreshTick.value, !!thumbs.value],
|
|
116
|
+
() => {
|
|
117
|
+
abortTask == null ? void 0 : abortTask();
|
|
118
|
+
load();
|
|
119
|
+
},
|
|
120
|
+
{ immediate: true }
|
|
121
|
+
);
|
|
122
|
+
onBeforeUnmount(() => {
|
|
123
|
+
abortTask == null ? void 0 : abortTask();
|
|
124
|
+
revoke();
|
|
125
|
+
});
|
|
126
|
+
return (_ctx, _cache) => {
|
|
127
|
+
return url.value ? (openBlock(), createElementBlock("img", mergeProps({
|
|
128
|
+
key: 0,
|
|
129
|
+
src: url.value
|
|
130
|
+
}, unref(attrs), { onLoad: revoke }), null, 16, _hoisted_1)) : createCommentVNode("", true);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
export {
|
|
135
|
+
_sfc_main as ThumbImg,
|
|
136
|
+
_sfc_main$1 as ThumbnailsPane,
|
|
137
|
+
useThumbnailCapability,
|
|
138
|
+
useThumbnailPlugin
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/vue/hooks/use-thumbnail.ts","../../src/vue/components/thumbnails-pane.vue","../../src/vue/components/thumbnail-img.vue"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { ThumbnailPlugin } from '@embedpdf/plugin-thumbnail';\n\nexport const useThumbnailPlugin = () => usePlugin<ThumbnailPlugin>(ThumbnailPlugin.id);\nexport const useThumbnailCapability = () => useCapability<ThumbnailPlugin>(ThumbnailPlugin.id);\n","<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount, ref, watchEffect, nextTick, useAttrs } from 'vue';\nimport { useThumbnailPlugin } from '../hooks';\nimport type { WindowState } from '@embedpdf/plugin-thumbnail';\n\nconst attrs = useAttrs();\n\nconst { plugin: thumbnailPlugin } = useThumbnailPlugin();\nconst viewportRef = ref<HTMLDivElement | null>(null);\nconst windowState = ref<WindowState | null>(null);\n\nlet offWindow: (() => void) | null = null;\nlet offScrollTo: (() => void) | null = null;\n\nwatchEffect((onCleanup) => {\n if (!thumbnailPlugin.value) return;\n offWindow?.();\n offWindow = thumbnailPlugin.value.onWindow((w) => (windowState.value = w));\n onCleanup(() => offWindow?.());\n});\n\n// Setup scroll listener on mount\nonMounted(() => {\n const vp = viewportRef.value;\n if (!vp || !thumbnailPlugin.value) return;\n\n const onScroll = () => thumbnailPlugin.value!.updateWindow(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n\n // initial push\n thumbnailPlugin.value.updateWindow(vp.scrollTop, vp.clientHeight);\n\n onBeforeUnmount(() => {\n vp.removeEventListener('scroll', onScroll);\n });\n});\n\n// Setup scrollTo subscription only after window is ready\nwatchEffect((onCleanup) => {\n const vp = viewportRef.value;\n if (!vp || !thumbnailPlugin.value || !windowState.value) return;\n\n offScrollTo = thumbnailPlugin.value.onScrollTo(({ top, behavior }) => {\n // Wait for Vue to finish rendering the content before scrolling\n nextTick(() => {\n vp.scrollTo({ top, behavior });\n });\n });\n\n onCleanup(() => offScrollTo?.());\n});\n\nonBeforeUnmount(() => {\n offWindow?.();\n offScrollTo?.();\n});\n</script>\n\n<template>\n <div ref=\"viewportRef\" :style=\"{ overflowY: 'auto', position: 'relative' }\" v-bind=\"attrs\">\n <div :style=\"{ height: (windowState?.totalHeight ?? 0) + 'px', position: 'relative' }\">\n <!-- ✅ Use a template v-for to render the default scoped slot -->\n <template v-for=\"m in windowState?.items ?? []\" :key=\"m.pageIndex\">\n <slot :meta=\"m\" />\n </template>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount, ref, watch, watchEffect, useAttrs } from 'vue';\nimport { useThumbnailCapability, useThumbnailPlugin } from '../hooks';\nimport type { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\n\nconst props = defineProps<{ meta: ThumbMeta }>();\nconst attrs = useAttrs();\n\nconst { provides: thumbs } = useThumbnailCapability();\nconst { plugin: thumbnailPlugin } = useThumbnailPlugin();\n\nconst url = ref<string | null>(null);\nlet urlToRevoke: string | null = null;\nconst refreshTick = ref(0);\n\nlet offRefresh: (() => void) | null = null;\n\nwatchEffect((onCleanup) => {\n if (!thumbnailPlugin.value) return;\n offRefresh?.();\n offRefresh = thumbnailPlugin.value.onRefreshPages((pages) => {\n if (pages.includes(props.meta.pageIndex)) {\n refreshTick.value++;\n }\n });\n onCleanup(() => offRefresh?.());\n});\n\nfunction revoke() {\n if (urlToRevoke) {\n URL.revokeObjectURL(urlToRevoke);\n urlToRevoke = null;\n }\n}\n\nlet abortTask: (() => void) | null = null;\n\nfunction load() {\n if (!thumbs.value) return; // wait until capability exists\n\n const task = thumbs.value.renderThumb(props.meta.pageIndex, window.devicePixelRatio);\n abortTask = () =>\n task.abort({\n code: PdfErrorCode.Cancelled,\n message: 'canceled render task',\n });\n\n task.wait((blob) => {\n revoke();\n const objectUrl = URL.createObjectURL(blob);\n urlToRevoke = objectUrl;\n url.value = objectUrl;\n }, ignore);\n}\n\n/* 🔧 Re-run when:\n - page changes,\n - the plugin tells us to refresh,\n - OR the capability becomes available later.\n*/\nwatch(\n () => [props.meta.pageIndex, refreshTick.value, !!thumbs.value],\n () => {\n abortTask?.();\n load();\n },\n { immediate: true },\n);\n\nonBeforeUnmount(() => {\n abortTask?.();\n revoke();\n});\n</script>\n\n<template>\n <img v-if=\"url\" :src=\"url\" v-bind=\"attrs\" @load=\"revoke\" />\n</template>\n"],"names":["_openBlock","_createElementBlock","_mergeProps","_unref","_createElementVNode","_normalizeStyle","_Fragment","_renderList","_renderSlot"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@embedpdf/plugin-thumbnail",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -21,24 +21,31 @@
|
|
|
21
21
|
"types": "./dist/react/index.d.ts",
|
|
22
22
|
"import": "./dist/react/index.js",
|
|
23
23
|
"require": "./dist/react/index.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./vue": {
|
|
26
|
+
"types": "./dist/vue/index.d.ts",
|
|
27
|
+
"import": "./dist/vue/index.js",
|
|
28
|
+
"require": "./dist/vue/index.cjs"
|
|
24
29
|
}
|
|
25
30
|
},
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"@embedpdf/models": "1.3.
|
|
32
|
+
"@embedpdf/models": "1.3.5"
|
|
28
33
|
},
|
|
29
34
|
"devDependencies": {
|
|
30
35
|
"@types/react": "^18.2.0",
|
|
31
36
|
"typescript": "^5.0.0",
|
|
32
37
|
"@embedpdf/build": "1.0.1",
|
|
33
|
-
"@embedpdf/
|
|
34
|
-
"@embedpdf/
|
|
38
|
+
"@embedpdf/plugin-scroll": "1.3.5",
|
|
39
|
+
"@embedpdf/core": "1.3.5",
|
|
40
|
+
"@embedpdf/plugin-render": "1.3.5"
|
|
35
41
|
},
|
|
36
42
|
"peerDependencies": {
|
|
37
43
|
"react": ">=16.8.0",
|
|
38
44
|
"react-dom": ">=16.8.0",
|
|
39
45
|
"preact": "^10.26.4",
|
|
40
|
-
"
|
|
41
|
-
"@embedpdf/
|
|
46
|
+
"vue": ">=3.2.0",
|
|
47
|
+
"@embedpdf/core": "1.3.5",
|
|
48
|
+
"@embedpdf/plugin-render": "1.3.5"
|
|
42
49
|
},
|
|
43
50
|
"files": [
|
|
44
51
|
"dist",
|
|
@@ -60,7 +67,8 @@
|
|
|
60
67
|
"build:base": "vite build --mode base",
|
|
61
68
|
"build:react": "vite build --mode react",
|
|
62
69
|
"build:preact": "vite build --mode preact",
|
|
63
|
-
"build": "
|
|
70
|
+
"build:vue": "vite build --mode vue",
|
|
71
|
+
"build": "pnpm run clean && concurrently -c auto -n base,react,preact,vue \"vite build --mode base\" \"vite build --mode react\" \"vite build --mode preact\" \"vite build --mode vue\"",
|
|
64
72
|
"clean": "rimraf dist",
|
|
65
73
|
"lint": "eslint src --color",
|
|
66
74
|
"lint:fix": "eslint src --color --fix"
|