@embedpdf/svelte 0.0.0-stage → 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/LICENSE +192 -0
- package/README.md +312 -2
- package/dist/actions/readers.svelte.d.ts +29 -0
- package/dist/actions/readers.svelte.js +55 -0
- package/dist/actions.d.ts +13 -0
- package/dist/actions.js +13 -0
- package/dist/anchored/Anchored.svelte +24 -0
- package/dist/anchored/Anchored.svelte.d.ts +4 -0
- package/dist/anchored/PlacedAnchored.svelte +97 -0
- package/dist/anchored/PlacedAnchored.svelte.d.ts +4 -0
- package/dist/anchored/context.d.ts +31 -0
- package/dist/anchored/context.js +36 -0
- package/dist/anchored/props.d.ts +29 -0
- package/dist/anchored/props.js +1 -0
- package/dist/anchored.d.ts +12 -0
- package/dist/anchored.js +2 -0
- package/dist/annotation/AnnotationChrome.svelte +173 -0
- package/dist/annotation/AnnotationChrome.svelte.d.ts +11 -0
- package/dist/annotation/AnnotationDraftMenu.svelte +27 -0
- package/dist/annotation/AnnotationDraftMenu.svelte.d.ts +4 -0
- package/dist/annotation/AnnotationFrame.svelte +46 -0
- package/dist/annotation/AnnotationFrame.svelte.d.ts +15 -0
- package/dist/annotation/AnnotationLayer.svelte +241 -0
- package/dist/annotation/AnnotationLayer.svelte.d.ts +4 -0
- package/dist/annotation/AnnotationLook.svelte +67 -0
- package/dist/annotation/AnnotationLook.svelte.d.ts +21 -0
- package/dist/annotation/AnnotationMenu.svelte +47 -0
- package/dist/annotation/AnnotationMenu.svelte.d.ts +4 -0
- package/dist/annotation/AnnotationRotationBadge.svelte +52 -0
- package/dist/annotation/AnnotationRotationBadge.svelte.d.ts +4 -0
- package/dist/annotation/AnnotationScene.svelte +27 -0
- package/dist/annotation/AnnotationScene.svelte.d.ts +7 -0
- package/dist/annotation/FreeTextBox.svelte +55 -0
- package/dist/annotation/FreeTextBox.svelte.d.ts +9 -0
- package/dist/annotation/NativeDrawing.svelte +61 -0
- package/dist/annotation/NativeDrawing.svelte.d.ts +10 -0
- package/dist/annotation/StampGhost.svelte +79 -0
- package/dist/annotation/StampGhost.svelte.d.ts +7 -0
- package/dist/annotation/chrome-paint.svelte.d.ts +30 -0
- package/dist/annotation/chrome-paint.svelte.js +27 -0
- package/dist/annotation/props.d.ts +181 -0
- package/dist/annotation/props.js +1 -0
- package/dist/annotation/readers.svelte.d.ts +59 -0
- package/dist/annotation/readers.svelte.js +136 -0
- package/dist/annotation/state.d.ts +18 -0
- package/dist/annotation/state.js +20 -0
- package/dist/annotation/text-box.svelte.d.ts +27 -0
- package/dist/annotation/text-box.svelte.js +94 -0
- package/dist/annotation.d.ts +32 -0
- package/dist/annotation.js +31 -0
- package/dist/commands/readers.svelte.d.ts +50 -0
- package/dist/commands/readers.svelte.js +83 -0
- package/dist/commands.d.ts +9 -0
- package/dist/commands.js +9 -0
- package/dist/form/ButtonControl.svelte +38 -0
- package/dist/form/ButtonControl.svelte.d.ts +11 -0
- package/dist/form/ComboControl.svelte +52 -0
- package/dist/form/ComboControl.svelte.d.ts +11 -0
- package/dist/form/FieldPictures.svelte +145 -0
- package/dist/form/FieldPictures.svelte.d.ts +3 -0
- package/dist/form/FormFocusRing.svelte +25 -0
- package/dist/form/FormFocusRing.svelte.d.ts +8 -0
- package/dist/form/FormLayer.svelte +98 -0
- package/dist/form/FormLayer.svelte.d.ts +3 -0
- package/dist/form/ListControl.svelte +49 -0
- package/dist/form/ListControl.svelte.d.ts +11 -0
- package/dist/form/NativeListBox.svelte +57 -0
- package/dist/form/NativeListBox.svelte.d.ts +4 -0
- package/dist/form/SignatureControl.svelte +53 -0
- package/dist/form/SignatureControl.svelte.d.ts +11 -0
- package/dist/form/TextControl.svelte +107 -0
- package/dist/form/TextControl.svelte.d.ts +11 -0
- package/dist/form/ToggleControl.svelte +50 -0
- package/dist/form/ToggleControl.svelte.d.ts +11 -0
- package/dist/form/WidgetBox.svelte +82 -0
- package/dist/form/WidgetBox.svelte.d.ts +15 -0
- package/dist/form/props.d.ts +21 -0
- package/dist/form/props.js +1 -0
- package/dist/form/readers.svelte.d.ts +46 -0
- package/dist/form/readers.svelte.js +52 -0
- package/dist/form/widget.d.ts +16 -0
- package/dist/form/widget.js +20 -0
- package/dist/form.d.ts +11 -0
- package/dist/form.js +12 -0
- package/dist/history/readers.d.ts +22 -0
- package/dist/history/readers.js +21 -0
- package/dist/history.d.ts +10 -0
- package/dist/history.js +11 -0
- package/dist/i18n/readers.svelte.d.ts +32 -0
- package/dist/i18n/readers.svelte.js +30 -0
- package/dist/i18n.d.ts +8 -0
- package/dist/i18n.js +9 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +28 -0
- package/dist/interaction/PagePointerSource.svelte +37 -0
- package/dist/interaction/PagePointerSource.svelte.d.ts +3 -0
- package/dist/interaction/readers.svelte.d.ts +26 -0
- package/dist/interaction/readers.svelte.js +47 -0
- package/dist/interaction.d.ts +11 -0
- package/dist/interaction.js +13 -0
- package/dist/link/LinkLayer.svelte +164 -0
- package/dist/link/LinkLayer.svelte.d.ts +4 -0
- package/dist/link/props.d.ts +17 -0
- package/dist/link/props.js +1 -0
- package/dist/link/readers.svelte.d.ts +18 -0
- package/dist/link/readers.svelte.js +38 -0
- package/dist/link.d.ts +11 -0
- package/dist/link.js +11 -0
- package/dist/measurement/readers.d.ts +44 -0
- package/dist/measurement/readers.js +46 -0
- package/dist/measurement.d.ts +12 -0
- package/dist/measurement.js +13 -0
- package/dist/metadata/readers.d.ts +22 -0
- package/dist/metadata/readers.js +22 -0
- package/dist/metadata.d.ts +12 -0
- package/dist/metadata.js +13 -0
- package/dist/page-edit/readers.d.ts +7 -0
- package/dist/page-edit/readers.js +11 -0
- package/dist/page-edit.d.ts +12 -0
- package/dist/page-edit.js +13 -0
- package/dist/page-view/PageView.svelte +192 -0
- package/dist/page-view/PageView.svelte.d.ts +4 -0
- package/dist/page-view/props.d.ts +32 -0
- package/dist/page-view/props.js +1 -0
- package/dist/page-view.d.ts +8 -0
- package/dist/page-view.js +7 -0
- package/dist/redaction/readers.d.ts +37 -0
- package/dist/redaction/readers.js +38 -0
- package/dist/redaction.d.ts +12 -0
- package/dist/redaction.js +13 -0
- package/dist/render/RenderLayer.svelte +116 -0
- package/dist/render/RenderLayer.svelte.d.ts +4 -0
- package/dist/render/TileImage.svelte +65 -0
- package/dist/render/TileImage.svelte.d.ts +11 -0
- package/dist/render/TilePlane.svelte +88 -0
- package/dist/render/TilePlane.svelte.d.ts +7 -0
- package/dist/render/props.d.ts +16 -0
- package/dist/render/props.js +1 -0
- package/dist/render/readers.svelte.d.ts +9 -0
- package/dist/render/readers.svelte.js +13 -0
- package/dist/render.d.ts +10 -0
- package/dist/render.js +10 -0
- package/dist/runtime/DocumentGate.svelte +26 -0
- package/dist/runtime/DocumentGate.svelte.d.ts +4 -0
- package/dist/runtime/DocumentScope.svelte +14 -0
- package/dist/runtime/DocumentScope.svelte.d.ts +4 -0
- package/dist/runtime/KernelProvider.svelte +33 -0
- package/dist/runtime/KernelProvider.svelte.d.ts +4 -0
- package/dist/runtime/SvgShapes.svelte +30 -0
- package/dist/runtime/SvgShapes.svelte.d.ts +8 -0
- package/dist/runtime/Viewer.svelte +124 -0
- package/dist/runtime/Viewer.svelte.d.ts +4 -0
- package/dist/runtime/binding.svelte.d.ts +33 -0
- package/dist/runtime/binding.svelte.js +71 -0
- package/dist/runtime/capability-handle.d.ts +6 -0
- package/dist/runtime/capability-handle.js +71 -0
- package/dist/runtime/dev.d.ts +10 -0
- package/dist/runtime/dev.js +23 -0
- package/dist/runtime/documents.svelte.d.ts +34 -0
- package/dist/runtime/documents.svelte.js +49 -0
- package/dist/runtime/page-layers.svelte.d.ts +20 -0
- package/dist/runtime/page-layers.svelte.js +40 -0
- package/dist/runtime/page.d.ts +10 -0
- package/dist/runtime/page.js +23 -0
- package/dist/runtime/props.d.ts +63 -0
- package/dist/runtime/props.js +1 -0
- package/dist/runtime/readers.svelte.d.ts +47 -0
- package/dist/runtime/readers.svelte.js +122 -0
- package/dist/runtime/state.svelte.d.ts +35 -0
- package/dist/runtime/state.svelte.js +90 -0
- package/dist/runtime/theme.d.ts +12 -0
- package/dist/runtime/theme.js +15 -0
- package/dist/runtime/values.svelte.d.ts +28 -0
- package/dist/runtime/values.svelte.js +51 -0
- package/dist/runtime.d.ts +26 -0
- package/dist/runtime.js +25 -0
- package/dist/search/SearchLayer.svelte +94 -0
- package/dist/search/SearchLayer.svelte.d.ts +4 -0
- package/dist/search/props.d.ts +11 -0
- package/dist/search/props.js +1 -0
- package/dist/search/readers.svelte.d.ts +41 -0
- package/dist/search/readers.svelte.js +40 -0
- package/dist/search.d.ts +11 -0
- package/dist/search.js +11 -0
- package/dist/selection/SelectionClipboard.svelte +35 -0
- package/dist/selection/SelectionClipboard.svelte.d.ts +4 -0
- package/dist/selection/SelectionHandles.svelte +192 -0
- package/dist/selection/SelectionHandles.svelte.d.ts +4 -0
- package/dist/selection/SelectionLayer.svelte +67 -0
- package/dist/selection/SelectionLayer.svelte.d.ts +3 -0
- package/dist/selection/SelectionMenu.svelte +36 -0
- package/dist/selection/SelectionMenu.svelte.d.ts +4 -0
- package/dist/selection/props.d.ts +26 -0
- package/dist/selection/props.js +1 -0
- package/dist/selection/readers.svelte.d.ts +31 -0
- package/dist/selection/readers.svelte.js +26 -0
- package/dist/selection.d.ts +16 -0
- package/dist/selection.js +18 -0
- package/dist/shell/readers.svelte.d.ts +40 -0
- package/dist/shell/readers.svelte.js +40 -0
- package/dist/shell/surface-focus.svelte.d.ts +16 -0
- package/dist/shell/surface-focus.svelte.js +33 -0
- package/dist/shell.d.ts +12 -0
- package/dist/shell.js +11 -0
- package/dist/signature/readers.svelte.d.ts +43 -0
- package/dist/signature/readers.svelte.js +46 -0
- package/dist/signature.d.ts +15 -0
- package/dist/signature.js +16 -0
- package/dist/stage/PageSurface.svelte +126 -0
- package/dist/stage/PageSurface.svelte.d.ts +18 -0
- package/dist/stage/Scrollbar.svelte +148 -0
- package/dist/stage/Scrollbar.svelte.d.ts +4 -0
- package/dist/stage/Stage.svelte +181 -0
- package/dist/stage/Stage.svelte.d.ts +4 -0
- package/dist/stage/StageScope.svelte +14 -0
- package/dist/stage/StageScope.svelte.d.ts +9 -0
- package/dist/stage/props.d.ts +71 -0
- package/dist/stage/props.js +1 -0
- package/dist/stage/readers.svelte.d.ts +39 -0
- package/dist/stage/readers.svelte.js +84 -0
- package/dist/stage/stage-scope.d.ts +18 -0
- package/dist/stage/stage-scope.js +35 -0
- package/dist/stage/two-way.svelte.d.ts +13 -0
- package/dist/stage/two-way.svelte.js +26 -0
- package/dist/stage.d.ts +17 -0
- package/dist/stage.js +15 -0
- package/dist/stamp/readers.svelte.d.ts +50 -0
- package/dist/stamp/readers.svelte.js +76 -0
- package/dist/stamp.d.ts +16 -0
- package/dist/stamp.js +18 -0
- package/dist/toolbar/DefaultCollapsed.svelte +39 -0
- package/dist/toolbar/DefaultCollapsed.svelte.d.ts +9 -0
- package/dist/toolbar/DefaultCommand.svelte +33 -0
- package/dist/toolbar/DefaultCommand.svelte.d.ts +8 -0
- package/dist/toolbar/DefaultGroupTrigger.svelte +39 -0
- package/dist/toolbar/DefaultGroupTrigger.svelte.d.ts +9 -0
- package/dist/toolbar/DefaultOverflowMenu.svelte +106 -0
- package/dist/toolbar/DefaultOverflowMenu.svelte.d.ts +9 -0
- package/dist/toolbar/DefaultOverflowTrigger.svelte +25 -0
- package/dist/toolbar/DefaultOverflowTrigger.svelte.d.ts +7 -0
- package/dist/toolbar/Measured.svelte +46 -0
- package/dist/toolbar/Measured.svelte.d.ts +11 -0
- package/dist/toolbar/NativeSlotSocket.svelte +43 -0
- package/dist/toolbar/NativeSlotSocket.svelte.d.ts +11 -0
- package/dist/toolbar/Toolbar.svelte +314 -0
- package/dist/toolbar/Toolbar.svelte.d.ts +4 -0
- package/dist/toolbar/paint.d.ts +3 -0
- package/dist/toolbar/paint.js +9 -0
- package/dist/toolbar/props.d.ts +48 -0
- package/dist/toolbar/props.js +1 -0
- package/dist/toolbar/strip.svelte.d.ts +14 -0
- package/dist/toolbar/strip.svelte.js +35 -0
- package/dist/toolbar/views.d.ts +14 -0
- package/dist/toolbar/views.js +1 -0
- package/dist/toolbar.d.ts +16 -0
- package/dist/toolbar.js +12 -0
- package/dist/view-manager/readers.d.ts +19 -0
- package/dist/view-manager/readers.js +17 -0
- package/dist/view-manager.d.ts +11 -0
- package/dist/view-manager.js +12 -0
- package/package.json +195 -3
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signature plugin's readers: `useSignature()` (the API), the state and the settings,
|
|
3
|
+
* `useSignatureEvent()`, and `useSignerRows()`, the people whose marks this browser holds, for a
|
|
4
|
+
* signature picker.
|
|
5
|
+
*/
|
|
6
|
+
import type { EventHook } from '@embedpdf/core';
|
|
7
|
+
import { signatureState } from '@embedpdf/plugin-signature';
|
|
8
|
+
import type { SignatureCapability, SignerRow } from '@embedpdf/plugin-signature';
|
|
9
|
+
import { type StateReader } from '../runtime/state.svelte.js';
|
|
10
|
+
import { type CurrentValue } from '../runtime/values.svelte.js';
|
|
11
|
+
/**
|
|
12
|
+
* The signature API for the document in scope: sign, place a mark, fill or clear a field, check
|
|
13
|
+
* the signatures. A handle: keep it whole and call through it (`signature.sign(…)`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function useSignature(): SignatureCapability;
|
|
16
|
+
/**
|
|
17
|
+
* The signatures' state: the signed fields with their verdicts (`signatures`), what they allow,
|
|
18
|
+
* the `target`, whether a signing runs (`busy`), a two-step signing in progress, and whether
|
|
19
|
+
* they're read, declared once in `signatureState`. A reactive object (`state.signatures`), or
|
|
20
|
+
* with a selector one value as `{ current }`. Empty without a document.
|
|
21
|
+
*
|
|
22
|
+
* Typed through the declaration, so the published types name it from `@embedpdf/plugin-signature`, a
|
|
23
|
+
* dependency of this package, and not from the engine package `protection`'s type comes from, which
|
|
24
|
+
* isn't one.
|
|
25
|
+
*/
|
|
26
|
+
export declare const useSignatureState: StateReader<ReturnType<typeof signatureState.read>>;
|
|
27
|
+
/**
|
|
28
|
+
* The signature settings (`key`, `mode`, `trust`, `allowCertify`), with or without a document.
|
|
29
|
+
* They belong to the plugin; change them with `useSignature().updateSettings()`.
|
|
30
|
+
*/
|
|
31
|
+
export declare const useSignatureSettings: import("../index.js").SettingsReader<import("@embedpdf/plugin-signature").SignatureSettings & object>;
|
|
32
|
+
/**
|
|
33
|
+
* Subscribe to one signature event while the component lives:
|
|
34
|
+
* `useSignatureEvent((signature) => signature.onSigned, handler)`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useSignatureEvent<T>(select: (signature: SignatureCapability) => EventHook<T>, handler: (event: T) => void): void;
|
|
37
|
+
/**
|
|
38
|
+
* The people whose marks this browser holds, one row per library of kind `signatures`, for a
|
|
39
|
+
* picker, as `{ current }`: `const rows = useSignerRows()`, then `rows.current`. Derived from the
|
|
40
|
+
* stamp plugin, so it needs `stampPlugin()`: rename, delete and export are the library verbs, and
|
|
41
|
+
* nothing is stored beyond the file.
|
|
42
|
+
*/
|
|
43
|
+
export declare function useSignerRows(): CurrentValue<readonly SignerRow[]>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { signatureState, signerRowsOf, SIGNATURES_LIBRARY_KIND, SignatureToken, } from '@embedpdf/plugin-signature';
|
|
2
|
+
import { useCapability, useCapabilityEvent } from '../runtime/readers.svelte.js';
|
|
3
|
+
import { settingsReader, stateReader } from '../runtime/state.svelte.js';
|
|
4
|
+
import { currentOf, derivedValue } from '../runtime/values.svelte.js';
|
|
5
|
+
import { useStampAssets, useStampLibraries } from '../stamp/readers.svelte.js';
|
|
6
|
+
/**
|
|
7
|
+
* The signature API for the document in scope: sign, place a mark, fill or clear a field, check
|
|
8
|
+
* the signatures. A handle: keep it whole and call through it (`signature.sign(…)`).
|
|
9
|
+
*/
|
|
10
|
+
export function useSignature() {
|
|
11
|
+
return useCapability(SignatureToken);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The signatures' state: the signed fields with their verdicts (`signatures`), what they allow,
|
|
15
|
+
* the `target`, whether a signing runs (`busy`), a two-step signing in progress, and whether
|
|
16
|
+
* they're read, declared once in `signatureState`. A reactive object (`state.signatures`), or
|
|
17
|
+
* with a selector one value as `{ current }`. Empty without a document.
|
|
18
|
+
*
|
|
19
|
+
* Typed through the declaration, so the published types name it from `@embedpdf/plugin-signature`, a
|
|
20
|
+
* dependency of this package, and not from the engine package `protection`'s type comes from, which
|
|
21
|
+
* isn't one.
|
|
22
|
+
*/
|
|
23
|
+
export const useSignatureState = stateReader(signatureState);
|
|
24
|
+
/**
|
|
25
|
+
* The signature settings (`key`, `mode`, `trust`, `allowCertify`), with or without a document.
|
|
26
|
+
* They belong to the plugin; change them with `useSignature().updateSettings()`.
|
|
27
|
+
*/
|
|
28
|
+
export const useSignatureSettings = settingsReader(SignatureToken);
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to one signature event while the component lives:
|
|
31
|
+
* `useSignatureEvent((signature) => signature.onSigned, handler)`.
|
|
32
|
+
*/
|
|
33
|
+
export function useSignatureEvent(select, handler) {
|
|
34
|
+
useCapabilityEvent(SignatureToken, select, handler);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The people whose marks this browser holds, one row per library of kind `signatures`, for a
|
|
38
|
+
* picker, as `{ current }`: `const rows = useSignerRows()`, then `rows.current`. Derived from the
|
|
39
|
+
* stamp plugin, so it needs `stampPlugin()`: rename, delete and export are the library verbs, and
|
|
40
|
+
* nothing is stored beyond the file.
|
|
41
|
+
*/
|
|
42
|
+
export function useSignerRows() {
|
|
43
|
+
const libraries = useStampLibraries({ kind: SIGNATURES_LIBRARY_KIND });
|
|
44
|
+
const assets = useStampAssets();
|
|
45
|
+
return currentOf(derivedValue(() => signerRowsOf(libraries.current, assets.current)));
|
|
46
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/signature — signing.
|
|
3
|
+
*
|
|
4
|
+
* Signing for the document in scope (`useSignature()`), its state as a reactive object (the
|
|
5
|
+
* signatures with their verdicts, what they allow, the target, a two-step signing in progress),
|
|
6
|
+
* the settings, the events, and the one derivation a signatures panel needs: the people
|
|
7
|
+
* (libraries of kind `signatures`) with their marks (`useSignerRows()`). The signers
|
|
8
|
+
* (`personalSigner`, `webCryptoSigner`, `remoteSigner`, `createTestSigner`) come with the plugin.
|
|
9
|
+
* An empty signature field is "sign here" in `<FormLayer>` (`@embedpdf/svelte/form`).
|
|
10
|
+
*
|
|
11
|
+
* const signature = useSignature();
|
|
12
|
+
* await signature.placeMark({ assetId }, { field: target }); // sign, fill or ask, by mode
|
|
13
|
+
*/
|
|
14
|
+
export * from '@embedpdf/plugin-signature';
|
|
15
|
+
export { useSignature, useSignatureEvent, useSignatureSettings, useSignatureState, useSignerRows, } from './signature/readers.svelte.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/signature — signing.
|
|
3
|
+
*
|
|
4
|
+
* Signing for the document in scope (`useSignature()`), its state as a reactive object (the
|
|
5
|
+
* signatures with their verdicts, what they allow, the target, a two-step signing in progress),
|
|
6
|
+
* the settings, the events, and the one derivation a signatures panel needs: the people
|
|
7
|
+
* (libraries of kind `signatures`) with their marks (`useSignerRows()`). The signers
|
|
8
|
+
* (`personalSigner`, `webCryptoSigner`, `remoteSigner`, `createTestSigner`) come with the plugin.
|
|
9
|
+
* An empty signature field is "sign here" in `<FormLayer>` (`@embedpdf/svelte/form`).
|
|
10
|
+
*
|
|
11
|
+
* const signature = useSignature();
|
|
12
|
+
* await signature.placeMark({ assetId }, { field: target }); // sign, fill or ask, by mode
|
|
13
|
+
*/
|
|
14
|
+
// Registration travels with the UI.
|
|
15
|
+
export * from '@embedpdf/plugin-signature';
|
|
16
|
+
export { useSignature, useSignatureEvent, useSignatureSettings, useSignatureState, useSignerRows, } from './signature/readers.svelte.js';
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
One visible page of a Stage: placed where the camera puts it, with its drop shadow, the page
|
|
3
|
+
content (which turns with the page) and the page chrome (which never turns). It provides the
|
|
4
|
+
page context to every layer inside.
|
|
5
|
+
|
|
6
|
+
All geometry comes from the page's transform: the display footprint (`viewWidth`/`viewHeight`,
|
|
7
|
+
already swapped for a quarter turn and snapped to device pixels) and the unturned content box
|
|
8
|
+
(`contentWidth`/`contentHeight`). Nothing here multiplies by the zoom or the pixel ratio.
|
|
9
|
+
-->
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import { untrack, type Snippet } from 'svelte';
|
|
12
|
+
import { toPageRef } from '@embedpdf/core';
|
|
13
|
+
import type { PageFrame } from '@embedpdf/core-geometry';
|
|
14
|
+
import type { VisiblePage } from '@embedpdf/plugin-stage';
|
|
15
|
+
import type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';
|
|
16
|
+
import type { PageViewDemand } from '@embedpdf/plugin-render/contract';
|
|
17
|
+
import {
|
|
18
|
+
livePageContext,
|
|
19
|
+
makePageContext,
|
|
20
|
+
pageSurfaceLayout,
|
|
21
|
+
paint,
|
|
22
|
+
stagePageDemand,
|
|
23
|
+
} from '@embedpdf/web';
|
|
24
|
+
import { useViewerSettings } from '../runtime/documents.svelte.js';
|
|
25
|
+
import { setPageContext, type PageContextValue } from '../runtime/page.js';
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
documentId,
|
|
29
|
+
page,
|
|
30
|
+
frame,
|
|
31
|
+
stage,
|
|
32
|
+
content,
|
|
33
|
+
chrome,
|
|
34
|
+
}: {
|
|
35
|
+
documentId: string;
|
|
36
|
+
/** This page as the stage places it now: a new object on every camera frame. */
|
|
37
|
+
page: VisiblePage;
|
|
38
|
+
/** The bands reserved around the page, in screen pixels; the layout left room for them. */
|
|
39
|
+
frame: PageFrame;
|
|
40
|
+
stage: StageHostCapability;
|
|
41
|
+
content: Snippet<[page: PageContextValue]>;
|
|
42
|
+
chrome?: Snippet<[page: PageContextValue]>;
|
|
43
|
+
} = $props();
|
|
44
|
+
|
|
45
|
+
let contentElement: HTMLDivElement | undefined;
|
|
46
|
+
|
|
47
|
+
// The viewer's `page` settings; `--epdf-page-*` CSS variables win over them.
|
|
48
|
+
const look = useViewerSettings((settings) => settings.page);
|
|
49
|
+
|
|
50
|
+
// The Stage keys its pages by object number, so this surface's number never changes: one
|
|
51
|
+
// PageRef for its lifetime, safe for layers to key their work on.
|
|
52
|
+
const pageRef = toPageRef(untrack(() => page.ref.objectNumber));
|
|
53
|
+
// Each its own derived value, so the context below changes only when one of them does, not on
|
|
54
|
+
// every camera frame.
|
|
55
|
+
const pageIndex = $derived(page.pageIndex);
|
|
56
|
+
const transform = $derived(page.transform);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* How much of this page the view wants, for raster planning: read live from the stage, a zero
|
|
60
|
+
* box while the page is off screen. It reads `page` first, so a reaction that asks (the tile
|
|
61
|
+
* plane) asks again on every camera frame.
|
|
62
|
+
*/
|
|
63
|
+
function viewDemand(): PageViewDemand {
|
|
64
|
+
const placed = page;
|
|
65
|
+
return stagePageDemand(stage, pageRef.objectNumber, placed.transform.deviceWidth);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const context = $derived(
|
|
69
|
+
makePageContext(
|
|
70
|
+
documentId,
|
|
71
|
+
// The hosting lens: raster planning keys tiles by it, so a thumbnail rail and the main
|
|
72
|
+
// view never fight over one plan.
|
|
73
|
+
stage.getLensId(),
|
|
74
|
+
pageRef,
|
|
75
|
+
pageIndex,
|
|
76
|
+
frame,
|
|
77
|
+
transform,
|
|
78
|
+
() => contentElement!.getBoundingClientRect(),
|
|
79
|
+
viewDemand,
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
const live = livePageContext(() => context);
|
|
83
|
+
setPageContext(live);
|
|
84
|
+
|
|
85
|
+
// The outer box (one frame further out than the footprint, so the content keeps its place),
|
|
86
|
+
// the shadow at the footprint, and the content box centred on it and turned about its centre.
|
|
87
|
+
const layout = $derived(
|
|
88
|
+
pageSurfaceLayout(transform, frame, { x: page.screenX, y: page.screenY }),
|
|
89
|
+
);
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<div
|
|
93
|
+
style:position="absolute"
|
|
94
|
+
style:left="{layout.outer.left}px"
|
|
95
|
+
style:top="{layout.outer.top}px"
|
|
96
|
+
style:width="{layout.outer.width}px"
|
|
97
|
+
style:height="{layout.outer.height}px"
|
|
98
|
+
>
|
|
99
|
+
<!-- The drop shadow only: at the content box, with no fill, so it never shows behind the
|
|
100
|
+
picture, and it stays put while the page turns. -->
|
|
101
|
+
<div
|
|
102
|
+
style:position="absolute"
|
|
103
|
+
style:left="{layout.shadow.left}px"
|
|
104
|
+
style:top="{layout.shadow.top}px"
|
|
105
|
+
style:width="{layout.shadow.width}px"
|
|
106
|
+
style:height="{layout.shadow.height}px"
|
|
107
|
+
style:box-shadow={paint('page-shadow', look.current.shadow)}
|
|
108
|
+
></div>
|
|
109
|
+
<!-- The page: its background and its layers as one box, the only thing the rotation turns.
|
|
110
|
+
Native text and image selection is off: the selection layer draws its own. -->
|
|
111
|
+
<div
|
|
112
|
+
bind:this={contentElement}
|
|
113
|
+
style="user-select: none; -webkit-user-select: none"
|
|
114
|
+
style:position="absolute"
|
|
115
|
+
style:left="{layout.content.left}px"
|
|
116
|
+
style:top="{layout.content.top}px"
|
|
117
|
+
style:width="{layout.content.width}px"
|
|
118
|
+
style:height="{layout.content.height}px"
|
|
119
|
+
style:background={paint('page-background', look.current.background)}
|
|
120
|
+
style:transform={layout.turn ?? undefined}
|
|
121
|
+
>
|
|
122
|
+
{@render content(live)}
|
|
123
|
+
</div>
|
|
124
|
+
<!-- The page chrome fills the outer box and never turns. -->
|
|
125
|
+
{@render chrome?.(live)}
|
|
126
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { PageFrame } from '@embedpdf/core-geometry';
|
|
3
|
+
import type { VisiblePage } from '@embedpdf/plugin-stage';
|
|
4
|
+
import type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';
|
|
5
|
+
import { type PageContextValue } from '../runtime/page.js';
|
|
6
|
+
type $$ComponentProps = {
|
|
7
|
+
documentId: string;
|
|
8
|
+
/** This page as the stage places it now: a new object on every camera frame. */
|
|
9
|
+
page: VisiblePage;
|
|
10
|
+
/** The bands reserved around the page, in screen pixels; the layout left room for them. */
|
|
11
|
+
frame: PageFrame;
|
|
12
|
+
stage: StageHostCapability;
|
|
13
|
+
content: Snippet<[page: PageContextValue]>;
|
|
14
|
+
chrome?: Snippet<[page: PageContextValue]>;
|
|
15
|
+
};
|
|
16
|
+
declare const PageSurface: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
17
|
+
type PageSurface = ReturnType<typeof PageSurface>;
|
|
18
|
+
export default PageSurface;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
A scrollbar for a Stage lens that you style yourself: a view of `stage.getScrollMetrics()`.
|
|
3
|
+
|
|
4
|
+
Thumb drag with pointer capture and a kept grab point, track paging with press-and-hold repeat
|
|
5
|
+
that stops at the pointer, a minimum thumb length on long documents, and macOS-style auto-hide.
|
|
6
|
+
On an unbounded stage the metrics already carry the union of content and view, so the bar
|
|
7
|
+
shrinks as you pan away; the mapping is frozen during a thumb drag, so the thumb never chases
|
|
8
|
+
itself while that union changes.
|
|
9
|
+
|
|
10
|
+
Geometry and behavior live here, looks in your CSS, against
|
|
11
|
+
[data-embedpdf-scrollbar][data-axis="y"][data-state="visible|hidden"]
|
|
12
|
+
[data-embedpdf-scrollbar][data-dragging]
|
|
13
|
+
[data-embedpdf-scrollbar-thumb]
|
|
14
|
+
Unstyled, it's a transparent track at the viewport's edge with a `--epdf-scrollbar-thumb` pill.
|
|
15
|
+
Build anything else (a minimap, a progress ring) from `useScrollMetrics()` and `stage.scrollTo()`.
|
|
16
|
+
-->
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import {
|
|
19
|
+
createScrollbarPresses,
|
|
20
|
+
paintDefault,
|
|
21
|
+
scrollbarLayout,
|
|
22
|
+
type ScrollbarTrack,
|
|
23
|
+
} from '@embedpdf/web';
|
|
24
|
+
import { useCapability } from '../runtime/readers.svelte.js';
|
|
25
|
+
import type { ScrollbarProps } from './props.js';
|
|
26
|
+
import { asHost, scrollMetricsOf } from './readers.svelte.js';
|
|
27
|
+
import { stageTokenOf } from './stage-scope.js';
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
axis,
|
|
31
|
+
token: explicitToken,
|
|
32
|
+
autoHide = 1200,
|
|
33
|
+
minThumbSize = 24,
|
|
34
|
+
trackPress = 'page',
|
|
35
|
+
class: className,
|
|
36
|
+
style,
|
|
37
|
+
thumbClass,
|
|
38
|
+
thumbStyle,
|
|
39
|
+
}: ScrollbarProps = $props();
|
|
40
|
+
|
|
41
|
+
const lens = stageTokenOf(() => explicitToken);
|
|
42
|
+
const stage = useCapability(() => asHost(lens()));
|
|
43
|
+
const metrics = scrollMetricsOf(lens);
|
|
44
|
+
|
|
45
|
+
const vertical = $derived(axis === 'y');
|
|
46
|
+
const scrollable = $derived(vertical ? metrics.scrollableY : metrics.scrollableX);
|
|
47
|
+
|
|
48
|
+
let track: HTMLDivElement | undefined = $state();
|
|
49
|
+
let trackPx = $state(0);
|
|
50
|
+
let dragging = $state(false);
|
|
51
|
+
let hovered = $state(false);
|
|
52
|
+
let active = $state(true); // the view moved recently
|
|
53
|
+
|
|
54
|
+
// The thumb along the track: one formula for drawing, dragging and paging, shared with every
|
|
55
|
+
// framework.
|
|
56
|
+
const layout = $derived(scrollbarLayout(metrics, vertical, trackPx, minThumbSize));
|
|
57
|
+
|
|
58
|
+
// ── auto-hide: any metrics change shows the bar and starts the fade again ──
|
|
59
|
+
const hideAfter = $derived(autoHide === false ? 0 : autoHide);
|
|
60
|
+
$effect(() => {
|
|
61
|
+
// Every number, so any movement counts.
|
|
62
|
+
void [metrics.scrollTop, metrics.scrollLeft, metrics.scrollHeight, metrics.scrollWidth];
|
|
63
|
+
void [metrics.clientHeight, metrics.clientWidth];
|
|
64
|
+
if (!hideAfter) return;
|
|
65
|
+
active = true;
|
|
66
|
+
const timer = setTimeout(() => (active = false), hideAfter);
|
|
67
|
+
return () => clearTimeout(timer);
|
|
68
|
+
});
|
|
69
|
+
const shown = $derived(!hideAfter || active || hovered || dragging);
|
|
70
|
+
|
|
71
|
+
// ── the track's length, which the pixel mapping needs ──
|
|
72
|
+
$effect(() => {
|
|
73
|
+
const element = track;
|
|
74
|
+
const isVertical = vertical;
|
|
75
|
+
if (!element) return;
|
|
76
|
+
const measure = () => (trackPx = isVertical ? element.clientHeight : element.clientWidth);
|
|
77
|
+
const observer = new ResizeObserver(measure);
|
|
78
|
+
observer.observe(element);
|
|
79
|
+
measure();
|
|
80
|
+
return () => observer.disconnect();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ── input: `@embedpdf/web`'s press handling (thumb drags, track paging and jumps), handed the
|
|
84
|
+
// bar as it is now on every event. ──
|
|
85
|
+
const presses = createScrollbarPresses((value) => (dragging = value));
|
|
86
|
+
const pressedTrack = (): ScrollbarTrack => ({
|
|
87
|
+
stage,
|
|
88
|
+
vertical,
|
|
89
|
+
element: track!,
|
|
90
|
+
layout,
|
|
91
|
+
// Fresh geometry for paging steps, from the capability rather than the last drawn frame.
|
|
92
|
+
liveLayout: () => scrollbarLayout(stage.getScrollMetrics(), vertical, trackPx, minThumbSize),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// No repeat timer outlives the bar.
|
|
96
|
+
$effect(() => () => presses.release());
|
|
97
|
+
|
|
98
|
+
const trackDefaults = $derived(
|
|
99
|
+
vertical
|
|
100
|
+
? 'position: absolute; top: 0; right: 0; bottom: 0; width: 12px;'
|
|
101
|
+
: 'position: absolute; left: 0; right: 0; bottom: 0; height: 12px;',
|
|
102
|
+
);
|
|
103
|
+
const thumbDefaults = $derived(
|
|
104
|
+
vertical
|
|
105
|
+
? `position: absolute; left: 2px; right: 2px; top: ${layout.thumbPosition}px; height: ${layout.thumbLength}px;`
|
|
106
|
+
: `position: absolute; top: 2px; bottom: 2px; left: ${layout.thumbPosition}px; width: ${layout.thumbLength}px;`,
|
|
107
|
+
);
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
{#if scrollable}
|
|
111
|
+
<!-- The bar drives the Stage, which is no single element to name in aria-controls, and the
|
|
112
|
+
pointer is its input: like a native overlay scrollbar, it takes no focus. -->
|
|
113
|
+
<!-- svelte-ignore a11y_role_has_required_aria_props, a11y_interactive_supports_focus -->
|
|
114
|
+
<div
|
|
115
|
+
bind:this={track}
|
|
116
|
+
role="scrollbar"
|
|
117
|
+
aria-orientation={vertical ? 'vertical' : 'horizontal'}
|
|
118
|
+
aria-valuemin={0}
|
|
119
|
+
aria-valuemax={Math.round(layout.maxOffset)}
|
|
120
|
+
aria-valuenow={Math.round(layout.offset)}
|
|
121
|
+
data-embedpdf-scrollbar=""
|
|
122
|
+
data-axis={axis}
|
|
123
|
+
data-state={shown ? 'visible' : 'hidden'}
|
|
124
|
+
data-dragging={dragging ? '' : undefined}
|
|
125
|
+
class={className}
|
|
126
|
+
style="{trackDefaults} touch-action: none; user-select: none; opacity: {shown
|
|
127
|
+
? 1
|
|
128
|
+
: 0}; transition: opacity 200ms; pointer-events: {shown ? 'auto' : 'none'};{style
|
|
129
|
+
? ` ${style}`
|
|
130
|
+
: ''}"
|
|
131
|
+
onpointerdown={(event) => presses.pressTrack(event, pressedTrack(), trackPress)}
|
|
132
|
+
onpointermove={(event) => presses.move(event, pressedTrack())}
|
|
133
|
+
onpointerup={() => presses.release()}
|
|
134
|
+
onpointercancel={() => presses.release()}
|
|
135
|
+
onpointerenter={() => (hovered = true)}
|
|
136
|
+
onpointerleave={() => (hovered = false)}
|
|
137
|
+
>
|
|
138
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
139
|
+
<div
|
|
140
|
+
data-embedpdf-scrollbar-thumb=""
|
|
141
|
+
class={thumbClass}
|
|
142
|
+
style="{thumbDefaults} border-radius: 6px; background: {paintDefault(
|
|
143
|
+
'scrollbar-thumb',
|
|
144
|
+
)};{thumbStyle ? ` ${thumbStyle}` : ''}"
|
|
145
|
+
onpointerdown={(event) => presses.pressThumb(event, pressedTrack())}
|
|
146
|
+
></div>
|
|
147
|
+
</div>
|
|
148
|
+
{/if}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The Stage: lays out the document's pages by the camera, keeps only the visible ones mounted,
|
|
3
|
+
and draws its content (the `children` snippet) on each; you bring the layers. Pointer, wheel and touch input go
|
|
4
|
+
through `@embedpdf/web`'s stage surface, so every framework has one feel.
|
|
5
|
+
|
|
6
|
+
Everything inside binds to this lens: a `useStageState()` in a page's chrome or a
|
|
7
|
+
`<Scrollbar>` in the overlay needs no token. The overlay also gets the Stage's projection, so
|
|
8
|
+
anchored UI follows the camera in the same flush as the pages.
|
|
9
|
+
-->
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import { untrack } from 'svelte';
|
|
12
|
+
import type { CapabilityToken } from '@embedpdf/core';
|
|
13
|
+
import type { PageFrame } from '@embedpdf/core-geometry';
|
|
14
|
+
import { createScrollHandler, DEFAULT_SETTINGS, stageState } from '@embedpdf/plugin-stage';
|
|
15
|
+
import type { VisiblePage } from '@embedpdf/plugin-stage';
|
|
16
|
+
import type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';
|
|
17
|
+
import { InteractionToken as InteractionPublicToken } from '@embedpdf/plugin-interaction/contract';
|
|
18
|
+
import { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';
|
|
19
|
+
import { createStageSurface, stageViewProjector } from '@embedpdf/web';
|
|
20
|
+
import { setProjectorBinding, setShownPages, type ShownPages } from '../anchored/context.js';
|
|
21
|
+
import {
|
|
22
|
+
useCapabilityEvent,
|
|
23
|
+
useDocumentId,
|
|
24
|
+
useKernelValue,
|
|
25
|
+
useOptionalCapability,
|
|
26
|
+
useOptionalSelector,
|
|
27
|
+
} from '../runtime/readers.svelte.js';
|
|
28
|
+
import { declaredState } from '../runtime/state.svelte.js';
|
|
29
|
+
import { useKernelBinding } from '../runtime/binding.svelte.js';
|
|
30
|
+
import PageSurface from './PageSurface.svelte';
|
|
31
|
+
import type { StageProps } from './props.js';
|
|
32
|
+
import { setStageToken, stageTokenOf } from './stage-scope.js';
|
|
33
|
+
import { syncTwoWay } from './two-way.svelte.js';
|
|
34
|
+
|
|
35
|
+
let {
|
|
36
|
+
children,
|
|
37
|
+
pageChrome,
|
|
38
|
+
overlay,
|
|
39
|
+
tool = $bindable(),
|
|
40
|
+
onToolChange,
|
|
41
|
+
page = $bindable(),
|
|
42
|
+
zoom = $bindable(),
|
|
43
|
+
token: explicitToken,
|
|
44
|
+
class: className,
|
|
45
|
+
style,
|
|
46
|
+
}: StageProps = $props();
|
|
47
|
+
|
|
48
|
+
const token = stageTokenOf(() => explicitToken);
|
|
49
|
+
setStageToken(token);
|
|
50
|
+
// The surface is a host of the lens: it reports the viewport size, drives gestures and reads
|
|
51
|
+
// the lens id. The host contract is the same runtime token, typed wider.
|
|
52
|
+
const hostToken = () => token() as unknown as CapabilityToken<StageHostCapability>;
|
|
53
|
+
const stage = useOptionalCapability(hostToken);
|
|
54
|
+
const interaction = useOptionalCapability(InteractionToken);
|
|
55
|
+
const documentId = useDocumentId();
|
|
56
|
+
|
|
57
|
+
const NO_PAGES: readonly VisiblePage[] = [];
|
|
58
|
+
// The visible pages already fold in the camera (each carries its snapped screen position and
|
|
59
|
+
// transform), so panning gives a new list: no separate camera subscription.
|
|
60
|
+
const pages = useOptionalSelector(hostToken, (lens) => lens.listVisiblePages(), NO_PAGES);
|
|
61
|
+
const settings = useOptionalSelector(hostToken, (lens) => lens.getSettings(), DEFAULT_SETTINGS);
|
|
62
|
+
// The reserved bands around every page, compared by side so an equal frame changes nothing.
|
|
63
|
+
const frame = useOptionalSelector(
|
|
64
|
+
hostToken,
|
|
65
|
+
(lens) => lens.getSettings().pageFrame,
|
|
66
|
+
DEFAULT_SETTINGS.pageFrame,
|
|
67
|
+
(left: PageFrame, right: PageFrame) =>
|
|
68
|
+
left.top === right.top &&
|
|
69
|
+
left.right === right.right &&
|
|
70
|
+
left.bottom === right.bottom &&
|
|
71
|
+
left.left === right.left,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// How this view takes pointer input is its own settings: routed to the interaction plugin
|
|
75
|
+
// (which also registers this view's pan-scroll handler, scoped to it, so two Stages on one
|
|
76
|
+
// document never pan each other), or the Stage's own drag-to-pan without it.
|
|
77
|
+
const routed = $derived(settings.current.interaction && interaction.current !== null);
|
|
78
|
+
const panFallback = $derived(settings.current.panFallback);
|
|
79
|
+
const zoomGestures = $derived(settings.current.zoomGestures);
|
|
80
|
+
// The cursor the interaction plugin resolves (text, grab, …), on the viewport while routed.
|
|
81
|
+
const hubCursor = useKernelValue(() => interaction.current?.getCursor() ?? 'default');
|
|
82
|
+
|
|
83
|
+
let element: HTMLDivElement | undefined;
|
|
84
|
+
|
|
85
|
+
$effect(() => {
|
|
86
|
+
const lens = stage.current;
|
|
87
|
+
const hub = routed ? interaction.current : null;
|
|
88
|
+
const gestures = zoomGestures;
|
|
89
|
+
const fallback = panFallback;
|
|
90
|
+
if (!element || !lens) return;
|
|
91
|
+
return untrack(() => {
|
|
92
|
+
const detachSurface = createStageSurface(element!, lens, {
|
|
93
|
+
hub,
|
|
94
|
+
source: lens.getLensId(),
|
|
95
|
+
zoomGestures: gestures,
|
|
96
|
+
});
|
|
97
|
+
const offScroll = hub
|
|
98
|
+
? hub.registerHandler(createScrollHandler(lens, hub, { panFallback: fallback }), {
|
|
99
|
+
source: lens.getLensId(),
|
|
100
|
+
})
|
|
101
|
+
: null;
|
|
102
|
+
return () => {
|
|
103
|
+
offScroll?.();
|
|
104
|
+
detachSurface();
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ── two-way values ─────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
// The bound tool wins whenever it's set, again when the document changes.
|
|
112
|
+
$effect(() => {
|
|
113
|
+
const wanted = tool;
|
|
114
|
+
const hub = interaction.current;
|
|
115
|
+
if (wanted === undefined || !hub) return;
|
|
116
|
+
untrack(() => {
|
|
117
|
+
if (hub.getActiveToolId() !== wanted) hub.activateTool(wanted);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
useCapabilityEvent(
|
|
121
|
+
InteractionPublicToken,
|
|
122
|
+
(hub) => hub.onToolChanged,
|
|
123
|
+
(event) => {
|
|
124
|
+
if (tool !== undefined) tool = event.toolId;
|
|
125
|
+
onToolChange?.(event.toolId);
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const binding = useKernelBinding();
|
|
130
|
+
const view = declaredState(binding, token, stageState.read, stageState.empty);
|
|
131
|
+
syncTwoWay({
|
|
132
|
+
stage: () => view().currentPageIndex,
|
|
133
|
+
bound: () => page,
|
|
134
|
+
ready: () => stage.current !== null,
|
|
135
|
+
write: (value) => (page = value),
|
|
136
|
+
apply: (value) => stage.current?.goToPage(value),
|
|
137
|
+
});
|
|
138
|
+
syncTwoWay({
|
|
139
|
+
stage: () => view().zoomLevel,
|
|
140
|
+
bound: () => zoom,
|
|
141
|
+
ready: () => stage.current !== null,
|
|
142
|
+
write: (value) => (zoom = value),
|
|
143
|
+
apply: (value) => stage.current?.zoomTo(value),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ── what the overlay anchors to ───────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
// Anchored UI positions through the camera: pure state, no DOM reads. The visible pages are
|
|
149
|
+
// the binding's revision, so a camera change moves the pages and every anchored element in
|
|
150
|
+
// the same flush.
|
|
151
|
+
const projector = stageViewProjector(() => stage.current);
|
|
152
|
+
const projectorBinding = $derived({ projector, revision: pages.current });
|
|
153
|
+
setProjectorBinding(() => projectorBinding);
|
|
154
|
+
// The pages on screen as one value that changes only when a page comes or goes, so anchored
|
|
155
|
+
// UI on the other pages sits out every camera frame.
|
|
156
|
+
const shownKey = $derived(pages.current.map((visible) => visible.ref.objectNumber).join(','));
|
|
157
|
+
const shownPages: ShownPages = $derived(new Set(shownKey ? shownKey.split(',').map(Number) : []));
|
|
158
|
+
setShownPages(() => shownPages);
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
<div
|
|
162
|
+
bind:this={element}
|
|
163
|
+
class={className}
|
|
164
|
+
style="position: relative; overflow: hidden; touch-action: none;{routed
|
|
165
|
+
? ` cursor: ${hubCursor.current};`
|
|
166
|
+
: ''}{style ? ` ${style}` : ''}"
|
|
167
|
+
>
|
|
168
|
+
{#if stage.current}
|
|
169
|
+
{#each pages.current as visible (visible.ref.objectNumber)}
|
|
170
|
+
<PageSurface
|
|
171
|
+
documentId={documentId.current ?? ''}
|
|
172
|
+
page={visible}
|
|
173
|
+
frame={frame.current}
|
|
174
|
+
stage={stage.current}
|
|
175
|
+
content={children}
|
|
176
|
+
chrome={pageChrome}
|
|
177
|
+
/>
|
|
178
|
+
{/each}
|
|
179
|
+
{/if}
|
|
180
|
+
{@render overlay?.()}
|
|
181
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Binds everything inside to a stage lens: the stage readers and stage chrome inside use `token`
|
|
3
|
+
unless they are given one themselves.
|
|
4
|
+
-->
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import type { Snippet } from 'svelte';
|
|
7
|
+
import { setStageToken, type StageTokenProp } from './stage-scope.js';
|
|
8
|
+
|
|
9
|
+
let { token, children }: { token: StageTokenProp; children?: Snippet } = $props();
|
|
10
|
+
|
|
11
|
+
setStageToken(() => token);
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
{@render children?.()}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type StageTokenProp } from './stage-scope.js';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
token: StageTokenProp;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
};
|
|
7
|
+
declare const StageScope: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type StageScope = ReturnType<typeof StageScope>;
|
|
9
|
+
export default StageScope;
|