@inkbar/svelte 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -12
- package/dist/components/InkAnnotation.svelte +0 -12
- package/dist/components/InkAnnotation.svelte.d.ts.map +1 -1
- package/dist/components/InkColorMenu.svelte +247 -0
- package/dist/components/InkColorMenu.svelte.d.ts +35 -0
- package/dist/components/InkColorMenu.svelte.d.ts.map +1 -0
- package/dist/components/InkHighlightMenu.svelte +175 -0
- package/dist/components/InkHighlightMenu.svelte.d.ts +16 -0
- package/dist/components/InkHighlightMenu.svelte.d.ts.map +1 -0
- package/dist/components/InkLineStyleMenu.svelte +141 -0
- package/dist/components/InkLineStyleMenu.svelte.d.ts +15 -0
- package/dist/components/InkLineStyleMenu.svelte.d.ts.map +1 -0
- package/dist/components/InkRoot.svelte +90 -3
- package/dist/components/InkRoot.svelte.d.ts +27 -2
- package/dist/components/InkRoot.svelte.d.ts.map +1 -1
- package/dist/components/InkShapeMenu.svelte +161 -0
- package/dist/components/InkShapeMenu.svelte.d.ts +15 -0
- package/dist/components/InkShapeMenu.svelte.d.ts.map +1 -0
- package/dist/components/InkSignatureDialog.svelte +70 -0
- package/dist/components/InkSignatureDialog.svelte.d.ts +22 -0
- package/dist/components/InkSignatureDialog.svelte.d.ts.map +1 -0
- package/dist/components/InkSignatureMenu.svelte +267 -0
- package/dist/components/InkSignatureMenu.svelte.d.ts +18 -0
- package/dist/components/InkSignatureMenu.svelte.d.ts.map +1 -0
- package/dist/components/InkTextStyleMenu.svelte +225 -0
- package/dist/components/InkTextStyleMenu.svelte.d.ts +15 -0
- package/dist/components/InkTextStyleMenu.svelte.d.ts.map +1 -0
- package/dist/components/{InkNotePopover.svelte → InkThreadPopover.svelte} +44 -32
- package/dist/components/InkThreadPopover.svelte.d.ts +9 -0
- package/dist/components/InkThreadPopover.svelte.d.ts.map +1 -0
- package/dist/components/InkToolButton.svelte +23 -7
- package/dist/components/InkToolButton.svelte.d.ts.map +1 -1
- package/dist/components/InkViewport.svelte +26 -2
- package/dist/components/InkViewport.svelte.d.ts.map +1 -1
- package/dist/components/SignatureForm.svelte +219 -0
- package/dist/components/SignatureForm.svelte.d.ts +10 -0
- package/dist/components/SignatureForm.svelte.d.ts.map +1 -0
- package/dist/components/ThreadForm.svelte +4 -2
- package/dist/components/ThreadForm.svelte.d.ts +1 -0
- package/dist/components/ThreadForm.svelte.d.ts.map +1 -1
- package/dist/components/child-snippet.d.ts +8 -0
- package/dist/components/child-snippet.d.ts.map +1 -1
- package/dist/components/child-snippet.js +15 -0
- package/dist/components/popover-focus.d.ts +16 -2
- package/dist/components/popover-focus.d.ts.map +1 -1
- package/dist/components/popover-focus.js +24 -3
- package/dist/context.d.ts +21 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/hooks/useAnnotations.svelte.d.ts +1333 -578
- package/dist/hooks/useAnnotations.svelte.d.ts.map +1 -1
- package/dist/hooks/useAnnotations.svelte.js +1 -7
- package/dist/hooks/useToolOptions.svelte.d.ts +11 -0
- package/dist/hooks/useToolOptions.svelte.d.ts.map +1 -0
- package/dist/hooks/useToolOptions.svelte.js +19 -0
- package/dist/hooks/useToolStyle.svelte.d.ts +33 -0
- package/dist/hooks/useToolStyle.svelte.d.ts.map +1 -0
- package/dist/hooks/useToolStyle.svelte.js +33 -0
- package/dist/index.d.ts +21 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/package.json +5 -5
- package/dist/components/InkNotePopover.svelte.d.ts +0 -9
- package/dist/components/InkNotePopover.svelte.d.ts.map +0 -1
- package/dist/components/InkSuggestionCard.svelte +0 -121
- package/dist/components/InkSuggestionCard.svelte.d.ts +0 -9
- package/dist/components/InkSuggestionCard.svelte.d.ts.map +0 -1
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { ThreadPopoverSlotProps } from '@inkbar/core/dom'
|
|
3
3
|
import type { Snippet } from 'svelte'
|
|
4
4
|
|
|
5
|
-
export interface
|
|
6
|
-
children?: Snippet<[
|
|
5
|
+
export interface InkThreadPopoverProps {
|
|
6
|
+
children?: Snippet<[ThreadPopoverSlotProps]> | undefined
|
|
7
7
|
}
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
/*
|
|
12
|
-
* The thread for the activated note, anchored to the
|
|
13
|
-
* reply opens the whole thread from its root. Closing (Close, Escape,
|
|
14
|
-
* `activated`.
|
|
12
|
+
* The thread for the activated note or text markup, anchored to the root's overlay
|
|
13
|
+
* element. Activating a reply opens the whole thread from its root. Closing (Close, Escape,
|
|
14
|
+
* a press outside) clears `activated`.
|
|
15
15
|
*
|
|
16
|
-
* A new note has no text. While its root is empty (and unlocked) the
|
|
17
|
-
* root's own `contents`, labelled "Comment", and the thread list
|
|
18
|
-
* something in it; after that the form adds replies. Closing a
|
|
19
|
-
* has no replies removes it, so an abandoned note does not stay
|
|
16
|
+
* A new note, or a highlight, has no text. While its root is empty (and unlocked) the
|
|
17
|
+
* default form writes the root's own `contents`, labelled "Comment", and the thread list
|
|
18
|
+
* stays hidden until there is something in it; after that the form adds replies. Closing a
|
|
19
|
+
* note that is still empty and has no replies removes it, so an abandoned note does not stay
|
|
20
|
+
* on the page. Markup stays: the mark is the point.
|
|
20
21
|
*/
|
|
21
22
|
import { isAbandonedNote, threadOf, threadRoot } from '@inkbar/core'
|
|
23
|
+
import { threadDeleteLabel, threadPopoverLabel } from '@inkbar/core/dom'
|
|
22
24
|
import { Popover } from 'bits-ui'
|
|
23
25
|
import { untrack } from 'svelte'
|
|
24
26
|
|
|
@@ -29,18 +31,18 @@
|
|
|
29
31
|
import { keepOpenOnAnnotation, useFocusReturn } from './popover-focus.js'
|
|
30
32
|
import ThreadForm from './ThreadForm.svelte'
|
|
31
33
|
|
|
32
|
-
import type { Annotation,
|
|
34
|
+
import type { Annotation, ThreadRoot } from '@inkbar/core'
|
|
33
35
|
|
|
34
|
-
let { children }:
|
|
36
|
+
let { children }: InkThreadPopoverProps = $props()
|
|
35
37
|
|
|
36
|
-
const ctx = useInkContext('
|
|
38
|
+
const ctx = useInkContext('InkThreadPopover')
|
|
37
39
|
const store = useAnnotations()
|
|
38
40
|
|
|
39
|
-
const note = $derived.by<
|
|
41
|
+
const note = $derived.by<ThreadRoot | null>(() => {
|
|
40
42
|
const a = ctx.activated
|
|
41
43
|
? store.annotations.find((x) => x.id === ctx.activated)
|
|
42
44
|
: undefined
|
|
43
|
-
return a
|
|
45
|
+
return a ? threadRoot(store.annotations, a) : null
|
|
44
46
|
})
|
|
45
47
|
const anchorRef = useAnnotationElement(
|
|
46
48
|
() => ctx.rootEl,
|
|
@@ -51,11 +53,11 @@
|
|
|
51
53
|
const open = $derived(note !== null && anchor !== null)
|
|
52
54
|
/** The root, then every reply below it, depth first, each level in creation order. */
|
|
53
55
|
const thread = $derived<Annotation[]>(note ? threadOf(store.annotations, note) : [])
|
|
54
|
-
/** True while the default form writes the root
|
|
56
|
+
/** True while the default form writes the root's own text rather than a reply. */
|
|
55
57
|
const editingRoot = $derived(note !== null && !note.locked && !note.contents?.trim())
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
|
-
*
|
|
60
|
+
* Roots this popover wrote text or a reply for. In controlled mode the write is a proposal
|
|
59
61
|
* the host may not have applied yet, so the applied list still shows the note empty; these
|
|
60
62
|
* are never dropped as abandoned. Bookkeeping only: nothing renders from it.
|
|
61
63
|
*/
|
|
@@ -67,7 +69,13 @@
|
|
|
67
69
|
|
|
68
70
|
/** Removes an unlocked root note with no text and no replies. True when it did. */
|
|
69
71
|
function dropIfEmpty(id: string): boolean {
|
|
70
|
-
|
|
72
|
+
const a = store.annotations.find((x) => x.id === id)
|
|
73
|
+
if (
|
|
74
|
+
!a ||
|
|
75
|
+
dropped.has(id) ||
|
|
76
|
+
written.has(id) ||
|
|
77
|
+
!isAbandonedNote(a, store.annotations)
|
|
78
|
+
)
|
|
71
79
|
return false
|
|
72
80
|
dropped.add(id)
|
|
73
81
|
store.remove(id)
|
|
@@ -97,13 +105,9 @@
|
|
|
97
105
|
inReplyTo: root.id,
|
|
98
106
|
})
|
|
99
107
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
!root.locked &&
|
|
104
|
-
!root.contents?.trim() &&
|
|
105
|
-
thread.length === 1
|
|
106
|
-
)
|
|
108
|
+
/** A note that closing now would remove. */
|
|
109
|
+
function isEmptyRoot(root: ThreadRoot): boolean {
|
|
110
|
+
return !written.has(root.id) && isAbandonedNote(root, store.annotations)
|
|
107
111
|
}
|
|
108
112
|
function close(): void {
|
|
109
113
|
const root = note
|
|
@@ -118,12 +122,12 @@
|
|
|
118
122
|
if (contents.trim()) written.add(note.id)
|
|
119
123
|
store.update(note.id, { contents })
|
|
120
124
|
}
|
|
121
|
-
/** Locked
|
|
125
|
+
/** Locked roots cannot be removed: removing one does nothing and keeps the popover. */
|
|
122
126
|
function removeThread(): void {
|
|
123
127
|
const root = note
|
|
124
128
|
if (!root || root.locked) return
|
|
125
129
|
focusReturn.beforeRemove(anchor)
|
|
126
|
-
// Under a slow controlled host the
|
|
130
|
+
// Under a slow controlled host the root stays listed for a while; it is not asked for again.
|
|
127
131
|
dropped.add(root.id)
|
|
128
132
|
store.remove(root.id)
|
|
129
133
|
ctx.setActivated(null)
|
|
@@ -133,17 +137,24 @@
|
|
|
133
137
|
}
|
|
134
138
|
</script>
|
|
135
139
|
|
|
140
|
+
<svelte:document
|
|
141
|
+
onpointerdowncapture={(e) =>
|
|
142
|
+
open && focusReturn.onPressOutside(e, '[data-ink-thread-popover]', close)}
|
|
143
|
+
onpointerup={focusReturn.onPressEnd}
|
|
144
|
+
onpointercancel={focusReturn.onPressEnd}
|
|
145
|
+
/>
|
|
146
|
+
|
|
136
147
|
<Popover.Root bind:open={() => open, onOpenChange}>
|
|
137
148
|
<Popover.Portal>
|
|
138
149
|
{#if note && anchor}
|
|
139
150
|
<Popover.Content
|
|
140
|
-
class="ink-
|
|
141
|
-
data-ink-
|
|
151
|
+
class="ink-thread-popover"
|
|
152
|
+
data-ink-thread-popover=""
|
|
142
153
|
role="dialog"
|
|
143
154
|
side="right"
|
|
144
155
|
sideOffset={8}
|
|
145
156
|
collisionPadding={8}
|
|
146
|
-
aria-label=
|
|
157
|
+
aria-label={threadPopoverLabel(note)}
|
|
147
158
|
customAnchor={anchor}
|
|
148
159
|
onInteractOutside={keepOpenOnAnnotation}
|
|
149
160
|
onOpenAutoFocus={focusReturn.onOpenAutoFocus}
|
|
@@ -151,7 +162,7 @@
|
|
|
151
162
|
>
|
|
152
163
|
{#if children}
|
|
153
164
|
{@render children({
|
|
154
|
-
note,
|
|
165
|
+
root: note,
|
|
155
166
|
replies: thread.slice(1),
|
|
156
167
|
reply,
|
|
157
168
|
update: updateContents,
|
|
@@ -176,11 +187,12 @@
|
|
|
176
187
|
{/each}
|
|
177
188
|
</ol>
|
|
178
189
|
{/if}
|
|
179
|
-
<!-- Keyed by
|
|
190
|
+
<!-- Keyed by root: moving to another thread starts with an empty draft at once. -->
|
|
180
191
|
{#key note.id}
|
|
181
192
|
<ThreadForm
|
|
182
193
|
{editingRoot}
|
|
183
194
|
locked={!!note.locked}
|
|
195
|
+
deleteLabel={threadDeleteLabel(note)}
|
|
184
196
|
onSubmit={(text) => (editingRoot ? updateContents(text) : reply(text))}
|
|
185
197
|
onRemove={removeThread}
|
|
186
198
|
onClose={close}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ThreadPopoverSlotProps } from '@inkbar/core/dom';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
export interface InkThreadPopoverProps {
|
|
4
|
+
children?: Snippet<[ThreadPopoverSlotProps]> | undefined;
|
|
5
|
+
}
|
|
6
|
+
declare const InkThreadPopover: import("svelte").Component<InkThreadPopoverProps, {}, "">;
|
|
7
|
+
type InkThreadPopover = ReturnType<typeof InkThreadPopover>;
|
|
8
|
+
export default InkThreadPopover;
|
|
9
|
+
//# sourceMappingURL=InkThreadPopover.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkThreadPopover.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkThreadPopover.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,sBAAsB,CAAC,CAAC,GAAG,SAAS,CAAA;CACzD;AAgMH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -14,7 +14,17 @@
|
|
|
14
14
|
|
|
15
15
|
<script lang="ts">
|
|
16
16
|
import { TOOL_LABELS } from '@inkbar/core/dom'
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
Highlighter,
|
|
19
|
+
MessageSquare,
|
|
20
|
+
PenLine,
|
|
21
|
+
PenTool,
|
|
22
|
+
Signature,
|
|
23
|
+
Square,
|
|
24
|
+
SquareDashedMousePointer,
|
|
25
|
+
TextCursor,
|
|
26
|
+
Type,
|
|
27
|
+
} from '@inkbar/icons'
|
|
18
28
|
import { Toolbar } from 'bits-ui'
|
|
19
29
|
|
|
20
30
|
import { useTool } from '../hooks/useTool.svelte.js'
|
|
@@ -24,12 +34,16 @@
|
|
|
24
34
|
|
|
25
35
|
import type { IconNode } from '@inkbar/icons'
|
|
26
36
|
|
|
27
|
-
const ICONS: Readonly<Record<ToolId, IconNode
|
|
28
|
-
|
|
29
|
-
select:
|
|
37
|
+
const ICONS: Readonly<Partial<Record<ToolId, IconNode>>> = {
|
|
38
|
+
'select-text': TextCursor,
|
|
39
|
+
select: SquareDashedMousePointer,
|
|
30
40
|
highlight: Highlighter,
|
|
31
41
|
'redact-area': Square,
|
|
42
|
+
sketch: PenTool,
|
|
43
|
+
draw: PenLine,
|
|
32
44
|
note: MessageSquare,
|
|
45
|
+
freetext: Type,
|
|
46
|
+
signature: Signature,
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
let {
|
|
@@ -44,7 +58,9 @@
|
|
|
44
58
|
|
|
45
59
|
const current = useTool()
|
|
46
60
|
const active = $derived(current.tool === tool)
|
|
47
|
-
|
|
61
|
+
// A host tool has no built-in name or icon: `label` and `children` supply them.
|
|
62
|
+
const name = $derived(label ?? TOOL_LABELS[tool] ?? tool)
|
|
63
|
+
const icon = $derived(ICONS[tool])
|
|
48
64
|
</script>
|
|
49
65
|
|
|
50
66
|
<Toolbar.Button
|
|
@@ -62,7 +78,7 @@
|
|
|
62
78
|
if (!e.defaultPrevented) current.setTool(tool)
|
|
63
79
|
}}
|
|
64
80
|
>
|
|
65
|
-
{#if children}{@render children({ active, label: name })}{:else}<InkIcon
|
|
66
|
-
icon
|
|
81
|
+
{#if children}{@render children({ active, label: name })}{:else if icon}<InkIcon
|
|
82
|
+
{icon}
|
|
67
83
|
/>{/if}
|
|
68
84
|
</Toolbar.Button>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InkToolButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolButton.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACpE,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC;
|
|
1
|
+
{"version":3,"file":"InkToolButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolButton.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACpE,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC;AA4EH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
import InkPage from './InkPage.svelte'
|
|
44
44
|
|
|
45
45
|
import type { InkDocument, InkStatus } from '@inkbar/core'
|
|
46
|
-
import type { PageMount, ViewerEngine, ZoomValue } from '@inkbar/core/dom'
|
|
46
|
+
import type { PageMount, ToolId, ViewerEngine, ZoomValue } from '@inkbar/core/dom'
|
|
47
47
|
|
|
48
48
|
let {
|
|
49
49
|
mode = 'continuous',
|
|
@@ -125,6 +125,15 @@
|
|
|
125
125
|
gap,
|
|
126
126
|
buffer,
|
|
127
127
|
tool: ctx.tool,
|
|
128
|
+
...(ctx.hostTools ? { tools: ctx.hostTools } : {}),
|
|
129
|
+
// Read through, so the effect below can re-apply a new function without a rebuild.
|
|
130
|
+
decorateAnnotation: (a) => ctx.decorateAnnotation?.(a),
|
|
131
|
+
...(ctx.toolStyle ? { toolStyle: ctx.toolStyle } : {}),
|
|
132
|
+
toolHistory: { batch: (fn) => ctx.history.batch(fn) },
|
|
133
|
+
toolOptions: ctx.toolOptions,
|
|
134
|
+
fonts: [...ctx.fonts],
|
|
135
|
+
// Read through, so a new `signatures` store reaches the tool as well as the menus.
|
|
136
|
+
signatureLibrary: () => ctx.signatures,
|
|
128
137
|
customAnnotations: custom,
|
|
129
138
|
onActivate: (id) => ctx.setActivated(id),
|
|
130
139
|
onBuild: (vp, tc) => {
|
|
@@ -148,7 +157,15 @@
|
|
|
148
157
|
lastReportedPage = p
|
|
149
158
|
ctx.reportPage(p)
|
|
150
159
|
}
|
|
151
|
-
|
|
160
|
+
// A tool that switches itself (a one-shot tool once used) is reported as a pick.
|
|
161
|
+
const followTool = (id: ToolId): void => {
|
|
162
|
+
if (id !== ctx.tool) ctx.setTool(id)
|
|
163
|
+
}
|
|
164
|
+
stops = [
|
|
165
|
+
vp.zoom.subscribe(reportZoom),
|
|
166
|
+
vp.currentPage.subscribe(reportPage),
|
|
167
|
+
tc.active.subscribe(followTool),
|
|
168
|
+
]
|
|
152
169
|
// Whatever the build restored is reported once; nothing is when it matched.
|
|
153
170
|
reportZoom(vp.zoom.get())
|
|
154
171
|
reportPage(vp.currentPage.get())
|
|
@@ -222,6 +239,13 @@
|
|
|
222
239
|
})
|
|
223
240
|
})
|
|
224
241
|
|
|
242
|
+
// A new decorateAnnotation re-applies to the mounted annotation elements at once.
|
|
243
|
+
const decorate = $derived(ctx.decorateAnnotation)
|
|
244
|
+
$effect(() => {
|
|
245
|
+
void decorate
|
|
246
|
+
untrack(() => engine?.redecorate())
|
|
247
|
+
})
|
|
248
|
+
|
|
225
249
|
// The setters below run untracked: core relays out inside them and the engine's page
|
|
226
250
|
// callbacks read and write `pages`. Tracked, each effect would depend on the list it changes
|
|
227
251
|
// and run again, without end when the host holds a zoom the viewport clamps.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InkViewport.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkViewport.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,SAAS,CAAA;IACvD,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;CACrE;
|
|
1
|
+
{"version":3,"file":"InkViewport.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkViewport.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,SAAS,CAAA;IACvD,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;CACrE;AA+SH,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* The body of `InkSignatureDialog`, mounted while it is open: one draft per opening. Draw,
|
|
4
|
+
* Type and Image tabs, then Cancel and Save.
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
createSignatureDraft,
|
|
8
|
+
fontFamilyValue,
|
|
9
|
+
handleRadioKeys,
|
|
10
|
+
handleTabKeys,
|
|
11
|
+
SIGNATURE_LABELS,
|
|
12
|
+
SIGNATURE_TABS,
|
|
13
|
+
type SignatureTab,
|
|
14
|
+
} from '@inkbar/core/dom'
|
|
15
|
+
import { Dialog } from 'bits-ui'
|
|
16
|
+
import { untrack } from 'svelte'
|
|
17
|
+
|
|
18
|
+
import { useInkContext } from '../context.js'
|
|
19
|
+
import { useTool } from '../hooks/useTool.svelte.js'
|
|
20
|
+
import { useToolOptions } from '../hooks/useToolOptions.svelte.js'
|
|
21
|
+
import { useReadable } from '../use-readable.svelte.js'
|
|
22
|
+
|
|
23
|
+
import type { SignatureOptions, SignatureTemplate } from '@inkbar/core'
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
fonts,
|
|
27
|
+
onSaved,
|
|
28
|
+
onClose,
|
|
29
|
+
}: {
|
|
30
|
+
fonts: readonly string[] | undefined
|
|
31
|
+
onSaved: (t: SignatureTemplate) => void
|
|
32
|
+
onClose: () => void
|
|
33
|
+
} = $props()
|
|
34
|
+
|
|
35
|
+
const ctx = useInkContext('InkSignatureDialog')
|
|
36
|
+
const current = useTool()
|
|
37
|
+
const signature = useToolOptions<SignatureOptions>('signature')
|
|
38
|
+
// One draft per opening: the fonts are read once.
|
|
39
|
+
const draft = untrack(() =>
|
|
40
|
+
createSignatureDraft({ fonts: fonts ?? ctx.signatureFonts })
|
|
41
|
+
)
|
|
42
|
+
const draftState = useReadable(() => draft.state, draft.state.get())
|
|
43
|
+
const s = $derived(draftState.current)
|
|
44
|
+
const uid = $props.id()
|
|
45
|
+
let padHost = $state<HTMLDivElement | null>(null)
|
|
46
|
+
|
|
47
|
+
$effect(() => {
|
|
48
|
+
if (!padHost) return
|
|
49
|
+
return draft.attachPad(padHost)
|
|
50
|
+
})
|
|
51
|
+
$effect(() => () => draft.destroy())
|
|
52
|
+
|
|
53
|
+
function save(): void {
|
|
54
|
+
draft.save(ctx.signatures).then(
|
|
55
|
+
(t) => {
|
|
56
|
+
if (!t) return
|
|
57
|
+
signature.setOptions({ templateId: t.id })
|
|
58
|
+
current.setTool('signature')
|
|
59
|
+
onSaved(t)
|
|
60
|
+
onClose()
|
|
61
|
+
},
|
|
62
|
+
() => {
|
|
63
|
+
// The host's store refused it: the dialog stays open to try again.
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const tabId = (tab: SignatureTab) => `${uid}-tab-${tab}`
|
|
69
|
+
const panelId = (tab: SignatureTab) => `${uid}-panel-${tab}`
|
|
70
|
+
const shown = $derived(s.name.trim() || SIGNATURE_LABELS.namePlaceholder)
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<div
|
|
74
|
+
role="tablist"
|
|
75
|
+
tabindex="-1"
|
|
76
|
+
aria-label={SIGNATURE_LABELS.tabs}
|
|
77
|
+
class="ink-tabs"
|
|
78
|
+
onkeydown={(e) => handleTabKeys(e, e.currentTarget)}
|
|
79
|
+
>
|
|
80
|
+
{#each SIGNATURE_TABS as tab (tab)}
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
role="tab"
|
|
84
|
+
id={tabId(tab)}
|
|
85
|
+
aria-controls={panelId(tab)}
|
|
86
|
+
aria-selected={s.tab === tab}
|
|
87
|
+
tabindex={s.tab === tab ? 0 : -1}
|
|
88
|
+
class="ink-tab"
|
|
89
|
+
data-key={tab}
|
|
90
|
+
data-state={s.tab === tab ? 'active' : 'inactive'}
|
|
91
|
+
onclick={() => draft.setTab(tab)}
|
|
92
|
+
>
|
|
93
|
+
{SIGNATURE_LABELS[tab]}
|
|
94
|
+
</button>
|
|
95
|
+
{/each}
|
|
96
|
+
</div>
|
|
97
|
+
<div
|
|
98
|
+
role="tabpanel"
|
|
99
|
+
id={panelId('draw')}
|
|
100
|
+
aria-labelledby={tabId('draw')}
|
|
101
|
+
class="ink-tab-panel"
|
|
102
|
+
data-key="draw"
|
|
103
|
+
hidden={s.tab !== 'draw'}
|
|
104
|
+
>
|
|
105
|
+
<div
|
|
106
|
+
bind:this={padHost}
|
|
107
|
+
class="ink-signature-pad-host"
|
|
108
|
+
data-ink-signature-pad-host=""
|
|
109
|
+
role="img"
|
|
110
|
+
aria-label={SIGNATURE_LABELS.pad}
|
|
111
|
+
></div>
|
|
112
|
+
<div class="ink-dialog-row">
|
|
113
|
+
<button
|
|
114
|
+
type="button"
|
|
115
|
+
class="ink-button"
|
|
116
|
+
data-ink-signature-clear=""
|
|
117
|
+
disabled={s.padEmpty}
|
|
118
|
+
onclick={() => draft.clearPad()}
|
|
119
|
+
>
|
|
120
|
+
{SIGNATURE_LABELS.clear}
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
<div
|
|
125
|
+
role="tabpanel"
|
|
126
|
+
id={panelId('type')}
|
|
127
|
+
aria-labelledby={tabId('type')}
|
|
128
|
+
class="ink-tab-panel"
|
|
129
|
+
data-key="type"
|
|
130
|
+
hidden={s.tab !== 'type'}
|
|
131
|
+
>
|
|
132
|
+
<label class="ink-field">
|
|
133
|
+
<span>{SIGNATURE_LABELS.name}</span>
|
|
134
|
+
<input
|
|
135
|
+
type="text"
|
|
136
|
+
class="ink-input"
|
|
137
|
+
data-ink-signature-name=""
|
|
138
|
+
autocomplete="name"
|
|
139
|
+
placeholder={SIGNATURE_LABELS.namePlaceholder}
|
|
140
|
+
value={s.name}
|
|
141
|
+
oninput={(e) => draft.setName(e.currentTarget.value)}
|
|
142
|
+
/>
|
|
143
|
+
</label>
|
|
144
|
+
<div
|
|
145
|
+
role="radiogroup"
|
|
146
|
+
tabindex="-1"
|
|
147
|
+
aria-label={SIGNATURE_LABELS.fonts}
|
|
148
|
+
class="ink-signature-fonts"
|
|
149
|
+
onkeydown={(e) => handleRadioKeys(e, e.currentTarget)}
|
|
150
|
+
>
|
|
151
|
+
{#each draft.fonts as font (font)}
|
|
152
|
+
<button
|
|
153
|
+
type="button"
|
|
154
|
+
role="radio"
|
|
155
|
+
aria-checked={s.font === font}
|
|
156
|
+
aria-label={font}
|
|
157
|
+
tabindex={s.font === font ? 0 : -1}
|
|
158
|
+
class="ink-signature-font"
|
|
159
|
+
data-key={font}
|
|
160
|
+
data-state={s.font === font ? 'checked' : 'unchecked'}
|
|
161
|
+
onclick={() => draft.setFont(font)}
|
|
162
|
+
>
|
|
163
|
+
<!-- The font is the option itself, so it is set here, not left to the theme. -->
|
|
164
|
+
<span class="ink-signature-font-sample" style:font-family={fontFamilyValue(font)}
|
|
165
|
+
>{shown}</span
|
|
166
|
+
>
|
|
167
|
+
</button>
|
|
168
|
+
{/each}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
<div
|
|
172
|
+
role="tabpanel"
|
|
173
|
+
id={panelId('image')}
|
|
174
|
+
aria-labelledby={tabId('image')}
|
|
175
|
+
class="ink-tab-panel"
|
|
176
|
+
data-key="image"
|
|
177
|
+
hidden={s.tab !== 'image'}
|
|
178
|
+
>
|
|
179
|
+
<label class="ink-field">
|
|
180
|
+
<span>{SIGNATURE_LABELS.file}</span>
|
|
181
|
+
<input
|
|
182
|
+
type="file"
|
|
183
|
+
accept="image/png,image/jpeg"
|
|
184
|
+
class="ink-input"
|
|
185
|
+
data-ink-signature-file=""
|
|
186
|
+
onchange={(e) => void draft.chooseFile(e.currentTarget.files?.[0])}
|
|
187
|
+
/>
|
|
188
|
+
</label>
|
|
189
|
+
{#if s.image?.kind === 'image'}
|
|
190
|
+
<img class="ink-signature-image" src={s.image.imagedata} alt="" />
|
|
191
|
+
{/if}
|
|
192
|
+
{#if s.imageError}
|
|
193
|
+
<p class="ink-field-error" role="alert">{s.imageError}</p>
|
|
194
|
+
{/if}
|
|
195
|
+
</div>
|
|
196
|
+
{#if s.saveError}
|
|
197
|
+
<p class="ink-field-error" role="alert" data-ink-signature-save-error="">
|
|
198
|
+
{s.saveError}
|
|
199
|
+
</p>
|
|
200
|
+
{/if}
|
|
201
|
+
<div class="ink-dialog-actions">
|
|
202
|
+
<Dialog.Close>
|
|
203
|
+
{#snippet child({ props })}
|
|
204
|
+
<button {...props} type="button" class="ink-button" data-ink-signature-cancel="">
|
|
205
|
+
{SIGNATURE_LABELS.cancel}
|
|
206
|
+
</button>
|
|
207
|
+
{/snippet}
|
|
208
|
+
</Dialog.Close>
|
|
209
|
+
<button
|
|
210
|
+
type="button"
|
|
211
|
+
class="ink-button"
|
|
212
|
+
data-variant="primary"
|
|
213
|
+
data-ink-signature-save=""
|
|
214
|
+
disabled={!s.canSave}
|
|
215
|
+
onclick={save}
|
|
216
|
+
>
|
|
217
|
+
{SIGNATURE_LABELS.save}
|
|
218
|
+
</button>
|
|
219
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SignatureTemplate } from '@inkbar/core';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
fonts: readonly string[] | undefined;
|
|
4
|
+
onSaved: (t: SignatureTemplate) => void;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
};
|
|
7
|
+
declare const SignatureForm: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type SignatureForm = ReturnType<typeof SignatureForm>;
|
|
9
|
+
export default SignatureForm;
|
|
10
|
+
//# sourceMappingURL=SignatureForm.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignatureForm.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/SignatureForm.svelte.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEvE,KAAK,gBAAgB,GAAI;IACtB,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACpC,OAAO,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACvC,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAC;AAuHJ,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
/** The default form: the root
|
|
2
|
+
/** The default form: the root's own text while it is empty, replies after that. */
|
|
3
3
|
let {
|
|
4
4
|
editingRoot,
|
|
5
5
|
locked,
|
|
6
|
+
deleteLabel,
|
|
6
7
|
onSubmit,
|
|
7
8
|
onRemove,
|
|
8
9
|
onClose,
|
|
9
10
|
}: {
|
|
10
11
|
editingRoot: boolean
|
|
11
12
|
locked: boolean
|
|
13
|
+
deleteLabel: string
|
|
12
14
|
onSubmit: (text: string) => void
|
|
13
15
|
onRemove: () => void
|
|
14
16
|
onClose: () => void
|
|
@@ -35,7 +37,7 @@
|
|
|
35
37
|
>{editingRoot ? 'Comment' : 'Reply'}</button
|
|
36
38
|
>
|
|
37
39
|
{#if !locked}
|
|
38
|
-
<button type="button" onclick={onRemove}>
|
|
40
|
+
<button type="button" onclick={onRemove}>{deleteLabel}</button>
|
|
39
41
|
{/if}
|
|
40
42
|
<button type="button" onclick={onClose}>Close</button>
|
|
41
43
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadForm.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/ThreadForm.svelte.ts"],"names":[],"mappings":"AAGC,KAAK,gBAAgB,GAAI;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"ThreadForm.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/ThreadForm.svelte.ts"],"names":[],"mappings":"AAGC,KAAK,gBAAgB,GAAI;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAC;AAmCJ,QAAA,MAAM,UAAU,sDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -19,4 +19,12 @@ export type BitsProps<T> = {
|
|
|
19
19
|
* `loop`) receives the explicit `null` or `undefined`. This is the one place the cast lives.
|
|
20
20
|
*/
|
|
21
21
|
export declare function asBitsProps<T extends object>(rest: T): BitsProps<T>;
|
|
22
|
+
type Handler<E> = ((e: E) => unknown) | null | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* One handler for an element both the host and the component listen on: the host's first,
|
|
25
|
+
* then each of `inner` in turn unless the host (or an earlier one) prevented the default.
|
|
26
|
+
* `inner` takes Bits UI's own handler from its `props`, typed `unknown` there.
|
|
27
|
+
*/
|
|
28
|
+
export declare function composeHandlers<E extends Event>(host: Handler<E>, ...inner: unknown[]): (e: E) => void;
|
|
29
|
+
export {};
|
|
22
30
|
//# sourceMappingURL=child-snippet.d.ts.map
|
|
@@ -1 +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"}
|
|
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;AAED,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;AAExD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,KAAK,EAC7C,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAChB,GAAG,KAAK,EAAE,OAAO,EAAE,GAClB,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAQhB"}
|
|
@@ -12,3 +12,18 @@
|
|
|
12
12
|
export function asBitsProps(rest) {
|
|
13
13
|
return rest;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* One handler for an element both the host and the component listen on: the host's first,
|
|
17
|
+
* then each of `inner` in turn unless the host (or an earlier one) prevented the default.
|
|
18
|
+
* `inner` takes Bits UI's own handler from its `props`, typed `unknown` there.
|
|
19
|
+
*/
|
|
20
|
+
export function composeHandlers(host, ...inner) {
|
|
21
|
+
return (e) => {
|
|
22
|
+
host?.(e);
|
|
23
|
+
for (const fn of inner) {
|
|
24
|
+
if (e.defaultPrevented)
|
|
25
|
+
return;
|
|
26
|
+
fn?.(e);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -5,6 +5,18 @@ export interface PopoverFocusReturn {
|
|
|
5
5
|
beforeRemove: FocusReturn['beforeRemove'];
|
|
6
6
|
/** Bits UI's `onOpenAutoFocus` handler. Leaves the default alone: focus moves into the popover. */
|
|
7
7
|
onOpenAutoFocus: (event: Event) => void;
|
|
8
|
+
/**
|
|
9
|
+
* A document-level pointer press, captured. `inside` selects the popover's content. A press on
|
|
10
|
+
* the viewer's own scroll element, outside the popover, that will not activate an annotation
|
|
11
|
+
* closes it now, through `close`, before the press moves focus. Bits UI closes it a beat
|
|
12
|
+
* later, and its focus trap pulls focus back into the popover in between, which ends a text
|
|
13
|
+
* selection the press has just begun. Nothing is focused when it closes this way: the press
|
|
14
|
+
* places focus. Every other press (a toolbar, a host's menu portaled out of the popover) is
|
|
15
|
+
* left to Bits UI, whose layer stack knows what belongs to the popover.
|
|
16
|
+
*/
|
|
17
|
+
onPressOutside: (event: PointerEvent, inside: string, close: () => void) => void;
|
|
18
|
+
/** A pointer release or cancel, captured. */
|
|
19
|
+
onPressEnd: () => void;
|
|
8
20
|
/** Bits UI's `onCloseAutoFocus` handler. Prevents the default; the planner places focus. */
|
|
9
21
|
onCloseAutoFocus: (event: Event, anchor: HTMLElement | null) => void;
|
|
10
22
|
}
|
|
@@ -21,8 +33,10 @@ export declare function keepOpenOnAnnotation(event: PointerEvent): void;
|
|
|
21
33
|
* close reaches the planner one microtask late, and only when no open was reported after it.
|
|
22
34
|
*
|
|
23
35
|
* 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`).
|
|
25
|
-
*
|
|
36
|
+
* call (observed in bits-ui 2.19.5, `FocusScope.mount`). The unit case 'a close followed by an
|
|
37
|
+
* open in the same tick moves no focus and uses up the removal plan' pins the planner's side, and
|
|
38
|
+
* the browser case 'a popover opened without passing focus through the annotation takes focus'
|
|
39
|
+
* checks that an open moves focus straight into the popover.
|
|
26
40
|
*
|
|
27
41
|
* A close that is superseded this way moves no focus, but it still uses up whatever
|
|
28
42
|
* `beforeRemove` planned for it, so the plan cannot leak into a later close. A close that is
|