@inkbar/svelte 0.0.0-stage → 0.1.1
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 +21 -0
- package/README.md +154 -2
- package/dist/class-names.d.ts +3 -0
- package/dist/class-names.d.ts.map +1 -0
- package/dist/class-names.js +5 -0
- package/dist/components/InkAnnotation.svelte +73 -0
- package/dist/components/InkAnnotation.svelte.d.ts +13 -0
- package/dist/components/InkAnnotation.svelte.d.ts.map +1 -0
- package/dist/components/InkNotePopover.svelte +193 -0
- package/dist/components/InkNotePopover.svelte.d.ts +9 -0
- package/dist/components/InkNotePopover.svelte.d.ts.map +1 -0
- package/dist/components/InkPage.svelte +125 -0
- package/dist/components/InkPage.svelte.d.ts +22 -0
- package/dist/components/InkPage.svelte.d.ts.map +1 -0
- package/dist/components/InkPageCount.svelte +20 -0
- package/dist/components/InkPageCount.svelte.d.ts +6 -0
- package/dist/components/InkPageCount.svelte.d.ts.map +1 -0
- package/dist/components/InkPageInput.svelte +49 -0
- package/dist/components/InkPageInput.svelte.d.ts +6 -0
- package/dist/components/InkPageInput.svelte.d.ts.map +1 -0
- package/dist/components/InkRoot.svelte +358 -0
- package/dist/components/InkRoot.svelte.d.ts +51 -0
- package/dist/components/InkRoot.svelte.d.ts.map +1 -0
- package/dist/components/InkSuggestionCard.svelte +121 -0
- package/dist/components/InkSuggestionCard.svelte.d.ts +9 -0
- package/dist/components/InkSuggestionCard.svelte.d.ts.map +1 -0
- package/dist/components/InkToolButton.svelte +54 -0
- package/dist/components/InkToolButton.svelte.d.ts +17 -0
- package/dist/components/InkToolButton.svelte.d.ts.map +1 -0
- package/dist/components/InkToolbar.svelte +40 -0
- package/dist/components/InkToolbar.svelte.d.ts +13 -0
- package/dist/components/InkToolbar.svelte.d.ts.map +1 -0
- package/dist/components/InkViewport.svelte +306 -0
- package/dist/components/InkViewport.svelte.d.ts +27 -0
- package/dist/components/InkViewport.svelte.d.ts.map +1 -0
- package/dist/components/InkZoomIn.svelte +34 -0
- package/dist/components/InkZoomIn.svelte.d.ts +5 -0
- package/dist/components/InkZoomIn.svelte.d.ts.map +1 -0
- package/dist/components/InkZoomOut.svelte +34 -0
- package/dist/components/InkZoomOut.svelte.d.ts +5 -0
- package/dist/components/InkZoomOut.svelte.d.ts.map +1 -0
- package/dist/components/InkZoomSelect.svelte +56 -0
- package/dist/components/InkZoomSelect.svelte.d.ts +6 -0
- package/dist/components/InkZoomSelect.svelte.d.ts.map +1 -0
- package/dist/components/ThreadForm.svelte +42 -0
- package/dist/components/ThreadForm.svelte.d.ts +11 -0
- package/dist/components/ThreadForm.svelte.d.ts.map +1 -0
- package/dist/components/annotation-element.svelte.d.ts +9 -0
- package/dist/components/annotation-element.svelte.d.ts.map +1 -0
- package/dist/components/annotation-element.svelte.js +25 -0
- package/dist/components/child-snippet.d.ts +22 -0
- package/dist/components/child-snippet.d.ts.map +1 -0
- package/dist/components/child-snippet.js +14 -0
- package/dist/components/popover-focus.d.ts +33 -0
- package/dist/components/popover-focus.d.ts.map +1 -0
- package/dist/components/popover-focus.js +63 -0
- package/dist/components/portal.d.ts +26 -0
- package/dist/components/portal.d.ts.map +1 -0
- package/dist/components/portal.js +55 -0
- package/dist/components/zoom-button.d.ts +10 -0
- package/dist/components/zoom-button.d.ts.map +1 -0
- package/dist/components/zoom-button.js +1 -0
- package/dist/compositions/DefaultToolbar.svelte +90 -0
- package/dist/compositions/DefaultToolbar.svelte.d.ts +10 -0
- package/dist/compositions/DefaultToolbar.svelte.d.ts.map +1 -0
- package/dist/context.d.ts +57 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +34 -0
- package/dist/hooks/useAnnotations.svelte.d.ts +1683 -0
- package/dist/hooks/useAnnotations.svelte.d.ts.map +1 -0
- package/dist/hooks/useAnnotations.svelte.js +33 -0
- package/dist/hooks/useDocument.svelte.d.ts +12 -0
- package/dist/hooks/useDocument.svelte.d.ts.map +1 -0
- package/dist/hooks/useDocument.svelte.js +28 -0
- package/dist/hooks/useEmbedded.svelte.d.ts +7 -0
- package/dist/hooks/useEmbedded.svelte.d.ts.map +1 -0
- package/dist/hooks/useEmbedded.svelte.js +17 -0
- package/dist/hooks/useHistory.svelte.d.ts +7 -0
- package/dist/hooks/useHistory.svelte.d.ts.map +1 -0
- package/dist/hooks/useHistory.svelte.js +8 -0
- package/dist/hooks/usePage.svelte.d.ts +10 -0
- package/dist/hooks/usePage.svelte.d.ts.map +1 -0
- package/dist/hooks/usePage.svelte.js +29 -0
- package/dist/hooks/useSelection.svelte.d.ts +6 -0
- package/dist/hooks/useSelection.svelte.d.ts.map +1 -0
- package/dist/hooks/useSelection.svelte.js +12 -0
- package/dist/hooks/useTool.svelte.d.ts +6 -0
- package/dist/hooks/useTool.svelte.d.ts.map +1 -0
- package/dist/hooks/useTool.svelte.js +10 -0
- package/dist/hooks/useZoom.svelte.d.ts +9 -0
- package/dist/hooks/useZoom.svelte.d.ts.map +1 -0
- package/dist/hooks/useZoom.svelte.js +18 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/use-readable.svelte.d.ts +13 -0
- package/dist/use-readable.svelte.d.ts.map +1 -0
- package/dist/use-readable.svelte.js +28 -0
- package/package.json +56 -3
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { observeAnnotationElement } from '@inkbar/core/dom';
|
|
2
|
+
/**
|
|
3
|
+
* The overlay element core draws for an annotation, for anchoring a popover to it. Core
|
|
4
|
+
* creates and removes these elements outside Svelte, so this follows the root's subtree while
|
|
5
|
+
* an id is wanted.
|
|
6
|
+
*/
|
|
7
|
+
export function useAnnotationElement(root, id) {
|
|
8
|
+
let element = $state.raw(null);
|
|
9
|
+
$effect(() => {
|
|
10
|
+
const r = root();
|
|
11
|
+
const i = id();
|
|
12
|
+
if (!r || !i) {
|
|
13
|
+
element = null;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
return observeAnnotationElement(r, i, (el) => {
|
|
17
|
+
element = el;
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
get current() {
|
|
22
|
+
return element;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
/** A `child` prop: render your own element, spread `props` on it. */
|
|
3
|
+
export type ChildSnippet = Snippet<[{
|
|
4
|
+
props: Record<string, unknown>;
|
|
5
|
+
}]>;
|
|
6
|
+
/** `T` as Bits UI types its props: no `null` and no explicit `undefined` in a value type. */
|
|
7
|
+
export type BitsProps<T> = {
|
|
8
|
+
[K in keyof T]: Exclude<T[K], null | undefined>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Svelte's attribute types allow `null` and `undefined` (`id?: string | null | undefined`)
|
|
12
|
+
* where Bits UI's do not (`id?: string`, under exactOptionalPropertyTypes), so the host's rest
|
|
13
|
+
* props do not type-check against a Bits UI component as they are. This takes both out of the
|
|
14
|
+
* value types and nothing else, so the keys and the remaining value types are still checked.
|
|
15
|
+
*
|
|
16
|
+
* It is a cast at the type level only: nothing is dropped at runtime, and a key the host set
|
|
17
|
+
* to `null` or `undefined` is passed on as that. For an attribute Bits UI hands through to the
|
|
18
|
+
* element, Svelte leaves it off. A prop Bits UI reads itself (`id`, `disabled`, `orientation`,
|
|
19
|
+
* `loop`) receives the explicit `null` or `undefined`. This is the one place the cast lives.
|
|
20
|
+
*/
|
|
21
|
+
export declare function asBitsProps<T extends object>(rest: T): BitsProps<T>;
|
|
22
|
+
//# sourceMappingURL=child-snippet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child-snippet.d.ts","sourceRoot":"","sources":["../../src/lib/components/child-snippet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,qEAAqE;AACrE,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC,CAAA;AAExE,6FAA6F;AAC7F,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CAAE,CAAA;AAE9E;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAEnE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte's attribute types allow `null` and `undefined` (`id?: string | null | undefined`)
|
|
3
|
+
* where Bits UI's do not (`id?: string`, under exactOptionalPropertyTypes), so the host's rest
|
|
4
|
+
* props do not type-check against a Bits UI component as they are. This takes both out of the
|
|
5
|
+
* value types and nothing else, so the keys and the remaining value types are still checked.
|
|
6
|
+
*
|
|
7
|
+
* It is a cast at the type level only: nothing is dropped at runtime, and a key the host set
|
|
8
|
+
* to `null` or `undefined` is passed on as that. For an attribute Bits UI hands through to the
|
|
9
|
+
* element, Svelte leaves it off. A prop Bits UI reads itself (`id`, `disabled`, `orientation`,
|
|
10
|
+
* `loop`) receives the explicit `null` or `undefined`. This is the one place the cast lives.
|
|
11
|
+
*/
|
|
12
|
+
export function asBitsProps(rest) {
|
|
13
|
+
return rest;
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type FocusReturn } from '@inkbar/core/dom';
|
|
2
|
+
import type { InkContext } from '../context.js';
|
|
3
|
+
/** Core's focus planner, as the two handlers Bits UI's popover content takes. */
|
|
4
|
+
export interface PopoverFocusReturn {
|
|
5
|
+
beforeRemove: FocusReturn['beforeRemove'];
|
|
6
|
+
/** Bits UI's `onOpenAutoFocus` handler. Leaves the default alone: focus moves into the popover. */
|
|
7
|
+
onOpenAutoFocus: (event: Event) => void;
|
|
8
|
+
/** Bits UI's `onCloseAutoFocus` handler. Prevents the default; the planner places focus. */
|
|
9
|
+
onCloseAutoFocus: (event: Event, anchor: HTMLElement | null) => void;
|
|
10
|
+
}
|
|
11
|
+
/** Bits UI's `onInteractOutside` handler: a press that will activate an annotation keeps the popover open. */
|
|
12
|
+
export declare function keepOpenOnAnnotation(event: PointerEvent): void;
|
|
13
|
+
/**
|
|
14
|
+
* Bits UI reports the close from an effect teardown, where a reactive read returns the value
|
|
15
|
+
* from before the change that closed the popover. The planner needs the activation as it is
|
|
16
|
+
* now, so it reads the untracked copy.
|
|
17
|
+
*
|
|
18
|
+
* Bits UI also reports a close while a popover opens: its focus scope mounts a second time,
|
|
19
|
+
* and the remount reports a close and then, in the same call, an open. That close is not one,
|
|
20
|
+
* and answering it would focus the anchor a frame before focus moves into the popover. So a
|
|
21
|
+
* close reaches the planner one microtask late, and only when no open was reported after it.
|
|
22
|
+
*
|
|
23
|
+
* This relies on Bits UI reporting a remount as a close and then an open within one synchronous
|
|
24
|
+
* call (observed in bits-ui 2.19.5, `FocusScope.mount`). If that changes, the browser case 'a
|
|
25
|
+
* card opened by selection alone takes focus without passing it through the annotation' fails.
|
|
26
|
+
*
|
|
27
|
+
* A close that is superseded this way moves no focus, but it still uses up whatever
|
|
28
|
+
* `beforeRemove` planned for it, so the plan cannot leak into a later close. A close that is
|
|
29
|
+
* only reported again in the same stretch is dropped, and the last report answers with the
|
|
30
|
+
* plan intact.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useFocusReturn(ctx: InkContext): PopoverFocusReturn;
|
|
33
|
+
//# sourceMappingURL=popover-focus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover-focus.d.ts","sourceRoot":"","sources":["../../src/lib/components/popover-focus.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,WAAW,CAAC,cAAc,CAAC,CAAA;IACzC,mGAAmG;IACnG,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACvC,4FAA4F;IAC5F,gBAAgB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;CACrE;AAED,8GAA8G;AAC9G,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAE9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,kBAAkB,CAkClE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createFocusReturn, shouldKeepOpenOnAnnotation, } from '@inkbar/core/dom';
|
|
2
|
+
import { peekActivated } from '../context.js';
|
|
3
|
+
/** Bits UI's `onInteractOutside` handler: a press that will activate an annotation keeps the popover open. */
|
|
4
|
+
export function keepOpenOnAnnotation(event) {
|
|
5
|
+
if (shouldKeepOpenOnAnnotation(event.target))
|
|
6
|
+
event.preventDefault();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Bits UI reports the close from an effect teardown, where a reactive read returns the value
|
|
10
|
+
* from before the change that closed the popover. The planner needs the activation as it is
|
|
11
|
+
* now, so it reads the untracked copy.
|
|
12
|
+
*
|
|
13
|
+
* Bits UI also reports a close while a popover opens: its focus scope mounts a second time,
|
|
14
|
+
* and the remount reports a close and then, in the same call, an open. That close is not one,
|
|
15
|
+
* and answering it would focus the anchor a frame before focus moves into the popover. So a
|
|
16
|
+
* close reaches the planner one microtask late, and only when no open was reported after it.
|
|
17
|
+
*
|
|
18
|
+
* This relies on Bits UI reporting a remount as a close and then an open within one synchronous
|
|
19
|
+
* call (observed in bits-ui 2.19.5, `FocusScope.mount`). If that changes, the browser case 'a
|
|
20
|
+
* card opened by selection alone takes focus without passing it through the annotation' fails.
|
|
21
|
+
*
|
|
22
|
+
* A close that is superseded this way moves no focus, but it still uses up whatever
|
|
23
|
+
* `beforeRemove` planned for it, so the plan cannot leak into a later close. A close that is
|
|
24
|
+
* only reported again in the same stretch is dropped, and the last report answers with the
|
|
25
|
+
* plan intact.
|
|
26
|
+
*/
|
|
27
|
+
export function useFocusReturn(ctx) {
|
|
28
|
+
/** True while a superseded close is handed to the planner only to clear its plan. */
|
|
29
|
+
let discarding = false;
|
|
30
|
+
const planner = createFocusReturn({
|
|
31
|
+
// Without a root the planner clears its plan and returns before it looks for focus.
|
|
32
|
+
root: () => (discarding ? null : ctx.rootEl),
|
|
33
|
+
activated: () => peekActivated(ctx),
|
|
34
|
+
});
|
|
35
|
+
/** Open and close reports so far, so a deferred close can tell what followed it. */
|
|
36
|
+
let opens = 0;
|
|
37
|
+
let closes = 0;
|
|
38
|
+
return {
|
|
39
|
+
beforeRemove: (anchor, prefer) => planner.beforeRemove(anchor, prefer),
|
|
40
|
+
onOpenAutoFocus() {
|
|
41
|
+
opens += 1;
|
|
42
|
+
},
|
|
43
|
+
onCloseAutoFocus(event, anchor) {
|
|
44
|
+
// Never the library's own focus return: there is no trigger for it to return to.
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
const close = ++closes;
|
|
47
|
+
const opensBefore = opens;
|
|
48
|
+
queueMicrotask(() => {
|
|
49
|
+
// Reported again since: the last close of the stretch answers, with the plan intact.
|
|
50
|
+
if (close !== closes)
|
|
51
|
+
return;
|
|
52
|
+
// An open followed: the popover is open, so only the plan is cleared.
|
|
53
|
+
discarding = opens !== opensBefore;
|
|
54
|
+
try {
|
|
55
|
+
planner.onCloseAutoFocus(event, anchor);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
discarding = false;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
/**
|
|
3
|
+
* Teleport for an element's content. On attach it moves every child node of the element into
|
|
4
|
+
* `target`, Svelte's anchor comments included; on cleanup it moves them back so Svelte's own
|
|
5
|
+
* teardown removes them. Svelte inserts later nodes relative to those anchors, so `{#if}` and
|
|
6
|
+
* `{#each}` blocks inside the moved content keep updating in the target. The target ends up with
|
|
7
|
+
* the content's own nodes and no wrapper, the same DOM the Vue and React packages produce.
|
|
8
|
+
*
|
|
9
|
+
* A new `target` is a new attachment: Svelte runs the cleanup and the attach again. The same
|
|
10
|
+
* target always gets the same attachment, so `{@attach portal(el)}` re-evaluated with an
|
|
11
|
+
* unchanged `el` leaves the content where it is.
|
|
12
|
+
*
|
|
13
|
+
* The cleanup relies on one invariant: the first and last nodes it moved are still children of
|
|
14
|
+
* the target, in that order, and every node between them belongs to the wrapper (the nodes
|
|
15
|
+
* moved on attach, plus whatever Svelte inserted between them since). Svelte keeps to it, because
|
|
16
|
+
* it only ever inserts relative to the anchors that were moved. Then the whole run from the
|
|
17
|
+
* first node to the last comes back, late nodes included. Nothing else may put nodes of its
|
|
18
|
+
* own between the two: they would be taken for the wrapper's and moved into it.
|
|
19
|
+
*
|
|
20
|
+
* When the invariant does not hold (the first or last node has left the target, or the walk
|
|
21
|
+
* from the first never reaches the last), the cleanup falls back to the nodes it moved on
|
|
22
|
+
* attach, wherever they are now, and moves only those back. Nodes Svelte added later are not
|
|
23
|
+
* moved.
|
|
24
|
+
*/
|
|
25
|
+
export declare function portal(target: Element): Attachment<HTMLElement>;
|
|
26
|
+
//# sourceMappingURL=portal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/lib/components/portal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAIpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CA2B/D"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const attachments = new WeakMap();
|
|
2
|
+
/**
|
|
3
|
+
* Teleport for an element's content. On attach it moves every child node of the element into
|
|
4
|
+
* `target`, Svelte's anchor comments included; on cleanup it moves them back so Svelte's own
|
|
5
|
+
* teardown removes them. Svelte inserts later nodes relative to those anchors, so `{#if}` and
|
|
6
|
+
* `{#each}` blocks inside the moved content keep updating in the target. The target ends up with
|
|
7
|
+
* the content's own nodes and no wrapper, the same DOM the Vue and React packages produce.
|
|
8
|
+
*
|
|
9
|
+
* A new `target` is a new attachment: Svelte runs the cleanup and the attach again. The same
|
|
10
|
+
* target always gets the same attachment, so `{@attach portal(el)}` re-evaluated with an
|
|
11
|
+
* unchanged `el` leaves the content where it is.
|
|
12
|
+
*
|
|
13
|
+
* The cleanup relies on one invariant: the first and last nodes it moved are still children of
|
|
14
|
+
* the target, in that order, and every node between them belongs to the wrapper (the nodes
|
|
15
|
+
* moved on attach, plus whatever Svelte inserted between them since). Svelte keeps to it, because
|
|
16
|
+
* it only ever inserts relative to the anchors that were moved. Then the whole run from the
|
|
17
|
+
* first node to the last comes back, late nodes included. Nothing else may put nodes of its
|
|
18
|
+
* own between the two: they would be taken for the wrapper's and moved into it.
|
|
19
|
+
*
|
|
20
|
+
* When the invariant does not hold (the first or last node has left the target, or the walk
|
|
21
|
+
* from the first never reaches the last), the cleanup falls back to the nodes it moved on
|
|
22
|
+
* attach, wherever they are now, and moves only those back. Nodes Svelte added later are not
|
|
23
|
+
* moved.
|
|
24
|
+
*/
|
|
25
|
+
export function portal(target) {
|
|
26
|
+
let attachment = attachments.get(target);
|
|
27
|
+
if (!attachment) {
|
|
28
|
+
attachment = (wrapper) => {
|
|
29
|
+
const moved = [...wrapper.childNodes];
|
|
30
|
+
for (const node of moved)
|
|
31
|
+
target.appendChild(node);
|
|
32
|
+
return () => {
|
|
33
|
+
const first = moved[0];
|
|
34
|
+
const last = moved.at(-1);
|
|
35
|
+
// Everything between the first and last moved node comes back, which takes in the
|
|
36
|
+
// nodes Svelte inserted since. The target may already be detached (core removed the
|
|
37
|
+
// annotation); the nodes move back all the same, so Svelte finds them where it left
|
|
38
|
+
// them.
|
|
39
|
+
const back = [];
|
|
40
|
+
if (first && last && first.parentNode === target && last.parentNode === target) {
|
|
41
|
+
for (let node = first; node; node = node.nextSibling) {
|
|
42
|
+
back.push(node);
|
|
43
|
+
if (node === last)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const node of back.at(-1) === last ? back : moved)
|
|
48
|
+
if (node.parentNode !== wrapper)
|
|
49
|
+
wrapper.appendChild(node);
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
attachments.set(target, attachment);
|
|
53
|
+
}
|
|
54
|
+
return attachment;
|
|
55
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ChildSnippet } from './child-snippet.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
|
+
/** Props of `InkZoomIn` and `InkZoomOut`: a toolbar button that may render through `child`. */
|
|
5
|
+
export interface InkZoomButtonProps extends Omit<HTMLButtonAttributes, 'children'> {
|
|
6
|
+
children?: Snippet | undefined;
|
|
7
|
+
/** Render your own button: spread `props` on it. */
|
|
8
|
+
child?: ChildSnippet | undefined;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=zoom-button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zoom-button.d.ts","sourceRoot":"","sources":["../../src/lib/components/zoom-button.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,+FAA+F;AAC/F,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAChF,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,oDAAoD;IACpD,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
|
|
4
|
+
export interface DefaultToolbarProps {
|
|
5
|
+
label?: string | undefined
|
|
6
|
+
class?: string | undefined
|
|
7
|
+
children?: Snippet | undefined
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
/*
|
|
13
|
+
* The standard toolbar: tools, zoom and page controls with icons. It adds no styles of its
|
|
14
|
+
* own; the theme styles the `data-ink-*` hooks. Extra controls go in `children`.
|
|
15
|
+
*/
|
|
16
|
+
// One module per icon: the package's index evaluates every icon it ships, which costs a
|
|
17
|
+
// server (or a test) that imports this file well over a second. A new icon also goes in the
|
|
18
|
+
// browser tests' `optimizeDeps.include` in vitest.config.ts.
|
|
19
|
+
import Hand from '@lucide/svelte/icons/hand'
|
|
20
|
+
import Highlighter from '@lucide/svelte/icons/highlighter'
|
|
21
|
+
import MessageSquare from '@lucide/svelte/icons/message-square'
|
|
22
|
+
import MousePointer2 from '@lucide/svelte/icons/mouse-pointer-2'
|
|
23
|
+
import Square from '@lucide/svelte/icons/square'
|
|
24
|
+
import ZoomIn from '@lucide/svelte/icons/zoom-in'
|
|
25
|
+
import ZoomOut from '@lucide/svelte/icons/zoom-out'
|
|
26
|
+
|
|
27
|
+
import { cx } from '../class-names.js'
|
|
28
|
+
import InkPageCount from '../components/InkPageCount.svelte'
|
|
29
|
+
import InkPageInput from '../components/InkPageInput.svelte'
|
|
30
|
+
import InkToolbar from '../components/InkToolbar.svelte'
|
|
31
|
+
import InkToolButton from '../components/InkToolButton.svelte'
|
|
32
|
+
import InkZoomIn from '../components/InkZoomIn.svelte'
|
|
33
|
+
import InkZoomOut from '../components/InkZoomOut.svelte'
|
|
34
|
+
import InkZoomSelect from '../components/InkZoomSelect.svelte'
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
label = 'Document tools',
|
|
38
|
+
class: className,
|
|
39
|
+
children,
|
|
40
|
+
}: DefaultToolbarProps = $props()
|
|
41
|
+
|
|
42
|
+
const ICON = 18
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<InkToolbar
|
|
46
|
+
{label}
|
|
47
|
+
class={cx('ink-default-toolbar', className)}
|
|
48
|
+
data-ink-default-toolbar=""
|
|
49
|
+
>
|
|
50
|
+
<div
|
|
51
|
+
class="ink-toolbar-group"
|
|
52
|
+
data-ink-toolbar-group="tools"
|
|
53
|
+
role="group"
|
|
54
|
+
aria-label="Tools"
|
|
55
|
+
>
|
|
56
|
+
<InkToolButton tool="view"><Hand size={ICON} aria-hidden="true" /></InkToolButton>
|
|
57
|
+
<InkToolButton tool="select"
|
|
58
|
+
><MousePointer2 size={ICON} aria-hidden="true" /></InkToolButton
|
|
59
|
+
>
|
|
60
|
+
<InkToolButton tool="highlight"
|
|
61
|
+
><Highlighter size={ICON} aria-hidden="true" /></InkToolButton
|
|
62
|
+
>
|
|
63
|
+
<InkToolButton tool="redact-area"
|
|
64
|
+
><Square size={ICON} aria-hidden="true" /></InkToolButton
|
|
65
|
+
>
|
|
66
|
+
<InkToolButton tool="note"
|
|
67
|
+
><MessageSquare size={ICON} aria-hidden="true" /></InkToolButton
|
|
68
|
+
>
|
|
69
|
+
</div>
|
|
70
|
+
<div
|
|
71
|
+
class="ink-toolbar-group"
|
|
72
|
+
data-ink-toolbar-group="zoom"
|
|
73
|
+
role="group"
|
|
74
|
+
aria-label="Zoom"
|
|
75
|
+
>
|
|
76
|
+
<InkZoomOut><ZoomOut size={ICON} aria-hidden="true" /></InkZoomOut>
|
|
77
|
+
<InkZoomSelect />
|
|
78
|
+
<InkZoomIn><ZoomIn size={ICON} aria-hidden="true" /></InkZoomIn>
|
|
79
|
+
</div>
|
|
80
|
+
<div
|
|
81
|
+
class="ink-toolbar-group"
|
|
82
|
+
data-ink-toolbar-group="page"
|
|
83
|
+
role="group"
|
|
84
|
+
aria-label="Page"
|
|
85
|
+
>
|
|
86
|
+
<InkPageInput />
|
|
87
|
+
<InkPageCount />
|
|
88
|
+
</div>
|
|
89
|
+
{@render children?.()}
|
|
90
|
+
</InkToolbar>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export interface DefaultToolbarProps {
|
|
3
|
+
label?: string | undefined;
|
|
4
|
+
class?: string | undefined;
|
|
5
|
+
children?: Snippet | undefined;
|
|
6
|
+
}
|
|
7
|
+
declare const DefaultToolbar: import("svelte").Component<DefaultToolbarProps, {}, "">;
|
|
8
|
+
type DefaultToolbar = ReturnType<typeof DefaultToolbar>;
|
|
9
|
+
export default DefaultToolbar;
|
|
10
|
+
//# sourceMappingURL=DefaultToolbar.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/compositions/DefaultToolbar.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAkFH,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Annotation, InkDocument, InkStatus, PdfAssets } from '@inkbar/core';
|
|
2
|
+
import type { ToolController, ToolId, Viewport, ZoomValue } from '@inkbar/core/dom';
|
|
3
|
+
export type { InkStatus };
|
|
4
|
+
/** Undo and redo for the current document, whichever mode its annotations are in. */
|
|
5
|
+
export interface InkHistory {
|
|
6
|
+
readonly canUndo: boolean;
|
|
7
|
+
readonly canRedo: boolean;
|
|
8
|
+
undo(): void;
|
|
9
|
+
redo(): void;
|
|
10
|
+
clear(): void;
|
|
11
|
+
/**
|
|
12
|
+
* Runs `fn` and records what it changes as one undo step. The host still receives one change
|
|
13
|
+
* per add. Controlled, the history groups the resulting list changes into one step, and
|
|
14
|
+
* `canUndo` updates once the current task ends. Any other change the host applies in that
|
|
15
|
+
* same window joins the step.
|
|
16
|
+
*/
|
|
17
|
+
batch(fn: () => void): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* What InkRoot shares with everything inside it. Every `readonly` member is a getter over
|
|
21
|
+
* InkRoot's state, so a template or effect that reads one re-runs when it changes.
|
|
22
|
+
*/
|
|
23
|
+
export interface InkContext {
|
|
24
|
+
readonly assets: PdfAssets;
|
|
25
|
+
readonly document: InkDocument | null;
|
|
26
|
+
/** `loading` from the moment a document starts opening, then the document's own status. */
|
|
27
|
+
readonly status: InkStatus;
|
|
28
|
+
readonly annotations: readonly Annotation[];
|
|
29
|
+
readonly selection: readonly string[];
|
|
30
|
+
/** Set by InkViewport once it has built its viewport and tool controller. */
|
|
31
|
+
readonly viewport: Viewport | null;
|
|
32
|
+
readonly tools: ToolController | null;
|
|
33
|
+
/** Desired tool; InkViewport applies it to the controller. */
|
|
34
|
+
readonly tool: ToolId;
|
|
35
|
+
setTool(id: ToolId): void;
|
|
36
|
+
/** Desired zoom and page from the host's props; InkViewport applies and reports back. */
|
|
37
|
+
readonly zoomModel: ZoomValue | undefined;
|
|
38
|
+
readonly pageModel: number | undefined;
|
|
39
|
+
reportZoom(z: ZoomValue): void;
|
|
40
|
+
reportPage(p: number): void;
|
|
41
|
+
/** Last activated annotation (Enter, click with the note tool, etc.). Popovers key off this. */
|
|
42
|
+
readonly activated: string | null;
|
|
43
|
+
setActivated(id: string | null): void;
|
|
44
|
+
readonly history: InkHistory;
|
|
45
|
+
readonly rootEl: HTMLElement | null;
|
|
46
|
+
setViewport(viewport: Viewport | null, tools: ToolController | null): void;
|
|
47
|
+
}
|
|
48
|
+
/** Called once by InkRoot during its initialisation. */
|
|
49
|
+
export declare function setInkContext(ctx: InkContext, peekActivated: () => string | null): void;
|
|
50
|
+
/** The untracked `activated` of a context made by InkRoot. Not part of the public entry. */
|
|
51
|
+
export declare function peekActivated(ctx: InkContext): string | null;
|
|
52
|
+
/**
|
|
53
|
+
* The context for hooks and components: throws a readable error when called outside a component
|
|
54
|
+
* rendered inside `InkRoot`, including when called outside any component at all.
|
|
55
|
+
*/
|
|
56
|
+
export declare function useInkContext(consumer: string): InkContext;
|
|
57
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/lib/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEnF,YAAY,EAAE,SAAS,EAAE,CAAA;AAEzB,qFAAqF;AACrF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,IAAI,IAAI,IAAI,CAAA;IACZ,IAAI,IAAI,IAAI,CAAA;IACZ,KAAK,IAAI,IAAI,CAAA;IACb;;;;;OAKG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAA;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IACrC,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAA;IAC3C,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAA;IACrC,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,yFAAyF;IACzF,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAA;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,UAAU,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IAC9B,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,gGAAgG;IAChG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IACrC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;IACnC,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAAA;CAC3E;AAWD,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAGvF;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAG5D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAY1D"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
const KEY = Symbol('inkbar');
|
|
3
|
+
/**
|
|
4
|
+
* `activated` as last set, not tracked, per context. Internal to the package. For code that
|
|
5
|
+
* runs while an effect is torn down (a popover's close handler): there Svelte serves a
|
|
6
|
+
* reactive read the value from before the change that caused the teardown.
|
|
7
|
+
*/
|
|
8
|
+
const activatedPeeks = new WeakMap();
|
|
9
|
+
/** Called once by InkRoot during its initialisation. */
|
|
10
|
+
export function setInkContext(ctx, peekActivated) {
|
|
11
|
+
activatedPeeks.set(ctx, peekActivated);
|
|
12
|
+
setContext(KEY, ctx);
|
|
13
|
+
}
|
|
14
|
+
/** The untracked `activated` of a context made by InkRoot. Not part of the public entry. */
|
|
15
|
+
export function peekActivated(ctx) {
|
|
16
|
+
const peek = activatedPeeks.get(ctx);
|
|
17
|
+
return peek ? peek() : ctx.activated;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The context for hooks and components: throws a readable error when called outside a component
|
|
21
|
+
* rendered inside `InkRoot`, including when called outside any component at all.
|
|
22
|
+
*/
|
|
23
|
+
export function useInkContext(consumer) {
|
|
24
|
+
let ctx;
|
|
25
|
+
try {
|
|
26
|
+
ctx = getContext(KEY);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
ctx = undefined;
|
|
30
|
+
}
|
|
31
|
+
if (!ctx)
|
|
32
|
+
throw new Error(`[inkbar] ${consumer} must be called in a component inside <InkRoot>.`);
|
|
33
|
+
return ctx;
|
|
34
|
+
}
|