@embedpdf/plugin-thumbnail 1.0.17 → 1.0.19
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 +16 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/thumbnail-plugin.d.ts +3 -1
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +11 -1
- 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 +11 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +7 -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"),e=require("@embedpdf/models"),i="thumbnail",s={id:i,name:"Thumbnail Plugin",version:"1.0.0",provides:["thumbnail"],requires:["render"],optional:[],defaultConfig:{enabled:!0,width:150,gap:10,buffer:3,labelHeight:16}},h=class extends t.BasePlugin{constructor(e,i,s){super(e,i),this.cfg=s,this.thumbs=[],this.window=null,this.emitWindow=t.createBehaviorEmitter(),this.taskCache=new Map,this.renderCapability=this.registry.getPlugin("render").provides(),this.coreStore.onAction(t.SET_DOCUMENT,((t,e)=>{this.taskCache.clear(),this.setWindowState(e)}))}async initialize(){}setWindowState(t){const e=t.core;if(!e.document)return;const i=this.cfg.width??120,s=this.cfg.labelHeight??16,h=this.cfg.gap??8;let n=0;this.thumbs=e.document.pages.map((t=>{const e=t.size.height/t.size.width,r=Math.round(i*e),a=r+s,o={pageIndex:t.index,width:i,height:r,wrapperHeight:a,top:n,labelHeight:s};return n+=a+h,o})),this.window={start:-1,end:-1,items:[],totalHeight:n-h},this.emitWindow.emit(this.window)}buildCapability(){return{onWindow:this.emitWindow.on,setViewport:(t,e)=>this.updateWindow(t,e),renderThumb:(t,e)=>this.renderThumb(t,e)}}updateWindow(t,e){const i=this.cfg.buffer??3;let s=0,h=this.thumbs.length-1,n=0;for(;s<=h;){const e=s+h>>1,i=this.thumbs[e];i.top+i.wrapperHeight<t?s=e+1:(n=e,h=e-1)}let r=n;const a=t+e;for(;r+1<this.thumbs.length&&this.thumbs[r].top<a;)r++;r=Math.min(this.thumbs.length-1,r+i);const o=Math.max(0,n-i);this.window&&o===this.window.start&&r===this.window.end||(this.window={start:o,end:r,items:this.thumbs.slice(o,r+1),totalHeight:this.window.totalHeight},this.emitWindow.emit(this.window))}renderThumb(t,i){if(this.taskCache.has(t))return this.taskCache.get(t);const s=this.coreState.core.document.pages[t],h=(this.cfg.width??120)/s.size.width,n=this.renderCapability.renderPageRect({pageIndex:t,rect:{origin:{x:0,y:0},size:s.size},scaleFactor:h,dpr:i});return this.taskCache.set(t,n),n.wait(e.ignore,(()=>{this.taskCache.delete(t)})),n}};h.id="thumbnail";let n=h;const r={manifest:s,create:(t,e
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@embedpdf/core"),e=require("@embedpdf/models"),i="thumbnail",s={id:i,name:"Thumbnail Plugin",version:"1.0.0",provides:["thumbnail"],requires:["render"],optional:[],defaultConfig:{enabled:!0,width:150,gap:10,buffer:3,labelHeight:16}},h=class extends t.BasePlugin{constructor(e,i,s){super(e,i),this.cfg=s,this.thumbs=[],this.window=null,this.emitWindow=t.createBehaviorEmitter(),this.refreshPages$=t.createEmitter(),this.taskCache=new Map,this.renderCapability=this.registry.getPlugin("render").provides(),this.coreStore.onAction(t.SET_DOCUMENT,((t,e)=>{this.taskCache.clear(),this.setWindowState(e)})),this.coreStore.onAction(t.REFRESH_PAGES,(t=>{this.refreshPages$.emit(t.payload);for(const e of t.payload)this.taskCache.delete(e)}))}async initialize(){}onRefreshPages(t){return this.refreshPages$.on(t)}setWindowState(t){const e=t.core;if(!e.document)return;const i=this.cfg.width??120,s=this.cfg.labelHeight??16,h=this.cfg.gap??8;let n=0;this.thumbs=e.document.pages.map((t=>{const e=t.size.height/t.size.width,r=Math.round(i*e),a=r+s,o={pageIndex:t.index,width:i,height:r,wrapperHeight:a,top:n,labelHeight:s};return n+=a+h,o})),this.window={start:-1,end:-1,items:[],totalHeight:n-h},this.emitWindow.emit(this.window)}buildCapability(){return{onWindow:this.emitWindow.on,setViewport:(t,e)=>this.updateWindow(t,e),renderThumb:(t,e)=>this.renderThumb(t,e)}}updateWindow(t,e){const i=this.cfg.buffer??3;let s=0,h=this.thumbs.length-1,n=0;for(;s<=h;){const e=s+h>>1,i=this.thumbs[e];i.top+i.wrapperHeight<t?s=e+1:(n=e,h=e-1)}let r=n;const a=t+e;for(;r+1<this.thumbs.length&&this.thumbs[r].top<a;)r++;r=Math.min(this.thumbs.length-1,r+i);const o=Math.max(0,n-i);this.window&&o===this.window.start&&r===this.window.end||(this.window={start:o,end:r,items:this.thumbs.slice(o,r+1),totalHeight:this.window.totalHeight},this.emitWindow.emit(this.window))}renderThumb(t,i){if(this.taskCache.has(t))return this.taskCache.get(t);const s=this.coreState.core.document.pages[t],h=(this.cfg.width??120)/s.size.width,n=this.renderCapability.renderPageRect({pageIndex:t,rect:{origin:{x:0,y:0},size:s.size},options:{scaleFactor:h,dpr:i}});return this.taskCache.set(t,n),n.wait(e.ignore,(()=>{this.taskCache.delete(t)})),n}};h.id="thumbnail";let n=h;const r={manifest:s,create:(t,e)=>new n(i,t,e),reducer:()=>{},initialState:{}};exports.THUMBNAIL_PLUGIN_ID=i,exports.ThumbnailPlugin=n,exports.ThumbnailPluginPackage=r,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 PluginRegistry,\n SET_DOCUMENT,\n StoreState,\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 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\n /* ------------ init ------------------------------------------------ */\n async initialize(): Promise<void> {}\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 scaleFactor: scale,\n dpr,\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, _engine, 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","taskCache","Map","renderCapability","getPlugin","coreStore","onAction","SET_DOCUMENT","_action","state","clear","setWindowState","initialize","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","emit","buildCapability","onWindow","on","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","scaleFactor","set","wait","ignore","delete","ThumbnailPlugin","ThumbnailPluginPackage","create","_engine","config","reducer","initialState"],"mappings":"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"}
|
|
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":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BasePlugin, createBehaviorEmitter, SET_DOCUMENT } from "@embedpdf/core";
|
|
1
|
+
import { BasePlugin, createBehaviorEmitter, createEmitter, SET_DOCUMENT, REFRESH_PAGES } from "@embedpdf/core";
|
|
2
2
|
import { ignore } from "@embedpdf/models";
|
|
3
3
|
const THUMBNAIL_PLUGIN_ID = "thumbnail";
|
|
4
4
|
const manifest = {
|
|
@@ -23,16 +23,26 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
23
23
|
this.thumbs = [];
|
|
24
24
|
this.window = null;
|
|
25
25
|
this.emitWindow = createBehaviorEmitter();
|
|
26
|
+
this.refreshPages$ = createEmitter();
|
|
26
27
|
this.taskCache = /* @__PURE__ */ new Map();
|
|
27
28
|
this.renderCapability = this.registry.getPlugin("render").provides();
|
|
28
29
|
this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {
|
|
29
30
|
this.taskCache.clear();
|
|
30
31
|
this.setWindowState(state);
|
|
31
32
|
});
|
|
33
|
+
this.coreStore.onAction(REFRESH_PAGES, (action) => {
|
|
34
|
+
this.refreshPages$.emit(action.payload);
|
|
35
|
+
for (const pageIdx of action.payload) {
|
|
36
|
+
this.taskCache.delete(pageIdx);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
32
39
|
}
|
|
33
40
|
/* ------------ init ------------------------------------------------ */
|
|
34
41
|
async initialize() {
|
|
35
42
|
}
|
|
43
|
+
onRefreshPages(fn) {
|
|
44
|
+
return this.refreshPages$.on(fn);
|
|
45
|
+
}
|
|
36
46
|
setWindowState(state) {
|
|
37
47
|
const core = state.core;
|
|
38
48
|
if (!core.document) return;
|
|
@@ -108,8 +118,10 @@ const _ThumbnailPlugin = class _ThumbnailPlugin extends BasePlugin {
|
|
|
108
118
|
const task = this.renderCapability.renderPageRect({
|
|
109
119
|
pageIndex: idx,
|
|
110
120
|
rect: { origin: { x: 0, y: 0 }, size: page.size },
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
options: {
|
|
122
|
+
scaleFactor: scale,
|
|
123
|
+
dpr
|
|
124
|
+
}
|
|
113
125
|
});
|
|
114
126
|
this.taskCache.set(idx, task);
|
|
115
127
|
task.wait(ignore, () => {
|
|
@@ -122,7 +134,7 @@ _ThumbnailPlugin.id = "thumbnail";
|
|
|
122
134
|
let ThumbnailPlugin = _ThumbnailPlugin;
|
|
123
135
|
const ThumbnailPluginPackage = {
|
|
124
136
|
manifest,
|
|
125
|
-
create: (registry,
|
|
137
|
+
create: (registry, config) => new ThumbnailPlugin(THUMBNAIL_PLUGIN_ID, registry, config),
|
|
126
138
|
reducer: () => {
|
|
127
139
|
},
|
|
128
140
|
initialState: {}
|
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 PluginRegistry,\n SET_DOCUMENT,\n StoreState,\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 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\n /* ------------ init ------------------------------------------------ */\n async initialize(): Promise<void> {}\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 scaleFactor: scale,\n
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { BasePlugin, PluginRegistry } from '@embedpdf/core';
|
|
1
|
+
import { BasePlugin, PluginRegistry, Unsubscribe } from '@embedpdf/core';
|
|
2
2
|
import { ThumbnailPluginConfig, ThumbnailCapability } from './types';
|
|
3
3
|
export declare class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, ThumbnailCapability> {
|
|
4
4
|
private cfg;
|
|
@@ -7,9 +7,11 @@ export declare class ThumbnailPlugin extends BasePlugin<ThumbnailPluginConfig, T
|
|
|
7
7
|
private thumbs;
|
|
8
8
|
private window;
|
|
9
9
|
private readonly emitWindow;
|
|
10
|
+
private readonly refreshPages$;
|
|
10
11
|
private readonly taskCache;
|
|
11
12
|
constructor(id: string, registry: PluginRegistry, cfg: ThumbnailPluginConfig);
|
|
12
13
|
initialize(): Promise<void>;
|
|
14
|
+
onRefreshPages(fn: (pages: number[]) => void): Unsubscribe;
|
|
13
15
|
private setWindowState;
|
|
14
16
|
protected buildCapability(): ThumbnailCapability;
|
|
15
17
|
private updateWindow;
|
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"),i=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...
|
|
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"),i=()=>e.usePlugin(t.ThumbnailPlugin.id),o=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...s}){const{provides:u}=o(),{plugin:c}=i(),[a,d]=n.useState(),p=n.useRef(null),[f,g]=n.useState(0);return n.useEffect((()=>{if(c)return c.onRefreshPages((t=>{t.includes(e.pageIndex)&&g((e=>e+1))}))}),[c]),n.useEffect((()=>{const t=null==u?void 0:u.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"})}}),[u,e.pageIndex,f]),a?r.jsx("img",{src:a,onLoad:()=>{p.current&&(URL.revokeObjectURL(p.current),p.current=null)},style:t,...s}):null},exports.ThumbnailsPane=function({style:e,selectedPage:t,scrollOptions:l={behavior:"smooth",block:"nearest",inline:"nearest"},...i}){const{provides:s}=o(),u=n.useRef(null),[c,a]=n.useState(null);return n.useEffect((()=>null==s?void 0:s.onWindow(a)),[s]),n.useEffect((()=>{const e=u.current;if(!e)return;const t=()=>null==s?void 0:s.setViewport(e.scrollTop,e.clientHeight);return e.addEventListener("scroll",t),()=>e.removeEventListener("scroll",t)}),[s]),n.useEffect((()=>{const e=u.current;e&&s&&0===(null==c?void 0:c.items.length)&&s.setViewport(e.scrollTop,e.clientHeight)}),[c,s]),n.useEffect((()=>{if(!t||!c)return;const e=c.items.find((e=>e.pageIndex+1===t));if(!e)return;const r=u.current;if(!r)return;e.top<r.scrollTop+8?r.scrollTo({top:e.top,...l}):e.top+e.wrapperHeight+e.labelHeight>r.scrollTop+r.clientHeight-8&&r.scrollTo({top:e.top+e.wrapperHeight+e.labelHeight-r.clientHeight,...l})}),[t,c,l]),r.jsx("div",{ref:u,style:{overflowY:"auto",position:"relative",...e},...i,children:r.jsx("div",{style:{height:(null==c?void 0:c.totalHeight)??0,position:"relative"},children:null==c?void 0:c.items.map((e=>i.children(e)))})})},exports.useThumbnailCapability=o,exports.useThumbnailPlugin=i;
|
|
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 } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability } 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 [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\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 }, [meta.pageIndex]);\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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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":["
|
|
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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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","selectedPage","scrollOptions","behavior","block","inline","viewportRef","setWindow","onWindow","vp","onScroll","setViewport","scrollTop","clientHeight","addEventListener","removeEventListener","items","length","item","find","it","top","scrollTo","wrapperHeight","labelHeight","ref","overflowY","position","children","height","totalHeight","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,yBC5CO,UAAwBD,MAC7BA,EAAAuC,aACAA,EAAAC,cACAA,EAAgB,CAAEC,SAAU,SAAUC,MAAO,UAAWC,OAAQ,cAC7D1C,IAEH,MAAQC,SAAUC,GAAWN,IACvB+C,EAAclC,SAAuB,OAEpCW,EAAQwB,GAAarC,EAAAA,SAA6B,aAGzDK,EAAAA,WAAU,IAAM,MAAAV,OAAA,EAAAA,EAAQ2C,SAASD,IAAY,CAAC1C,IAG9CU,EAAAA,WAAU,KACR,MAAMkC,EAAKH,EAAYhB,QACvB,IAAKmB,EAAI,OACT,MAAMC,EAAW,IAAM,MAAA7C,OAAA,EAAAA,EAAQ8C,YAAYF,EAAGG,UAAWH,EAAGI,cAE5D,OADGJ,EAAAK,iBAAiB,SAAUJ,GACvB,IAAMD,EAAGM,oBAAoB,SAAUL,EAAQ,GACrD,CAAC7C,IAGJU,EAAAA,WAAU,KACR,MAAMkC,EAAKH,EAAYhB,QAClBmB,GAAO5C,GAEiB,KAAzB,MAAAkB,OAAA,EAAAA,EAAQiC,MAAMC,SAChBpD,EAAO8C,YAAYF,EAAGG,UAAWH,EAAGI,aAAY,GAEjD,CAAC9B,EAAQlB,IAGZU,EAAAA,WAAU,KACJ,IAAC0B,IAAiBlB,EAAQ,OAExB,MAAAmC,EAAOnC,EAAOiC,MAAMG,MAAMC,GAAOA,EAAGzC,UAAY,IAAMsB,IAC5D,IAAKiB,EAAM,OAEX,MAAMT,EAAKH,EAAYhB,QACvB,IAAKmB,EAAI,OAILS,EAAKG,IAAMZ,EAAGG,UADH,EAEbH,EAAGa,SAAS,CAAED,IAAKH,EAAKG,OAAQnB,IAEhCgB,EAAKG,IAAMH,EAAKK,cAAgBL,EAAKM,YACrCf,EAAGG,UAAYH,EAAGI,aALL,GAObJ,EAAGa,SAAS,CACVD,IAAKH,EAAKG,IAAMH,EAAKK,cAAgBL,EAAKM,YAAcf,EAAGI,gBACxDX,GACJ,GAEF,CAACD,EAAclB,EAAQmB,UAGvB,MAAI,CAAAuB,IAAKnB,EAAa5C,MAAO,CAAEgE,UAAW,OAAQC,SAAU,cAAejE,MAAaC,EAEvFiE,eAAC,MAAI,CAAAlE,MAAO,CAAEmE,QAAQ,MAAA9C,SAAAA,EAAQ+C,cAAe,EAAGH,SAAU,YACvDC,SAAQ,MAAA7C,OAAA,EAAAA,EAAAiC,MAAMe,KAAKC,GAAMrE,EAAMiE,SAASI,QAIjD"}
|
package/dist/preact/index.js
CHANGED
|
@@ -50,8 +50,18 @@ function ThumbnailsPane({
|
|
|
50
50
|
}
|
|
51
51
|
function ThumbImg({ meta, style, ...props }) {
|
|
52
52
|
const { provides: thumbs } = useThumbnailCapability();
|
|
53
|
+
const { plugin: thumbnailPlugin } = useThumbnailPlugin();
|
|
53
54
|
const [url, setUrl] = useState();
|
|
54
55
|
const urlRef = useRef(null);
|
|
56
|
+
const [refreshTick, setRefreshTick] = useState(0);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!thumbnailPlugin) return;
|
|
59
|
+
return thumbnailPlugin.onRefreshPages((pages) => {
|
|
60
|
+
if (pages.includes(meta.pageIndex)) {
|
|
61
|
+
setRefreshTick((tick) => tick + 1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}, [thumbnailPlugin]);
|
|
55
65
|
useEffect(() => {
|
|
56
66
|
const task = thumbs == null ? void 0 : thumbs.renderThumb(meta.pageIndex, window.devicePixelRatio);
|
|
57
67
|
task == null ? void 0 : task.wait((blob) => {
|
|
@@ -70,7 +80,7 @@ function ThumbImg({ meta, style, ...props }) {
|
|
|
70
80
|
});
|
|
71
81
|
}
|
|
72
82
|
};
|
|
73
|
-
}, [meta.pageIndex]);
|
|
83
|
+
}, [thumbs, meta.pageIndex, refreshTick]);
|
|
74
84
|
const handleImageLoad = () => {
|
|
75
85
|
if (urlRef.current) {
|
|
76
86
|
URL.revokeObjectURL(urlRef.current);
|
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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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 } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability } 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 [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\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 }, [meta.pageIndex]);\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":";;;;;;AAGO,MAAM,qBAAqB,MAAM,UAA2B,gBAAgB,EAAE;AAC9E,MAAM,yBAAyB,MAAM,cAA+B,gBAAgB,EAAE;ACOtF,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,gBAAgB,EAAE,UAAU,UAAU,OAAO,WAAW,QAAQ,UAAU;AAAA,EAC1E,GAAG;AACL,GAAoB;AAClB,QAAM,EAAE,UAAU,OAAO,IAAI,uBAAuB;AAC9C,QAAA,cAAc,OAAuB,IAAI;AAE/C,QAAM,CAACA,SAAQ,SAAS,IAAI,SAA6B,IAAI;AAG7D,YAAU,MAAM,iCAAQ,SAAS,YAAY,CAAC,MAAM,CAAC;AAGrD,YAAU,MAAM;AACd,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AACT,UAAM,WAAW,MAAM,iCAAQ,YAAY,GAAG,WAAW,GAAG;AACzD,OAAA,iBAAiB,UAAU,QAAQ;AACtC,WAAO,MAAM,GAAG,oBAAoB,UAAU,QAAQ;AAAA,EAAA,GACrD,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,UAAM,KAAK,YAAY;AACnB,QAAA,CAAC,MAAM,CAAC,OAAQ;AAEhB,SAAAA,WAAA,gBAAAA,QAAQ,MAAM,YAAW,GAAG;AAC9B,aAAO,YAAY,GAAG,WAAW,GAAG,YAAY;AAAA,IAAA;AAAA,EAClD,GACC,CAACA,SAAQ,MAAM,CAAC;AAGnB,YAAU,MAAM;AACV,QAAA,CAAC,gBAAgB,CAACA,QAAQ;AAExB,UAAA,OAAOA,QAAO,MAAM,KAAK,CAAC,OAAO,GAAG,YAAY,MAAM,YAAY;AACxE,QAAI,CAAC,KAAM;AAEX,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AAGT,UAAM,SAAS;AACf,QAAI,KAAK,MAAM,GAAG,YAAY,QAAQ;AACpC,SAAG,SAAS,EAAE,KAAK,KAAK,KAAK,GAAG,eAAe;AAAA,IACjD,WACE,KAAK,MAAM,KAAK,gBAAgB,KAAK,cACrC,GAAG,YAAY,GAAG,eAAe,QACjC;AACA,SAAG,SAAS;AAAA,QACV,KAAK,KAAK,MAAM,KAAK,gBAAgB,KAAK,cAAc,GAAG;AAAA,QAC3D,GAAG;AAAA,MAAA,CACJ;AAAA,IAAA;AAAA,EAEF,GAAA,CAAC,cAAcA,SAAQ,aAAa,CAAC;AAExC,6BACG,OAAI,EAAA,KAAK,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,YAAY,GAAG,SAAU,GAAG,OAEvF,8BAAC,OAAI,EAAA,OAAO,EAAE,SAAQA,WAAA,gBAAAA,QAAQ,gBAAe,GAAG,UAAU,WAAA,GACvD,UAAQA,WAAA,gBAAAA,QAAA,MAAM,IAAI,CAAC,MAAM,MAAM,SAAS,CAAC,GAC5C,CAAA,GACF;AAEJ;ACnEO,SAAS,SAAS,EAAE,MAAM,OAAO,GAAG,SAA4B;AACrE,QAAM,EAAE,UAAU,OAAO,IAAI,uBAAuB;AACpD,QAAM,CAAC,KAAK,MAAM,IAAI,SAAiB;AACjC,QAAA,SAAS,OAAsB,IAAI;
|
|
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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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"),i=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...
|
|
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"),i=()=>e.usePlugin(t.ThumbnailPlugin.id),o=()=>e.useCapability(t.ThumbnailPlugin.id);exports.ThumbImg=function({meta:e,style:t,...s}){const{provides:u}=o(),{plugin:c}=i(),[a,d]=n.useState(),p=n.useRef(null),[f,g]=n.useState(0);return n.useEffect((()=>{if(c)return c.onRefreshPages((t=>{t.includes(e.pageIndex)&&g((e=>e+1))}))}),[c]),n.useEffect((()=>{const t=null==u?void 0:u.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"})}}),[u,e.pageIndex,f]),a?r.jsx("img",{src:a,onLoad:()=>{p.current&&(URL.revokeObjectURL(p.current),p.current=null)},style:t,...s}):null},exports.ThumbnailsPane=function({style:e,selectedPage:t,scrollOptions:l={behavior:"smooth",block:"nearest",inline:"nearest"},...i}){const{provides:s}=o(),u=n.useRef(null),[c,a]=n.useState(null);return n.useEffect((()=>null==s?void 0:s.onWindow(a)),[s]),n.useEffect((()=>{const e=u.current;if(!e)return;const t=()=>null==s?void 0:s.setViewport(e.scrollTop,e.clientHeight);return e.addEventListener("scroll",t),()=>e.removeEventListener("scroll",t)}),[s]),n.useEffect((()=>{const e=u.current;e&&s&&0===(null==c?void 0:c.items.length)&&s.setViewport(e.scrollTop,e.clientHeight)}),[c,s]),n.useEffect((()=>{if(!t||!c)return;const e=c.items.find((e=>e.pageIndex+1===t));if(!e)return;const r=u.current;if(!r)return;e.top<r.scrollTop+8?r.scrollTo({top:e.top,...l}):e.top+e.wrapperHeight+e.labelHeight>r.scrollTop+r.clientHeight-8&&r.scrollTo({top:e.top+e.wrapperHeight+e.labelHeight-r.clientHeight,...l})}),[t,c,l]),r.jsx("div",{ref:u,style:{overflowY:"auto",position:"relative",...e},...i,children:r.jsx("div",{style:{height:(null==c?void 0:c.totalHeight)??0,position:"relative"},children:null==c?void 0:c.items.map((e=>i.children(e)))})})},exports.useThumbnailCapability=o,exports.useThumbnailPlugin=i;
|
|
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 } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability } 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 [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\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 }, [meta.pageIndex]);\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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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":["
|
|
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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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","selectedPage","scrollOptions","behavior","block","inline","viewportRef","setWindow","onWindow","vp","onScroll","setViewport","scrollTop","clientHeight","addEventListener","removeEventListener","items","length","item","find","it","top","scrollTo","wrapperHeight","labelHeight","ref","overflowY","position","children","height","totalHeight","map","m"],"mappings":"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"}
|
package/dist/react/index.js
CHANGED
|
@@ -49,8 +49,18 @@ function ThumbnailsPane({
|
|
|
49
49
|
}
|
|
50
50
|
function ThumbImg({ meta, style, ...props }) {
|
|
51
51
|
const { provides: thumbs } = useThumbnailCapability();
|
|
52
|
+
const { plugin: thumbnailPlugin } = useThumbnailPlugin();
|
|
52
53
|
const [url, setUrl] = useState();
|
|
53
54
|
const urlRef = useRef(null);
|
|
55
|
+
const [refreshTick, setRefreshTick] = useState(0);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!thumbnailPlugin) return;
|
|
58
|
+
return thumbnailPlugin.onRefreshPages((pages) => {
|
|
59
|
+
if (pages.includes(meta.pageIndex)) {
|
|
60
|
+
setRefreshTick((tick) => tick + 1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}, [thumbnailPlugin]);
|
|
54
64
|
useEffect(() => {
|
|
55
65
|
const task = thumbs == null ? void 0 : thumbs.renderThumb(meta.pageIndex, window.devicePixelRatio);
|
|
56
66
|
task == null ? void 0 : task.wait((blob) => {
|
|
@@ -69,7 +79,7 @@ function ThumbImg({ meta, style, ...props }) {
|
|
|
69
79
|
});
|
|
70
80
|
}
|
|
71
81
|
};
|
|
72
|
-
}, [meta.pageIndex]);
|
|
82
|
+
}, [thumbs, meta.pageIndex, refreshTick]);
|
|
73
83
|
const handleImageLoad = () => {
|
|
74
84
|
if (urlRef.current) {
|
|
75
85
|
URL.revokeObjectURL(urlRef.current);
|
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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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 } from '@framework';\nimport { ThumbMeta } from '@embedpdf/plugin-thumbnail';\nimport { ignore, PdfErrorCode } from '@embedpdf/models';\nimport { useThumbnailCapability } 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 [url, setUrl] = useState<string>();\n const urlRef = useRef<string | null>(null);\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 }, [meta.pageIndex]);\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":";;;;;AAGO,MAAM,qBAAqB,MAAM,UAA2B,gBAAgB,EAAE;AAC9E,MAAM,yBAAyB,MAAM,cAA+B,gBAAgB,EAAE;ACOtF,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,gBAAgB,EAAE,UAAU,UAAU,OAAO,WAAW,QAAQ,UAAU;AAAA,EAC1E,GAAG;AACL,GAAoB;AAClB,QAAM,EAAE,UAAU,OAAO,IAAI,uBAAuB;AAC9C,QAAA,cAAc,OAAuB,IAAI;AAE/C,QAAM,CAACA,SAAQ,SAAS,IAAI,SAA6B,IAAI;AAG7D,YAAU,MAAM,iCAAQ,SAAS,YAAY,CAAC,MAAM,CAAC;AAGrD,YAAU,MAAM;AACd,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AACT,UAAM,WAAW,MAAM,iCAAQ,YAAY,GAAG,WAAW,GAAG;AACzD,OAAA,iBAAiB,UAAU,QAAQ;AACtC,WAAO,MAAM,GAAG,oBAAoB,UAAU,QAAQ;AAAA,EAAA,GACrD,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,UAAM,KAAK,YAAY;AACnB,QAAA,CAAC,MAAM,CAAC,OAAQ;AAEhB,SAAAA,WAAA,gBAAAA,QAAQ,MAAM,YAAW,GAAG;AAC9B,aAAO,YAAY,GAAG,WAAW,GAAG,YAAY;AAAA,IAAA;AAAA,EAClD,GACC,CAACA,SAAQ,MAAM,CAAC;AAGnB,YAAU,MAAM;AACV,QAAA,CAAC,gBAAgB,CAACA,QAAQ;AAExB,UAAA,OAAOA,QAAO,MAAM,KAAK,CAAC,OAAO,GAAG,YAAY,MAAM,YAAY;AACxE,QAAI,CAAC,KAAM;AAEX,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AAGT,UAAM,SAAS;AACf,QAAI,KAAK,MAAM,GAAG,YAAY,QAAQ;AACpC,SAAG,SAAS,EAAE,KAAK,KAAK,KAAK,GAAG,eAAe;AAAA,IACjD,WACE,KAAK,MAAM,KAAK,gBAAgB,KAAK,cACrC,GAAG,YAAY,GAAG,eAAe,QACjC;AACA,SAAG,SAAS;AAAA,QACV,KAAK,KAAK,MAAM,KAAK,gBAAgB,KAAK,cAAc,GAAG;AAAA,QAC3D,GAAG;AAAA,MAAA,CACJ;AAAA,IAAA;AAAA,EAEF,GAAA,CAAC,cAAcA,SAAQ,aAAa,CAAC;AAExC,6BACG,OAAI,EAAA,KAAK,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,YAAY,GAAG,SAAU,GAAG,OAEvF,8BAAC,OAAI,EAAA,OAAO,EAAE,SAAQA,WAAA,gBAAAA,QAAQ,gBAAe,GAAG,UAAU,WAAA,GACvD,UAAQA,WAAA,gBAAAA,QAAA,MAAM,IAAI,CAAC,MAAM,MAAM,SAAS,CAAC,GAC5C,CAAA,GACF;AAEJ;ACnEO,SAAS,SAAS,EAAE,MAAM,OAAO,GAAG,SAA4B;AACrE,QAAM,EAAE,UAAU,OAAO,IAAI,uBAAuB;AACpD,QAAM,CAAC,KAAK,MAAM,IAAI,SAAiB;AACjC,QAAA,SAAS,OAAsB,IAAI;
|
|
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 { useThumbnailCapability } from '../hooks';\n\ntype ThumbnailsProps = Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'children'> & {\n style?: CSSProperties;\n children: (m: ThumbMeta) => ReactNode;\n selectedPage?: number;\n scrollOptions?: ScrollIntoViewOptions;\n};\n\nexport function ThumbnailsPane({\n style,\n selectedPage,\n scrollOptions = { behavior: 'smooth', block: 'nearest', inline: 'nearest' },\n ...props\n}: ThumbnailsProps) {\n const { provides: thumbs } = useThumbnailCapability();\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(() => thumbs?.onWindow(setWindow), [thumbs]);\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 = () => thumbs?.setViewport(vp.scrollTop, vp.clientHeight);\n vp.addEventListener('scroll', onScroll);\n return () => vp.removeEventListener('scroll', onScroll);\n }, [thumbs]);\n\n /* 3️⃣ kick-start (or re-kick) after document change */\n useEffect(() => {\n const vp = viewportRef.current;\n if (!vp || !thumbs) return;\n\n if (window?.items.length === 0) {\n thumbs.setViewport(vp.scrollTop, vp.clientHeight);\n }\n }, [window, thumbs]);\n\n /* 4️⃣ whenever selectedPage or window changes, ensure it’s visible */\n useEffect(() => {\n if (!selectedPage || !window) return;\n\n const item = window.items.find((it) => it.pageIndex + 1 === selectedPage);\n if (!item) return; // not in current window yet → wait for next update\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Scroll only if the item is above or below the viewport “padding” zone\n const margin = 8; // px\n if (item.top < vp.scrollTop + margin) {\n vp.scrollTo({ top: item.top, ...scrollOptions });\n } else if (\n item.top + item.wrapperHeight + item.labelHeight >\n vp.scrollTop + vp.clientHeight - margin\n ) {\n vp.scrollTo({\n top: item.top + item.wrapperHeight + item.labelHeight - vp.clientHeight,\n ...scrollOptions,\n });\n }\n }, [selectedPage, window, scrollOptions]);\n\n return (\n <div ref={viewportRef} style={{ overflowY: 'auto', position: 'relative', ...style }} {...props}>\n {/* spacer keeps correct scroll height even before first window arrives */}\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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@embedpdf/plugin-thumbnail",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -23,21 +23,21 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@embedpdf/models": "1.0.
|
|
26
|
+
"@embedpdf/models": "1.0.19"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/react": "^18.2.0",
|
|
30
30
|
"typescript": "^5.0.0",
|
|
31
|
-
"@embedpdf/
|
|
32
|
-
"@embedpdf/
|
|
33
|
-
"@embedpdf/
|
|
31
|
+
"@embedpdf/build": "1.0.0",
|
|
32
|
+
"@embedpdf/core": "1.0.19",
|
|
33
|
+
"@embedpdf/plugin-render": "1.0.19"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"react": ">=16.8.0",
|
|
37
37
|
"react-dom": ">=16.8.0",
|
|
38
38
|
"preact": "^10.26.4",
|
|
39
|
-
"@embedpdf/core": "1.0.
|
|
40
|
-
"@embedpdf/plugin-render": "1.0.
|
|
39
|
+
"@embedpdf/core": "1.0.19",
|
|
40
|
+
"@embedpdf/plugin-render": "1.0.19"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
43
|
"dist",
|