@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,52 @@
|
|
|
1
|
+
import { FormToken, formState } from '@embedpdf/plugin-form';
|
|
2
|
+
import { formColorsOf } from '@embedpdf/web';
|
|
3
|
+
import { useViewerSettings } from '../runtime/documents.svelte.js';
|
|
4
|
+
import { useCapability, useCapabilityEvent, useOptionalSelector, } from '../runtime/readers.svelte.js';
|
|
5
|
+
import { settingsReader, stateReader } from '../runtime/state.svelte.js';
|
|
6
|
+
import { currentOf, derivedValue, valueOf, } from '../runtime/values.svelte.js';
|
|
7
|
+
/**
|
|
8
|
+
* The form API for the document in scope: reading, filling, form data and building. A handle:
|
|
9
|
+
* always the capability of the document in scope, so keep it whole (`form.setValue(…)`).
|
|
10
|
+
*/
|
|
11
|
+
export function useForm() {
|
|
12
|
+
return useCapability(FormToken);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Subscribe to one form event while the component lives:
|
|
16
|
+
* `useFormEvent((form) => form.onValueChanged, handler)`.
|
|
17
|
+
*/
|
|
18
|
+
export function useFormEvent(select, handler) {
|
|
19
|
+
useCapabilityEvent(FormToken, select, handler);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The form's state: every field (`fields`), whether it's read (`status`), what kind of form the
|
|
23
|
+
* document has (`formKind`), and the field of the selected widget (`selectedField`), declared
|
|
24
|
+
* once in `formState`. A reactive object (`state.fields`), or with a selector one value as
|
|
25
|
+
* `{ current }` that changes only when what it picks changes. Empty without a document.
|
|
26
|
+
*/
|
|
27
|
+
export const useFormState = stateReader(formState);
|
|
28
|
+
/**
|
|
29
|
+
* The form settings (`validation`, `focus`, `fields`), with or without a document. They belong
|
|
30
|
+
* to the plugin; change them with `useForm().updateSettings()`.
|
|
31
|
+
*/
|
|
32
|
+
export const useFormSettings = settingsReader(FormToken);
|
|
33
|
+
/**
|
|
34
|
+
* One field's value, in the shape `setValue()` takes, or `null`, as `{ current }`: it changes
|
|
35
|
+
* only when that field's value changes. Pass a function (`() => ref`) for a field that changes.
|
|
36
|
+
* `null` without a document.
|
|
37
|
+
*/
|
|
38
|
+
export function useFormValue(ref) {
|
|
39
|
+
// The plugin keeps one value object per field until the field changes, so a reference
|
|
40
|
+
// comparison is enough.
|
|
41
|
+
return useOptionalSelector(FormToken, (form) => form.getValue(valueOf(ref)), null);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The colors the layer draws fields with: the form settings over the viewer's accent, each a
|
|
45
|
+
* `var(--epdf-form-*)` first, so CSS wins.
|
|
46
|
+
*/
|
|
47
|
+
export function useFormColors() {
|
|
48
|
+
const accent = useViewerSettings((settings) => settings.accent);
|
|
49
|
+
const focus = useFormSettings((settings) => settings.focus);
|
|
50
|
+
const fields = useFormSettings((settings) => settings.fields);
|
|
51
|
+
return currentOf(derivedValue(() => formColorsOf({ focus: focus.current, fields: fields.current }, accent.current)));
|
|
52
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every control of `<FormLayer>` shares besides its box (`WidgetBox.svelte`): running the
|
|
3
|
+
* widget's action on a click, and the style of a control that fills the box, as CSS text. A
|
|
4
|
+
* Svelte `style` attribute is a string, while `@embedpdf/web` hands styles out as records
|
|
5
|
+
* (`cssText` writes one as text).
|
|
6
|
+
*/
|
|
7
|
+
import type { FormWidgetItem } from '@embedpdf/plugin-form';
|
|
8
|
+
/**
|
|
9
|
+
* A click on a widget runs its `/A` action. ISO 32000 puts the activate action on the widget,
|
|
10
|
+
* whatever its field, and many forms ship "buttons" as read-only text fields with an `/A` that
|
|
11
|
+
* Acrobat runs; a widget without one is inert. The widget's address is read at the click: the
|
|
12
|
+
* engine gives a widget it has just placed its address later.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useWidgetActivation(annotationRef: () => FormWidgetItem['annotationRef']): () => void;
|
|
15
|
+
/** Fills the box it sits in, as CSS text. */
|
|
16
|
+
export declare const FILL: string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The layer's lens on the plugin: the same capability, typed wider for the framework layers.
|
|
2
|
+
import { FormToken as FormHostToken } from '@embedpdf/plugin-form/contract/host';
|
|
3
|
+
import { cssText, FORM_CONTROL_FILL } from '@embedpdf/web';
|
|
4
|
+
import { useCapability } from '../runtime/readers.svelte.js';
|
|
5
|
+
/**
|
|
6
|
+
* A click on a widget runs its `/A` action. ISO 32000 puts the activate action on the widget,
|
|
7
|
+
* whatever its field, and many forms ship "buttons" as read-only text fields with an `/A` that
|
|
8
|
+
* Acrobat runs; a widget without one is inert. The widget's address is read at the click: the
|
|
9
|
+
* engine gives a widget it has just placed its address later.
|
|
10
|
+
*/
|
|
11
|
+
export function useWidgetActivation(annotationRef) {
|
|
12
|
+
const form = useCapability(FormHostToken);
|
|
13
|
+
return () => {
|
|
14
|
+
const target = annotationRef();
|
|
15
|
+
if (target)
|
|
16
|
+
void form.activateWidget(target);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Fills the box it sits in, as CSS text. */
|
|
20
|
+
export const FILL = cssText(FORM_CONTROL_FILL);
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/form — filling and building forms.
|
|
3
|
+
*
|
|
4
|
+
* `<FormLayer>` puts a real HTML control over each field of its page, for people to fill in;
|
|
5
|
+
* `useForm()` reads, fills and builds the form from code; `useFormState()` follows the fields as
|
|
6
|
+
* a reactive object, `useFormValue()` one field's value as `{ current }`, `useFormSettings()` the
|
|
7
|
+
* settings, and `useFormEvent()` the plugin's events.
|
|
8
|
+
*/
|
|
9
|
+
export * from '@embedpdf/plugin-form';
|
|
10
|
+
export { default as FormLayer } from './form/FormLayer.svelte';
|
|
11
|
+
export { useForm, useFormEvent, useFormSettings, useFormState, useFormValue, } from './form/readers.svelte.js';
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/form — filling and building forms.
|
|
3
|
+
*
|
|
4
|
+
* `<FormLayer>` puts a real HTML control over each field of its page, for people to fill in;
|
|
5
|
+
* `useForm()` reads, fills and builds the form from code; `useFormState()` follows the fields as
|
|
6
|
+
* a reactive object, `useFormValue()` one field's value as `{ current }`, `useFormSettings()` the
|
|
7
|
+
* settings, and `useFormEvent()` the plugin's events.
|
|
8
|
+
*/
|
|
9
|
+
// Registration travels with the UI.
|
|
10
|
+
export * from '@embedpdf/plugin-form';
|
|
11
|
+
export { default as FormLayer } from './form/FormLayer.svelte';
|
|
12
|
+
export { useForm, useFormEvent, useFormSettings, useFormState, useFormValue, } from './form/readers.svelte.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** The history's readers: the API, the state declared once in `historyState`, and its events. */
|
|
2
|
+
import type { EventHook } from '@embedpdf/core';
|
|
3
|
+
import type { HistoryCapability } from '@embedpdf/plugin-history';
|
|
4
|
+
/**
|
|
5
|
+
* The history API (`undo`, `redo`, `canUndo`, `canRedo`, the labels, `clear`) of the nearest
|
|
6
|
+
* `<DocumentScope>`'s document, else the active one. Outside a ready document, every method
|
|
7
|
+
* throws `not-ready`. `canUndo()` is a read, so `disabled={!history.canUndo()}` updates by itself.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useHistory(): HistoryCapability;
|
|
10
|
+
/**
|
|
11
|
+
* Whether undo and redo can run, and what they would undo and redo (the page's State table,
|
|
12
|
+
* declared once in `historyState`), as a reactive object: `state.canUndo`. With a selector, the
|
|
13
|
+
* value it picks as `{ current }`, which changes only when that value does.
|
|
14
|
+
*/
|
|
15
|
+
export declare const useHistoryState: import("../index.js").StateReader<{
|
|
16
|
+
canUndo: boolean;
|
|
17
|
+
canRedo: boolean;
|
|
18
|
+
undoLabel: import("@embedpdf/core").ChangeLabel | null;
|
|
19
|
+
redoLabel: import("@embedpdf/core").ChangeLabel | null;
|
|
20
|
+
}>;
|
|
21
|
+
/** Subscribe to one history event while the component lives: `useHistoryEvent((history) => history.onUndone, handler)`. */
|
|
22
|
+
export declare function useHistoryEvent<T>(select: (history: HistoryCapability) => EventHook<T>, handler: (event: T) => void): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HistoryToken, historyState } from '@embedpdf/plugin-history';
|
|
2
|
+
import { useCapability, useCapabilityEvent } from '../runtime/readers.svelte.js';
|
|
3
|
+
import { stateReader } from '../runtime/state.svelte.js';
|
|
4
|
+
/**
|
|
5
|
+
* The history API (`undo`, `redo`, `canUndo`, `canRedo`, the labels, `clear`) of the nearest
|
|
6
|
+
* `<DocumentScope>`'s document, else the active one. Outside a ready document, every method
|
|
7
|
+
* throws `not-ready`. `canUndo()` is a read, so `disabled={!history.canUndo()}` updates by itself.
|
|
8
|
+
*/
|
|
9
|
+
export function useHistory() {
|
|
10
|
+
return useCapability(HistoryToken);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Whether undo and redo can run, and what they would undo and redo (the page's State table,
|
|
14
|
+
* declared once in `historyState`), as a reactive object: `state.canUndo`. With a selector, the
|
|
15
|
+
* value it picks as `{ current }`, which changes only when that value does.
|
|
16
|
+
*/
|
|
17
|
+
export const useHistoryState = stateReader(historyState);
|
|
18
|
+
/** Subscribe to one history event while the component lives: `useHistoryEvent((history) => history.onUndone, handler)`. */
|
|
19
|
+
export function useHistoryEvent(select, handler) {
|
|
20
|
+
useCapabilityEvent(HistoryToken, select, handler);
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/history — undo and redo of what you change through the viewer, one history
|
|
3
|
+
* per open document. An action shows undone at once, even while it is still on its way:
|
|
4
|
+
*
|
|
5
|
+
* const history = useHistory();
|
|
6
|
+
* const state = useHistoryState(); // state.canUndo, state.undoLabel, …
|
|
7
|
+
* <button disabled={!state.canUndo} onclick={() => history.undo()}>Undo</button>
|
|
8
|
+
*/
|
|
9
|
+
export * from '@embedpdf/plugin-history';
|
|
10
|
+
export { useHistory, useHistoryEvent, useHistoryState } from './history/readers.js';
|
package/dist/history.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/history — undo and redo of what you change through the viewer, one history
|
|
3
|
+
* per open document. An action shows undone at once, even while it is still on its way:
|
|
4
|
+
*
|
|
5
|
+
* const history = useHistory();
|
|
6
|
+
* const state = useHistoryState(); // state.canUndo, state.undoLabel, …
|
|
7
|
+
* <button disabled={!state.canUndo} onclick={() => history.undo()}>Undo</button>
|
|
8
|
+
*/
|
|
9
|
+
// Registration travels with the UI.
|
|
10
|
+
export * from '@embedpdf/plugin-history';
|
|
11
|
+
export { useHistory, useHistoryEvent, useHistoryState } from './history/readers.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The i18n plugin's readers. The plugin belongs to the workspace and needs no engine, so they
|
|
3
|
+
* work from the first frame, before a document opens: a loading screen and a password prompt
|
|
4
|
+
* translate too.
|
|
5
|
+
*/
|
|
6
|
+
import type { EventHook } from '@embedpdf/core';
|
|
7
|
+
import type { I18nCapability, TranslateOptions } from '@embedpdf/plugin-i18n';
|
|
8
|
+
/** The i18n API: `t`, `setLocale`, `addTranslations`, `registerLocale`, the settings calls. */
|
|
9
|
+
export declare function useI18n(): I18nCapability;
|
|
10
|
+
/** Subscribe to one i18n event while the component lives: `useI18nEvent((i18n) => i18n.onLocaleChanged, handler)`. */
|
|
11
|
+
export declare function useI18nEvent<T>(select: (i18n: I18nCapability) => EventHook<T>, handler: (event: T) => void): void;
|
|
12
|
+
/**
|
|
13
|
+
* The current language, how it reads, every language you can switch to, and the one loading
|
|
14
|
+
* (declared once in `i18nState`), as a reactive object (`useI18nState().direction`). Takes a
|
|
15
|
+
* selector.
|
|
16
|
+
*/
|
|
17
|
+
export declare const useI18nState: import("../index.js").StateReader<{
|
|
18
|
+
locale: string;
|
|
19
|
+
direction: import("@embedpdf/plugin-i18n").TextDirection;
|
|
20
|
+
locales: readonly import("@embedpdf/plugin-i18n").LocaleInfo[];
|
|
21
|
+
loading: string | null;
|
|
22
|
+
}>;
|
|
23
|
+
/** The i18n settings (`locale`, `fallbackLocale`, `locales`, `loaders`). Takes a selector. */
|
|
24
|
+
export declare const useI18nSettings: import("../index.js").SettingsReader<import("@embedpdf/plugin-i18n").I18nSettings & object>;
|
|
25
|
+
/**
|
|
26
|
+
* A translate function that reads the current language each time it's called, so a template, a
|
|
27
|
+
* `$derived` or an effect that calls it updates when the language or the strings change:
|
|
28
|
+
*
|
|
29
|
+
* const t = useT();
|
|
30
|
+
* <span>{t('search.results', { params: { count } })}</span>
|
|
31
|
+
*/
|
|
32
|
+
export declare function useT(): (key: string, options?: TranslateOptions) => string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { I18nToken, i18nState } from '@embedpdf/plugin-i18n';
|
|
2
|
+
import { useCapability, useCapabilityEvent, useSelector } from '../runtime/readers.svelte.js';
|
|
3
|
+
import { settingsReader, stateReader } from '../runtime/state.svelte.js';
|
|
4
|
+
/** The i18n API: `t`, `setLocale`, `addTranslations`, `registerLocale`, the settings calls. */
|
|
5
|
+
export function useI18n() {
|
|
6
|
+
return useCapability(I18nToken);
|
|
7
|
+
}
|
|
8
|
+
/** Subscribe to one i18n event while the component lives: `useI18nEvent((i18n) => i18n.onLocaleChanged, handler)`. */
|
|
9
|
+
export function useI18nEvent(select, handler) {
|
|
10
|
+
useCapabilityEvent(I18nToken, select, handler);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The current language, how it reads, every language you can switch to, and the one loading
|
|
14
|
+
* (declared once in `i18nState`), as a reactive object (`useI18nState().direction`). Takes a
|
|
15
|
+
* selector.
|
|
16
|
+
*/
|
|
17
|
+
export const useI18nState = stateReader(i18nState);
|
|
18
|
+
/** The i18n settings (`locale`, `fallbackLocale`, `locales`, `loaders`). Takes a selector. */
|
|
19
|
+
export const useI18nSettings = settingsReader(I18nToken);
|
|
20
|
+
/**
|
|
21
|
+
* A translate function that reads the current language each time it's called, so a template, a
|
|
22
|
+
* `$derived` or an effect that calls it updates when the language or the strings change:
|
|
23
|
+
*
|
|
24
|
+
* const t = useT();
|
|
25
|
+
* <span>{t('search.results', { params: { count } })}</span>
|
|
26
|
+
*/
|
|
27
|
+
export function useT() {
|
|
28
|
+
const translator = useSelector(I18nToken, (i18n) => i18n.getTranslator());
|
|
29
|
+
return (key, options) => translator.current(key, options);
|
|
30
|
+
}
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/i18n — your viewer in the reader's language.
|
|
3
|
+
*
|
|
4
|
+
* The readers are the i18n plugin's API, state, settings and events; `useT()` gives a translate
|
|
5
|
+
* function that follows the language.
|
|
6
|
+
*/
|
|
7
|
+
export * from '@embedpdf/plugin-i18n';
|
|
8
|
+
export { useI18n, useI18nEvent, useI18nSettings, useI18nState, useT } from './i18n/readers.svelte.js';
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/i18n — your viewer in the reader's language.
|
|
3
|
+
*
|
|
4
|
+
* The readers are the i18n plugin's API, state, settings and events; `useT()` gives a translate
|
|
5
|
+
* function that follows the language.
|
|
6
|
+
*/
|
|
7
|
+
// Registration travels with the UI.
|
|
8
|
+
export * from '@embedpdf/plugin-i18n';
|
|
9
|
+
export { useI18n, useI18nEvent, useI18nSettings, useI18nState, useT } from './i18n/readers.svelte.js';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte — every entry point at once. Apps import per feature
|
|
3
|
+
* (`@embedpdf/svelte/stage`), so a plugin they don't use stays out of the bundle.
|
|
4
|
+
*/
|
|
5
|
+
export * from './runtime.js';
|
|
6
|
+
export * from './anchored.js';
|
|
7
|
+
export * from './stage.js';
|
|
8
|
+
export * from './render.js';
|
|
9
|
+
export * from './interaction.js';
|
|
10
|
+
export * from './commands.js';
|
|
11
|
+
export * from './toolbar.js';
|
|
12
|
+
export * from './shell.js';
|
|
13
|
+
export * from './i18n.js';
|
|
14
|
+
export * from './page-view.js';
|
|
15
|
+
export * from './selection.js';
|
|
16
|
+
export * from './search.js';
|
|
17
|
+
export * from './link.js';
|
|
18
|
+
export * from './actions.js';
|
|
19
|
+
export * from './view-manager.js';
|
|
20
|
+
export * from './page-edit.js';
|
|
21
|
+
export * from './metadata.js';
|
|
22
|
+
export * from './history.js';
|
|
23
|
+
export * from './annotation.js';
|
|
24
|
+
export * from './redaction.js';
|
|
25
|
+
export * from './measurement.js';
|
|
26
|
+
export * from './stamp.js';
|
|
27
|
+
export * from './form.js';
|
|
28
|
+
export * from './signature.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte — every entry point at once. Apps import per feature
|
|
3
|
+
* (`@embedpdf/svelte/stage`), so a plugin they don't use stays out of the bundle.
|
|
4
|
+
*/
|
|
5
|
+
export * from './runtime.js';
|
|
6
|
+
export * from './anchored.js';
|
|
7
|
+
export * from './stage.js';
|
|
8
|
+
export * from './render.js';
|
|
9
|
+
export * from './interaction.js';
|
|
10
|
+
export * from './commands.js';
|
|
11
|
+
export * from './toolbar.js';
|
|
12
|
+
export * from './shell.js';
|
|
13
|
+
export * from './i18n.js';
|
|
14
|
+
export * from './page-view.js';
|
|
15
|
+
export * from './selection.js';
|
|
16
|
+
export * from './search.js';
|
|
17
|
+
export * from './link.js';
|
|
18
|
+
export * from './actions.js';
|
|
19
|
+
export * from './view-manager.js';
|
|
20
|
+
export * from './page-edit.js';
|
|
21
|
+
export * from './metadata.js';
|
|
22
|
+
export * from './history.js';
|
|
23
|
+
export * from './annotation.js';
|
|
24
|
+
export * from './redaction.js';
|
|
25
|
+
export * from './measurement.js';
|
|
26
|
+
export * from './stamp.js';
|
|
27
|
+
export * from './form.js';
|
|
28
|
+
export * from './signature.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The one pointer listener per page: it turns pointer events into page points through the page
|
|
3
|
+
context and hands them to the interaction plugin. It binds only to the page context, so it
|
|
4
|
+
works the same on a Stage page and on a `<PageView>`. Features never listen to the pointer
|
|
5
|
+
themselves: they register handlers with the interaction plugin, and a tool of your own brings
|
|
6
|
+
its pointer methods (`registerTool`).
|
|
7
|
+
-->
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { untrack } from 'svelte';
|
|
10
|
+
import { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';
|
|
11
|
+
import { attachPagePointer } from '@embedpdf/web';
|
|
12
|
+
import { usePage } from '../runtime/page.js';
|
|
13
|
+
import { useOptionalCapability, useOptionalSelector } from '../runtime/readers.svelte.js';
|
|
14
|
+
|
|
15
|
+
const page = usePage();
|
|
16
|
+
// The pointer source is a host of the interaction plugin: it dispatches samples.
|
|
17
|
+
const interaction = useOptionalCapability(InteractionToken);
|
|
18
|
+
const cursor = useOptionalSelector(InteractionToken, (hub) => hub.getCursor(), 'default');
|
|
19
|
+
|
|
20
|
+
let element: HTMLDivElement | undefined;
|
|
21
|
+
|
|
22
|
+
// The listener is `@embedpdf/web`'s, shared with every framework. It reads the page when an
|
|
23
|
+
// event arrives: a zoom changes it, and a drag carries on through it.
|
|
24
|
+
$effect(() => {
|
|
25
|
+
const hub = interaction.current;
|
|
26
|
+
const target = element;
|
|
27
|
+
if (!hub || !target) return;
|
|
28
|
+
return untrack(() => attachPagePointer(target, hub, () => page));
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<!-- On top, as the page's event surface; the drawing layers below take no pointer events. -->
|
|
33
|
+
<div
|
|
34
|
+
bind:this={element}
|
|
35
|
+
style="position: absolute; inset: 0; touch-action: none"
|
|
36
|
+
style:cursor={cursor.current}
|
|
37
|
+
></div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EventHook } from '@embedpdf/core';
|
|
2
|
+
import type { InteractionCapability } from '@embedpdf/plugin-interaction';
|
|
3
|
+
import { type ToolCursorSpec } from '@embedpdf/web';
|
|
4
|
+
import { type MaybeGetter } from '../runtime/values.svelte.js';
|
|
5
|
+
/** The interaction API: switch tools, add your own, set their cursors. */
|
|
6
|
+
export declare function useInteraction(): InteractionCapability;
|
|
7
|
+
/** Subscribe to one interaction event while the component lives: `useInteractionEvent((interaction) => interaction.onToolChanged, handler)`. */
|
|
8
|
+
export declare function useInteractionEvent<T>(select: (interaction: InteractionCapability) => EventHook<T>, handler: (event: T) => void): void;
|
|
9
|
+
/**
|
|
10
|
+
* The tools' state: the active tool's id and every tool you can switch to (declared once in
|
|
11
|
+
* `interactionState`). Without a document there is no active tool (`null`) and no tool.
|
|
12
|
+
*/
|
|
13
|
+
export declare const useInteractionState: import("../index.js").StateReader<{
|
|
14
|
+
activeToolId: import("@embedpdf/plugin-interaction").ToolId | null;
|
|
15
|
+
tools: readonly import("@embedpdf/plugin-interaction").Tool[];
|
|
16
|
+
}>;
|
|
17
|
+
/** The interaction settings (`defaultTool`, `tools`), with or without a document. */
|
|
18
|
+
export declare const useInteractionSettings: import("../index.js").SettingsReader<import("@embedpdf/plugin-interaction").InteractionSettings & object>;
|
|
19
|
+
/**
|
|
20
|
+
* Give a tool image cursors: the armed-tool indicator. The interaction plugin already decides
|
|
21
|
+
* which cursor shows (hover claims over annotations and text win, page gaps show the tool's
|
|
22
|
+
* `gapCursor`), so nothing chases the pointer in the DOM. Pass a function, so the cursor follows
|
|
23
|
+
* what it reads (a color); `null` installs nothing. Going away restores the tool's own cursors.
|
|
24
|
+
* Without a document it installs nothing until one is ready.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useToolCursor(spec: MaybeGetter<ToolCursorSpec | null>): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** The interaction plugin's readers: its API, state, settings and events, and tool cursors. */
|
|
2
|
+
import { untrack } from 'svelte';
|
|
3
|
+
import { InteractionToken, interactionState } from '@embedpdf/plugin-interaction';
|
|
4
|
+
import { InteractionToken as InteractionHostToken } from '@embedpdf/plugin-interaction/contract/host';
|
|
5
|
+
import { toolCursorsOf } from '@embedpdf/web';
|
|
6
|
+
import { useCapability, useCapabilityEvent, useOptionalCapability, } from '../runtime/readers.svelte.js';
|
|
7
|
+
import { settingsReader, stateReader } from '../runtime/state.svelte.js';
|
|
8
|
+
import { valueOf } from '../runtime/values.svelte.js';
|
|
9
|
+
/** The interaction API: switch tools, add your own, set their cursors. */
|
|
10
|
+
export function useInteraction() {
|
|
11
|
+
return useCapability(InteractionToken);
|
|
12
|
+
}
|
|
13
|
+
/** Subscribe to one interaction event while the component lives: `useInteractionEvent((interaction) => interaction.onToolChanged, handler)`. */
|
|
14
|
+
export function useInteractionEvent(select, handler) {
|
|
15
|
+
useCapabilityEvent(InteractionToken, select, handler);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The tools' state: the active tool's id and every tool you can switch to (declared once in
|
|
19
|
+
* `interactionState`). Without a document there is no active tool (`null`) and no tool.
|
|
20
|
+
*/
|
|
21
|
+
export const useInteractionState = stateReader(interactionState);
|
|
22
|
+
/** The interaction settings (`defaultTool`, `tools`), with or without a document. */
|
|
23
|
+
export const useInteractionSettings = settingsReader(InteractionToken);
|
|
24
|
+
/**
|
|
25
|
+
* Give a tool image cursors: the armed-tool indicator. The interaction plugin already decides
|
|
26
|
+
* which cursor shows (hover claims over annotations and text win, page gaps show the tool's
|
|
27
|
+
* `gapCursor`), so nothing chases the pointer in the DOM. Pass a function, so the cursor follows
|
|
28
|
+
* what it reads (a color); `null` installs nothing. Going away restores the tool's own cursors.
|
|
29
|
+
* Without a document it installs nothing until one is ready.
|
|
30
|
+
*/
|
|
31
|
+
export function useToolCursor(spec) {
|
|
32
|
+
const interaction = useOptionalCapability(InteractionHostToken);
|
|
33
|
+
// By value: a function builds a fresh object every time, and an identical one must not
|
|
34
|
+
// replace the cursor.
|
|
35
|
+
const key = $derived.by(() => {
|
|
36
|
+
const current = valueOf(spec);
|
|
37
|
+
return current ? JSON.stringify(current) : null;
|
|
38
|
+
});
|
|
39
|
+
$effect(() => {
|
|
40
|
+
const host = interaction.current;
|
|
41
|
+
if (!host || key === null)
|
|
42
|
+
return;
|
|
43
|
+
const cursorSpec = untrack(() => valueOf(spec));
|
|
44
|
+
untrack(() => host.setToolCursor(cursorSpec.toolId, toolCursorsOf(cursorSpec.cursors)));
|
|
45
|
+
return () => host.setToolCursor(cursorSpec.toolId, null);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/interaction — pointer input and tools.
|
|
3
|
+
*
|
|
4
|
+
* `<PagePointerSource>` is the one pointer listener per page; the readers are the interaction
|
|
5
|
+
* plugin's API, state, settings and events, and `useToolCursor()` gives a tool its cursors.
|
|
6
|
+
*/
|
|
7
|
+
export * from '@embedpdf/plugin-interaction';
|
|
8
|
+
export { createClickCounter, svgCursor, vibrationFeedback, wkFeedback } from '@embedpdf/web';
|
|
9
|
+
export type { SvgCursorOptions, ToolCursorImage, ToolCursorSpec } from '@embedpdf/web';
|
|
10
|
+
export { default as PagePointerSource } from './interaction/PagePointerSource.svelte';
|
|
11
|
+
export { useInteraction, useInteractionEvent, useInteractionSettings, useInteractionState, useToolCursor, } from './interaction/readers.svelte.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @embedpdf/svelte/interaction — pointer input and tools.
|
|
3
|
+
*
|
|
4
|
+
* `<PagePointerSource>` is the one pointer listener per page; the readers are the interaction
|
|
5
|
+
* plugin's API, state, settings and events, and `useToolCursor()` gives a tool its cursors.
|
|
6
|
+
*/
|
|
7
|
+
// Registration travels with the UI.
|
|
8
|
+
export * from '@embedpdf/plugin-interaction';
|
|
9
|
+
// The browser helpers live in @embedpdf/web (the plugin has no DOM); they're here too, so app
|
|
10
|
+
// code has one import for the feature.
|
|
11
|
+
export { createClickCounter, svgCursor, vibrationFeedback, wkFeedback } from '@embedpdf/web';
|
|
12
|
+
export { default as PagePointerSource } from './interaction/PagePointerSource.svelte';
|
|
13
|
+
export { useInteraction, useInteractionEvent, useInteractionSettings, useInteractionState, useToolCursor, } from './interaction/readers.svelte.js';
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The links on one page: one absolutely placed `<a>` per clickable area. Real anchors on purpose:
|
|
3
|
+
a website gets an `href`, so a middle click, "copy link", the status-bar preview and keyboard
|
|
4
|
+
focus work as in any page. A click or a key follows the link through the plugin's `activate()`,
|
|
5
|
+
so every way of following one fires `onActivated`; the plugin opens websites through the opener
|
|
6
|
+
this binding registers.
|
|
7
|
+
|
|
8
|
+
The layer stands down while the active tool doesn't enable `link-nav`: the annotation layer
|
|
9
|
+
owns links then (select, move, resize). While a tool can edit annotations, a link attached to
|
|
10
|
+
another annotation is that annotation's, and its anchor stands down too.
|
|
11
|
+
-->
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import { ActionsToken, createHoverPump } from '@embedpdf/plugin-actions/contract';
|
|
14
|
+
import { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';
|
|
15
|
+
import { LinkToken, type Link } from '@embedpdf/plugin-link';
|
|
16
|
+
import { LinkToken as LinkHostToken } from '@embedpdf/plugin-link/contract/host';
|
|
17
|
+
import {
|
|
18
|
+
hoverLink,
|
|
19
|
+
isModifiedClick,
|
|
20
|
+
isolatePointerDown,
|
|
21
|
+
linkActivateContextOf,
|
|
22
|
+
linkAnchorOf,
|
|
23
|
+
navigableLinksOf,
|
|
24
|
+
sendLinkEvent,
|
|
25
|
+
type LinkAnchorEvent,
|
|
26
|
+
} from '@embedpdf/web';
|
|
27
|
+
import { usePage } from '../runtime/page.js';
|
|
28
|
+
import {
|
|
29
|
+
shallowArray,
|
|
30
|
+
useCapability,
|
|
31
|
+
useOptionalCapability,
|
|
32
|
+
useOptionalSelector,
|
|
33
|
+
} from '../runtime/readers.svelte.js';
|
|
34
|
+
import { stageTokenOf } from '../stage/stage-scope.js';
|
|
35
|
+
import type { LinkLayerProps } from './props.js';
|
|
36
|
+
import { useUriOpener } from './readers.svelte.js';
|
|
37
|
+
|
|
38
|
+
const NO_LINKS: readonly Link[] = Object.freeze([]);
|
|
39
|
+
|
|
40
|
+
let { link: drawLink }: LinkLayerProps = $props();
|
|
41
|
+
|
|
42
|
+
const page = usePage();
|
|
43
|
+
// The API for following a link (a handle: always this document's), and the plugin's own object
|
|
44
|
+
// for the reads here, which then wake nothing but a new document.
|
|
45
|
+
const links = useCapability(LinkToken);
|
|
46
|
+
const capability = useOptionalCapability(LinkToken);
|
|
47
|
+
useUriOpener();
|
|
48
|
+
// A page destination moves the view this page is shown in.
|
|
49
|
+
const stage = useOptionalCapability(stageTokenOf());
|
|
50
|
+
// The links' own /AA events (enter, exit, down, up, focus, blur) can only fire from here: while
|
|
51
|
+
// a link navigates, its pixels are these anchors. One hover pump per layer; crossing layers
|
|
52
|
+
// still orders Exit before Enter, since both sides submit in DOM event order.
|
|
53
|
+
const actions = useOptionalCapability(ActionsToken);
|
|
54
|
+
const pump = $derived(actions.current ? createHoverPump(actions.current.dispatch) : null);
|
|
55
|
+
|
|
56
|
+
const items = useOptionalSelector(
|
|
57
|
+
LinkToken,
|
|
58
|
+
(lens) => lens.listLinks(page.ref),
|
|
59
|
+
NO_LINKS,
|
|
60
|
+
shallowArray,
|
|
61
|
+
);
|
|
62
|
+
const engaged = useOptionalSelector(LinkHostToken, (lens) => lens.isNavigationEngaged(), false);
|
|
63
|
+
// One owner per pixel: while the active tool can edit annotations, a link attached to another
|
|
64
|
+
// annotation belongs to it, and its anchor stands down. Standalone links navigate under any
|
|
65
|
+
// tool that enables `link-nav`.
|
|
66
|
+
const editEnabled = useOptionalSelector(
|
|
67
|
+
InteractionToken,
|
|
68
|
+
(interaction) => interaction.activeToolEnables('annotation-edit'),
|
|
69
|
+
false,
|
|
70
|
+
);
|
|
71
|
+
const visible = $derived(
|
|
72
|
+
engaged.current && items.current.length > 0
|
|
73
|
+
? navigableLinksOf(items.current, editEnabled.current)
|
|
74
|
+
: NO_LINKS,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
$effect(() => {
|
|
78
|
+
const lens = capability.current;
|
|
79
|
+
const ref = page.ref;
|
|
80
|
+
if (!lens) return;
|
|
81
|
+
void lens.ensureLoaded(ref);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/** Follow `item` through the plugin, which moves the view or opens a website. */
|
|
85
|
+
function follow(item: Link) {
|
|
86
|
+
links.activate(item, linkActivateContextOf(item, page.ref, stage.current));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The link's own /AA event for a press, a release or focus, with the actions plugin. */
|
|
90
|
+
function notify(item: Link, event: LinkAnchorEvent) {
|
|
91
|
+
sendLinkEvent(actions.current, item, page.ref, event);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function onClick(href: string | null, item: Link, event: MouseEvent) {
|
|
95
|
+
// A modified click on a real `href` keeps the browser's own behavior (a background tab, a
|
|
96
|
+
// new window). Every other click follows the link through the plugin.
|
|
97
|
+
if (href && isModifiedClick(event)) return;
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
follow(item);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function onKeyDown(item: Link, event: KeyboardEvent) {
|
|
103
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
follow(item);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A press inside a link must not reach the Stage (it would start the active tool's gesture, or
|
|
110
|
+
* end an edit as a click outside). Svelte hands `onpointerdown` to the root, after the Stage
|
|
111
|
+
* saw the press, so the press stops on a native listener on the anchor, which also sends the
|
|
112
|
+
* link's "mouse down" action.
|
|
113
|
+
*/
|
|
114
|
+
const isolate = (item: () => Link) => (element: HTMLElement) =>
|
|
115
|
+
isolatePointerDown(element, () => notify(item(), 'mouseDown'));
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
{#if visible.length > 0}
|
|
119
|
+
<div style="position: absolute; inset: 0; pointer-events: none">
|
|
120
|
+
{#each visible as item (item.id)}
|
|
121
|
+
{@const { box, href, label } = linkAnchorOf(
|
|
122
|
+
item,
|
|
123
|
+
page.transform,
|
|
124
|
+
(link) => capability.current?.getLabel(link) ?? '',
|
|
125
|
+
)}
|
|
126
|
+
<!-- Its box, its label, and a native `href` only for a website with nothing chained after
|
|
127
|
+
it. -->
|
|
128
|
+
<!-- The layer's own anchor; a `link` snippet draws around it. -->
|
|
129
|
+
{#snippet native()}
|
|
130
|
+
<!-- `role` stays: an anchor without `href` (a page destination) has no role of its own. -->
|
|
131
|
+
<!-- svelte-ignore a11y_no_redundant_roles -->
|
|
132
|
+
<a
|
|
133
|
+
{@attach isolate(() => item)}
|
|
134
|
+
href={href ?? undefined}
|
|
135
|
+
target="_blank"
|
|
136
|
+
rel="noopener noreferrer"
|
|
137
|
+
role="link"
|
|
138
|
+
tabindex="0"
|
|
139
|
+
title={label}
|
|
140
|
+
aria-label={label}
|
|
141
|
+
onclick={(event) => onClick(href, item, event)}
|
|
142
|
+
onkeydown={(event) => onKeyDown(item, event)}
|
|
143
|
+
onpointerenter={() => hoverLink(pump, item, page.ref)}
|
|
144
|
+
onpointerleave={() => pump?.hover(null)}
|
|
145
|
+
onpointerup={() => notify(item, 'mouseUp')}
|
|
146
|
+
onfocus={() => notify(item, 'focus')}
|
|
147
|
+
onblur={() => notify(item, 'blur')}
|
|
148
|
+
style:position="absolute"
|
|
149
|
+
style:left="{box.left}px"
|
|
150
|
+
style:top="{box.top}px"
|
|
151
|
+
style:width="{box.width}px"
|
|
152
|
+
style:height="{box.height}px"
|
|
153
|
+
style:cursor="pointer"
|
|
154
|
+
style:pointer-events="auto"
|
|
155
|
+
></a>
|
|
156
|
+
{/snippet}
|
|
157
|
+
{#if drawLink}
|
|
158
|
+
{@render drawLink({ link: item, native })}
|
|
159
|
+
{:else}
|
|
160
|
+
{@render native()}
|
|
161
|
+
{/if}
|
|
162
|
+
{/each}
|
|
163
|
+
</div>
|
|
164
|
+
{/if}
|