@inkbar/svelte 0.1.1 → 0.3.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 (46) hide show
  1. package/README.md +51 -9
  2. package/dist/components/InkIcon.svelte +24 -0
  3. package/dist/components/InkIcon.svelte.d.ts +9 -0
  4. package/dist/components/InkIcon.svelte.d.ts.map +1 -0
  5. package/dist/components/InkNextPage.svelte +38 -0
  6. package/dist/components/InkNextPage.svelte.d.ts +5 -0
  7. package/dist/components/InkNextPage.svelte.d.ts.map +1 -0
  8. package/dist/components/InkNotePopover.svelte +2 -2
  9. package/dist/components/InkPrevPage.svelte +38 -0
  10. package/dist/components/InkPrevPage.svelte.d.ts +5 -0
  11. package/dist/components/InkPrevPage.svelte.d.ts.map +1 -0
  12. package/dist/components/InkRedo.svelte +38 -0
  13. package/dist/components/InkRedo.svelte.d.ts +5 -0
  14. package/dist/components/InkRedo.svelte.d.ts.map +1 -0
  15. package/dist/components/InkToolButton.svelte +15 -1
  16. package/dist/components/InkToolButton.svelte.d.ts.map +1 -1
  17. package/dist/components/InkToolbarSeparator.svelte +18 -0
  18. package/dist/components/InkToolbarSeparator.svelte.d.ts +6 -0
  19. package/dist/components/InkToolbarSeparator.svelte.d.ts.map +1 -0
  20. package/dist/components/InkUndo.svelte +38 -0
  21. package/dist/components/InkUndo.svelte.d.ts +5 -0
  22. package/dist/components/InkUndo.svelte.d.ts.map +1 -0
  23. package/dist/components/InkZoomIn.svelte +3 -1
  24. package/dist/components/InkZoomIn.svelte.d.ts.map +1 -1
  25. package/dist/components/InkZoomOut.svelte +3 -1
  26. package/dist/components/InkZoomOut.svelte.d.ts.map +1 -1
  27. package/dist/components/InkZoomSelect.svelte +73 -24
  28. package/dist/components/InkZoomSelect.svelte.d.ts +3 -2
  29. package/dist/components/InkZoomSelect.svelte.d.ts.map +1 -1
  30. package/dist/components/history-button.d.ts +10 -0
  31. package/dist/components/history-button.d.ts.map +1 -0
  32. package/dist/components/history-button.js +1 -0
  33. package/dist/components/page-button.d.ts +10 -0
  34. package/dist/components/page-button.d.ts.map +1 -0
  35. package/dist/components/page-button.js +1 -0
  36. package/dist/hooks/useAnnotations.svelte.d.ts +98 -98
  37. package/dist/index.d.ts +10 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +6 -1
  40. package/package.json +5 -5
  41. package/dist/class-names.d.ts +0 -3
  42. package/dist/class-names.d.ts.map +0 -1
  43. package/dist/class-names.js +0 -5
  44. package/dist/compositions/DefaultToolbar.svelte +0 -90
  45. package/dist/compositions/DefaultToolbar.svelte.d.ts +0 -10
  46. package/dist/compositions/DefaultToolbar.svelte.d.ts.map +0 -1
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  ---
22
22
 
23
- Drop a PDF viewer into your Svelte app and let people highlight text, leave notes with replies and review redactions. `@inkbar/svelte` gives you a default toolbar and popovers styled by `@inkbar/theme`, plus the unstyled components and hooks they are made from, so you can keep the defaults or build your own UI.
23
+ Drop a PDF viewer into your Svelte app and let people highlight text, leave notes with replies and review redactions. `@inkbar/svelte` gives you components to build your own toolbars, popovers for notes and suggested redactions, and hooks for anything else. `@inkbar/theme` styles them all.
24
24
 
25
25
  Annotations are plain typed objects. Bind them with `bind:annotations` to keep them in your own state, or let Inkbar hold them with undo and redo.
26
26
 
@@ -30,7 +30,7 @@ Annotations are plain typed objects. Bind them with `bind:annotations` to keep t
30
30
  pnpm add @inkbar/svelte @inkbar/core @inkbar/theme
31
31
  ```
32
32
 
33
- Svelte 5.46.4 or later is a peer dependency. Add the Vite plugin from `@inkbar/core/vite` so the PDF engine can load its worker and fonts; the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#serving-pdfjs-assets) covers it. The package ships `.svelte` sources, so your bundler compiles them for your target, including the server.
33
+ Svelte 5.46.4 or later is a peer dependency. Add the Vite plugin from `@inkbar/core/vite` so PDF.js can load its worker and fonts; the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#serving-pdfjs-assets) covers it. The package ships `.svelte` sources, so your bundler compiles them for your target, including the server.
34
34
 
35
35
  ## Usage
36
36
 
@@ -39,7 +39,18 @@ Svelte 5.46.4 or later is a peer dependency. Add the Vite plugin from `@inkbar/c
39
39
  import {
40
40
  InkRoot,
41
41
  InkViewport,
42
- DefaultToolbar,
42
+ InkToolbar,
43
+ InkToolButton,
44
+ InkToolbarSeparator,
45
+ InkUndo,
46
+ InkRedo,
47
+ InkPrevPage,
48
+ InkPageInput,
49
+ InkPageCount,
50
+ InkNextPage,
51
+ InkZoomOut,
52
+ InkZoomSelect,
53
+ InkZoomIn,
43
54
  InkNotePopover,
44
55
  InkSuggestionCard,
45
56
  } from '@inkbar/svelte'
@@ -53,14 +64,32 @@ Svelte 5.46.4 or later is a peer dependency. Add the Vite plugin from `@inkbar/c
53
64
  </script>
54
65
 
55
66
  <InkRoot src={{ url: signedUrl, key: documentId }} {assets} bind:annotations>
56
- <DefaultToolbar />
67
+ <InkToolbar>
68
+ <InkToolButton tool="view" />
69
+ <InkToolButton tool="select" />
70
+ <InkToolButton tool="highlight" />
71
+ <InkToolButton tool="redact-area" />
72
+ <InkToolButton tool="note" />
73
+ <InkToolbarSeparator />
74
+ <InkUndo />
75
+ <InkRedo />
76
+ </InkToolbar>
57
77
  <InkViewport />
78
+ <InkToolbar label="Page and zoom">
79
+ <InkPrevPage />
80
+ <InkPageInput />
81
+ <InkPageCount />
82
+ <InkNextPage />
83
+ <InkZoomOut />
84
+ <InkZoomSelect />
85
+ <InkZoomIn />
86
+ </InkToolbar>
58
87
  <InkNotePopover />
59
88
  <InkSuggestionCard />
60
89
  </InkRoot>
61
90
  ```
62
91
 
63
- `bind:annotations` makes your list the source of truth. Leave it off and Inkbar keeps the state itself, with undo and redo.
92
+ Each toolbar control is a component, so keep the ones you need and put them where you like. Buttons show icons from `@inkbar/icons`. Pass your own as `children` to replace one, and use `InkIcon` to put the same icons on your own buttons. `bind:annotations` makes your list the source of truth. Leave it off and Inkbar keeps the state itself, with undo and redo.
64
93
 
65
94
  ## Controlled mode
66
95
 
@@ -122,13 +151,13 @@ Inside `InkRoot`, `useHistory()` returns the same object. Its `batch(fn)` runs `
122
151
 
123
152
  `InkNotePopover` opens the thread of the activated note. A new note has no text yet, so the form first writes the note's own text (labelled "Comment"); after that it adds replies.
124
153
 
125
- A root note with no text and no replies is removed when its popover closes, or when activation moves to another annotation, so abandoned notes do not stay on the page. Locked notes are never removed. If you add notes yourself (`add({ type: 'note', ... })`), give them `contents`, or they will be removed the first time someone opens and closes them.
154
+ A root note with no text and no replies is removed when its popover closes, or when activation moves to another annotation, so abandoned notes do not stay on the page. Locked notes are never removed. If you add notes yourself (`add({ type: 'text', ... })`), give them `contents`, or they will be removed the first time someone opens and closes them.
126
155
 
127
156
  ## Custom rendering
128
157
 
129
158
  Slots are snippets. `InkViewport` takes `loading`, `empty`, `error`, `annotation` and `pageError`. `InkNotePopover` and `InkSuggestionCard` take `children` as a snippet that receives the popover's props. `InkToolButton` takes `children` as a snippet of `{ active, label }`. The props each receives are typed as `AnnotationSlotProps`, `NotePopoverSlotProps` and `SuggestionCardSlotProps`, the same types the Vue and React packages export.
130
159
 
131
- `InkRoot`, `InkToolbar`, `InkToolButton`, `InkZoomIn` and `InkZoomOut` take a `child` snippet to render your own element. Spread the `props` it gives you on that element. The snippet type is exported as `ChildSnippet`.
160
+ `InkRoot`, `InkToolbar`, `InkToolButton`, `InkUndo`, `InkRedo`, `InkPrevPage`, `InkNextPage`, `InkZoomIn` and `InkZoomOut` take a `child` snippet to render your own element. Spread the `props` it gives you on that element. The snippet type is exported as `ChildSnippet`.
132
161
 
133
162
  ```svelte
134
163
  <InkToolbar>
@@ -140,11 +169,24 @@ Slots are snippets. `InkViewport` takes `loading`, `empty`, `error`, `annotation
140
169
 
141
170
  ## Keyboard
142
171
 
143
- The scroll element takes focus. With the highlight or redact tool active, `Enter` turns the text selection into a mark; with the note tool, `Enter` places a note in the middle of the current page and opens it. On a focused annotation, `Enter` or `Space` selects and activates it (a note opens its thread and a suggested redaction its card; other types are only selected), `ArrowUp` and `ArrowDown` move between annotations, `Shift` + arrow moves it by 1 pt (add `Alt` for 10 pt), `Ctrl` or `Cmd` + `Shift` + arrow resizes an area mark, and `Delete` removes it. The full table is in the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#tools-and-keyboard).
172
+ The scroll area takes focus. From there:
173
+
174
+ - With the `highlight` or `redact-area` tool active, `Enter` turns the text selection into a mark.
175
+ - With the `note` tool active, `Enter` places a note at the centre of the current page and opens it.
176
+
177
+ On a focused annotation:
178
+
179
+ - `Enter` or `Space` selects and activates it. A note opens its thread and a suggested redaction its card; other types are only selected.
180
+ - `ArrowUp` and `ArrowDown` move to the previous or next annotation on the page.
181
+ - `Shift` + arrow moves it by 1 pt, or by 10 pt with `Alt` as well.
182
+ - `Ctrl` or `Cmd` + `Shift` + arrow resizes an area redaction, text box or shape.
183
+ - `Delete` or `Backspace` removes it.
184
+
185
+ The full table, with the zoom and page keys, is in the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#tools-and-keyboard).
144
186
 
145
187
  ## Styling
146
188
 
147
- Components carry state on data attributes, for example `[data-annotation][data-state="selected"]` and `[data-tool="highlight"][data-state="on"]`. `@inkbar/theme` gives a default look. Your own CSS overrides it without `!important`. `InkRoot`, `InkViewport`, `DefaultToolbar` and the toolbar controls accept `class`.
189
+ Components carry state on data attributes, for example `[data-annotation][data-state="selected"]` and `[data-tool="highlight"][data-state="on"]`. `@inkbar/theme` gives a default look. Your own CSS overrides it without `!important`. `InkRoot`, `InkViewport`, `InkToolbar` and the toolbar controls accept `class`.
148
190
 
149
191
  ## Hooks
150
192
 
@@ -0,0 +1,24 @@
1
+ <script module lang="ts">
2
+ import type { IconNode } from '@inkbar/icons'
3
+
4
+ export interface InkIconProps {
5
+ icon: IconNode
6
+ size?: number | undefined
7
+ }
8
+ </script>
9
+
10
+ <script lang="ts">
11
+ /*
12
+ * An icon from `@inkbar/icons`, hidden from assistive technology: the control it sits in
13
+ * carries the name. The theme sizes it with `--ink-icon-size`.
14
+ */
15
+ import { ICON_ATTRIBUTES } from '@inkbar/icons'
16
+
17
+ let { icon, size = 18 }: InkIconProps = $props()
18
+ </script>
19
+
20
+ <svg {...ICON_ATTRIBUTES} width={size} height={size} class="ink-icon" aria-hidden="true">
21
+ {#each icon as [tag, attrs], i (i)}
22
+ <svelte:element this={tag} {...attrs} />
23
+ {/each}
24
+ </svg>
@@ -0,0 +1,9 @@
1
+ import type { IconNode } from '@inkbar/icons';
2
+ export interface InkIconProps {
3
+ icon: IconNode;
4
+ size?: number | undefined;
5
+ }
6
+ declare const InkIcon: import("svelte").Component<InkIconProps, {}, "">;
7
+ type InkIcon = ReturnType<typeof InkIcon>;
8
+ export default InkIcon;
9
+ //# sourceMappingURL=InkIcon.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkIcon.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkIcon.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1B;AA0BH,QAAA,MAAM,OAAO,kDAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { ChevronRight } from '@inkbar/icons'
3
+ import { Toolbar } from 'bits-ui'
4
+
5
+ import { usePage } from '../hooks/usePage.svelte.js'
6
+
7
+ import { asBitsProps } from './child-snippet.js'
8
+ import InkIcon from './InkIcon.svelte'
9
+
10
+ import type { InkPageButtonProps } from './page-button.js'
11
+
12
+ let {
13
+ class: className,
14
+ children,
15
+ child,
16
+ disabled,
17
+ onclick,
18
+ ...rest
19
+ }: InkPageButtonProps = $props()
20
+
21
+ const page = usePage()
22
+ </script>
23
+
24
+ <Toolbar.Button
25
+ class={['ink-next-page', className]}
26
+ data-ink-next-page=""
27
+ aria-label="Next page"
28
+ title="Next page"
29
+ disabled={page.current >= page.count - 1 || !!disabled}
30
+ {...child ? { child } : {}}
31
+ {...asBitsProps(rest)}
32
+ onclick={(e) => {
33
+ onclick?.(e)
34
+ if (!e.defaultPrevented) page.next()
35
+ }}
36
+ >
37
+ {#if children}{@render children()}{:else}<InkIcon icon={ChevronRight} />{/if}
38
+ </Toolbar.Button>
@@ -0,0 +1,5 @@
1
+ import type { InkPageButtonProps } from './page-button.js';
2
+ declare const InkNextPage: import("svelte").Component<InkPageButtonProps, {}, "">;
3
+ type InkNextPage = ReturnType<typeof InkNextPage>;
4
+ export default InkNextPage;
5
+ //# sourceMappingURL=InkNextPage.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkNextPage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkNextPage.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAkC3D,QAAA,MAAM,WAAW,wDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -40,7 +40,7 @@
40
40
  const a = ctx.activated
41
41
  ? store.annotations.find((x) => x.id === ctx.activated)
42
42
  : undefined
43
- return a?.type === 'note' ? threadRoot(store.annotations, a) : null
43
+ return a?.type === 'text' ? threadRoot(store.annotations, a) : null
44
44
  })
45
45
  const anchorRef = useAnnotationElement(
46
46
  () => ctx.rootEl,
@@ -90,7 +90,7 @@
90
90
  if (!root || !contents) return
91
91
  written.add(root.id)
92
92
  store.add({
93
- type: 'note',
93
+ type: 'text',
94
94
  page: root.page,
95
95
  rect: root.rect,
96
96
  contents,
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { ChevronLeft } from '@inkbar/icons'
3
+ import { Toolbar } from 'bits-ui'
4
+
5
+ import { usePage } from '../hooks/usePage.svelte.js'
6
+
7
+ import { asBitsProps } from './child-snippet.js'
8
+ import InkIcon from './InkIcon.svelte'
9
+
10
+ import type { InkPageButtonProps } from './page-button.js'
11
+
12
+ let {
13
+ class: className,
14
+ children,
15
+ child,
16
+ disabled,
17
+ onclick,
18
+ ...rest
19
+ }: InkPageButtonProps = $props()
20
+
21
+ const page = usePage()
22
+ </script>
23
+
24
+ <Toolbar.Button
25
+ class={['ink-prev-page', className]}
26
+ data-ink-prev-page=""
27
+ aria-label="Previous page"
28
+ title="Previous page"
29
+ disabled={page.current <= 0 || !!disabled}
30
+ {...child ? { child } : {}}
31
+ {...asBitsProps(rest)}
32
+ onclick={(e) => {
33
+ onclick?.(e)
34
+ if (!e.defaultPrevented) page.previous()
35
+ }}
36
+ >
37
+ {#if children}{@render children()}{:else}<InkIcon icon={ChevronLeft} />{/if}
38
+ </Toolbar.Button>
@@ -0,0 +1,5 @@
1
+ import type { InkPageButtonProps } from './page-button.js';
2
+ declare const InkPrevPage: import("svelte").Component<InkPageButtonProps, {}, "">;
3
+ type InkPrevPage = ReturnType<typeof InkPrevPage>;
4
+ export default InkPrevPage;
5
+ //# sourceMappingURL=InkPrevPage.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkPrevPage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkPrevPage.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAkC3D,QAAA,MAAM,WAAW,wDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { Redo2 } from '@inkbar/icons'
3
+ import { Toolbar } from 'bits-ui'
4
+
5
+ import { useHistory } from '../hooks/useHistory.svelte.js'
6
+
7
+ import { asBitsProps } from './child-snippet.js'
8
+ import InkIcon from './InkIcon.svelte'
9
+
10
+ import type { InkHistoryButtonProps } from './history-button.js'
11
+
12
+ let {
13
+ class: className,
14
+ children,
15
+ child,
16
+ disabled,
17
+ onclick,
18
+ ...rest
19
+ }: InkHistoryButtonProps = $props()
20
+
21
+ const history = useHistory()
22
+ </script>
23
+
24
+ <Toolbar.Button
25
+ class={['ink-redo', className]}
26
+ data-ink-redo=""
27
+ aria-label="Redo"
28
+ title="Redo"
29
+ disabled={!history.canRedo || !!disabled}
30
+ {...child ? { child } : {}}
31
+ {...asBitsProps(rest)}
32
+ onclick={(e) => {
33
+ onclick?.(e)
34
+ if (!e.defaultPrevented) history.redo()
35
+ }}
36
+ >
37
+ {#if children}{@render children()}{:else}<InkIcon icon={Redo2} />{/if}
38
+ </Toolbar.Button>
@@ -0,0 +1,5 @@
1
+ import type { InkHistoryButtonProps } from './history-button.js';
2
+ declare const InkRedo: import("svelte").Component<InkHistoryButtonProps, {}, "">;
3
+ type InkRedo = ReturnType<typeof InkRedo>;
4
+ export default InkRedo;
5
+ //# sourceMappingURL=InkRedo.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkRedo.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkRedo.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAkCjE,QAAA,MAAM,OAAO,2DAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
@@ -14,11 +14,23 @@
14
14
 
15
15
  <script lang="ts">
16
16
  import { TOOL_LABELS } from '@inkbar/core/dom'
17
+ import { Hand, Highlighter, MessageSquare, MousePointer2, Square } from '@inkbar/icons'
17
18
  import { Toolbar } from 'bits-ui'
18
19
 
19
20
  import { useTool } from '../hooks/useTool.svelte.js'
20
21
 
21
22
  import { asBitsProps } from './child-snippet.js'
23
+ import InkIcon from './InkIcon.svelte'
24
+
25
+ import type { IconNode } from '@inkbar/icons'
26
+
27
+ const ICONS: Readonly<Record<ToolId, IconNode>> = {
28
+ view: Hand,
29
+ select: MousePointer2,
30
+ highlight: Highlighter,
31
+ 'redact-area': Square,
32
+ note: MessageSquare,
33
+ }
22
34
 
23
35
  let {
24
36
  tool,
@@ -50,5 +62,7 @@
50
62
  if (!e.defaultPrevented) current.setTool(tool)
51
63
  }}
52
64
  >
53
- {#if children}{@render children({ active, label: name })}{:else}{name}{/if}
65
+ {#if children}{@render children({ active, label: name })}{:else}<InkIcon
66
+ icon={ICONS[tool]}
67
+ />{/if}
54
68
  </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;AA4CH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
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;AA4DH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,18 @@
1
+ <script module lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+
4
+ export type InkToolbarSeparatorProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'>
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ /* A divider between groups of controls in a horizontal toolbar. Not focusable. */
9
+ let { class: className, ...rest }: InkToolbarSeparatorProps = $props()
10
+ </script>
11
+
12
+ <div
13
+ role="separator"
14
+ aria-orientation="vertical"
15
+ class={['ink-toolbar-separator', className]}
16
+ data-ink-toolbar-separator=""
17
+ {...rest}
18
+ ></div>
@@ -0,0 +1,6 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ export type InkToolbarSeparatorProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
3
+ declare const InkToolbarSeparator: import("svelte").Component<InkToolbarSeparatorProps, {}, "">;
4
+ type InkToolbarSeparator = ReturnType<typeof InkToolbarSeparator>;
5
+ export default InkToolbarSeparator;
6
+ //# sourceMappingURL=InkToolbarSeparator.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkToolbarSeparator.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolbarSeparator.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,CACxF;AAYD,QAAA,MAAM,mBAAmB,8DAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { Undo2 } from '@inkbar/icons'
3
+ import { Toolbar } from 'bits-ui'
4
+
5
+ import { useHistory } from '../hooks/useHistory.svelte.js'
6
+
7
+ import { asBitsProps } from './child-snippet.js'
8
+ import InkIcon from './InkIcon.svelte'
9
+
10
+ import type { InkHistoryButtonProps } from './history-button.js'
11
+
12
+ let {
13
+ class: className,
14
+ children,
15
+ child,
16
+ disabled,
17
+ onclick,
18
+ ...rest
19
+ }: InkHistoryButtonProps = $props()
20
+
21
+ const history = useHistory()
22
+ </script>
23
+
24
+ <Toolbar.Button
25
+ class={['ink-undo', className]}
26
+ data-ink-undo=""
27
+ aria-label="Undo"
28
+ title="Undo"
29
+ disabled={!history.canUndo || !!disabled}
30
+ {...child ? { child } : {}}
31
+ {...asBitsProps(rest)}
32
+ onclick={(e) => {
33
+ onclick?.(e)
34
+ if (!e.defaultPrevented) history.undo()
35
+ }}
36
+ >
37
+ {#if children}{@render children()}{:else}<InkIcon icon={Undo2} />{/if}
38
+ </Toolbar.Button>
@@ -0,0 +1,5 @@
1
+ import type { InkHistoryButtonProps } from './history-button.js';
2
+ declare const InkUndo: import("svelte").Component<InkHistoryButtonProps, {}, "">;
3
+ type InkUndo = ReturnType<typeof InkUndo>;
4
+ export default InkUndo;
5
+ //# sourceMappingURL=InkUndo.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkUndo.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkUndo.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAkCjE,QAAA,MAAM,OAAO,2DAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
@@ -1,9 +1,11 @@
1
1
  <script lang="ts">
2
+ import { ZoomIn } from '@inkbar/icons'
2
3
  import { Toolbar } from 'bits-ui'
3
4
 
4
5
  import { useZoom } from '../hooks/useZoom.svelte.js'
5
6
 
6
7
  import { asBitsProps } from './child-snippet.js'
8
+ import InkIcon from './InkIcon.svelte'
7
9
 
8
10
  import type { InkZoomButtonProps } from './zoom-button.js'
9
11
 
@@ -30,5 +32,5 @@
30
32
  if (!e.defaultPrevented) zoom.zoomIn()
31
33
  }}
32
34
  >
33
- {#if children}{@render children()}{:else}+{/if}
35
+ {#if children}{@render children()}{:else}<InkIcon icon={ZoomIn} />{/if}
34
36
  </Toolbar.Button>
@@ -1 +1 @@
1
- {"version":3,"file":"InkZoomIn.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkZoomIn.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AA+B3D,QAAA,MAAM,SAAS,wDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"InkZoomIn.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkZoomIn.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAiC3D,QAAA,MAAM,SAAS,wDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -1,9 +1,11 @@
1
1
  <script lang="ts">
2
+ import { ZoomOut } from '@inkbar/icons'
2
3
  import { Toolbar } from 'bits-ui'
3
4
 
4
5
  import { useZoom } from '../hooks/useZoom.svelte.js'
5
6
 
6
7
  import { asBitsProps } from './child-snippet.js'
8
+ import InkIcon from './InkIcon.svelte'
7
9
 
8
10
  import type { InkZoomButtonProps } from './zoom-button.js'
9
11
 
@@ -30,5 +32,5 @@
30
32
  if (!e.defaultPrevented) zoom.zoomOut()
31
33
  }}
32
34
  >
33
- {#if children}{@render children()}{:else}−{/if}
35
+ {#if children}{@render children()}{:else}<InkIcon icon={ZoomOut} />{/if}
34
36
  </Toolbar.Button>
@@ -1 +1 @@
1
- {"version":3,"file":"InkZoomOut.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkZoomOut.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AA+B3D,QAAA,MAAM,UAAU,wDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"InkZoomOut.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkZoomOut.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAiC3D,QAAA,MAAM,UAAU,wDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -1,21 +1,29 @@
1
1
  <script module lang="ts">
2
- import type { HTMLSelectAttributes } from 'svelte/elements'
2
+ import type { HTMLButtonAttributes } from 'svelte/elements'
3
3
 
4
- export type InkZoomSelectProps = Omit<
5
- HTMLSelectAttributes,
6
- 'value' | 'onchange' | 'children'
7
- >
4
+ /** Attributes for the trigger button. The listbox and its options are the component's own. */
5
+ export type InkZoomSelectProps = Omit<HTMLButtonAttributes, 'value' | 'children'>
8
6
  </script>
9
7
 
10
8
  <script lang="ts">
11
9
  import { ZOOM_LEVELS, type ZoomValue } from '@inkbar/core/dom'
10
+ import { Check, ChevronDown } from '@inkbar/icons'
11
+ import { Select } from 'bits-ui'
12
12
 
13
13
  import { useZoom } from '../hooks/useZoom.svelte.js'
14
14
 
15
+ import { asBitsProps } from './child-snippet.js'
16
+ import InkIcon from './InkIcon.svelte'
17
+
15
18
  let { class: className, ...rest }: InkZoomSelectProps = $props()
16
19
 
17
20
  const CUSTOM = 'custom:'
21
+ const FITS = [
22
+ { value: 'fit-width', label: 'Fit width' },
23
+ { value: 'fit-page', label: 'Fit page' },
24
+ ]
18
25
  const level = (z: number) => ZOOM_LEVELS.find((l) => Math.abs(l - z) < 0.001)
26
+ const percent = (z: number) => `${Math.round(z * 100)}%`
19
27
 
20
28
  const zoom = useZoom()
21
29
  /** A numeric zoom that is not one of the levels (zoom in and out multiply). */
@@ -29,28 +37,69 @@
29
37
  ? String(level(zoom.zoom))
30
38
  : `${CUSTOM}${zoom.zoom}`
31
39
  )
32
- function onchange(e: Event & { currentTarget: HTMLSelectElement }): void {
33
- const v = e.currentTarget.value
40
+ /** The levels, with a custom zoom in its place among them. */
41
+ const levels = $derived.by(() => {
42
+ const list = ZOOM_LEVELS.map((l) => ({ z: l, value: String(l), label: percent(l) }))
43
+ if (custom !== null)
44
+ list.push({ z: custom, value: `${CUSTOM}${custom}`, label: percent(zoom.scale) })
45
+ return list.sort((a, b) => a.z - b.z)
46
+ })
47
+ // Every option, for the trigger's label and typeahead: the items mount only while it is open.
48
+ const items = $derived([...FITS, ...levels])
49
+
50
+ function onValueChange(v: string): void {
51
+ if (v === value) return
34
52
  const next: ZoomValue =
35
53
  v === 'fit-width' || v === 'fit-page' ? v : Number(v.replace(CUSTOM, ''))
36
54
  zoom.setZoom(next)
37
55
  }
38
56
  </script>
39
57
 
40
- <select
41
- class={['ink-zoom-select', className]}
42
- data-ink-zoom-select=""
43
- aria-label="Zoom"
44
- {value}
45
- {onchange}
46
- {...rest}
47
- >
48
- <option value="fit-width">Fit width</option>
49
- <option value="fit-page">Fit page</option>
50
- {#each ZOOM_LEVELS as l (l)}
51
- <option value={String(l)}>{Math.round(l * 100)}%</option>
52
- {/each}
53
- {#if custom !== null}
54
- <option value={`${CUSTOM}${custom}`}>{Math.round(zoom.scale * 100)}%</option>
55
- {/if}
56
- </select>
58
+ <!-- Bits UI's trigger is a plain button with a listbox popup; the role makes it the
59
+ select-only combobox of the ARIA pattern, as Reka UI and Radix render it. -->
60
+ <Select.Root type="single" {items} bind:value={() => value, onValueChange}>
61
+ <Select.Trigger
62
+ class={['ink-zoom-select', 'ink-select-trigger', className]}
63
+ data-ink-zoom-select=""
64
+ role="combobox"
65
+ aria-label="Zoom"
66
+ {...asBitsProps(rest)}
67
+ >
68
+ <Select.Value class="ink-select-value" />
69
+ <span class="ink-select-icon" aria-hidden="true">
70
+ <InkIcon icon={ChevronDown} size={16} />
71
+ </span>
72
+ </Select.Trigger>
73
+ <Select.Portal>
74
+ <Select.Content
75
+ class="ink-select-content"
76
+ data-ink-select-content=""
77
+ side="top"
78
+ sideOffset={4}
79
+ >
80
+ <Select.Viewport>
81
+ {#each items as o, i (o.value)}
82
+ <!-- The fits, then the levels. -->
83
+ {#if i === FITS.length}
84
+ <div class="ink-select-separator" aria-hidden="true"></div>
85
+ {/if}
86
+ <Select.Item
87
+ value={o.value}
88
+ label={o.label}
89
+ class="ink-select-item"
90
+ data-ink-select-item=""
91
+ >
92
+ {#snippet children({ selected })}
93
+ <span>{o.label}</span>
94
+ {#if selected}
95
+ <span class="ink-select-indicator">
96
+ <InkIcon icon={Check} size={16} />
97
+ </span>
98
+ {/if}
99
+ {/snippet}
100
+ </Select.Item>
101
+ {/each}
102
+ </Select.Viewport>
103
+ </Select.Content>
104
+ </Select.Portal>
105
+ </Select.Root>
@@ -1,5 +1,6 @@
1
- import type { HTMLSelectAttributes } from 'svelte/elements';
2
- export type InkZoomSelectProps = Omit<HTMLSelectAttributes, 'value' | 'onchange' | 'children'>;
1
+ import type { HTMLButtonAttributes } from 'svelte/elements';
2
+ /** Attributes for the trigger button. The listbox and its options are the component's own. */
3
+ export type InkZoomSelectProps = Omit<HTMLButtonAttributes, 'value' | 'children'>;
3
4
  declare const InkZoomSelect: import("svelte").Component<InkZoomSelectProps, {}, "">;
4
5
  type InkZoomSelect = ReturnType<typeof InkZoomSelect>;
5
6
  export default InkZoomSelect;
@@ -1 +1 @@
1
- {"version":3,"file":"InkZoomSelect.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkZoomSelect.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,oBAAoB,EACpB,OAAO,GAAG,UAAU,GAAG,UAAU,CAClC,CACF;AAmDD,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"InkZoomSelect.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkZoomSelect.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,8FAA8F;AAC9F,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,UAAU,CAAC,CAClF;AAiGD,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ChildSnippet } from './child-snippet.js';
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ /** Props of `InkUndo` and `InkRedo`: a toolbar button that may render through `child`. */
5
+ export interface InkHistoryButtonProps extends Omit<HTMLButtonAttributes, 'children'> {
6
+ children?: Snippet | undefined;
7
+ /** Render your own button: spread `props` on it. */
8
+ child?: ChildSnippet | undefined;
9
+ }
10
+ //# sourceMappingURL=history-button.d.ts.map