@inkbar/svelte 0.3.0 → 0.4.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.
Files changed (57) hide show
  1. package/README.md +16 -9
  2. package/dist/components/InkColorMenu.svelte +242 -0
  3. package/dist/components/InkColorMenu.svelte.d.ts +33 -0
  4. package/dist/components/InkColorMenu.svelte.d.ts.map +1 -0
  5. package/dist/components/InkHighlightMenu.svelte +166 -0
  6. package/dist/components/InkHighlightMenu.svelte.d.ts +16 -0
  7. package/dist/components/InkHighlightMenu.svelte.d.ts.map +1 -0
  8. package/dist/components/InkLineStyleMenu.svelte +141 -0
  9. package/dist/components/InkLineStyleMenu.svelte.d.ts +15 -0
  10. package/dist/components/InkLineStyleMenu.svelte.d.ts.map +1 -0
  11. package/dist/components/InkRoot.svelte +77 -3
  12. package/dist/components/InkRoot.svelte.d.ts +20 -2
  13. package/dist/components/InkRoot.svelte.d.ts.map +1 -1
  14. package/dist/components/InkShapeMenu.svelte +157 -0
  15. package/dist/components/InkShapeMenu.svelte.d.ts +15 -0
  16. package/dist/components/InkShapeMenu.svelte.d.ts.map +1 -0
  17. package/dist/components/InkSignatureDialog.svelte +70 -0
  18. package/dist/components/InkSignatureDialog.svelte.d.ts +22 -0
  19. package/dist/components/InkSignatureDialog.svelte.d.ts.map +1 -0
  20. package/dist/components/InkSignatureMenu.svelte +263 -0
  21. package/dist/components/InkSignatureMenu.svelte.d.ts +18 -0
  22. package/dist/components/InkSignatureMenu.svelte.d.ts.map +1 -0
  23. package/dist/components/InkTextStyleMenu.svelte +225 -0
  24. package/dist/components/InkTextStyleMenu.svelte.d.ts +15 -0
  25. package/dist/components/InkTextStyleMenu.svelte.d.ts.map +1 -0
  26. package/dist/components/{InkNotePopover.svelte → InkThreadPopover.svelte} +37 -32
  27. package/dist/components/InkThreadPopover.svelte.d.ts +9 -0
  28. package/dist/components/InkThreadPopover.svelte.d.ts.map +1 -0
  29. package/dist/components/InkToolButton.svelte +23 -7
  30. package/dist/components/InkToolButton.svelte.d.ts.map +1 -1
  31. package/dist/components/InkViewport.svelte +7 -0
  32. package/dist/components/InkViewport.svelte.d.ts.map +1 -1
  33. package/dist/components/SignatureForm.svelte +219 -0
  34. package/dist/components/SignatureForm.svelte.d.ts +10 -0
  35. package/dist/components/SignatureForm.svelte.d.ts.map +1 -0
  36. package/dist/components/ThreadForm.svelte +4 -2
  37. package/dist/components/ThreadForm.svelte.d.ts +1 -0
  38. package/dist/components/ThreadForm.svelte.d.ts.map +1 -1
  39. package/dist/components/child-snippet.d.ts +8 -0
  40. package/dist/components/child-snippet.d.ts.map +1 -1
  41. package/dist/components/child-snippet.js +15 -0
  42. package/dist/context.d.ts +14 -2
  43. package/dist/context.d.ts.map +1 -1
  44. package/dist/hooks/useAnnotations.svelte.d.ts +1289 -71
  45. package/dist/hooks/useAnnotations.svelte.d.ts.map +1 -1
  46. package/dist/hooks/useToolOptions.svelte.d.ts +11 -0
  47. package/dist/hooks/useToolOptions.svelte.d.ts.map +1 -0
  48. package/dist/hooks/useToolOptions.svelte.js +19 -0
  49. package/dist/hooks/useToolStyle.svelte.d.ts +33 -0
  50. package/dist/hooks/useToolStyle.svelte.d.ts.map +1 -0
  51. package/dist/hooks/useToolStyle.svelte.js +33 -0
  52. package/dist/index.d.ts +20 -3
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +10 -1
  55. package/package.json +5 -5
  56. package/dist/components/InkNotePopover.svelte.d.ts +0 -9
  57. package/dist/components/InkNotePopover.svelte.d.ts.map +0 -1
@@ -0,0 +1,263 @@
1
+ <script module lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import type { HTMLAttributes } from 'svelte/elements'
4
+
5
+ /**
6
+ * A split button for the signature tool. The main part activates the tool with the chosen
7
+ * signature, or opens the menu when none is chosen; the chevron opens the saved signatures
8
+ * as previews, each with a remove button, and "Create signature", which opens
9
+ * `InkSignatureDialog`. Choosing a signature switches to the tool with it.
10
+ */
11
+ export interface InkSignatureMenuProps extends Omit<
12
+ HTMLAttributes<HTMLDivElement>,
13
+ 'children'
14
+ > {
15
+ /** Replaces the main button's icon. */
16
+ children?: Snippet | undefined
17
+ /** The fonts the dialog's Type tab offers. Default: InkRoot's `signatureFonts`. */
18
+ fonts?: readonly string[] | undefined
19
+ }
20
+ </script>
21
+
22
+ <script lang="ts">
23
+ import {
24
+ focusInitialMenuItem,
25
+ handleMenuKeys,
26
+ menuItems,
27
+ SIGNATURE_LABELS,
28
+ signatureMenuModel,
29
+ signaturePreview,
30
+ } from '@inkbar/core/dom'
31
+ import { ChevronDown, Plus, Signature, Trash2 } from '@inkbar/icons'
32
+ import { Popover, Toolbar } from 'bits-ui'
33
+ import { tick, untrack } from 'svelte'
34
+
35
+ import { useInkContext } from '../context.js'
36
+ import { useTool } from '../hooks/useTool.svelte.js'
37
+ import { useToolOptions } from '../hooks/useToolOptions.svelte.js'
38
+ import { useReadable } from '../use-readable.svelte.js'
39
+
40
+ import InkIcon from './InkIcon.svelte'
41
+ import InkSignatureDialog from './InkSignatureDialog.svelte'
42
+
43
+ import type { SignatureOptions, SignatureTemplate } from '@inkbar/core'
44
+
45
+ let { class: className, children, fonts, ...rest }: InkSignatureMenuProps = $props()
46
+
47
+ const NO_TEMPLATES: readonly SignatureTemplate[] = []
48
+
49
+ const ctx = useInkContext('InkSignatureMenu')
50
+ const current = useTool()
51
+ const signature = useToolOptions<SignatureOptions>('signature')
52
+ const templates = useReadable(() => ctx.signatures.templates, NO_TEMPLATES)
53
+ const removeError = useReadable(() => ctx.signatures.removeError, undefined)
54
+ // An old removal error is not news when the menu opens again.
55
+ $effect(() => {
56
+ if (open) untrack(() => ctx.signatures.clearRemoveError())
57
+ })
58
+ const model = $derived(signatureMenuModel(templates.current, signature.options))
59
+ const active = $derived(current.tool === 'signature')
60
+ let open = $state(false)
61
+ let creating = $state(false)
62
+ let menu = $state<HTMLElement | null>(null)
63
+ let main = $state<HTMLElement | null>(null)
64
+
65
+ /** ArrowDown opens on the first item; a click opens on the checked one. */
66
+ let focusFirst = false
67
+ // Bits runs the open-focus callback more than once, so the flag outlives it; reset on close.
68
+ $effect(() => {
69
+ if (!open) focusFirst = false
70
+ })
71
+ /** Set while the menu closes for the dialog, which takes focus instead of the trigger. */
72
+ let toDialog = false
73
+
74
+ function onTriggerKeydown(e: KeyboardEvent): void {
75
+ if (e.key !== 'ArrowDown' || e.defaultPrevented || open) return
76
+ e.preventDefault()
77
+ focusFirst = true
78
+ open = true
79
+ }
80
+
81
+ function onMain(): void {
82
+ if (model.selected) current.setTool('signature')
83
+ else open = true
84
+ }
85
+
86
+ function choose(id: string): void {
87
+ signature.setOptions({ templateId: id })
88
+ current.setTool('signature')
89
+ open = false
90
+ }
91
+
92
+ /** Removes a template; focus moves to the signature now in its row, else "Create signature". */
93
+ function remove(id: string, row: number): void {
94
+ ctx.signatures.remove(id).then(
95
+ async () => {
96
+ await tick()
97
+ if (!menu) return
98
+ const rows = menu.querySelectorAll<HTMLElement>('.ink-signature-item')
99
+ const next =
100
+ rows[Math.min(row, rows.length - 1)] ??
101
+ menu.querySelector<HTMLElement>('[data-ink-signature-create]')
102
+ next?.focus({ preventScroll: true })
103
+ },
104
+ () => {
105
+ // The host's store refused: the signature stays.
106
+ }
107
+ )
108
+ }
109
+
110
+ function create(): void {
111
+ toDialog = true
112
+ open = false
113
+ creating = true
114
+ }
115
+ </script>
116
+
117
+ <div
118
+ role="group"
119
+ aria-label={SIGNATURE_LABELS.sign}
120
+ class={['ink-split-button ink-signature-menu', className]}
121
+ data-ink-signature-menu=""
122
+ {...rest}
123
+ >
124
+ <Toolbar.Button
125
+ bind:ref={main}
126
+ class="ink-tool-button ink-signature-button"
127
+ data-ink-tool-button=""
128
+ data-ink-signature-button=""
129
+ data-tool="signature"
130
+ data-state={active ? 'on' : 'off'}
131
+ aria-pressed={active}
132
+ aria-label={SIGNATURE_LABELS.sign}
133
+ title={SIGNATURE_LABELS.sign}
134
+ onclick={onMain}
135
+ >
136
+ {#if children}{@render children()}{:else}<InkIcon icon={Signature} />{/if}
137
+ </Toolbar.Button>
138
+ <Popover.Root bind:open>
139
+ <Popover.Trigger>
140
+ {#snippet child({ props })}
141
+ <Toolbar.Button
142
+ {...props}
143
+ class="ink-menu-trigger ink-signature-menu-trigger"
144
+ data-ink-signature-menu-trigger=""
145
+ data-state={open ? 'open' : 'closed'}
146
+ aria-label={SIGNATURE_LABELS.trigger}
147
+ title={SIGNATURE_LABELS.trigger}
148
+ aria-haspopup="menu"
149
+ onkeydown={onTriggerKeydown}
150
+ >
151
+ <InkIcon icon={ChevronDown} />
152
+ </Toolbar.Button>
153
+ {/snippet}
154
+ </Popover.Trigger>
155
+ <Popover.Portal>
156
+ <Popover.Content
157
+ bind:ref={menu}
158
+ role="menu"
159
+ aria-label={SIGNATURE_LABELS.menu}
160
+ class="ink-select-content ink-menu-content"
161
+ data-ink-select-content=""
162
+ data-ink-menu="signature"
163
+ side="bottom"
164
+ align="start"
165
+ sideOffset={4}
166
+ collisionPadding={8}
167
+ onOpenAutoFocus={(e) => {
168
+ e.preventDefault()
169
+ if (menu) {
170
+ if (focusFirst) menuItems(menu)[0]?.focus({ preventScroll: true })
171
+ else focusInitialMenuItem(menu)
172
+ }
173
+ }}
174
+ onCloseAutoFocus={(e) => {
175
+ if (toDialog) e.preventDefault()
176
+ toDialog = false
177
+ }}
178
+ onkeydown={(e) => menu && handleMenuKeys(e, menu)}
179
+ >
180
+ {#if model.items.length > 0}
181
+ <div
182
+ role="group"
183
+ aria-label={SIGNATURE_LABELS.saved}
184
+ class="ink-signature-list"
185
+ data-ink-menu-columns="2"
186
+ >
187
+ {#each model.items as item, row (item.id)}
188
+ {@const preview =
189
+ item.template.kind === 'ink' ? signaturePreview(item.template) : null}
190
+ <button
191
+ type="button"
192
+ role="menuitemradio"
193
+ aria-checked={item.checked}
194
+ aria-label={item.label}
195
+ tabindex={-1}
196
+ class="ink-select-item ink-menu-item ink-signature-item"
197
+ data-ink-select-item=""
198
+ data-key={item.id}
199
+ data-state={item.checked ? 'checked' : 'unchecked'}
200
+ onclick={() => choose(item.id)}
201
+ >
202
+ <span class="ink-signature-preview" data-kind={item.template.kind}>
203
+ {#if item.template.kind === 'image'}
204
+ <img src={item.template.imagedata} alt="" draggable="false" />
205
+ {:else if preview}
206
+ <svg viewBox={preview.viewBox} aria-hidden="true">
207
+ {#each preview.paths as d, i (i)}
208
+ <path {d} fill="currentColor" />
209
+ {/each}
210
+ </svg>
211
+ {/if}
212
+ </span>
213
+ </button>
214
+ <button
215
+ type="button"
216
+ role="menuitem"
217
+ aria-label={item.removeLabel}
218
+ title={item.removeLabel}
219
+ tabindex={-1}
220
+ class="ink-menu-item ink-signature-remove"
221
+ data-ink-signature-remove=""
222
+ data-key={item.id}
223
+ onclick={() => remove(item.id, row)}
224
+ >
225
+ <InkIcon icon={Trash2} size={16} />
226
+ </button>
227
+ {/each}
228
+ </div>
229
+ <div role="separator" class="ink-select-separator"></div>
230
+ {/if}
231
+ <button
232
+ type="button"
233
+ role="menuitem"
234
+ tabindex={-1}
235
+ class="ink-select-item ink-menu-item ink-signature-create"
236
+ data-ink-select-item=""
237
+ data-ink-signature-create=""
238
+ onclick={create}
239
+ >
240
+ <InkIcon icon={Plus} size={16} />
241
+ {SIGNATURE_LABELS.create}
242
+ </button>
243
+ {#if removeError.current}
244
+ <p
245
+ class="ink-field-error ink-signature-remove-error"
246
+ role="alert"
247
+ data-ink-signature-remove-error=""
248
+ >
249
+ {removeError.current}
250
+ </p>
251
+ {/if}
252
+ </Popover.Content>
253
+ </Popover.Portal>
254
+ </Popover.Root>
255
+ <InkSignatureDialog
256
+ bind:open={creating}
257
+ {fonts}
258
+ onCloseAutoFocus={(e) => {
259
+ e.preventDefault()
260
+ main?.focus({ preventScroll: true })
261
+ }}
262
+ />
263
+ </div>
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ /**
4
+ * A split button for the signature tool. The main part activates the tool with the chosen
5
+ * signature, or opens the menu when none is chosen; the chevron opens the saved signatures
6
+ * as previews, each with a remove button, and "Create signature", which opens
7
+ * `InkSignatureDialog`. Choosing a signature switches to the tool with it.
8
+ */
9
+ export interface InkSignatureMenuProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
10
+ /** Replaces the main button's icon. */
11
+ children?: Snippet | undefined;
12
+ /** The fonts the dialog's Type tab offers. Default: InkRoot's `signatureFonts`. */
13
+ fonts?: readonly string[] | undefined;
14
+ }
15
+ declare const InkSignatureMenu: import("svelte").Component<InkSignatureMenuProps, {}, "">;
16
+ type InkSignatureMenu = ReturnType<typeof InkSignatureMenu>;
17
+ export default InkSignatureMenu;
18
+ //# sourceMappingURL=InkSignatureMenu.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkSignatureMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSignatureMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,mFAAmF;IACnF,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CACtC;AAuLH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,225 @@
1
+ <script module lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import type { HTMLButtonAttributes } from 'svelte/elements'
4
+
5
+ /**
6
+ * Font, size, text colour, bold, italic, underline and alignment for the selected text
7
+ * boxes or the text tool. A popover panel rather than a menu list: it holds a select, a
8
+ * number input with a stepper, toggle buttons and a radio group, each named and operable on
9
+ * its own. The trigger carries `data-mixed` when the selection disagrees on any of them.
10
+ */
11
+ export interface InkTextStyleMenuProps extends Omit<HTMLButtonAttributes, 'children'> {
12
+ children?: Snippet | undefined
13
+ }
14
+ </script>
15
+
16
+ <script lang="ts">
17
+ import { handleRadioKeys, textStyleMenuModel } from '@inkbar/core/dom'
18
+ import {
19
+ ALargeSmall,
20
+ AlignCenter,
21
+ AlignJustify,
22
+ AlignLeft,
23
+ AlignRight,
24
+ Bold,
25
+ Italic,
26
+ Minus,
27
+ Plus,
28
+ Underline,
29
+ } from '@inkbar/icons'
30
+ import { Popover, Toolbar } from 'bits-ui'
31
+
32
+ import { useInkContext } from '../context.js'
33
+ import { useToolStyle } from '../hooks/useToolStyle.svelte.js'
34
+
35
+ import { asBitsProps, composeHandlers } from './child-snippet.js'
36
+ import InkColorMenu from './InkColorMenu.svelte'
37
+ import InkIcon from './InkIcon.svelte'
38
+
39
+ let {
40
+ class: className,
41
+ children,
42
+ disabled,
43
+ onclick,
44
+ onkeydown,
45
+ ...rest
46
+ }: InkTextStyleMenuProps = $props()
47
+
48
+ const ctx = useInkContext('InkTextStyleMenu')
49
+ const style = useToolStyle()
50
+ let open = $state(false)
51
+ let trigger = $state<HTMLElement | null>(null)
52
+ /** The size as typed, until it is committed with Enter or blur. */
53
+ let draft = $state<string | null>(null)
54
+
55
+ const model = $derived(textStyleMenuModel(style.current, style.supported, ctx.fonts))
56
+ const sizeShown = $derived(
57
+ draft ?? (model.size.value === null ? '' : String(model.size.value))
58
+ )
59
+
60
+ const TOGGLE_ICONS = { bold: Bold, italic: Italic, underline: Underline }
61
+ const ALIGN_ICONS = {
62
+ left: AlignLeft,
63
+ center: AlignCenter,
64
+ right: AlignRight,
65
+ justify: AlignJustify,
66
+ }
67
+
68
+ // The menu closes when the control disables (the selection cleared while it was open).
69
+ $effect(() => {
70
+ if (!model.enabled) open = false
71
+ })
72
+
73
+ function onTriggerKeydown(e: KeyboardEvent): void {
74
+ if (e.key !== 'ArrowDown' || e.defaultPrevented || open || !model.enabled) return
75
+ e.preventDefault()
76
+ open = true
77
+ }
78
+
79
+ function commitSize(): void {
80
+ if (draft === null) return
81
+ if (draft.trim() !== '') {
82
+ const patch = model.size.commit(Number(draft))
83
+ if (patch) style.set(patch)
84
+ }
85
+ draft = null
86
+ }
87
+ function nudge(direction: 1 | -1): void {
88
+ const patch = model.size.nudge(direction)
89
+ if (patch) style.set(patch)
90
+ draft = null
91
+ }
92
+ </script>
93
+
94
+ <Popover.Root bind:open>
95
+ <Popover.Trigger>
96
+ {#snippet child({ props })}
97
+ <Toolbar.Button
98
+ bind:ref={trigger}
99
+ {...props}
100
+ {...asBitsProps(rest)}
101
+ class={['ink-menu-trigger ink-text-style-menu', className]}
102
+ data-ink-text-style-menu=""
103
+ data-state={open ? 'open' : 'closed'}
104
+ data-mixed={model.mixed ? '' : undefined}
105
+ aria-label={model.label}
106
+ title={model.label}
107
+ aria-haspopup="dialog"
108
+ onclick={composeHandlers(onclick, props.onclick)}
109
+ onkeydown={composeHandlers(onkeydown, props.onkeydown, onTriggerKeydown)}
110
+ disabled={!!disabled || !model.enabled}
111
+ >
112
+ {#if children}{@render children()}{:else}<InkIcon icon={ALargeSmall} />{/if}
113
+ </Toolbar.Button>
114
+ {/snippet}
115
+ </Popover.Trigger>
116
+ <Popover.Portal>
117
+ <Popover.Content
118
+ role="dialog"
119
+ aria-label={model.label}
120
+ class="ink-select-content ink-menu-content ink-text-style-content"
121
+ data-ink-select-content=""
122
+ data-ink-menu="text-style"
123
+ side="bottom"
124
+ align="start"
125
+ sideOffset={4}
126
+ collisionPadding={8}
127
+ onCloseAutoFocus={(e) => {
128
+ // Focus may be inside the nested colour menu when the panel closes.
129
+ e.preventDefault()
130
+ trigger?.focus()
131
+ }}
132
+ >
133
+ <select
134
+ class="ink-text-style-font"
135
+ aria-label={model.font.label}
136
+ value={model.font.value}
137
+ onchange={(e) => style.set(model.font.patch(e.currentTarget.value))}
138
+ >
139
+ {#if model.font.value === ''}
140
+ <option value="" disabled hidden>Mixed</option>
141
+ {/if}
142
+ {#each model.font.options as f (f)}
143
+ <option value={f}>{f}</option>
144
+ {/each}
145
+ </select>
146
+ <div class="ink-text-style-row">
147
+ <InkColorMenu property="textColor" standalone />
148
+ <div class="ink-text-style-size" role="group" aria-label="Size">
149
+ <button
150
+ type="button"
151
+ class="ink-text-style-button"
152
+ aria-label="Decrease font size"
153
+ onclick={() => nudge(-1)}
154
+ >
155
+ <InkIcon icon={Minus} size={16} />
156
+ </button>
157
+ <input
158
+ type="number"
159
+ class="ink-text-style-input"
160
+ aria-label={model.size.label}
161
+ min={model.size.min}
162
+ max={model.size.max}
163
+ step={model.size.step}
164
+ value={sizeShown}
165
+ oninput={(e) => (draft = e.currentTarget.value)}
166
+ onblur={commitSize}
167
+ onkeydown={(e) => {
168
+ if (e.key === 'Enter') {
169
+ e.preventDefault()
170
+ commitSize()
171
+ }
172
+ }}
173
+ />
174
+ <button
175
+ type="button"
176
+ class="ink-text-style-button"
177
+ aria-label="Increase font size"
178
+ onclick={() => nudge(1)}
179
+ >
180
+ <InkIcon icon={Plus} size={16} />
181
+ </button>
182
+ </div>
183
+ </div>
184
+ <div class="ink-text-style-row" role="group" aria-label="Emphasis">
185
+ {#each model.toggles as t (t.key)}
186
+ <button
187
+ type="button"
188
+ class="ink-text-style-button"
189
+ aria-label={t.label}
190
+ title={t.label}
191
+ aria-pressed={t.pressed}
192
+ data-state={t.pressed === true ? 'on' : 'off'}
193
+ data-mixed={t.pressed === 'mixed' ? '' : undefined}
194
+ onclick={() => style.set(t.patch)}
195
+ >
196
+ <InkIcon icon={TOGGLE_ICONS[t.key]} size={16} />
197
+ </button>
198
+ {/each}
199
+ </div>
200
+ <div
201
+ class="ink-text-style-row"
202
+ role="radiogroup"
203
+ aria-label={model.align.label}
204
+ tabindex="-1"
205
+ onkeydown={(e) => handleRadioKeys(e, e.currentTarget)}
206
+ >
207
+ {#each model.align.choices as c (c.key)}
208
+ <button
209
+ type="button"
210
+ role="radio"
211
+ class="ink-text-style-button"
212
+ aria-label={c.label}
213
+ title={c.label}
214
+ aria-checked={c.checked}
215
+ tabindex={c.tabStop ? 0 : -1}
216
+ data-state={c.checked ? 'on' : 'off'}
217
+ onclick={() => style.set(c.patch)}
218
+ >
219
+ <InkIcon icon={ALIGN_ICONS[c.key]} size={16} />
220
+ </button>
221
+ {/each}
222
+ </div>
223
+ </Popover.Content>
224
+ </Popover.Portal>
225
+ </Popover.Root>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+ /**
4
+ * Font, size, text colour, bold, italic, underline and alignment for the selected text
5
+ * boxes or the text tool. A popover panel rather than a menu list: it holds a select, a
6
+ * number input with a stepper, toggle buttons and a radio group, each named and operable on
7
+ * its own. The trigger carries `data-mixed` when the selection disagrees on any of them.
8
+ */
9
+ export interface InkTextStyleMenuProps extends Omit<HTMLButtonAttributes, 'children'> {
10
+ children?: Snippet | undefined;
11
+ }
12
+ declare const InkTextStyleMenu: import("svelte").Component<InkTextStyleMenuProps, {}, "">;
13
+ type InkTextStyleMenu = ReturnType<typeof InkTextStyleMenu>;
14
+ export default InkTextStyleMenu;
15
+ //# sourceMappingURL=InkTextStyleMenu.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkTextStyleMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkTextStyleMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IACnF,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAyJH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}