@embedpdf/viewer-react 3.0.0-next.13 → 3.0.0-next.15
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/README.md +38 -8
- package/dist/component.cjs +19 -0
- package/dist/component.d.cts +2 -0
- package/dist/component.d.ts +2 -0
- package/dist/component.js +3 -0
- package/dist/core.cjs +20 -6
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -5
- package/dist/core.d.ts +2 -5
- package/dist/core.js +3 -4
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +21 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -8
- package/dist/index.d.ts +3 -8
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -0
- package/dist/parts-BNOPi71I.js +315 -0
- package/dist/parts-BNOPi71I.js.map +1 -0
- package/dist/parts-CDiQnn2K.d.cts +134 -0
- package/dist/parts-DraHrZwl.cjs +368 -0
- package/dist/parts-DraHrZwl.cjs.map +1 -0
- package/dist/parts-U3eTUt92.d.ts +134 -0
- package/package.json +18 -4
- package/dist/component-B4Bvq3Dk.js +0 -31
- package/dist/component-B4Bvq3Dk.js.map +0 -1
- package/dist/component-B8nwtJtn.d.cts +0 -29
- package/dist/component-CGxTTf8T.d.ts +0 -29
- package/dist/component-DmxO8uud.cjs +0 -36
- package/dist/component-DmxO8uud.cjs.map +0 -1
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { KernelProvider } from "@embedpdf/react/runtime";
|
|
2
|
+
import { createContext, createElement, useContext, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { useCommand } from "@embedpdf/react/commands";
|
|
4
|
+
//#region src/hooks.ts
|
|
5
|
+
/** The `viewer` the components inside a <PDFViewer> belong to. */
|
|
6
|
+
const ViewerContext = createContext(null);
|
|
7
|
+
/**
|
|
8
|
+
* The `viewer`, in a component inside <PDFViewer>: `get(name)`, `documents`, `commands`,
|
|
9
|
+
* `watch` and `forDocument`. Your components mount once the viewer is live, so it is always
|
|
10
|
+
* there. Outside the viewer, keep the one `onReady` gives you; called there, this throws.
|
|
11
|
+
*/
|
|
12
|
+
function usePDFViewer() {
|
|
13
|
+
const viewer = useContext(ViewerContext);
|
|
14
|
+
if (!viewer) throw new Error("[embedpdf] usePDFViewer() works in a component inside <PDFViewer>. Outside the viewer, keep the viewer its onReady gives you.");
|
|
15
|
+
return viewer;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/component.ts
|
|
19
|
+
/**
|
|
20
|
+
* <PDFViewer> — the React face of the <embedpdf-viewer> custom element, and
|
|
21
|
+
* deliberately engine-blind: this module imports the viewer for types only, so
|
|
22
|
+
* it holds no runtime edge to any engine. Which engine ships is decided by the
|
|
23
|
+
* entry that re-exports it:
|
|
24
|
+
*
|
|
25
|
+
* ./index.ts → `@embedpdf/viewer` — local PDFium engine, registered as
|
|
26
|
+
* the default (the zero-config door)
|
|
27
|
+
* ./core.ts → `@embedpdf/viewer/core` — no default engine; you inject one,
|
|
28
|
+
* and PDFium is structurally absent
|
|
29
|
+
*
|
|
30
|
+
* That side-effect import is the entire difference between the two doors, and
|
|
31
|
+
* it lives only in those two files. A runtime import here would weld PDFium
|
|
32
|
+
* into every consumer's bundle — cloud builds included — so an import-boundary
|
|
33
|
+
* lint rule (see eslint.config.js) holds this module to `import type`.
|
|
34
|
+
*
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <PDFViewer src="/report.pdf" style={{ height: '100vh' }}>
|
|
37
|
+
* <ToolbarItem id="doc-picker"><DocPicker /></ToolbarItem> // ← slot="item:doc-picker"
|
|
38
|
+
* </PDFViewer>
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* The wrapper is deliberately thin: it renders the <embedpdf-viewer> custom
|
|
42
|
+
* element, hands it the config before its deferred first mount, and passes
|
|
43
|
+
* children straight through as light DOM — the browser projects a child with
|
|
44
|
+
* `slot="item:<id>"` into the chrome's matching `custom()` socket while the child
|
|
45
|
+
* stays in the host React tree, so its context, state, and page CSS all keep
|
|
46
|
+
* working. There is no reactSlot()
|
|
47
|
+
* bridge because none is needed: a slot is a child. The children mount once
|
|
48
|
+
* the viewer is ready, inside its kernel, so the headless hooks of
|
|
49
|
+
* `@embedpdf/react` in them read this viewer, and `usePDFViewer()` gives
|
|
50
|
+
* them its `viewer`. When the viewer goes (an unmount, or a new one with a
|
|
51
|
+
* new config) they unmount with it, and mount again inside the next one.
|
|
52
|
+
*
|
|
53
|
+
* On the server it renders the empty element, and the viewer starts in the
|
|
54
|
+
* browser: nothing here, or in the viewer it imports, touches the DOM when
|
|
55
|
+
* imported.
|
|
56
|
+
*
|
|
57
|
+
* `src`, `theme`, `locale`, `disable` and `readOnly` follow their props: the
|
|
58
|
+
* element changes them in place. `onReady`, `onDocumentChange` and
|
|
59
|
+
* `onPanelResize` call the latest prop. Every other prop is read once, when the viewer starts; a later
|
|
60
|
+
* change is ignored (development warns), and a new `key` mounts a new viewer
|
|
61
|
+
* with it.
|
|
62
|
+
*/
|
|
63
|
+
/** Layout effects in the browser; on the server, where effects never run, the plain kind. */
|
|
64
|
+
const useBrowserLayoutEffect = typeof document === "undefined" ? useEffect : useLayoutEffect;
|
|
65
|
+
/** Where the viewer keeps its kernel (`createViewer` in `@embedpdf/viewer-chrome`). */
|
|
66
|
+
const VIEWER_KERNEL = Symbol.for("@embedpdf/viewer/kernel");
|
|
67
|
+
const kernelOf = (viewer) => viewer[VIEWER_KERNEL] ?? null;
|
|
68
|
+
/** A key for each viewer, so the children mount again inside a new one. */
|
|
69
|
+
const viewerKeys = /* @__PURE__ */ new WeakMap();
|
|
70
|
+
let nextViewerKey = 0;
|
|
71
|
+
const keyOf = (viewer) => {
|
|
72
|
+
let key = viewerKeys.get(viewer);
|
|
73
|
+
if (key === void 0) viewerKeys.set(viewer, key = nextViewerKey++);
|
|
74
|
+
return key;
|
|
75
|
+
};
|
|
76
|
+
/** The children inside the viewer: its kernel for the headless hooks, and the `viewer`. */
|
|
77
|
+
function inViewer(viewer, children) {
|
|
78
|
+
const key = keyOf(viewer);
|
|
79
|
+
const inside = createElement(ViewerContext.Provider, {
|
|
80
|
+
key,
|
|
81
|
+
value: viewer
|
|
82
|
+
}, children);
|
|
83
|
+
const kernel = kernelOf(viewer);
|
|
84
|
+
return kernel ? createElement(KernelProvider, {
|
|
85
|
+
key,
|
|
86
|
+
kernel
|
|
87
|
+
}, inside) : inside;
|
|
88
|
+
}
|
|
89
|
+
/** The props the element follows while it runs; the wrapper hands their changes on. */
|
|
90
|
+
const LIVE_PROPS = {
|
|
91
|
+
src: true,
|
|
92
|
+
theme: true,
|
|
93
|
+
locale: true,
|
|
94
|
+
disable: true,
|
|
95
|
+
readOnly: true
|
|
96
|
+
};
|
|
97
|
+
/** Every other prop is read once: a changed one is ignored, and development warns once. */
|
|
98
|
+
const warnConfigChange = (initial, next) => {
|
|
99
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(initial), ...Object.keys(next)]);
|
|
100
|
+
for (const key of keys) {
|
|
101
|
+
if (key in LIVE_PROPS) continue;
|
|
102
|
+
if (!Object.is(initial[key], next[key])) {
|
|
103
|
+
console.warn(`[embedpdf] <PDFViewer>: \`${key}\` is read once, when the viewer starts, so the change is ignored. Pass a stable value (module scope, useState, useMemo), or give the viewer a new \`key\` to mount a new one with it.`);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
};
|
|
109
|
+
function PDFViewer({ className, style, children, elementRef, onReady, onDocumentChange, onPanelResize, ...config }) {
|
|
110
|
+
const ref = useRef(null);
|
|
111
|
+
const [viewer, setViewer] = useState(null);
|
|
112
|
+
const configRef = useRef(config);
|
|
113
|
+
configRef.current = config;
|
|
114
|
+
const onReadyRef = useRef(onReady);
|
|
115
|
+
onReadyRef.current = onReady;
|
|
116
|
+
const onDocumentChangeRef = useRef(onDocumentChange);
|
|
117
|
+
onDocumentChangeRef.current = onDocumentChange;
|
|
118
|
+
const onPanelResizeRef = useRef(onPanelResize);
|
|
119
|
+
onPanelResizeRef.current = onPanelResize;
|
|
120
|
+
const callbacks = useRef({
|
|
121
|
+
onReady: (live) => onReadyRef.current?.(live),
|
|
122
|
+
onDocumentChange: (documentId) => onDocumentChangeRef.current?.(documentId),
|
|
123
|
+
onPanelResize: (event) => onPanelResizeRef.current?.(event)
|
|
124
|
+
});
|
|
125
|
+
const initialConfig = useRef(null);
|
|
126
|
+
const warned = useRef(false);
|
|
127
|
+
if (process.env.NODE_ENV !== "production") {
|
|
128
|
+
if (initialConfig.current === null) initialConfig.current = config;
|
|
129
|
+
else if (!warned.current) warned.current = warnConfigChange(initialConfig.current, config);
|
|
130
|
+
}
|
|
131
|
+
useBrowserLayoutEffect(() => {
|
|
132
|
+
const element = ref.current;
|
|
133
|
+
if (!element) return;
|
|
134
|
+
const onEvent = () => setViewer(element.viewer);
|
|
135
|
+
element.addEventListener("epdf:ready", onEvent);
|
|
136
|
+
element.config = {
|
|
137
|
+
...configRef.current,
|
|
138
|
+
...callbacks.current
|
|
139
|
+
};
|
|
140
|
+
if (element.viewer) onEvent();
|
|
141
|
+
return () => element.removeEventListener("epdf:ready", onEvent);
|
|
142
|
+
}, []);
|
|
143
|
+
useBrowserLayoutEffect(() => {
|
|
144
|
+
const element = ref.current;
|
|
145
|
+
if (!element) return;
|
|
146
|
+
element.src = config.src;
|
|
147
|
+
element.theme = config.theme;
|
|
148
|
+
element.locale = config.locale;
|
|
149
|
+
element.disable = config.disable;
|
|
150
|
+
element.readOnly = config.readOnly;
|
|
151
|
+
});
|
|
152
|
+
return createElement("embedpdf-viewer", {
|
|
153
|
+
ref: (element) => {
|
|
154
|
+
ref.current = element;
|
|
155
|
+
if (typeof elementRef === "function") elementRef(element);
|
|
156
|
+
else if (elementRef) elementRef.current = element;
|
|
157
|
+
},
|
|
158
|
+
...className === void 0 ? {} : { class: className },
|
|
159
|
+
style,
|
|
160
|
+
"data-epdf-wrapped": ""
|
|
161
|
+
}, viewer ? inViewer(viewer, children) : null);
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/slots.ts
|
|
165
|
+
/** The element around what you put in a place adds no box: your component lays out there. */
|
|
166
|
+
const CONTENTS = { display: "contents" };
|
|
167
|
+
const EDGES = [
|
|
168
|
+
"top",
|
|
169
|
+
"bottom",
|
|
170
|
+
"start",
|
|
171
|
+
"end",
|
|
172
|
+
"float",
|
|
173
|
+
"selection"
|
|
174
|
+
];
|
|
175
|
+
const SIZES = [
|
|
176
|
+
"compact",
|
|
177
|
+
"medium",
|
|
178
|
+
"expanded"
|
|
179
|
+
];
|
|
180
|
+
/** Before the viewer says where the item is: the main bar's place. */
|
|
181
|
+
const AT_THE_TOP = {
|
|
182
|
+
orientation: "horizontal",
|
|
183
|
+
edge: "top",
|
|
184
|
+
variant: null,
|
|
185
|
+
size: "expanded"
|
|
186
|
+
};
|
|
187
|
+
const ATTRIBUTES = [
|
|
188
|
+
"data-orientation",
|
|
189
|
+
"data-edge",
|
|
190
|
+
"data-variant",
|
|
191
|
+
"data-size"
|
|
192
|
+
];
|
|
193
|
+
/** Where the item whose element is `element` sits, as the viewer wrote it there. */
|
|
194
|
+
function toolbarItemContextOf(element) {
|
|
195
|
+
const edge = element.getAttribute("data-edge");
|
|
196
|
+
const size = element.getAttribute("data-size");
|
|
197
|
+
return {
|
|
198
|
+
orientation: element.getAttribute("data-orientation") === "vertical" ? "vertical" : "horizontal",
|
|
199
|
+
edge: edge && EDGES.includes(edge) ? edge : AT_THE_TOP.edge,
|
|
200
|
+
variant: element.getAttribute("data-variant"),
|
|
201
|
+
size: size && SIZES.includes(size) ? size : AT_THE_TOP.size
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const sameContext = (left, right) => left.orientation === right.orientation && left.edge === right.edge && left.variant === right.variant && left.size === right.size;
|
|
205
|
+
const ToolbarItemCtx = createContext(null);
|
|
206
|
+
/** What the viewer says on the element `element` holds, as a store: the same object until it changes. */
|
|
207
|
+
function contextStoreOf(element) {
|
|
208
|
+
let current = AT_THE_TOP;
|
|
209
|
+
return {
|
|
210
|
+
subscribe(onChange) {
|
|
211
|
+
const target = element.current;
|
|
212
|
+
if (!target) return () => {};
|
|
213
|
+
const observer = new MutationObserver(onChange);
|
|
214
|
+
observer.observe(target, {
|
|
215
|
+
attributes: true,
|
|
216
|
+
attributeFilter: ATTRIBUTES
|
|
217
|
+
});
|
|
218
|
+
return () => observer.disconnect();
|
|
219
|
+
},
|
|
220
|
+
read() {
|
|
221
|
+
const target = element.current;
|
|
222
|
+
const next = target ? toolbarItemContextOf(target) : current;
|
|
223
|
+
if (!sameContext(current, next)) current = next;
|
|
224
|
+
return current;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const atTheTop = () => AT_THE_TOP;
|
|
229
|
+
/** Your component in the toolbar, where the layout's `{ custom: id }` makes room for it. */
|
|
230
|
+
function ToolbarItem({ id, children }) {
|
|
231
|
+
const element = useRef(null);
|
|
232
|
+
const [store] = useState(() => contextStoreOf(element));
|
|
233
|
+
const context = useSyncExternalStore(store.subscribe, store.read, atTheTop);
|
|
234
|
+
return createElement(ToolbarItemCtx.Provider, { value: context }, createElement("div", {
|
|
235
|
+
ref: element,
|
|
236
|
+
slot: `item:${id}`,
|
|
237
|
+
style: CONTENTS
|
|
238
|
+
}, children));
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Where the toolbar item this component is in sits: which way its bar runs (`orientation`), the
|
|
242
|
+
* bar's place (`edge`), which of the item's `variants` the toolbar picked (`variant`), and the
|
|
243
|
+
* viewer's size (`size`). Works inside a `ToolbarItem`.
|
|
244
|
+
*/
|
|
245
|
+
function useToolbarItem() {
|
|
246
|
+
const context = useContext(ToolbarItemCtx);
|
|
247
|
+
if (!context) throw new Error("[embedpdf] useToolbarItem() works inside a <ToolbarItem>: it says where that item sits.");
|
|
248
|
+
return context;
|
|
249
|
+
}
|
|
250
|
+
/** What goes inside a panel: the viewer draws its frame, header and the way it opens. */
|
|
251
|
+
function Panel({ id, children }) {
|
|
252
|
+
return createElement("div", {
|
|
253
|
+
slot: `panel:${id}`,
|
|
254
|
+
style: CONTENTS
|
|
255
|
+
}, children);
|
|
256
|
+
}
|
|
257
|
+
/** Your component in one of the viewer's regions. */
|
|
258
|
+
function Region({ name, children }) {
|
|
259
|
+
return createElement("div", {
|
|
260
|
+
slot: name,
|
|
261
|
+
style: CONTENTS
|
|
262
|
+
}, children);
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/viewer-components.ts
|
|
266
|
+
/**
|
|
267
|
+
* The viewer's own components, for yours: `Button`, `Menu` and `MenuItem`
|
|
268
|
+
* render the viewer's `<epdf-button>`, `<epdf-menu>` and `<epdf-menu-item>`
|
|
269
|
+
* elements, which the viewer draws like its own toolbar buttons and menus.
|
|
270
|
+
* Use them inside the viewer: in a `ToolbarItem`, a `Panel` or a `Region`.
|
|
271
|
+
* The elements come with the viewer, so this module imports nothing from it.
|
|
272
|
+
*/
|
|
273
|
+
/** A toolbar button, the viewer's own. */
|
|
274
|
+
function Button({ command, icon, label, onClick, className, style }) {
|
|
275
|
+
return createElement("epdf-button", {
|
|
276
|
+
command,
|
|
277
|
+
icon,
|
|
278
|
+
label,
|
|
279
|
+
onClick,
|
|
280
|
+
style,
|
|
281
|
+
...className === void 0 ? {} : { class: className }
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* A button that opens a menu of `MenuItem`s, the way the viewer's menus open: away from its bar,
|
|
286
|
+
* flipped when there's no room, and up from the bottom on a phone.
|
|
287
|
+
*/
|
|
288
|
+
function Menu({ icon, label, children }) {
|
|
289
|
+
return createElement("epdf-menu", {
|
|
290
|
+
icon,
|
|
291
|
+
label
|
|
292
|
+
}, children);
|
|
293
|
+
}
|
|
294
|
+
/** A row in a `Menu`. */
|
|
295
|
+
function MenuItem({ command, icon, onSelect, children }) {
|
|
296
|
+
const element = useRef(null);
|
|
297
|
+
const select = useRef(onSelect);
|
|
298
|
+
select.current = onSelect;
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
const target = element.current;
|
|
301
|
+
if (!target) return;
|
|
302
|
+
const onPicked = () => select.current?.();
|
|
303
|
+
target.addEventListener("select", onPicked);
|
|
304
|
+
return () => target.removeEventListener("select", onPicked);
|
|
305
|
+
}, []);
|
|
306
|
+
return createElement("epdf-menu-item", {
|
|
307
|
+
ref: element,
|
|
308
|
+
command,
|
|
309
|
+
icon
|
|
310
|
+
}, children);
|
|
311
|
+
}
|
|
312
|
+
//#endregion
|
|
313
|
+
export { Region as a, PDFViewer as c, Panel as i, useCommand as l, Menu as n, ToolbarItem as o, MenuItem as r, useToolbarItem as s, Button as t, usePDFViewer as u };
|
|
314
|
+
|
|
315
|
+
//# sourceMappingURL=parts-BNOPi71I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parts-BNOPi71I.js","names":[],"sources":["../src/hooks.ts","../src/component.ts","../src/slots.ts","../src/viewer-components.ts"],"sourcesContent":["'use client';\n/**\n * The hooks for components inside <PDFViewer>: the `viewer`, and a command as a button needs it.\n * They work in the components you give the viewer, which mount once it's live, inside it. The\n * headless hooks of `@embedpdf/react` work there too, for any plugin's API and state.\n */\n// Types only, so this module holds no runtime edge to any engine (see ./component).\nimport type { Viewer } from '@embedpdf/viewer/core';\nimport { createContext, useContext } from 'react';\n\n/**\n * A command as a button needs it, for the active document: its `label`, `icon` and `shortcut`,\n * whether it's `enabled`, `active` and `visible`, and `run()`. The headless hook, which reads the\n * kernel <PDFViewer> gives its children: an id the viewer doesn't have is neither visible nor\n * enabled.\n */\nexport { useCommand } from '@embedpdf/react/commands';\n\n/** The `viewer` the components inside a <PDFViewer> belong to. */\nexport const ViewerContext = createContext<Viewer | null>(null);\n\n/**\n * The `viewer`, in a component inside <PDFViewer>: `get(name)`, `documents`, `commands`,\n * `watch` and `forDocument`. Your components mount once the viewer is live, so it is always\n * there. Outside the viewer, keep the one `onReady` gives you; called there, this throws.\n */\nexport function usePDFViewer(): Viewer {\n const viewer = useContext(ViewerContext);\n if (!viewer) {\n throw new Error(\n '[embedpdf] usePDFViewer() works in a component inside <PDFViewer>. Outside the viewer, ' +\n 'keep the viewer its onReady gives you.',\n );\n }\n return viewer;\n}\n","'use client';\n/**\n * <PDFViewer> — the React face of the <embedpdf-viewer> custom element, and\n * deliberately engine-blind: this module imports the viewer for types only, so\n * it holds no runtime edge to any engine. Which engine ships is decided by the\n * entry that re-exports it:\n *\n * ./index.ts → `@embedpdf/viewer` — local PDFium engine, registered as\n * the default (the zero-config door)\n * ./core.ts → `@embedpdf/viewer/core` — no default engine; you inject one,\n * and PDFium is structurally absent\n *\n * That side-effect import is the entire difference between the two doors, and\n * it lives only in those two files. A runtime import here would weld PDFium\n * into every consumer's bundle — cloud builds included — so an import-boundary\n * lint rule (see eslint.config.js) holds this module to `import type`.\n *\n * ```tsx\n * <PDFViewer src=\"/report.pdf\" style={{ height: '100vh' }}>\n * <ToolbarItem id=\"doc-picker\"><DocPicker /></ToolbarItem> // ← slot=\"item:doc-picker\"\n * </PDFViewer>\n * ```\n *\n * The wrapper is deliberately thin: it renders the <embedpdf-viewer> custom\n * element, hands it the config before its deferred first mount, and passes\n * children straight through as light DOM — the browser projects a child with\n * `slot=\"item:<id>\"` into the chrome's matching `custom()` socket while the child\n * stays in the host React tree, so its context, state, and page CSS all keep\n * working. There is no reactSlot()\n * bridge because none is needed: a slot is a child. The children mount once\n * the viewer is ready, inside its kernel, so the headless hooks of\n * `@embedpdf/react` in them read this viewer, and `usePDFViewer()` gives\n * them its `viewer`. When the viewer goes (an unmount, or a new one with a\n * new config) they unmount with it, and mount again inside the next one.\n *\n * On the server it renders the empty element, and the viewer starts in the\n * browser: nothing here, or in the viewer it imports, touches the DOM when\n * imported.\n *\n * `src`, `theme`, `locale`, `disable` and `readOnly` follow their props: the\n * element changes them in place. `onReady`, `onDocumentChange` and\n * `onPanelResize` call the latest prop. Every other prop is read once, when the viewer starts; a later\n * change is ignored (development warns), and a new `key` mounts a new viewer\n * with it.\n */\n// The headless runtime carries no engine: it gives the children the viewer's kernel.\nimport { KernelProvider, type Kernel } from '@embedpdf/react/runtime';\n// `ElementConfig` is the kernel's config — the widest of all, with the engine\n// seam left `unknown` — so the shared implementation sits above every door and\n// each entry re-types it narrower by plain assignment (parameter types are\n// contravariant, so this direction needs no cast). Every import from the\n// viewer is type-only, which is what keeps this module engine-blind.\nimport type {\n ElementConfig,\n EmbedPdfViewerElement,\n LiveConfigKey,\n Viewer,\n} from '@embedpdf/viewer/core';\nimport {\n createElement,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type MutableRefObject,\n type ReactNode,\n type Ref,\n} from 'react';\n\nimport { ViewerContext } from './hooks';\n\n/** The framework-side props — everything that is not viewer config. */\nexport interface PDFViewerExtras {\n className?: string;\n style?: CSSProperties;\n /** Light-DOM children, shown where their slot says: `<ToolbarItem>`, `<Panel>`, `<Region>`. */\n children?: ReactNode;\n /** The underlying element, if you need the imperative surface. */\n elementRef?: Ref<EmbedPdfViewerElement>;\n}\n\n/** Layout effects in the browser; on the server, where effects never run, the plain kind. */\nconst useBrowserLayoutEffect = typeof document === 'undefined' ? useEffect : useLayoutEffect;\n\n/** Where the viewer keeps its kernel (`createViewer` in `@embedpdf/viewer-chrome`). */\nconst VIEWER_KERNEL = Symbol.for('@embedpdf/viewer/kernel');\nconst kernelOf = (viewer: Viewer): Kernel | null =>\n (viewer as unknown as Partial<Record<symbol, Kernel>>)[VIEWER_KERNEL] ?? null;\n\n/** A key for each viewer, so the children mount again inside a new one. */\nconst viewerKeys = new WeakMap<Viewer, number>();\nlet nextViewerKey = 0;\nconst keyOf = (viewer: Viewer): number => {\n let key = viewerKeys.get(viewer);\n if (key === undefined) viewerKeys.set(viewer, (key = nextViewerKey++));\n return key;\n};\n\n/** The children inside the viewer: its kernel for the headless hooks, and the `viewer`. */\nfunction inViewer(viewer: Viewer, children: ReactNode): ReactNode {\n const key = keyOf(viewer);\n const inside = createElement(ViewerContext.Provider, { key, value: viewer }, children);\n const kernel = kernelOf(viewer);\n return kernel ? createElement(KernelProvider, { key, kernel }, inside) : inside;\n}\n\n/** The props the element follows while it runs; the wrapper hands their changes on. */\nconst LIVE_PROPS: Readonly<Record<LiveConfigKey, true>> = {\n src: true,\n theme: true,\n locale: true,\n disable: true,\n readOnly: true,\n};\n\n/** Every other prop is read once: a changed one is ignored, and development warns once. */\nconst warnConfigChange = (initial: ElementConfig, next: ElementConfig): boolean => {\n const keys = new Set([...Object.keys(initial), ...Object.keys(next)]);\n for (const key of keys) {\n if (key in LIVE_PROPS) continue;\n if (\n !Object.is((initial as Record<string, unknown>)[key], (next as Record<string, unknown>)[key])\n ) {\n console.warn(\n `[embedpdf] <PDFViewer>: \\`${key}\\` is read once, when the viewer starts, so the change is ` +\n 'ignored. Pass a stable value (module scope, useState, useMemo), or give the viewer a new ' +\n '`key` to mount a new one with it.',\n );\n return true;\n }\n }\n return false;\n};\n\n/**\n * Props for the shared implementation. Each entry re-exports <PDFViewer>\n * narrowed to its door's config — see `PDFViewerProps` in ./index.ts (engine\n * optional) and ./core.ts (engine required).\n */\nexport type PDFViewerImplProps = ElementConfig & PDFViewerExtras;\n\nexport function PDFViewer({\n className,\n style,\n children,\n elementRef,\n onReady,\n onDocumentChange,\n onPanelResize,\n ...config\n}: PDFViewerImplProps) {\n const ref = useRef<EmbedPdfViewerElement | null>(null);\n // The viewer once it's ready: the children render inside its kernel, so the headless hooks in\n // them read this viewer. Before that the viewer has no sockets to show them in.\n const [viewer, setViewer] = useState<Viewer | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onDocumentChangeRef = useRef(onDocumentChange);\n onDocumentChangeRef.current = onDocumentChange;\n const onPanelResizeRef = useRef(onPanelResize);\n onPanelResizeRef.current = onPanelResize;\n // The callbacks reach the element once, as functions that call the latest prop: an inline\n // callback is a new function every render, and that changes nothing.\n const callbacks = useRef<Pick<ElementConfig, 'onReady' | 'onDocumentChange' | 'onPanelResize'>>({\n onReady: (live) => onReadyRef.current?.(live),\n onDocumentChange: (documentId) => onDocumentChangeRef.current?.(documentId),\n onPanelResize: (event) => onPanelResizeRef.current?.(event),\n });\n // Read once, like <Viewer engine/plugins>: the element boots once with the\n // first config. A later change to anything but the live props cannot mean\n // \"rebuild\" without dropping every open document, so it is ignored — loudly,\n // in development.\n const initialConfig = useRef<ElementConfig | null>(null);\n const warned = useRef(false);\n if (process.env.NODE_ENV !== 'production') {\n if (initialConfig.current === null) initialConfig.current = config;\n else if (!warned.current) warned.current = warnConfigChange(initialConfig.current, config);\n }\n\n // Layout effects run in the insertion task, before the element's deferred\n // (microtask) declarative mount — so the viewer boots exactly once, with\n // this config, and the ready listener is in place before it can fire.\n // Empty deps: config is init-only by contract.\n useBrowserLayoutEffect(() => {\n const element = ref.current;\n if (!element) return;\n // Each mount of the element brings a new viewer: a config change that isn't live, or the\n // element taken out and put back.\n const onEvent = () => setViewer(element.viewer);\n element.addEventListener('epdf:ready', onEvent);\n element.config = { ...configRef.current, ...callbacks.current };\n // A remount-with-key that reuses a live element cannot miss the event.\n if (element.viewer) onEvent();\n return () => element.removeEventListener('epdf:ready', onEvent);\n }, []);\n\n // The live props, after every render: the element compares them by value, so an unchanged\n // one (an inline `disable={['forms']}` too) changes nothing.\n useBrowserLayoutEffect(() => {\n const element = ref.current;\n if (!element) return;\n element.src = config.src;\n element.theme = config.theme;\n element.locale = config.locale;\n element.disable = config.disable;\n element.readOnly = config.readOnly;\n });\n\n return createElement(\n 'embedpdf-viewer',\n {\n ref: (element: EmbedPdfViewerElement | null) => {\n ref.current = element;\n if (typeof elementRef === 'function') elementRef(element);\n else if (elementRef)\n (elementRef as MutableRefObject<EmbedPdfViewerElement | null>).current = element;\n },\n // A custom element's class is its `class` attribute: React 18 writes `className` on one\n // as an attribute of that name, in the browser and on the server.\n ...(className === undefined ? {} : { class: className }),\n style,\n // The element waits for the config this sets, also over the server's HTML before hydration.\n 'data-epdf-wrapped': '',\n },\n viewer ? inViewer(viewer, children) : null,\n );\n}\n","/**\n * Your own components in the viewer: `ToolbarItem`, `Panel` and `Region` put\n * your component in the place the viewer makes for it (`item:<id>`,\n * `panel:<id>`, or a region's name). It renders as a child of the\n * `<embedpdf-viewer>` element, so it stays in your React tree, with your\n * context, your state and your CSS; the browser shows it in that place.\n *\n * `useToolbarItem()` tells a component inside a `ToolbarItem` where it sits.\n * The viewer keeps that on the item's element as `data-orientation`,\n * `data-edge`, `data-variant` and `data-size`; `ToolbarItem` reads them, live,\n * into a context for the hook, and redraws before the browser lays the bar out\n * (an external store's change renders at once), so the toolbar measures your\n * component in the variant it picked. Types only from the viewer, as in\n * `./component`: this module is shared by both doors.\n */\nimport type { ToolbarItemContext, ToolbarItemEdge, ViewerSize } from '@embedpdf/viewer/core';\nimport {\n createContext,\n createElement,\n useContext,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n type RefObject,\n} from 'react';\n\n/** The element around what you put in a place adds no box: your component lays out there. */\nconst CONTENTS = { display: 'contents' } as const;\n\n// ── a toolbar item ───────────────────────────────────────────────────────────\n\nconst EDGES: readonly ToolbarItemEdge[] = ['top', 'bottom', 'start', 'end', 'float', 'selection'];\nconst SIZES: readonly ViewerSize[] = ['compact', 'medium', 'expanded'];\n\n/** Before the viewer says where the item is: the main bar's place. */\nconst AT_THE_TOP: ToolbarItemContext = {\n orientation: 'horizontal',\n edge: 'top',\n variant: null,\n size: 'expanded',\n};\n\nconst ATTRIBUTES = ['data-orientation', 'data-edge', 'data-variant', 'data-size'];\n\n/** Where the item whose element is `element` sits, as the viewer wrote it there. */\nfunction toolbarItemContextOf(element: Element): ToolbarItemContext {\n const edge = element.getAttribute('data-edge') as ToolbarItemEdge | null;\n const size = element.getAttribute('data-size') as ViewerSize | null;\n return {\n orientation:\n element.getAttribute('data-orientation') === 'vertical' ? 'vertical' : 'horizontal',\n edge: edge && EDGES.includes(edge) ? edge : AT_THE_TOP.edge,\n variant: element.getAttribute('data-variant'),\n size: size && SIZES.includes(size) ? size : AT_THE_TOP.size,\n };\n}\n\nconst sameContext = (left: ToolbarItemContext, right: ToolbarItemContext): boolean =>\n left.orientation === right.orientation &&\n left.edge === right.edge &&\n left.variant === right.variant &&\n left.size === right.size;\n\nconst ToolbarItemCtx = createContext<ToolbarItemContext | null>(null);\n\nexport interface ToolbarItemProps {\n /** The item's id: `{ custom: id }` in the layout. */\n id: string;\n children?: ReactNode;\n}\n\n/** What the viewer says on the element `element` holds, as a store: the same object until it changes. */\nfunction contextStoreOf(element: RefObject<Element | null>) {\n let current = AT_THE_TOP;\n return {\n subscribe(onChange: () => void): () => void {\n const target = element.current;\n if (!target) return () => {};\n const observer = new MutationObserver(onChange);\n observer.observe(target, { attributes: true, attributeFilter: ATTRIBUTES });\n return () => observer.disconnect();\n },\n read(): ToolbarItemContext {\n const target = element.current;\n const next = target ? toolbarItemContextOf(target) : current;\n if (!sameContext(current, next)) current = next;\n return current;\n },\n };\n}\n\nconst atTheTop = () => AT_THE_TOP;\n\n/** Your component in the toolbar, where the layout's `{ custom: id }` makes room for it. */\nexport function ToolbarItem({ id, children }: ToolbarItemProps) {\n const element = useRef<HTMLDivElement | null>(null);\n const [store] = useState(() => contextStoreOf(element));\n const context = useSyncExternalStore(store.subscribe, store.read, atTheTop);\n return createElement(\n ToolbarItemCtx.Provider,\n { value: context },\n createElement('div', { ref: element, slot: `item:${id}`, style: CONTENTS }, children),\n );\n}\n\n/**\n * Where the toolbar item this component is in sits: which way its bar runs (`orientation`), the\n * bar's place (`edge`), which of the item's `variants` the toolbar picked (`variant`), and the\n * viewer's size (`size`). Works inside a `ToolbarItem`.\n */\nexport function useToolbarItem(): ToolbarItemContext {\n const context = useContext(ToolbarItemCtx);\n if (!context) {\n throw new Error(\n '[embedpdf] useToolbarItem() works inside a <ToolbarItem>: it says where that item sits.',\n );\n }\n return context;\n}\n\n// ── a panel, a region ────────────────────────────────────────────────────────\n\nexport interface PanelProps {\n /** The panel's id, as `addPanel(id, …)` names it in the layout. */\n id: string;\n children?: ReactNode;\n}\n\n/** What goes inside a panel: the viewer draws its frame, header and the way it opens. */\nexport function Panel({ id, children }: PanelProps) {\n return createElement('div', { slot: `panel:${id}`, style: CONTENTS }, children);\n}\n\n/**\n * A place around the viewer for your own components: `header` above everything, `footer` below\n * everything, `tabs` in place of the document tabs, `empty` over the pages while no document is\n * open.\n */\nexport type RegionName = 'header' | 'footer' | 'tabs' | 'empty';\n\nexport interface RegionProps {\n name: RegionName;\n children?: ReactNode;\n}\n\n/** Your component in one of the viewer's regions. */\nexport function Region({ name, children }: RegionProps) {\n return createElement('div', { slot: name, style: CONTENTS }, children);\n}\n","/**\n * The viewer's own components, for yours: `Button`, `Menu` and `MenuItem`\n * render the viewer's `<epdf-button>`, `<epdf-menu>` and `<epdf-menu-item>`\n * elements, which the viewer draws like its own toolbar buttons and menus.\n * Use them inside the viewer: in a `ToolbarItem`, a `Panel` or a `Region`.\n * The elements come with the viewer, so this module imports nothing from it.\n */\nimport {\n createElement,\n useEffect,\n useRef,\n type CSSProperties,\n type MouseEvent,\n type ReactNode,\n} from 'react';\n\nexport interface ButtonProps {\n /** Makes it this command's button: its icon, tooltip and state, and it runs it. */\n command?: string;\n /** An icon by name, the viewer's or yours, for a button of your own. */\n icon?: string;\n /** The name of a button of your own, for its tooltip and screen readers. */\n label?: string;\n onClick?: (event: MouseEvent<HTMLElement>) => void;\n className?: string;\n style?: CSSProperties;\n}\n\n/** A toolbar button, the viewer's own. */\nexport function Button({ command, icon, label, onClick, className, style }: ButtonProps) {\n return createElement('epdf-button', {\n command,\n icon,\n label,\n onClick,\n style,\n // A custom element's class is its `class` attribute: React 18 writes `className` as it is.\n ...(className === undefined ? {} : { class: className }),\n });\n}\n\nexport interface MenuProps {\n /** The icon of the button that opens it, by name. */\n icon?: string;\n /** Its name, for the button's tooltip and screen readers. */\n label: string;\n /** Its rows: `MenuItem`s. */\n children?: ReactNode;\n}\n\n/**\n * A button that opens a menu of `MenuItem`s, the way the viewer's menus open: away from its bar,\n * flipped when there's no room, and up from the bottom on a phone.\n */\nexport function Menu({ icon, label, children }: MenuProps) {\n return createElement('epdf-menu', { icon, label }, children);\n}\n\nexport interface MenuItemProps {\n /** Makes it this command's row: its icon, label and state, and picking it runs it. */\n command?: string;\n /** An icon by name, the viewer's or yours. */\n icon?: string;\n /** Called when the row is picked. */\n onSelect?: () => void;\n /** Its text. */\n children?: ReactNode;\n}\n\n/** A row in a `Menu`. */\nexport function MenuItem({ command, icon, onSelect, children }: MenuItemProps) {\n const element = useRef<HTMLElement | null>(null);\n const select = useRef(onSelect);\n select.current = onSelect;\n // The element's `select` event: React passes no handler to a custom element's own events.\n useEffect(() => {\n const target = element.current;\n if (!target) return;\n const onPicked = () => select.current?.();\n target.addEventListener('select', onPicked);\n return () => target.removeEventListener('select', onPicked);\n }, []);\n return createElement('epdf-menu-item', { ref: element, command, icon }, children);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ElementConfig, EmbedPdfViewerElement, ToolbarItemContext, Viewer } from "@embedpdf/viewer/core";
|
|
2
|
+
import { CSSProperties, MouseEvent, ReactNode, Ref } from "react";
|
|
3
|
+
import { useCommand } from "@embedpdf/react/commands";
|
|
4
|
+
//#region src/component.d.ts
|
|
5
|
+
/** The framework-side props — everything that is not viewer config. */
|
|
6
|
+
interface PDFViewerExtras {
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: CSSProperties;
|
|
9
|
+
/** Light-DOM children, shown where their slot says: `<ToolbarItem>`, `<Panel>`, `<Region>`. */
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
/** The underlying element, if you need the imperative surface. */
|
|
12
|
+
elementRef?: Ref<EmbedPdfViewerElement>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Props for the shared implementation. Each entry re-exports <PDFViewer>
|
|
16
|
+
* narrowed to its door's config — see `PDFViewerProps` in ./index.ts (engine
|
|
17
|
+
* optional) and ./core.ts (engine required).
|
|
18
|
+
*/
|
|
19
|
+
type PDFViewerImplProps = ElementConfig & PDFViewerExtras;
|
|
20
|
+
declare function PDFViewer({ className, style, children, elementRef, onReady, onDocumentChange, onPanelResize, ...config }: PDFViewerImplProps): import("react").ReactElement<{
|
|
21
|
+
style: CSSProperties | undefined;
|
|
22
|
+
'data-epdf-wrapped': string;
|
|
23
|
+
class?: string | undefined;
|
|
24
|
+
ref: (element: EmbedPdfViewerElement | null) => void;
|
|
25
|
+
}, string | import("react").JSXElementConstructor<any>>;
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/hooks.d.ts
|
|
28
|
+
/**
|
|
29
|
+
* The `viewer`, in a component inside <PDFViewer>: `get(name)`, `documents`, `commands`,
|
|
30
|
+
* `watch` and `forDocument`. Your components mount once the viewer is live, so it is always
|
|
31
|
+
* there. Outside the viewer, keep the one `onReady` gives you; called there, this throws.
|
|
32
|
+
*/
|
|
33
|
+
declare function usePDFViewer(): Viewer;
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/slots.d.ts
|
|
36
|
+
interface ToolbarItemProps {
|
|
37
|
+
/** The item's id: `{ custom: id }` in the layout. */
|
|
38
|
+
id: string;
|
|
39
|
+
children?: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/** Your component in the toolbar, where the layout's `{ custom: id }` makes room for it. */
|
|
42
|
+
declare function ToolbarItem({ id, children }: ToolbarItemProps): import("react").FunctionComponentElement<import("react").ProviderProps<ToolbarItemContext | null>>;
|
|
43
|
+
/**
|
|
44
|
+
* Where the toolbar item this component is in sits: which way its bar runs (`orientation`), the
|
|
45
|
+
* bar's place (`edge`), which of the item's `variants` the toolbar picked (`variant`), and the
|
|
46
|
+
* viewer's size (`size`). Works inside a `ToolbarItem`.
|
|
47
|
+
*/
|
|
48
|
+
declare function useToolbarItem(): ToolbarItemContext;
|
|
49
|
+
interface PanelProps {
|
|
50
|
+
/** The panel's id, as `addPanel(id, …)` names it in the layout. */
|
|
51
|
+
id: string;
|
|
52
|
+
children?: ReactNode;
|
|
53
|
+
}
|
|
54
|
+
/** What goes inside a panel: the viewer draws its frame, header and the way it opens. */
|
|
55
|
+
declare function Panel({ id, children }: PanelProps): import("react").DetailedReactHTMLElement<{
|
|
56
|
+
slot: string;
|
|
57
|
+
style: {
|
|
58
|
+
readonly display: "contents";
|
|
59
|
+
};
|
|
60
|
+
}, HTMLElement>;
|
|
61
|
+
/**
|
|
62
|
+
* A place around the viewer for your own components: `header` above everything, `footer` below
|
|
63
|
+
* everything, `tabs` in place of the document tabs, `empty` over the pages while no document is
|
|
64
|
+
* open.
|
|
65
|
+
*/
|
|
66
|
+
type RegionName = 'header' | 'footer' | 'tabs' | 'empty';
|
|
67
|
+
interface RegionProps {
|
|
68
|
+
name: RegionName;
|
|
69
|
+
children?: ReactNode;
|
|
70
|
+
}
|
|
71
|
+
/** Your component in one of the viewer's regions. */
|
|
72
|
+
declare function Region({ name, children }: RegionProps): import("react").DetailedReactHTMLElement<{
|
|
73
|
+
slot: RegionName;
|
|
74
|
+
style: {
|
|
75
|
+
readonly display: "contents";
|
|
76
|
+
};
|
|
77
|
+
}, HTMLElement>;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/viewer-components.d.ts
|
|
80
|
+
interface ButtonProps {
|
|
81
|
+
/** Makes it this command's button: its icon, tooltip and state, and it runs it. */
|
|
82
|
+
command?: string;
|
|
83
|
+
/** An icon by name, the viewer's or yours, for a button of your own. */
|
|
84
|
+
icon?: string;
|
|
85
|
+
/** The name of a button of your own, for its tooltip and screen readers. */
|
|
86
|
+
label?: string;
|
|
87
|
+
onClick?: (event: MouseEvent<HTMLElement>) => void;
|
|
88
|
+
className?: string;
|
|
89
|
+
style?: CSSProperties;
|
|
90
|
+
}
|
|
91
|
+
/** A toolbar button, the viewer's own. */
|
|
92
|
+
declare function Button({ command, icon, label, onClick, className, style }: ButtonProps): import("react").DOMElement<{
|
|
93
|
+
class?: string | undefined;
|
|
94
|
+
command: string | undefined;
|
|
95
|
+
icon: string | undefined;
|
|
96
|
+
label: string | undefined;
|
|
97
|
+
onClick: ((event: MouseEvent<HTMLElement>) => void) | undefined;
|
|
98
|
+
style: CSSProperties | undefined;
|
|
99
|
+
}, Element>;
|
|
100
|
+
interface MenuProps {
|
|
101
|
+
/** The icon of the button that opens it, by name. */
|
|
102
|
+
icon?: string;
|
|
103
|
+
/** Its name, for the button's tooltip and screen readers. */
|
|
104
|
+
label: string;
|
|
105
|
+
/** Its rows: `MenuItem`s. */
|
|
106
|
+
children?: ReactNode;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A button that opens a menu of `MenuItem`s, the way the viewer's menus open: away from its bar,
|
|
110
|
+
* flipped when there's no room, and up from the bottom on a phone.
|
|
111
|
+
*/
|
|
112
|
+
declare function Menu({ icon, label, children }: MenuProps): import("react").ReactElement<{
|
|
113
|
+
icon: string | undefined;
|
|
114
|
+
label: string;
|
|
115
|
+
}, string | import("react").JSXElementConstructor<any>>;
|
|
116
|
+
interface MenuItemProps {
|
|
117
|
+
/** Makes it this command's row: its icon, label and state, and picking it runs it. */
|
|
118
|
+
command?: string;
|
|
119
|
+
/** An icon by name, the viewer's or yours. */
|
|
120
|
+
icon?: string;
|
|
121
|
+
/** Called when the row is picked. */
|
|
122
|
+
onSelect?: () => void;
|
|
123
|
+
/** Its text. */
|
|
124
|
+
children?: ReactNode;
|
|
125
|
+
}
|
|
126
|
+
/** A row in a `Menu`. */
|
|
127
|
+
declare function MenuItem({ command, icon, onSelect, children }: MenuItemProps): import("react").ReactElement<{
|
|
128
|
+
ref: import("react").MutableRefObject<HTMLElement | null>;
|
|
129
|
+
command: string | undefined;
|
|
130
|
+
icon: string | undefined;
|
|
131
|
+
}, string | import("react").JSXElementConstructor<any>>;
|
|
132
|
+
//#endregion
|
|
133
|
+
export { PDFViewer as _, MenuItemProps as a, PanelProps as c, RegionProps as d, ToolbarItem as f, usePDFViewer as g, useCommand as h, MenuItem as i, Region as l, useToolbarItem as m, ButtonProps as n, MenuProps as o, ToolbarItemProps as p, Menu as r, Panel as s, Button as t, RegionName as u, PDFViewerExtras as v, PDFViewerImplProps as y };
|
|
134
|
+
//# sourceMappingURL=parts-CDiQnn2K.d.cts.map
|