@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,6 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
export type InkPageCountProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children'>;
|
|
3
|
+
declare const InkPageCount: import("svelte").Component<InkPageCountProps, {}, "">;
|
|
4
|
+
type InkPageCount = ReturnType<typeof InkPageCount>;
|
|
5
|
+
export default InkPageCount;
|
|
6
|
+
//# sourceMappingURL=InkPageCount.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkPageCount.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkPageCount.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,UAAU,CAAC,CAClF;AAkBD,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements'
|
|
3
|
+
|
|
4
|
+
export type InkPageInputProps = Omit<
|
|
5
|
+
HTMLInputAttributes,
|
|
6
|
+
'value' | 'onchange' | 'oninput' | 'type' | 'min' | 'max' | 'children'
|
|
7
|
+
>
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import { usePage } from '../hooks/usePage.svelte.js'
|
|
12
|
+
|
|
13
|
+
let { class: className, onkeydown, ...rest }: InkPageInputProps = $props()
|
|
14
|
+
|
|
15
|
+
const page = usePage()
|
|
16
|
+
// Writable derived: follows the current page, and holds what the user types until then.
|
|
17
|
+
let draft = $derived(String(page.current + 1))
|
|
18
|
+
|
|
19
|
+
/** 1-based in the input, 0-based in the viewport. Out of range resets to the current page. */
|
|
20
|
+
function commit(value: string): void {
|
|
21
|
+
const n = Number(value)
|
|
22
|
+
if (Number.isInteger(n) && n >= 1 && n <= page.count) {
|
|
23
|
+
if (n - 1 !== page.current) page.goTo(n - 1)
|
|
24
|
+
} else draft = String(page.current + 1)
|
|
25
|
+
}
|
|
26
|
+
function onKeyDown(e: KeyboardEvent & { currentTarget: HTMLInputElement }): void {
|
|
27
|
+
onkeydown?.(e)
|
|
28
|
+
if (e.defaultPrevented || e.key !== 'Enter') return
|
|
29
|
+
e.preventDefault()
|
|
30
|
+
commit(draft)
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<!-- `onchange` is the native change event: it fires on blur after typing, and on each step of
|
|
35
|
+
the spinner or the arrow keys. `oninput` keeps the draft for Enter. -->
|
|
36
|
+
<input
|
|
37
|
+
class={['ink-page-input', className]}
|
|
38
|
+
data-ink-page-input=""
|
|
39
|
+
type="number"
|
|
40
|
+
inputmode="numeric"
|
|
41
|
+
aria-label="Page"
|
|
42
|
+
min={1}
|
|
43
|
+
max={page.count}
|
|
44
|
+
value={draft}
|
|
45
|
+
oninput={(e) => (draft = e.currentTarget.value)}
|
|
46
|
+
onchange={(e) => commit(e.currentTarget.value)}
|
|
47
|
+
onkeydown={onKeyDown}
|
|
48
|
+
{...rest}
|
|
49
|
+
/>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
|
+
export type InkPageInputProps = Omit<HTMLInputAttributes, 'value' | 'onchange' | 'oninput' | 'type' | 'min' | 'max' | 'children'>;
|
|
3
|
+
declare const InkPageInput: import("svelte").Component<InkPageInputProps, {}, "">;
|
|
4
|
+
type InkPageInput = ReturnType<typeof InkPageInput>;
|
|
5
|
+
export default InkPageInput;
|
|
6
|
+
//# sourceMappingURL=InkPageInput.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkPageInput.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkPageInput.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,mBAAmB,EACnB,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,UAAU,CACvE,CACF;AAmCD,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { ChildSnippet } from './child-snippet.js'
|
|
3
|
+
import type {
|
|
4
|
+
Annotation,
|
|
5
|
+
AnnotationChange,
|
|
6
|
+
AnnotationDefaults,
|
|
7
|
+
DocumentSource,
|
|
8
|
+
InkDocument,
|
|
9
|
+
InkError,
|
|
10
|
+
InkStatus,
|
|
11
|
+
PdfAssets,
|
|
12
|
+
} from '@inkbar/core'
|
|
13
|
+
import type { ToolId, ZoomValue } from '@inkbar/core/dom'
|
|
14
|
+
import type { Snippet } from 'svelte'
|
|
15
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
16
|
+
|
|
17
|
+
export interface InkRootProps extends Omit<
|
|
18
|
+
HTMLAttributes<HTMLDivElement>,
|
|
19
|
+
'children' | 'onchange' | 'onerror'
|
|
20
|
+
> {
|
|
21
|
+
src: DocumentSource | null
|
|
22
|
+
assets: PdfAssets
|
|
23
|
+
password?: string | undefined
|
|
24
|
+
/** Colour defaults. Without it, the theme's CSS variables on the root element apply. */
|
|
25
|
+
defaults?: AnnotationDefaults | undefined
|
|
26
|
+
/**
|
|
27
|
+
* Pass (with `bind:`) to put annotations under the host's control. The mode is fixed when a
|
|
28
|
+
* document opens: passing or dropping this prop later takes effect on the next open. A host
|
|
29
|
+
* that decides on each proposal uses a function binding.
|
|
30
|
+
*/
|
|
31
|
+
annotations?: Annotation[] | undefined
|
|
32
|
+
onAnnotationsChange?: ((next: Annotation[]) => void) | undefined
|
|
33
|
+
onChange?: ((change: AnnotationChange) => void) | undefined
|
|
34
|
+
tool?: ToolId | undefined
|
|
35
|
+
onToolChange?: ((tool: ToolId) => void) | undefined
|
|
36
|
+
page?: number | undefined
|
|
37
|
+
onPageChange?: ((page: number) => void) | undefined
|
|
38
|
+
zoom?: ZoomValue | undefined
|
|
39
|
+
onZoomChange?: ((zoom: ZoomValue) => void) | undefined
|
|
40
|
+
/** Undo history. Defaults to on for uncontrolled, off for controlled. */
|
|
41
|
+
history?: boolean | undefined
|
|
42
|
+
onReady?: ((doc: InkDocument) => void) | undefined
|
|
43
|
+
onError?: ((error: InkError) => void) | undefined
|
|
44
|
+
onActivate?: ((id: string) => void) | undefined
|
|
45
|
+
/** The root element, for hosts that bind it. */
|
|
46
|
+
ref?: HTMLElement | null
|
|
47
|
+
children?: Snippet<[{ status: InkStatus; document: InkDocument | null }]>
|
|
48
|
+
/**
|
|
49
|
+
* Render your own root element: spread `props` on it and put your content inside. The
|
|
50
|
+
* context is still provided, so Inkbar components work inside it.
|
|
51
|
+
*/
|
|
52
|
+
child?: ChildSnippet
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<script lang="ts">
|
|
57
|
+
import {
|
|
58
|
+
createSnapshotHistory,
|
|
59
|
+
InkError as InkErrorClass,
|
|
60
|
+
openDocument,
|
|
61
|
+
} from '@inkbar/core'
|
|
62
|
+
import { cssVariableDefaults } from '@inkbar/core/dom'
|
|
63
|
+
import { DEV } from 'esm-env'
|
|
64
|
+
import { untrack } from 'svelte'
|
|
65
|
+
import { createAttachmentKey } from 'svelte/attachments'
|
|
66
|
+
|
|
67
|
+
import { setInkContext, type InkContext, type InkHistory } from '../context.js'
|
|
68
|
+
import { useReadable } from '../use-readable.svelte.js'
|
|
69
|
+
|
|
70
|
+
import type { ToolController, Viewport } from '@inkbar/core/dom'
|
|
71
|
+
|
|
72
|
+
let {
|
|
73
|
+
src,
|
|
74
|
+
assets,
|
|
75
|
+
password,
|
|
76
|
+
defaults,
|
|
77
|
+
annotations = $bindable(),
|
|
78
|
+
onAnnotationsChange,
|
|
79
|
+
onChange,
|
|
80
|
+
tool = $bindable(),
|
|
81
|
+
onToolChange,
|
|
82
|
+
page = $bindable(),
|
|
83
|
+
onPageChange,
|
|
84
|
+
zoom = $bindable(),
|
|
85
|
+
onZoomChange,
|
|
86
|
+
history: historyProp,
|
|
87
|
+
onReady,
|
|
88
|
+
onError,
|
|
89
|
+
onActivate,
|
|
90
|
+
ref = $bindable(null),
|
|
91
|
+
children,
|
|
92
|
+
child,
|
|
93
|
+
...rest
|
|
94
|
+
}: InkRootProps = $props()
|
|
95
|
+
|
|
96
|
+
type TestHookElement = HTMLElement & { __inkDocument?: InkDocument | undefined }
|
|
97
|
+
|
|
98
|
+
const IDLE: InkStatus = { state: 'idle' }
|
|
99
|
+
const NO_ANNOTATIONS: readonly Annotation[] = []
|
|
100
|
+
const NO_IDS: readonly string[] = []
|
|
101
|
+
|
|
102
|
+
/** Signed URLs change; the key does not. Bytes and Blobs are their own identity. */
|
|
103
|
+
function identity(source: DocumentSource | null): unknown {
|
|
104
|
+
if (source === null) return null
|
|
105
|
+
if (typeof source === 'string') return source
|
|
106
|
+
if ('key' in source) return source.key
|
|
107
|
+
return source
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function setTestHook(el: HTMLElement | null, doc: InkDocument | undefined): void {
|
|
111
|
+
if (el && DEV) (el as TestHookElement).__inkDocument = doc
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let document = $state.raw<InkDocument | null>(null)
|
|
115
|
+
/** Overrides the document's status while opening, and when opening itself failed. */
|
|
116
|
+
let override = $state.raw<InkStatus | null>(null)
|
|
117
|
+
let activated = $state<string | null>(null)
|
|
118
|
+
/** The same value outside the reactive graph, for the popovers' close handlers. */
|
|
119
|
+
let activatedNow: string | null = null
|
|
120
|
+
let viewport = $state.raw<Viewport | null>(null)
|
|
121
|
+
let tools = $state.raw<ToolController | null>(null)
|
|
122
|
+
|
|
123
|
+
const documentStatus = useReadable<InkStatus>(() => document?.status, IDLE)
|
|
124
|
+
const status = $derived(override ?? documentStatus.current)
|
|
125
|
+
const annotationList = useReadable<readonly Annotation[]>(
|
|
126
|
+
() => document?.annotations.list,
|
|
127
|
+
NO_ANNOTATIONS
|
|
128
|
+
)
|
|
129
|
+
const selection = useReadable<readonly string[]>(
|
|
130
|
+
() => document?.annotations.selection,
|
|
131
|
+
NO_IDS
|
|
132
|
+
)
|
|
133
|
+
const currentTool = $derived<ToolId>(tool ?? 'view')
|
|
134
|
+
|
|
135
|
+
// What the next open will do follows the prop; the open document's mode is fixed.
|
|
136
|
+
const controlledProp = $derived(annotations !== undefined)
|
|
137
|
+
const controlled = $derived(
|
|
138
|
+
document ? document.annotations.mode === 'controlled' : controlledProp
|
|
139
|
+
)
|
|
140
|
+
const historyEnabled = $derived(controlled && historyProp === true)
|
|
141
|
+
|
|
142
|
+
const snapshotHistory = createSnapshotHistory({
|
|
143
|
+
apply: (list) => document?.annotations.replaceAll(list, 'history'),
|
|
144
|
+
enabled: () => historyEnabled,
|
|
145
|
+
})
|
|
146
|
+
$effect(() => {
|
|
147
|
+
snapshotHistory.observe(annotations)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
// Open for a new source identity or password. The effect reads them through deriveds, which
|
|
151
|
+
// notify only when the value changes: a host that spreads its props, or passes a new URL under
|
|
152
|
+
// the same key, invalidates `src` without changing the identity. Everything else is read under
|
|
153
|
+
// `untrack`, so a changed `assets`, `defaults` or callback never reopens.
|
|
154
|
+
const sourceIdentity = $derived(identity(src))
|
|
155
|
+
const sourcePassword = $derived(password)
|
|
156
|
+
$effect(() => {
|
|
157
|
+
void sourceIdentity
|
|
158
|
+
const pw = sourcePassword
|
|
159
|
+
let cancelled = false
|
|
160
|
+
let opened: InkDocument | null = null
|
|
161
|
+
untrack(() => snapshotHistory.clear())
|
|
162
|
+
const source = untrack(() => src)
|
|
163
|
+
if (source === null) {
|
|
164
|
+
override = null
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
override = { state: 'loading' }
|
|
168
|
+
void (async () => {
|
|
169
|
+
try {
|
|
170
|
+
const doc = await openDocument(
|
|
171
|
+
source,
|
|
172
|
+
untrack(() => ({
|
|
173
|
+
assets,
|
|
174
|
+
password: pw,
|
|
175
|
+
defaults: defaults ?? (ref ? cssVariableDefaults(ref) : undefined),
|
|
176
|
+
// Controlled hosts get the snapshot history; core's own stack stays off.
|
|
177
|
+
history: controlledProp ? false : (historyProp ?? true),
|
|
178
|
+
annotations: controlledProp
|
|
179
|
+
? {
|
|
180
|
+
mode: 'controlled' as const,
|
|
181
|
+
initial: annotations ?? [],
|
|
182
|
+
onChange: (next: Annotation[], change: AnnotationChange) => {
|
|
183
|
+
// A proposal made in the tick this root unmounts or reopens never reaches
|
|
184
|
+
// the host. Writing the bindable hands it to a bound host now; the effect
|
|
185
|
+
// below pushes the applied list to the store in Svelte's next effect flush.
|
|
186
|
+
if (cancelled) return
|
|
187
|
+
annotations = next
|
|
188
|
+
onAnnotationsChange?.(next)
|
|
189
|
+
onChange?.(change)
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
: { mode: 'uncontrolled' as const },
|
|
193
|
+
}))
|
|
194
|
+
)
|
|
195
|
+
if (cancelled) {
|
|
196
|
+
doc.destroy()
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
opened = doc
|
|
200
|
+
// The prop may have changed while the file loaded, when there was no store to apply it to.
|
|
201
|
+
const current = untrack(() => annotations)
|
|
202
|
+
if (doc.annotations.mode === 'controlled' && current !== undefined)
|
|
203
|
+
doc.annotations.setControlledValue(current)
|
|
204
|
+
document = doc
|
|
205
|
+
override = null
|
|
206
|
+
setTestHook(ref, doc)
|
|
207
|
+
const s = doc.status.get()
|
|
208
|
+
if (s.state === 'ready') onReady?.(doc)
|
|
209
|
+
else if (s.state === 'error') onError?.(s.error)
|
|
210
|
+
} catch (cause) {
|
|
211
|
+
if (cancelled) return
|
|
212
|
+
const error =
|
|
213
|
+
cause instanceof InkErrorClass
|
|
214
|
+
? cause
|
|
215
|
+
: new InkErrorClass(
|
|
216
|
+
'InvalidPdf',
|
|
217
|
+
cause instanceof Error ? cause.message : String(cause),
|
|
218
|
+
{},
|
|
219
|
+
cause
|
|
220
|
+
)
|
|
221
|
+
override = { state: 'error', error }
|
|
222
|
+
onError?.(error)
|
|
223
|
+
}
|
|
224
|
+
})()
|
|
225
|
+
return () => {
|
|
226
|
+
cancelled = true
|
|
227
|
+
opened?.destroy()
|
|
228
|
+
document = null
|
|
229
|
+
setTestHook(ref, undefined)
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
// The host's list reaches the store; the echo of a proposal it applied is the same list.
|
|
234
|
+
$effect(() => {
|
|
235
|
+
const list = annotations
|
|
236
|
+
const doc = document
|
|
237
|
+
if (list && doc?.annotations.mode === 'controlled')
|
|
238
|
+
untrack(() => doc.annotations.setControlledValue(list))
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
function setTool(id: ToolId): void {
|
|
242
|
+
tool = id
|
|
243
|
+
onToolChange?.(id)
|
|
244
|
+
}
|
|
245
|
+
function setActivated(id: string | null): void {
|
|
246
|
+
activated = id
|
|
247
|
+
activatedNow = id
|
|
248
|
+
if (id) onActivate?.(id)
|
|
249
|
+
}
|
|
250
|
+
function reportZoom(z: ZoomValue): void {
|
|
251
|
+
zoom = z
|
|
252
|
+
onZoomChange?.(z)
|
|
253
|
+
}
|
|
254
|
+
function reportPage(p: number): void {
|
|
255
|
+
page = p
|
|
256
|
+
onPageChange?.(p)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const snapCanUndo = useReadable(() => snapshotHistory.canUndo, false)
|
|
260
|
+
const snapCanRedo = useReadable(() => snapshotHistory.canRedo, false)
|
|
261
|
+
const coreCanUndo = useReadable(() => document?.history.canUndo, false)
|
|
262
|
+
const coreCanRedo = useReadable(() => document?.history.canRedo, false)
|
|
263
|
+
const inkHistory: InkHistory = {
|
|
264
|
+
get canUndo() {
|
|
265
|
+
return controlled ? historyEnabled && snapCanUndo.current : coreCanUndo.current
|
|
266
|
+
},
|
|
267
|
+
get canRedo() {
|
|
268
|
+
return controlled ? historyEnabled && snapCanRedo.current : coreCanRedo.current
|
|
269
|
+
},
|
|
270
|
+
undo: () => (controlled ? snapshotHistory.undo() : document?.history.undo()),
|
|
271
|
+
redo: () => (controlled ? snapshotHistory.redo() : document?.history.redo()),
|
|
272
|
+
clear: () => (controlled ? snapshotHistory.clear() : document?.history.clear()),
|
|
273
|
+
batch: (fn) => {
|
|
274
|
+
if (controlled) snapshotHistory.batch(fn)
|
|
275
|
+
else if (document) document.history.batch(fn)
|
|
276
|
+
else fn()
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const context: InkContext = {
|
|
281
|
+
get assets() {
|
|
282
|
+
return assets
|
|
283
|
+
},
|
|
284
|
+
get document() {
|
|
285
|
+
return document
|
|
286
|
+
},
|
|
287
|
+
get status() {
|
|
288
|
+
return status
|
|
289
|
+
},
|
|
290
|
+
get annotations() {
|
|
291
|
+
return annotationList.current
|
|
292
|
+
},
|
|
293
|
+
get selection() {
|
|
294
|
+
return selection.current
|
|
295
|
+
},
|
|
296
|
+
get viewport() {
|
|
297
|
+
return viewport
|
|
298
|
+
},
|
|
299
|
+
get tools() {
|
|
300
|
+
return tools
|
|
301
|
+
},
|
|
302
|
+
get tool() {
|
|
303
|
+
return currentTool
|
|
304
|
+
},
|
|
305
|
+
setTool,
|
|
306
|
+
get zoomModel() {
|
|
307
|
+
return zoom
|
|
308
|
+
},
|
|
309
|
+
get pageModel() {
|
|
310
|
+
return page
|
|
311
|
+
},
|
|
312
|
+
reportZoom,
|
|
313
|
+
reportPage,
|
|
314
|
+
get activated() {
|
|
315
|
+
return activated
|
|
316
|
+
},
|
|
317
|
+
setActivated,
|
|
318
|
+
history: inkHistory,
|
|
319
|
+
get rootEl() {
|
|
320
|
+
return ref
|
|
321
|
+
},
|
|
322
|
+
setViewport(vp, tc) {
|
|
323
|
+
viewport = vp
|
|
324
|
+
tools = tc
|
|
325
|
+
},
|
|
326
|
+
}
|
|
327
|
+
setInkContext(context, () => activatedNow)
|
|
328
|
+
|
|
329
|
+
// Instance exports, for hosts that `bind:this`. Svelte exposes constants and functions, not
|
|
330
|
+
// getters, so the document and status are read through functions.
|
|
331
|
+
export const history = inkHistory
|
|
332
|
+
export function getDocument(): InkDocument | null {
|
|
333
|
+
return document
|
|
334
|
+
}
|
|
335
|
+
export function getStatus(): InkStatus {
|
|
336
|
+
return status
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** For the `child` snippet: captures the host's element the way `bind:this` would. */
|
|
340
|
+
const attachRoot = {
|
|
341
|
+
[createAttachmentKey()]: (node: HTMLElement) => {
|
|
342
|
+
ref = node
|
|
343
|
+
return () => {
|
|
344
|
+
ref = null
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
}
|
|
348
|
+
</script>
|
|
349
|
+
|
|
350
|
+
{#if child}
|
|
351
|
+
{@render child({
|
|
352
|
+
props: { ...rest, 'data-ink-root': '', 'data-state': status.state, ...attachRoot },
|
|
353
|
+
})}
|
|
354
|
+
{:else}
|
|
355
|
+
<div bind:this={ref} {...rest} data-ink-root="" data-state={status.state}>
|
|
356
|
+
{@render children?.({ status, document })}
|
|
357
|
+
</div>
|
|
358
|
+
{/if}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ChildSnippet } from './child-snippet.js';
|
|
2
|
+
import type { Annotation, AnnotationChange, AnnotationDefaults, DocumentSource, InkDocument, InkError, InkStatus, PdfAssets } from '@inkbar/core';
|
|
3
|
+
import type { ToolId, ZoomValue } from '@inkbar/core/dom';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
6
|
+
export interface InkRootProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onchange' | 'onerror'> {
|
|
7
|
+
src: DocumentSource | null;
|
|
8
|
+
assets: PdfAssets;
|
|
9
|
+
password?: string | undefined;
|
|
10
|
+
/** Colour defaults. Without it, the theme's CSS variables on the root element apply. */
|
|
11
|
+
defaults?: AnnotationDefaults | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Pass (with `bind:`) to put annotations under the host's control. The mode is fixed when a
|
|
14
|
+
* document opens: passing or dropping this prop later takes effect on the next open. A host
|
|
15
|
+
* that decides on each proposal uses a function binding.
|
|
16
|
+
*/
|
|
17
|
+
annotations?: Annotation[] | undefined;
|
|
18
|
+
onAnnotationsChange?: ((next: Annotation[]) => void) | undefined;
|
|
19
|
+
onChange?: ((change: AnnotationChange) => void) | undefined;
|
|
20
|
+
tool?: ToolId | undefined;
|
|
21
|
+
onToolChange?: ((tool: ToolId) => void) | undefined;
|
|
22
|
+
page?: number | undefined;
|
|
23
|
+
onPageChange?: ((page: number) => void) | undefined;
|
|
24
|
+
zoom?: ZoomValue | undefined;
|
|
25
|
+
onZoomChange?: ((zoom: ZoomValue) => void) | undefined;
|
|
26
|
+
/** Undo history. Defaults to on for uncontrolled, off for controlled. */
|
|
27
|
+
history?: boolean | undefined;
|
|
28
|
+
onReady?: ((doc: InkDocument) => void) | undefined;
|
|
29
|
+
onError?: ((error: InkError) => void) | undefined;
|
|
30
|
+
onActivate?: ((id: string) => void) | undefined;
|
|
31
|
+
/** The root element, for hosts that bind it. */
|
|
32
|
+
ref?: HTMLElement | null;
|
|
33
|
+
children?: Snippet<[{
|
|
34
|
+
status: InkStatus;
|
|
35
|
+
document: InkDocument | null;
|
|
36
|
+
}]>;
|
|
37
|
+
/**
|
|
38
|
+
* Render your own root element: spread `props` on it and put your content inside. The
|
|
39
|
+
* context is still provided, so Inkbar components work inside it.
|
|
40
|
+
*/
|
|
41
|
+
child?: ChildSnippet;
|
|
42
|
+
}
|
|
43
|
+
import { type InkHistory } from '../context.js';
|
|
44
|
+
declare const InkRoot: import("svelte").Component<InkRootProps, {
|
|
45
|
+
history: InkHistory;
|
|
46
|
+
getDocument: () => InkDocument | null;
|
|
47
|
+
getStatus: () => InkStatus;
|
|
48
|
+
}, "page" | "annotations" | "tool" | "zoom" | "ref">;
|
|
49
|
+
type InkRoot = ReturnType<typeof InkRoot>;
|
|
50
|
+
export default InkRoot;
|
|
51
|
+
//# sourceMappingURL=InkRoot.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkRoot.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkRoot.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,QAAQ,EACR,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,YAAa,SAAQ,IAAI,CACxC,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,GAAG,UAAU,GAAG,SAAS,CACpC;IACC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;IACzC;;;;OAIG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,GAAG,SAAS,CAAA;IACtC,mBAAmB,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAChE,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC3D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACnD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACnD,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACtD,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAClD,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACjD,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC/C,gDAAgD;IAChD,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC,CAAA;IACzE;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;CACrB;AAaH,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAqT/E,QAAA,MAAM,OAAO;;uBAhCc,WAAW,GAAG,IAAI;qBAGpB,SAAS;oDA6BmB,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { SuggestionCardSlotProps } from '@inkbar/core/dom'
|
|
3
|
+
import type { Snippet } from 'svelte'
|
|
4
|
+
|
|
5
|
+
export interface InkSuggestionCardProps {
|
|
6
|
+
children?: Snippet<[SuggestionCardSlotProps]> | undefined
|
|
7
|
+
}
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
/*
|
|
12
|
+
* Review card for a suggested redaction: opens when the activated annotation, or the only
|
|
13
|
+
* selected one, is a redaction with `origin: 'suggested'`. Locked suggestions cannot be
|
|
14
|
+
* dismissed, so they get no Dismiss button. A press on the overlay selects and activates, so a
|
|
15
|
+
* click opens it whatever the tool. Closing clears `activated` and the selection.
|
|
16
|
+
*/
|
|
17
|
+
import { confidenceBucket } from '@inkbar/core'
|
|
18
|
+
import { Popover } from 'bits-ui'
|
|
19
|
+
|
|
20
|
+
import { useInkContext } from '../context.js'
|
|
21
|
+
import { useAnnotations } from '../hooks/useAnnotations.svelte.js'
|
|
22
|
+
|
|
23
|
+
import { useAnnotationElement } from './annotation-element.svelte.js'
|
|
24
|
+
import { keepOpenOnAnnotation, useFocusReturn } from './popover-focus.js'
|
|
25
|
+
|
|
26
|
+
import type { ConfidenceBucket, RedactAnnotation } from '@inkbar/core'
|
|
27
|
+
|
|
28
|
+
let { children }: InkSuggestionCardProps = $props()
|
|
29
|
+
|
|
30
|
+
const ctx = useInkContext('InkSuggestionCard')
|
|
31
|
+
const store = useAnnotations()
|
|
32
|
+
|
|
33
|
+
function suggestion(id: string | null | undefined): RedactAnnotation | null {
|
|
34
|
+
const a = id ? store.annotations.find((x) => x.id === id) : undefined
|
|
35
|
+
return a?.type === 'redact' && a.origin === 'suggested' ? a : null
|
|
36
|
+
}
|
|
37
|
+
// The activated annotation when it is a suggestion; otherwise the only selected one (a
|
|
38
|
+
// marquee selects without activating).
|
|
39
|
+
const target = $derived(
|
|
40
|
+
suggestion(ctx.activated) ??
|
|
41
|
+
(store.selection.length === 1 ? suggestion(store.selection[0]) : null)
|
|
42
|
+
)
|
|
43
|
+
const anchorRef = useAnnotationElement(
|
|
44
|
+
() => ctx.rootEl,
|
|
45
|
+
() => target?.id ?? null
|
|
46
|
+
)
|
|
47
|
+
const anchor = $derived(anchorRef.current)
|
|
48
|
+
const focusReturn = useFocusReturn(ctx)
|
|
49
|
+
const open = $derived(target !== null && anchor !== null)
|
|
50
|
+
const bucket = $derived<ConfidenceBucket | undefined>(
|
|
51
|
+
target?.suggestion ? confidenceBucket(target.suggestion.confidence) : undefined
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
function close(): void {
|
|
55
|
+
ctx.setActivated(null)
|
|
56
|
+
store.select([])
|
|
57
|
+
}
|
|
58
|
+
function accept(): void {
|
|
59
|
+
if (target) store.accept(target.id)
|
|
60
|
+
close()
|
|
61
|
+
}
|
|
62
|
+
/** Locked annotations cannot be removed: dismissing one does nothing and keeps the card. */
|
|
63
|
+
function dismiss(): void {
|
|
64
|
+
if (!target || target.locked) return
|
|
65
|
+
focusReturn.beforeRemove(anchor, '[data-origin="suggested"]')
|
|
66
|
+
store.dismiss(target.id)
|
|
67
|
+
close()
|
|
68
|
+
}
|
|
69
|
+
function onOpenChange(next: boolean): void {
|
|
70
|
+
if (!next) close()
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<Popover.Root bind:open={() => open, onOpenChange}>
|
|
75
|
+
<Popover.Portal>
|
|
76
|
+
{#if target && anchor}
|
|
77
|
+
<Popover.Content
|
|
78
|
+
class="ink-suggestion-card"
|
|
79
|
+
data-ink-suggestion-card=""
|
|
80
|
+
role="dialog"
|
|
81
|
+
side="bottom"
|
|
82
|
+
sideOffset={6}
|
|
83
|
+
collisionPadding={8}
|
|
84
|
+
aria-label="Suggested redaction"
|
|
85
|
+
customAnchor={anchor}
|
|
86
|
+
onInteractOutside={keepOpenOnAnnotation}
|
|
87
|
+
onOpenAutoFocus={focusReturn.onOpenAutoFocus}
|
|
88
|
+
onCloseAutoFocus={(e) => focusReturn.onCloseAutoFocus(e, anchor)}
|
|
89
|
+
>
|
|
90
|
+
{#if children}
|
|
91
|
+
{@render children({
|
|
92
|
+
annotation: target,
|
|
93
|
+
suggestion: target.suggestion,
|
|
94
|
+
bucket,
|
|
95
|
+
accept,
|
|
96
|
+
dismiss,
|
|
97
|
+
close,
|
|
98
|
+
})}
|
|
99
|
+
{:else}
|
|
100
|
+
<p class="ink-suggestion-reason">
|
|
101
|
+
<strong>{target.suggestion?.reason ?? 'Suggested redaction'}</strong>
|
|
102
|
+
{#if bucket}
|
|
103
|
+
<span class="ink-suggestion-confidence" data-confidence={bucket}>
|
|
104
|
+
{bucket} confidence
|
|
105
|
+
</span>
|
|
106
|
+
{/if}
|
|
107
|
+
</p>
|
|
108
|
+
{#if target.suggestion?.explanation}
|
|
109
|
+
<p class="ink-suggestion-explanation">{target.suggestion.explanation}</p>
|
|
110
|
+
{/if}
|
|
111
|
+
<div class="ink-suggestion-actions">
|
|
112
|
+
<button type="button" onclick={accept}>Accept</button>
|
|
113
|
+
{#if !target.locked}
|
|
114
|
+
<button type="button" onclick={dismiss}>Dismiss</button>
|
|
115
|
+
{/if}
|
|
116
|
+
</div>
|
|
117
|
+
{/if}
|
|
118
|
+
</Popover.Content>
|
|
119
|
+
{/if}
|
|
120
|
+
</Popover.Portal>
|
|
121
|
+
</Popover.Root>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SuggestionCardSlotProps } from '@inkbar/core/dom';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
export interface InkSuggestionCardProps {
|
|
4
|
+
children?: Snippet<[SuggestionCardSlotProps]> | undefined;
|
|
5
|
+
}
|
|
6
|
+
declare const InkSuggestionCard: import("svelte").Component<InkSuggestionCardProps, {}, "">;
|
|
7
|
+
type InkSuggestionCard = ReturnType<typeof InkSuggestionCard>;
|
|
8
|
+
export default InkSuggestionCard;
|
|
9
|
+
//# sourceMappingURL=InkSuggestionCard.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkSuggestionCard.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSuggestionCard.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,uBAAuB,CAAC,CAAC,GAAG,SAAS,CAAA;CAC1D;AAwHH,QAAA,MAAM,iBAAiB,4DAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { ChildSnippet } from './child-snippet.js'
|
|
3
|
+
import type { ToolId } from '@inkbar/core/dom'
|
|
4
|
+
import type { Snippet } from 'svelte'
|
|
5
|
+
import type { HTMLButtonAttributes } from 'svelte/elements'
|
|
6
|
+
|
|
7
|
+
export interface InkToolButtonProps extends Omit<HTMLButtonAttributes, 'children'> {
|
|
8
|
+
tool: ToolId
|
|
9
|
+
label?: string | undefined
|
|
10
|
+
children?: Snippet<[{ active: boolean; label: string }]> | undefined
|
|
11
|
+
child?: ChildSnippet | undefined
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import { TOOL_LABELS } from '@inkbar/core/dom'
|
|
17
|
+
import { Toolbar } from 'bits-ui'
|
|
18
|
+
|
|
19
|
+
import { useTool } from '../hooks/useTool.svelte.js'
|
|
20
|
+
|
|
21
|
+
import { asBitsProps } from './child-snippet.js'
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
tool,
|
|
25
|
+
label,
|
|
26
|
+
class: className,
|
|
27
|
+
children,
|
|
28
|
+
child,
|
|
29
|
+
onclick,
|
|
30
|
+
...rest
|
|
31
|
+
}: InkToolButtonProps = $props()
|
|
32
|
+
|
|
33
|
+
const current = useTool()
|
|
34
|
+
const active = $derived(current.tool === tool)
|
|
35
|
+
const name = $derived(label ?? TOOL_LABELS[tool])
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<Toolbar.Button
|
|
39
|
+
class={['ink-tool-button', className]}
|
|
40
|
+
data-ink-tool-button=""
|
|
41
|
+
data-tool={tool}
|
|
42
|
+
data-state={active ? 'on' : 'off'}
|
|
43
|
+
aria-pressed={active}
|
|
44
|
+
aria-label={name}
|
|
45
|
+
title={name}
|
|
46
|
+
{...child ? { child } : {}}
|
|
47
|
+
{...asBitsProps(rest)}
|
|
48
|
+
onclick={(e) => {
|
|
49
|
+
onclick?.(e)
|
|
50
|
+
if (!e.defaultPrevented) current.setTool(tool)
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
{#if children}{@render children({ active, label: name })}{:else}{name}{/if}
|
|
54
|
+
</Toolbar.Button>
|