@inkbar/svelte 0.8.0 → 0.10.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 (86) hide show
  1. package/README.md +71 -6
  2. package/dist/components/InkDownloadButton.svelte +62 -0
  3. package/dist/components/InkDownloadButton.svelte.d.ts +14 -0
  4. package/dist/components/InkDownloadButton.svelte.d.ts.map +1 -0
  5. package/dist/components/InkFindBar.svelte +18 -15
  6. package/dist/components/InkFindBar.svelte.d.ts.map +1 -1
  7. package/dist/components/InkHighlightMenu.svelte +101 -93
  8. package/dist/components/InkHighlightMenu.svelte.d.ts.map +1 -1
  9. package/dist/components/InkPanel.svelte +73 -0
  10. package/dist/components/InkPanel.svelte.d.ts +18 -0
  11. package/dist/components/InkPanel.svelte.d.ts.map +1 -0
  12. package/dist/components/InkPrintButton.svelte +57 -0
  13. package/dist/components/InkPrintButton.svelte.d.ts +12 -0
  14. package/dist/components/InkPrintButton.svelte.d.ts.map +1 -0
  15. package/dist/components/InkRecordList.svelte +91 -0
  16. package/dist/components/InkRecordList.svelte.d.ts +72 -0
  17. package/dist/components/InkRecordList.svelte.d.ts.map +1 -0
  18. package/dist/components/InkRoot.svelte +53 -5
  19. package/dist/components/InkRoot.svelte.d.ts +7 -2
  20. package/dist/components/InkRoot.svelte.d.ts.map +1 -1
  21. package/dist/components/InkSearchResults.svelte +72 -0
  22. package/dist/components/InkSearchResults.svelte.d.ts +9 -0
  23. package/dist/components/InkSearchResults.svelte.d.ts.map +1 -0
  24. package/dist/components/InkShapeMenu.svelte +89 -84
  25. package/dist/components/InkShapeMenu.svelte.d.ts.map +1 -1
  26. package/dist/components/InkSidebar.svelte +179 -0
  27. package/dist/components/InkSidebar.svelte.d.ts +22 -0
  28. package/dist/components/InkSidebar.svelte.d.ts.map +1 -0
  29. package/dist/components/InkSignatureDialog.svelte +7 -3
  30. package/dist/components/InkSignatureDialog.svelte.d.ts +4 -3
  31. package/dist/components/InkSignatureDialog.svelte.d.ts.map +1 -1
  32. package/dist/components/InkSignatureMenu.svelte +85 -77
  33. package/dist/components/InkSignatureMenu.svelte.d.ts.map +1 -1
  34. package/dist/components/InkThumbnail.svelte +74 -0
  35. package/dist/components/InkThumbnail.svelte.d.ts +19 -0
  36. package/dist/components/InkThumbnail.svelte.d.ts.map +1 -0
  37. package/dist/components/InkThumbnails.svelte +146 -0
  38. package/dist/components/InkThumbnails.svelte.d.ts +24 -0
  39. package/dist/components/InkThumbnails.svelte.d.ts.map +1 -0
  40. package/dist/components/InkToolButton.svelte +23 -24
  41. package/dist/components/InkToolButton.svelte.d.ts.map +1 -1
  42. package/dist/components/InkToolbarButton.svelte +33 -0
  43. package/dist/components/InkToolbarButton.svelte.d.ts +12 -0
  44. package/dist/components/InkToolbarButton.svelte.d.ts.map +1 -0
  45. package/dist/components/InkViewport.svelte +4 -0
  46. package/dist/components/InkViewport.svelte.d.ts.map +1 -1
  47. package/dist/components/InkZoomSelect.svelte +1 -1
  48. package/dist/components/RecordCards.svelte +173 -0
  49. package/dist/components/RecordCards.svelte.d.ts +16 -0
  50. package/dist/components/RecordCards.svelte.d.ts.map +1 -0
  51. package/dist/components/SidebarTrigger.svelte +20 -0
  52. package/dist/components/SidebarTrigger.svelte.d.ts +9 -0
  53. package/dist/components/SidebarTrigger.svelte.d.ts.map +1 -0
  54. package/dist/components/SignatureForm.svelte +9 -3
  55. package/dist/components/SignatureForm.svelte.d.ts.map +1 -1
  56. package/dist/components/body-lock.d.ts +3 -0
  57. package/dist/components/body-lock.d.ts.map +1 -0
  58. package/dist/components/body-lock.js +43 -0
  59. package/dist/components/sidebar-context.d.ts +24 -0
  60. package/dist/components/sidebar-context.d.ts.map +1 -0
  61. package/dist/components/sidebar-context.js +13 -0
  62. package/dist/context.d.ts +6 -0
  63. package/dist/context.d.ts.map +1 -1
  64. package/dist/hooks/useAnnotations.svelte.d.ts +78 -0
  65. package/dist/hooks/useAnnotations.svelte.d.ts.map +1 -1
  66. package/dist/hooks/useRecordSelection.svelte.d.ts +11 -0
  67. package/dist/hooks/useRecordSelection.svelte.d.ts.map +1 -0
  68. package/dist/hooks/useRecordSelection.svelte.js +19 -0
  69. package/dist/hooks/useRecords.svelte.d.ts +14 -0
  70. package/dist/hooks/useRecords.svelte.d.ts.map +1 -0
  71. package/dist/hooks/useRecords.svelte.js +24 -0
  72. package/dist/hooks/useRegisteredTool.svelte.d.ts +24 -0
  73. package/dist/hooks/useRegisteredTool.svelte.d.ts.map +1 -0
  74. package/dist/hooks/useRegisteredTool.svelte.js +47 -0
  75. package/dist/hooks/useSearch.svelte.d.ts.map +1 -1
  76. package/dist/hooks/useSearch.svelte.js +1 -0
  77. package/dist/hooks/useToolOptions.svelte.d.ts +8 -0
  78. package/dist/hooks/useToolOptions.svelte.d.ts.map +1 -1
  79. package/dist/hooks/useToolOptions.svelte.js +12 -2
  80. package/dist/index.d.ts +20 -0
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +11 -0
  83. package/dist/to-readable.svelte.d.ts +8 -0
  84. package/dist/to-readable.svelte.d.ts.map +1 -0
  85. package/dist/to-readable.svelte.js +15 -0
  86. package/package.json +5 -5
@@ -29,8 +29,9 @@
29
29
  import { ChevronDown } from '@inkbar/icons'
30
30
  import { Popover, Toolbar } from 'bits-ui'
31
31
 
32
+ import { useRegisteredTool } from '../hooks/useRegisteredTool.svelte.js'
32
33
  import { useTool } from '../hooks/useTool.svelte.js'
33
- import { useToolOptions } from '../hooks/useToolOptions.svelte.js'
34
+ import { useToolOptionsWhile } from '../hooks/useToolOptions.svelte.js'
34
35
 
35
36
  import InkIcon from './InkIcon.svelte'
36
37
  import { createMenuOpenFocus } from './menu-open-focus.js'
@@ -38,7 +39,9 @@
38
39
  let { class: className, children, ...rest }: InkShapeMenuProps = $props()
39
40
 
40
41
  const current = useTool()
41
- const shape = useToolOptions<ShapeOptions>('shape')
42
+ // Nothing renders while no `shape` tool is registered.
43
+ const registered = useRegisteredTool('shape')
44
+ const shape = useToolOptionsWhile<ShapeOptions>('shape', () => registered.current)
42
45
  let open = $state(false)
43
46
  /** The split button: the panel lines up under it rather than the chevron. */
44
47
  let group = $state<HTMLElement | null>(null)
@@ -67,88 +70,90 @@
67
70
  }
68
71
  </script>
69
72
 
70
- <div
71
- bind:this={group}
72
- role="group"
73
- aria-label="Shape"
74
- class={['ink-split-button ink-shape-menu', className]}
75
- data-ink-shape-menu=""
76
- {...rest}
77
- >
78
- <Toolbar.Button
79
- class="ink-tool-button ink-shape-button"
80
- data-ink-tool-button=""
81
- data-ink-shape-button=""
82
- data-tool="shape"
83
- data-kind={model.kind}
84
- data-state={active ? 'on' : 'off'}
85
- aria-pressed={active}
86
- aria-label={model.label}
87
- title={model.label}
88
- onclick={() => current.setTool('shape')}
73
+ {#if registered.current}
74
+ <div
75
+ bind:this={group}
76
+ role="group"
77
+ aria-label="Shape"
78
+ class={['ink-split-button ink-shape-menu', className]}
79
+ data-ink-shape-menu=""
80
+ {...rest}
89
81
  >
90
- {#if children}{@render children()}{:else}<InkIcon
91
- icon={SHAPE_ICONS[model.kind]}
92
- />{/if}
93
- </Toolbar.Button>
94
- <Popover.Root bind:open>
95
- <Popover.Trigger>
96
- {#snippet child({ props })}
97
- <Toolbar.Button
98
- {...props}
99
- class="ink-menu-trigger ink-shape-menu-trigger"
100
- data-ink-shape-menu-trigger=""
101
- data-state={open ? 'open' : 'closed'}
102
- aria-label="Choose shape"
103
- title="Choose shape"
104
- aria-haspopup="menu"
105
- onkeydown={onTriggerKeydown}
106
- >
107
- <InkIcon icon={ChevronDown} />
108
- </Toolbar.Button>
109
- {/snippet}
110
- </Popover.Trigger>
111
- <Popover.Portal>
112
- <Popover.Content
113
- bind:ref={menu}
114
- role="menu"
115
- aria-label="Shapes"
116
- class="ink-select-content ink-menu-content"
117
- data-ink-select-content=""
118
- data-ink-menu="shape"
119
- customAnchor={group}
120
- side="bottom"
121
- align="start"
122
- sideOffset={4}
123
- collisionPadding={8}
124
- onOpenAutoFocus={(e) => openFocus.onOpenAutoFocus(e, menu)}
125
- onfocusin={openFocus.onfocusin}
126
- onkeydown={(e) => menu && handleMenuKeys(e, menu)}
127
- >
128
- <div
129
- role="group"
82
+ <Toolbar.Button
83
+ class="ink-tool-button ink-shape-button"
84
+ data-ink-tool-button=""
85
+ data-ink-shape-button=""
86
+ data-tool="shape"
87
+ data-kind={model.kind}
88
+ data-state={active ? 'on' : 'off'}
89
+ aria-pressed={active}
90
+ aria-label={model.label}
91
+ title={model.label}
92
+ onclick={() => current.setTool('shape')}
93
+ >
94
+ {#if children}{@render children()}{:else}<InkIcon
95
+ icon={SHAPE_ICONS[model.kind]}
96
+ />{/if}
97
+ </Toolbar.Button>
98
+ <Popover.Root bind:open>
99
+ <Popover.Trigger>
100
+ {#snippet child({ props })}
101
+ <Toolbar.Button
102
+ {...props}
103
+ class="ink-menu-trigger ink-shape-menu-trigger"
104
+ data-ink-shape-menu-trigger=""
105
+ data-state={open ? 'open' : 'closed'}
106
+ aria-label="Choose shape"
107
+ title="Choose shape"
108
+ aria-haspopup="menu"
109
+ onkeydown={onTriggerKeydown}
110
+ >
111
+ <InkIcon icon={ChevronDown} />
112
+ </Toolbar.Button>
113
+ {/snippet}
114
+ </Popover.Trigger>
115
+ <Popover.Portal>
116
+ <Popover.Content
117
+ bind:ref={menu}
118
+ role="menu"
130
119
  aria-label="Shapes"
131
- class="ink-shape-grid"
132
- data-ink-menu-columns={SHAPE_COLUMNS}
120
+ class="ink-select-content ink-menu-content"
121
+ data-ink-select-content=""
122
+ data-ink-menu="shape"
123
+ customAnchor={group}
124
+ side="bottom"
125
+ align="start"
126
+ sideOffset={4}
127
+ collisionPadding={8}
128
+ onOpenAutoFocus={(e) => openFocus.onOpenAutoFocus(e, menu)}
129
+ onfocusin={openFocus.onfocusin}
130
+ onkeydown={(e) => menu && handleMenuKeys(e, menu)}
133
131
  >
134
- {#each model.items as item (item.kind)}
135
- <button
136
- type="button"
137
- role="menuitemradio"
138
- class="ink-select-item ink-menu-item ink-shape-item"
139
- data-ink-select-item=""
140
- tabindex={-1}
141
- data-key={item.kind}
142
- aria-checked={item.checked}
143
- data-state={item.checked ? 'checked' : 'unchecked'}
144
- onclick={() => choose(item.kind)}
145
- >
146
- <InkIcon icon={SHAPE_ICONS[item.kind]} size={16} />
147
- {item.label}
148
- </button>
149
- {/each}
150
- </div>
151
- </Popover.Content>
152
- </Popover.Portal>
153
- </Popover.Root>
154
- </div>
132
+ <div
133
+ role="group"
134
+ aria-label="Shapes"
135
+ class="ink-shape-grid"
136
+ data-ink-menu-columns={SHAPE_COLUMNS}
137
+ >
138
+ {#each model.items as item (item.kind)}
139
+ <button
140
+ type="button"
141
+ role="menuitemradio"
142
+ class="ink-select-item ink-menu-item ink-shape-item"
143
+ data-ink-select-item=""
144
+ tabindex={-1}
145
+ data-key={item.kind}
146
+ aria-checked={item.checked}
147
+ data-state={item.checked ? 'checked' : 'unchecked'}
148
+ onclick={() => choose(item.kind)}
149
+ >
150
+ <InkIcon icon={SHAPE_ICONS[item.kind]} size={16} />
151
+ {item.label}
152
+ </button>
153
+ {/each}
154
+ </div>
155
+ </Popover.Content>
156
+ </Popover.Portal>
157
+ </Popover.Root>
158
+ </div>
159
+ {/if}
@@ -1 +1 @@
1
- {"version":3,"file":"InkShapeMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkShapeMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AA+FH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"InkShapeMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkShapeMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAqGH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,179 @@
1
+ <script module lang="ts">
2
+ import type { SidebarLayout, SidebarOpen, SidebarSide } from '@inkbar/core/dom'
3
+ import type { Snippet } from 'svelte'
4
+ import type { HTMLAttributes } from 'svelte/elements'
5
+
6
+ export interface InkSidebarProps extends Omit<
7
+ HTMLAttributes<HTMLDivElement>,
8
+ 'children'
9
+ > {
10
+ /** Which edge the rail and border sit on. Default `'left'`. */
11
+ side?: SidebarSide | undefined
12
+ /** `'rail'` (default), `'tabs'` or `'stack'`. */
13
+ layout?: SidebarLayout | undefined
14
+ /** Default true. False hides the whole sidebar and keeps its open panel, content and scroll for when it shows again. */
15
+ visible?: boolean | undefined
16
+ /**
17
+ * Bindable: the open panel's id (or null) for rail and tabs, the expanded ids for stack.
18
+ * Left out, the first panel starts open.
19
+ */
20
+ open?: SidebarOpen | undefined
21
+ /** The `InkPanel`s. */
22
+ children?: Snippet | undefined
23
+ }
24
+ </script>
25
+
26
+ <script lang="ts">
27
+ /*
28
+ * A sidebar of `InkPanel`s: `createSidebar`'s markup on core's sidebar controller, which holds
29
+ * the open state, the ARIA, the keys and the clicks. Rail and tabs render their triggers here;
30
+ * in a stack each panel renders its own header. Panels register as they initialise and take
31
+ * their place in DOM order once mounted, so a panel added later with `{#if}` slots in where
32
+ * it stands. A closed panel stays mounted and hidden, and the panels' content stays in place
33
+ * across layouts, so its state survives switching.
34
+ *
35
+ * `bind:open` is the open panel's id (or null) for rail and tabs, and the expanded ids for
36
+ * stack. Left out, the first panel starts open. It needs no document, so it works outside
37
+ * `InkRoot`. The server renders the root, the panels (the open one shown) and the stack
38
+ * headers; rail and tab buttons follow once mounted, as the panels register after the root
39
+ * has rendered.
40
+ */
41
+ import { createSidebarController } from '@inkbar/core/dom'
42
+ import { untrack } from 'svelte'
43
+
44
+ import { useReadable } from '../use-readable.svelte.js'
45
+
46
+ import { setSidebarContext, type SidebarPanelEntry } from './sidebar-context.js'
47
+ import SidebarTrigger from './SidebarTrigger.svelte'
48
+
49
+ let {
50
+ side = 'left',
51
+ layout = 'rail',
52
+ visible = true,
53
+ open = $bindable(),
54
+ class: className,
55
+ children,
56
+ ...rest
57
+ }: InkSidebarProps = $props()
58
+
59
+ // The same ids on the server and the client.
60
+ const idPrefix = $props.id()
61
+
62
+ let entries = $state.raw<SidebarPanelEntry[]>([])
63
+ const infos = () => entries.map((e) => e.info())
64
+
65
+ // Pending until mounted: the panels register one by one as they initialise, and an open id
66
+ // not registered yet is not a removed panel.
67
+ const controller = untrack(() =>
68
+ createSidebarController({
69
+ layout,
70
+ side,
71
+ visible,
72
+ panels: [],
73
+ ...(open !== undefined && { open }),
74
+ pending: true,
75
+ onOpenChange: (value) => {
76
+ open = value
77
+ },
78
+ idPrefix,
79
+ })
80
+ )
81
+ const current = useReadable(() => controller.state, controller.state.get())
82
+ // Destroyed before the panels go: this pre-effect comes before the template's, so its
83
+ // teardown runs first. Unregistering into a live controller would take each panel for a
84
+ // removed one and write the fallback to the bound `open`.
85
+ $effect.pre(() => () => controller.destroy())
86
+
87
+ // Before the DOM updates, so the panels render the attributes these give.
88
+ $effect.pre(() => {
89
+ const next = { layout, side, visible, open }
90
+ untrack(() =>
91
+ controller.update({
92
+ layout: next.layout,
93
+ side: next.side,
94
+ visible: next.visible,
95
+ ...(next.open !== undefined && { open: next.open }),
96
+ })
97
+ )
98
+ })
99
+ // A panel's label, icon or badge changing.
100
+ $effect.pre(() => {
101
+ const panels = infos()
102
+ untrack(() => controller.update({ panels }))
103
+ })
104
+
105
+ // Panels register while they initialise, inside the parent's render: untracked, so the
106
+ // writes are not taken for state changed by a template expression. On the server too.
107
+ function register(entry: SidebarPanelEntry): () => void {
108
+ untrack(() => {
109
+ entries = [...entries, entry]
110
+ controller.update({ panels: infos() })
111
+ })
112
+ return () =>
113
+ untrack(() => {
114
+ entries = entries.filter((e) => e !== entry)
115
+ controller.update({ panels: infos() })
116
+ })
117
+ }
118
+
119
+ function arrange(): void {
120
+ untrack(() => {
121
+ const list = entries
122
+ const sorted = [...list].sort((a, b) => {
123
+ const x = a.element()
124
+ const y = b.element()
125
+ if (!x || !y || x === y) return 0
126
+ return x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
127
+ })
128
+ if (sorted.some((e, i) => e !== list[i])) {
129
+ entries = sorted
130
+ controller.update({ panels: infos() })
131
+ }
132
+ })
133
+ }
134
+
135
+ setSidebarContext({ controller, state: current, register, arrange })
136
+
137
+ const view = $derived.by(() => {
138
+ const s = current.current
139
+ return {
140
+ layout: s.layout,
141
+ root: controller.rootAttrs(),
142
+ tablist: controller.tablistAttrs(),
143
+ triggers: s.panels.map((panel) => ({
144
+ panel,
145
+ attrs: controller.triggerAttrs(panel.id),
146
+ })),
147
+ }
148
+ })
149
+
150
+ let root = $state<HTMLDivElement | null>(null)
151
+ // The controller handles the triggers' clicks and keys, stack headers included. The panels
152
+ // have registered by now.
153
+ $effect(() => {
154
+ const el = root
155
+ if (!el) return
156
+ const unbind = untrack(() => controller.bindTriggers(el))
157
+ untrack(() => controller.update({ pending: false }))
158
+ return unbind
159
+ })
160
+ </script>
161
+
162
+ <div {...rest} bind:this={root} class={['ink-sidebar', className]} {...view.root}>
163
+ {#if view.layout !== 'stack'}
164
+ <div
165
+ class={view.layout === 'rail' ? 'ink-sidebar-rail' : 'ink-sidebar-tabs'}
166
+ {...view.tablist}
167
+ >
168
+ {#each view.triggers as t (t.panel.id)}
169
+ <button {...t.attrs}>
170
+ <SidebarTrigger panel={t.panel} rail={view.layout === 'rail'} />
171
+ </button>
172
+ {/each}
173
+ </div>
174
+ {/if}
175
+ <!-- One element in every layout, so the panels are never remounted. -->
176
+ <div class={{ 'ink-sidebar-panels': view.layout !== 'stack' }}>
177
+ {@render children?.()}
178
+ </div>
179
+ </div>
@@ -0,0 +1,22 @@
1
+ import type { SidebarLayout, SidebarOpen, SidebarSide } from '@inkbar/core/dom';
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ export interface InkSidebarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
5
+ /** Which edge the rail and border sit on. Default `'left'`. */
6
+ side?: SidebarSide | undefined;
7
+ /** `'rail'` (default), `'tabs'` or `'stack'`. */
8
+ layout?: SidebarLayout | undefined;
9
+ /** Default true. False hides the whole sidebar and keeps its open panel, content and scroll for when it shows again. */
10
+ visible?: boolean | undefined;
11
+ /**
12
+ * Bindable: the open panel's id (or null) for rail and tabs, the expanded ids for stack.
13
+ * Left out, the first panel starts open.
14
+ */
15
+ open?: SidebarOpen | undefined;
16
+ /** The `InkPanel`s. */
17
+ children?: Snippet | undefined;
18
+ }
19
+ declare const InkSidebar: import("svelte").Component<InkSidebarProps, {}, "open">;
20
+ type InkSidebar = ReturnType<typeof InkSidebar>;
21
+ export default InkSidebar;
22
+ //# sourceMappingURL=InkSidebar.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkSidebar.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSidebar.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,+DAA+D;IAC/D,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IAC9B,iDAAiD;IACjD,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;IAClC,wHAAwH;IACxH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B;;;OAGG;IACH,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IAC9B,uBAAuB;IACvB,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAwKH,QAAA,MAAM,UAAU,yDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -5,7 +5,7 @@
5
5
  * "Create signature": a modal with Draw, Type and Image tabs. Draw is a pad with the draw
6
6
  * tool's renderer; Type sets a name in one of the host's handwriting fonts; Image takes a PNG
7
7
  * or JPEG scan. Save stores the template through InkRoot's signature store and, unless
8
- * `select` is false, selects it for the `signature` tool. Escape, Cancel or a press outside
8
+ * `select` is false or no `signature` tool is registered, selects it for that tool. Escape, Cancel or a press outside
9
9
  * closes it.
10
10
  */
11
11
  export interface InkSignatureDialogProps {
@@ -14,8 +14,9 @@
14
14
  /** The fonts the Type tab offers. Default: InkRoot's `signatureFonts`. */
15
15
  fonts?: readonly string[] | undefined
16
16
  /**
17
- * Whether saving selects the new signature for the `signature` tool. Default true. False
18
- * leaves the tool alone, for a dialog that signs a field with it through `onSave`.
17
+ * Whether saving selects the new signature for the `signature` tool, when one is
18
+ * registered. Default true. False leaves the tool alone, for a dialog that signs a field
19
+ * with it through `onSave`.
19
20
  */
20
21
  select?: boolean | undefined
21
22
  /** Called with the new template once it is saved (and selected, with `select`). */
@@ -30,6 +31,7 @@
30
31
  import { SIGNATURE_LABELS } from '@inkbar/core/dom'
31
32
  import { Dialog } from 'bits-ui'
32
33
 
34
+ import { lockBody } from './body-lock.js'
33
35
  import SignatureForm from './SignatureForm.svelte'
34
36
 
35
37
  let {
@@ -58,6 +60,8 @@
58
60
  class={['ink-dialog ink-signature-dialog', className]}
59
61
  data-ink-signature-dialog=""
60
62
  aria-modal="true"
63
+ preventScroll={false}
64
+ {@attach lockBody}
61
65
  onOpenAutoFocus={(e) => {
62
66
  e.preventDefault()
63
67
  content
@@ -3,7 +3,7 @@ import type { SignatureTemplate } from '@inkbar/core';
3
3
  * "Create signature": a modal with Draw, Type and Image tabs. Draw is a pad with the draw
4
4
  * tool's renderer; Type sets a name in one of the host's handwriting fonts; Image takes a PNG
5
5
  * or JPEG scan. Save stores the template through InkRoot's signature store and, unless
6
- * `select` is false, selects it for the `signature` tool. Escape, Cancel or a press outside
6
+ * `select` is false or no `signature` tool is registered, selects it for that tool. Escape, Cancel or a press outside
7
7
  * closes it.
8
8
  */
9
9
  export interface InkSignatureDialogProps {
@@ -12,8 +12,9 @@ export interface InkSignatureDialogProps {
12
12
  /** The fonts the Type tab offers. Default: InkRoot's `signatureFonts`. */
13
13
  fonts?: readonly string[] | undefined;
14
14
  /**
15
- * Whether saving selects the new signature for the `signature` tool. Default true. False
16
- * leaves the tool alone, for a dialog that signs a field with it through `onSave`.
15
+ * Whether saving selects the new signature for the `signature` tool, when one is
16
+ * registered. Default true. False leaves the tool alone, for a dialog that signs a field
17
+ * with it through `onSave`.
17
18
  */
18
19
  select?: boolean | undefined;
19
20
  /** Called with the new template once it is saved (and selected, with `select`). */
@@ -1 +1 @@
1
- {"version":3,"file":"InkSignatureDialog.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSignatureDialog.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACpD,0EAA0E;IAC1E,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC5D,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACvD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3B;AAmDH,QAAA,MAAM,kBAAkB,iEAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"InkSignatureDialog.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSignatureDialog.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACpD,0EAA0E;IAC1E,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC5D,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACvD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3B;AAqDH,QAAA,MAAM,kBAAkB,iEAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -26,8 +26,9 @@
26
26
  import { untrack } from 'svelte'
27
27
 
28
28
  import { useInkContext } from '../context.js'
29
+ import { useRegisteredTool } from '../hooks/useRegisteredTool.svelte.js'
29
30
  import { useTool } from '../hooks/useTool.svelte.js'
30
- import { useToolOptions } from '../hooks/useToolOptions.svelte.js'
31
+ import { useToolOptionsWhile } from '../hooks/useToolOptions.svelte.js'
31
32
  import { useReadable } from '../use-readable.svelte.js'
32
33
 
33
34
  import InkIcon from './InkIcon.svelte'
@@ -43,7 +44,12 @@
43
44
 
44
45
  const ctx = useInkContext('InkSignatureMenu')
45
46
  const current = useTool()
46
- const signature = useToolOptions<SignatureOptions>('signature')
47
+ // Nothing renders while no `signature` tool is registered.
48
+ const registered = useRegisteredTool('signature')
49
+ const signature = useToolOptionsWhile<SignatureOptions>(
50
+ 'signature',
51
+ () => registered.current
52
+ )
47
53
  const templates = useReadable(() => ctx.signatures.templates, NO_TEMPLATES)
48
54
  // An old removal error is not news when the menu opens again.
49
55
  $effect(() => {
@@ -91,81 +97,83 @@
91
97
  }
92
98
  </script>
93
99
 
94
- <div
95
- bind:this={group}
96
- role="group"
97
- aria-label={SIGNATURE_LABELS.sign}
98
- class={['ink-split-button ink-signature-menu', className]}
99
- data-ink-signature-menu=""
100
- {...rest}
101
- >
102
- <Toolbar.Button
103
- bind:ref={main}
104
- class="ink-tool-button ink-signature-button"
105
- data-ink-tool-button=""
106
- data-ink-signature-button=""
107
- data-tool="signature"
108
- data-state={active ? 'on' : 'off'}
109
- aria-pressed={active}
100
+ {#if registered.current}
101
+ <div
102
+ bind:this={group}
103
+ role="group"
110
104
  aria-label={SIGNATURE_LABELS.sign}
111
- title={SIGNATURE_LABELS.sign}
112
- onclick={onMain}
105
+ class={['ink-split-button ink-signature-menu', className]}
106
+ data-ink-signature-menu=""
107
+ {...rest}
113
108
  >
114
- {#if children}{@render children()}{:else}<InkIcon icon={Signature} />{/if}
115
- </Toolbar.Button>
116
- <Popover.Root bind:open>
117
- <Popover.Trigger>
118
- {#snippet child({ props })}
119
- <Toolbar.Button
120
- {...props}
121
- class="ink-menu-trigger ink-signature-menu-trigger"
122
- data-ink-signature-menu-trigger=""
123
- data-state={open ? 'open' : 'closed'}
124
- aria-label={SIGNATURE_LABELS.trigger}
125
- title={SIGNATURE_LABELS.trigger}
126
- aria-haspopup="menu"
127
- onkeydown={onTriggerKeydown}
109
+ <Toolbar.Button
110
+ bind:ref={main}
111
+ class="ink-tool-button ink-signature-button"
112
+ data-ink-tool-button=""
113
+ data-ink-signature-button=""
114
+ data-tool="signature"
115
+ data-state={active ? 'on' : 'off'}
116
+ aria-pressed={active}
117
+ aria-label={SIGNATURE_LABELS.sign}
118
+ title={SIGNATURE_LABELS.sign}
119
+ onclick={onMain}
120
+ >
121
+ {#if children}{@render children()}{:else}<InkIcon icon={Signature} />{/if}
122
+ </Toolbar.Button>
123
+ <Popover.Root bind:open>
124
+ <Popover.Trigger>
125
+ {#snippet child({ props })}
126
+ <Toolbar.Button
127
+ {...props}
128
+ class="ink-menu-trigger ink-signature-menu-trigger"
129
+ data-ink-signature-menu-trigger=""
130
+ data-state={open ? 'open' : 'closed'}
131
+ aria-label={SIGNATURE_LABELS.trigger}
132
+ title={SIGNATURE_LABELS.trigger}
133
+ aria-haspopup="menu"
134
+ onkeydown={onTriggerKeydown}
135
+ >
136
+ <InkIcon icon={ChevronDown} />
137
+ </Toolbar.Button>
138
+ {/snippet}
139
+ </Popover.Trigger>
140
+ <Popover.Portal>
141
+ <Popover.Content
142
+ bind:ref={menu}
143
+ role="menu"
144
+ aria-label={SIGNATURE_LABELS.menu}
145
+ class="ink-select-content ink-menu-content"
146
+ data-ink-select-content=""
147
+ data-ink-menu="signature"
148
+ customAnchor={group}
149
+ side="bottom"
150
+ align="start"
151
+ sideOffset={4}
152
+ collisionPadding={8}
153
+ onOpenAutoFocus={(e) => openFocus.onOpenAutoFocus(e, menu)}
154
+ onfocusin={openFocus.onfocusin}
155
+ onCloseAutoFocus={(e) => {
156
+ if (toDialog) e.preventDefault()
157
+ toDialog = false
158
+ }}
159
+ onkeydown={(e) => menu && handleMenuKeys(e, menu)}
128
160
  >
129
- <InkIcon icon={ChevronDown} />
130
- </Toolbar.Button>
131
- {/snippet}
132
- </Popover.Trigger>
133
- <Popover.Portal>
134
- <Popover.Content
135
- bind:ref={menu}
136
- role="menu"
137
- aria-label={SIGNATURE_LABELS.menu}
138
- class="ink-select-content ink-menu-content"
139
- data-ink-select-content=""
140
- data-ink-menu="signature"
141
- customAnchor={group}
142
- side="bottom"
143
- align="start"
144
- sideOffset={4}
145
- collisionPadding={8}
146
- onOpenAutoFocus={(e) => openFocus.onOpenAutoFocus(e, menu)}
147
- onfocusin={openFocus.onfocusin}
148
- onCloseAutoFocus={(e) => {
149
- if (toDialog) e.preventDefault()
150
- toDialog = false
151
- }}
152
- onkeydown={(e) => menu && handleMenuKeys(e, menu)}
153
- >
154
- <SignatureMenuPanel
155
- items={model.items}
156
- menu={() => menu}
157
- onChoose={(t) => choose(t.id)}
158
- onCreate={create}
159
- />
160
- </Popover.Content>
161
- </Popover.Portal>
162
- </Popover.Root>
163
- <InkSignatureDialog
164
- bind:open={creating}
165
- {fonts}
166
- onCloseAutoFocus={(e) => {
167
- e.preventDefault()
168
- main?.focus({ preventScroll: true })
169
- }}
170
- />
171
- </div>
161
+ <SignatureMenuPanel
162
+ items={model.items}
163
+ menu={() => menu}
164
+ onChoose={(t) => choose(t.id)}
165
+ onCreate={create}
166
+ />
167
+ </Popover.Content>
168
+ </Popover.Portal>
169
+ </Popover.Root>
170
+ <InkSignatureDialog
171
+ bind:open={creating}
172
+ {fonts}
173
+ onCloseAutoFocus={(e) => {
174
+ e.preventDefault()
175
+ main?.focus({ preventScroll: true })
176
+ }}
177
+ />
178
+ </div>
179
+ {/if}
@@ -1 +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;AA2HH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
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;AAoIH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}