@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.
- package/README.md +71 -6
- package/dist/components/InkDownloadButton.svelte +62 -0
- package/dist/components/InkDownloadButton.svelte.d.ts +14 -0
- package/dist/components/InkDownloadButton.svelte.d.ts.map +1 -0
- package/dist/components/InkFindBar.svelte +18 -15
- package/dist/components/InkFindBar.svelte.d.ts.map +1 -1
- package/dist/components/InkHighlightMenu.svelte +101 -93
- package/dist/components/InkHighlightMenu.svelte.d.ts.map +1 -1
- package/dist/components/InkPanel.svelte +73 -0
- package/dist/components/InkPanel.svelte.d.ts +18 -0
- package/dist/components/InkPanel.svelte.d.ts.map +1 -0
- package/dist/components/InkPrintButton.svelte +57 -0
- package/dist/components/InkPrintButton.svelte.d.ts +12 -0
- package/dist/components/InkPrintButton.svelte.d.ts.map +1 -0
- package/dist/components/InkRecordList.svelte +91 -0
- package/dist/components/InkRecordList.svelte.d.ts +72 -0
- package/dist/components/InkRecordList.svelte.d.ts.map +1 -0
- package/dist/components/InkRoot.svelte +53 -5
- package/dist/components/InkRoot.svelte.d.ts +7 -2
- package/dist/components/InkRoot.svelte.d.ts.map +1 -1
- package/dist/components/InkSearchResults.svelte +72 -0
- package/dist/components/InkSearchResults.svelte.d.ts +9 -0
- package/dist/components/InkSearchResults.svelte.d.ts.map +1 -0
- package/dist/components/InkShapeMenu.svelte +89 -84
- package/dist/components/InkShapeMenu.svelte.d.ts.map +1 -1
- package/dist/components/InkSidebar.svelte +179 -0
- package/dist/components/InkSidebar.svelte.d.ts +22 -0
- package/dist/components/InkSidebar.svelte.d.ts.map +1 -0
- package/dist/components/InkSignatureDialog.svelte +7 -3
- package/dist/components/InkSignatureDialog.svelte.d.ts +4 -3
- package/dist/components/InkSignatureDialog.svelte.d.ts.map +1 -1
- package/dist/components/InkSignatureMenu.svelte +85 -77
- package/dist/components/InkSignatureMenu.svelte.d.ts.map +1 -1
- package/dist/components/InkThumbnail.svelte +74 -0
- package/dist/components/InkThumbnail.svelte.d.ts +19 -0
- package/dist/components/InkThumbnail.svelte.d.ts.map +1 -0
- package/dist/components/InkThumbnails.svelte +146 -0
- package/dist/components/InkThumbnails.svelte.d.ts +24 -0
- package/dist/components/InkThumbnails.svelte.d.ts.map +1 -0
- package/dist/components/InkToolButton.svelte +23 -24
- package/dist/components/InkToolButton.svelte.d.ts.map +1 -1
- package/dist/components/InkToolbarButton.svelte +33 -0
- package/dist/components/InkToolbarButton.svelte.d.ts +12 -0
- package/dist/components/InkToolbarButton.svelte.d.ts.map +1 -0
- package/dist/components/InkViewport.svelte +4 -0
- package/dist/components/InkViewport.svelte.d.ts.map +1 -1
- package/dist/components/InkZoomSelect.svelte +1 -1
- package/dist/components/RecordCards.svelte +173 -0
- package/dist/components/RecordCards.svelte.d.ts +16 -0
- package/dist/components/RecordCards.svelte.d.ts.map +1 -0
- package/dist/components/SidebarTrigger.svelte +20 -0
- package/dist/components/SidebarTrigger.svelte.d.ts +9 -0
- package/dist/components/SidebarTrigger.svelte.d.ts.map +1 -0
- package/dist/components/SignatureForm.svelte +9 -3
- package/dist/components/SignatureForm.svelte.d.ts.map +1 -1
- package/dist/components/body-lock.d.ts +3 -0
- package/dist/components/body-lock.d.ts.map +1 -0
- package/dist/components/body-lock.js +43 -0
- package/dist/components/sidebar-context.d.ts +24 -0
- package/dist/components/sidebar-context.d.ts.map +1 -0
- package/dist/components/sidebar-context.js +13 -0
- package/dist/context.d.ts +6 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/hooks/useAnnotations.svelte.d.ts +78 -0
- package/dist/hooks/useAnnotations.svelte.d.ts.map +1 -1
- package/dist/hooks/useRecordSelection.svelte.d.ts +11 -0
- package/dist/hooks/useRecordSelection.svelte.d.ts.map +1 -0
- package/dist/hooks/useRecordSelection.svelte.js +19 -0
- package/dist/hooks/useRecords.svelte.d.ts +14 -0
- package/dist/hooks/useRecords.svelte.d.ts.map +1 -0
- package/dist/hooks/useRecords.svelte.js +24 -0
- package/dist/hooks/useRegisteredTool.svelte.d.ts +24 -0
- package/dist/hooks/useRegisteredTool.svelte.d.ts.map +1 -0
- package/dist/hooks/useRegisteredTool.svelte.js +47 -0
- package/dist/hooks/useSearch.svelte.d.ts.map +1 -1
- package/dist/hooks/useSearch.svelte.js +1 -0
- package/dist/hooks/useToolOptions.svelte.d.ts +8 -0
- package/dist/hooks/useToolOptions.svelte.d.ts.map +1 -1
- package/dist/hooks/useToolOptions.svelte.js +12 -2
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/to-readable.svelte.d.ts +8 -0
- package/dist/to-readable.svelte.d.ts.map +1 -0
- package/dist/to-readable.svelte.js +15 -0
- package/package.json +5 -5
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
3
|
+
|
|
4
|
+
export interface InkThumbnailProps extends Omit<
|
|
5
|
+
HTMLAttributes<HTMLDivElement>,
|
|
6
|
+
'children'
|
|
7
|
+
> {
|
|
8
|
+
/** 0-based. */
|
|
9
|
+
page: number
|
|
10
|
+
/** Width in CSS pixels; the height follows the page. Default 120 unless `height` is given. */
|
|
11
|
+
width?: number | undefined
|
|
12
|
+
/** Height in CSS pixels; the width follows the page. */
|
|
13
|
+
height?: number | undefined
|
|
14
|
+
/** Draw the page's annotations. Default true. */
|
|
15
|
+
annotations?: boolean | undefined
|
|
16
|
+
/** Draw plugin marks. Default true. */
|
|
17
|
+
marks?: boolean | undefined
|
|
18
|
+
/** The accessible name. Default "Page N". */
|
|
19
|
+
label?: string | undefined
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
/*
|
|
25
|
+
* One page as a static image, with its live annotations and plugin marks: core's thumbnail
|
|
26
|
+
* painter on this element, which sizes it, names it ("Page N" unless `label` says otherwise)
|
|
27
|
+
* and draws it once it nears the visible area. It is not focusable and does nothing on click:
|
|
28
|
+
* wrap it in the control you need. Inside `InkRoot`; it renders empty on the server.
|
|
29
|
+
*/
|
|
30
|
+
import { createThumbnailPainter, type ThumbnailPainter } from '@inkbar/core/dom'
|
|
31
|
+
import { untrack } from 'svelte'
|
|
32
|
+
|
|
33
|
+
import { useInkContext } from '../context.js'
|
|
34
|
+
import { toReadable } from '../to-readable.svelte.js'
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
page,
|
|
38
|
+
width,
|
|
39
|
+
height,
|
|
40
|
+
annotations = true,
|
|
41
|
+
marks = true,
|
|
42
|
+
label,
|
|
43
|
+
class: className,
|
|
44
|
+
...rest
|
|
45
|
+
}: InkThumbnailProps = $props()
|
|
46
|
+
|
|
47
|
+
const ctx = useInkContext('InkThumbnail')
|
|
48
|
+
const deps = {
|
|
49
|
+
document: toReadable(() => ctx.document),
|
|
50
|
+
pluginHost: toReadable(() => ctx.pluginHost),
|
|
51
|
+
}
|
|
52
|
+
const options = () => ({ page, width, height, annotations, marks, label })
|
|
53
|
+
|
|
54
|
+
let el = $state<HTMLDivElement | null>(null)
|
|
55
|
+
let painter: ThumbnailPainter | null = null
|
|
56
|
+
// Before the painter is made, so a new painter does not take its first options twice.
|
|
57
|
+
$effect(() => {
|
|
58
|
+
const next = options()
|
|
59
|
+
untrack(() => painter?.update(next))
|
|
60
|
+
})
|
|
61
|
+
$effect(() => {
|
|
62
|
+
const target = el
|
|
63
|
+
if (!target) return
|
|
64
|
+
const p = untrack(() => createThumbnailPainter(target, deps, options()))
|
|
65
|
+
painter = p
|
|
66
|
+
return () => {
|
|
67
|
+
p.destroy()
|
|
68
|
+
painter = null
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<!-- The class is the painter's too: kept here, a re-render never drops it. -->
|
|
74
|
+
<div {...rest} bind:this={el} class={['ink-thumbnail', className]}></div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
export interface InkThumbnailProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
3
|
+
/** 0-based. */
|
|
4
|
+
page: number;
|
|
5
|
+
/** Width in CSS pixels; the height follows the page. Default 120 unless `height` is given. */
|
|
6
|
+
width?: number | undefined;
|
|
7
|
+
/** Height in CSS pixels; the width follows the page. */
|
|
8
|
+
height?: number | undefined;
|
|
9
|
+
/** Draw the page's annotations. Default true. */
|
|
10
|
+
annotations?: boolean | undefined;
|
|
11
|
+
/** Draw plugin marks. Default true. */
|
|
12
|
+
marks?: boolean | undefined;
|
|
13
|
+
/** The accessible name. Default "Page N". */
|
|
14
|
+
label?: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
declare const InkThumbnail: import("svelte").Component<InkThumbnailProps, {}, "">;
|
|
17
|
+
type InkThumbnail = ReturnType<typeof InkThumbnail>;
|
|
18
|
+
export default InkThumbnail;
|
|
19
|
+
//# sourceMappingURL=InkThumbnail.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkThumbnail.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkThumbnail.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,eAAe;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACjC,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3B;AAmEH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
4
|
+
|
|
5
|
+
export interface InkThumbnailsProps extends Omit<
|
|
6
|
+
HTMLAttributes<HTMLDivElement>,
|
|
7
|
+
'children'
|
|
8
|
+
> {
|
|
9
|
+
/** 0-based pages to list, in order. Default: every page. */
|
|
10
|
+
pages?: readonly number[] | undefined
|
|
11
|
+
/** Thumbnail width in CSS pixels. Default: the list's width less a 16px gutter each side. */
|
|
12
|
+
width?: number | undefined
|
|
13
|
+
/** Draw each page's annotations. Default true. */
|
|
14
|
+
annotations?: boolean | undefined
|
|
15
|
+
/** Draw plugin marks. Default true. */
|
|
16
|
+
marks?: boolean | undefined
|
|
17
|
+
/**
|
|
18
|
+
* Renders inside each item, after its page number. The item's height is fixed from the
|
|
19
|
+
* page's, so keep what you add out of the flow (position it absolutely) or small.
|
|
20
|
+
*/
|
|
21
|
+
item?: Snippet<[{ page: number; current: boolean }]> | undefined
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
/*
|
|
27
|
+
* The pages as a list of thumbnails: `createThumbnails`' markup on core's thumbnails
|
|
28
|
+
* controller, which holds the current page, the keys, the clicks, the Tab stop and the window.
|
|
29
|
+
* Only the items near the visible part are mounted, each placed at its offset over a spacer of
|
|
30
|
+
* the full height, and each holds an `InkThumbnail`. An item is a button that scrolls the
|
|
31
|
+
* viewport to its page; the current page's has `aria-current="page"`. Inside `InkRoot`; it
|
|
32
|
+
* renders empty on the server.
|
|
33
|
+
*/
|
|
34
|
+
import {
|
|
35
|
+
createThumbnailsController,
|
|
36
|
+
THUMBNAIL_WIDTH,
|
|
37
|
+
type ThumbnailsController,
|
|
38
|
+
type ThumbnailsState,
|
|
39
|
+
} from '@inkbar/core/dom'
|
|
40
|
+
import { untrack } from 'svelte'
|
|
41
|
+
|
|
42
|
+
import { useInkContext } from '../context.js'
|
|
43
|
+
import { toReadable } from '../to-readable.svelte.js'
|
|
44
|
+
|
|
45
|
+
import InkThumbnail from './InkThumbnail.svelte'
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
pages,
|
|
49
|
+
width,
|
|
50
|
+
annotations = true,
|
|
51
|
+
marks = true,
|
|
52
|
+
item,
|
|
53
|
+
class: className,
|
|
54
|
+
...rest
|
|
55
|
+
}: InkThumbnailsProps = $props()
|
|
56
|
+
|
|
57
|
+
const ctx = useInkContext('InkThumbnails')
|
|
58
|
+
const deps = {
|
|
59
|
+
document: toReadable(() => ctx.document),
|
|
60
|
+
pluginHost: toReadable(() => ctx.pluginHost),
|
|
61
|
+
viewport: toReadable(() => ctx.viewport),
|
|
62
|
+
}
|
|
63
|
+
const options = () => ({ pages, width, annotations, marks })
|
|
64
|
+
|
|
65
|
+
let scroller = $state<HTMLDivElement | null>(null)
|
|
66
|
+
let controller = $state.raw<ThumbnailsController | null>(null)
|
|
67
|
+
/** Until the list mounts, and on the server: nothing listed. */
|
|
68
|
+
let listed = $state.raw<ThumbnailsState>({
|
|
69
|
+
pages: [],
|
|
70
|
+
current: -1,
|
|
71
|
+
window: { start: 0, end: 0 },
|
|
72
|
+
offsets: [0],
|
|
73
|
+
width: THUMBNAIL_WIDTH,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// A list unchanged in content publishes nothing, so an inline `pages` array cannot loop.
|
|
77
|
+
$effect(() => {
|
|
78
|
+
const c = controller
|
|
79
|
+
const next = options()
|
|
80
|
+
if (c) untrack(() => c.update(next))
|
|
81
|
+
})
|
|
82
|
+
$effect(() => {
|
|
83
|
+
const el = scroller
|
|
84
|
+
if (!el) return
|
|
85
|
+
return untrack(() => {
|
|
86
|
+
const c = createThumbnailsController(deps, options())
|
|
87
|
+
controller = c
|
|
88
|
+
listed = c.state.get()
|
|
89
|
+
const stop = c.state.subscribe((s) => {
|
|
90
|
+
listed = s
|
|
91
|
+
})
|
|
92
|
+
c.attach(el)
|
|
93
|
+
return () => {
|
|
94
|
+
stop()
|
|
95
|
+
c.destroy()
|
|
96
|
+
controller = null
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const items = $derived.by(() => {
|
|
102
|
+
const { pages: list, current, window, offsets } = listed
|
|
103
|
+
return list.slice(window.start, window.end).map((page, k) => {
|
|
104
|
+
const top = offsets[window.start + k]!
|
|
105
|
+
const bottom = offsets[window.start + k + 1]!
|
|
106
|
+
// Only a mounted controller has a window, so `controller` is set here.
|
|
107
|
+
const attrs = controller?.itemAttrs(window.start + k) ?? {}
|
|
108
|
+
return { page, current: page === current, top, height: bottom - top, attrs }
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<div
|
|
114
|
+
{...rest}
|
|
115
|
+
bind:this={scroller}
|
|
116
|
+
class={['ink-thumbnails', className]}
|
|
117
|
+
data-ink-thumbnails=""
|
|
118
|
+
role="list"
|
|
119
|
+
>
|
|
120
|
+
<div
|
|
121
|
+
class="ink-thumbnails-spacer"
|
|
122
|
+
aria-hidden="true"
|
|
123
|
+
style:height="{listed.offsets.at(-1) ?? 0}px"
|
|
124
|
+
></div>
|
|
125
|
+
{#each items as entry (entry.page)}
|
|
126
|
+
<div
|
|
127
|
+
class="ink-thumbnails-entry"
|
|
128
|
+
{...entry.attrs}
|
|
129
|
+
style:top="{entry.top}px"
|
|
130
|
+
style:height="{entry.height}px"
|
|
131
|
+
>
|
|
132
|
+
<!-- The controller sets the tabindex. The thumbnail names the button "Page N"; the
|
|
133
|
+
visible number repeats it. -->
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
class="ink-thumbnails-item"
|
|
137
|
+
data-ink-thumbnail-item={entry.page}
|
|
138
|
+
aria-current={entry.current ? 'page' : undefined}
|
|
139
|
+
>
|
|
140
|
+
<InkThumbnail page={entry.page} width={listed.width} {annotations} {marks} />
|
|
141
|
+
<span class="ink-thumbnails-label" aria-hidden="true">{entry.page + 1}</span>
|
|
142
|
+
{@render item?.({ page: entry.page, current: entry.current })}
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
{/each}
|
|
146
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
export interface InkThumbnailsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
4
|
+
/** 0-based pages to list, in order. Default: every page. */
|
|
5
|
+
pages?: readonly number[] | undefined;
|
|
6
|
+
/** Thumbnail width in CSS pixels. Default: the list's width less a 16px gutter each side. */
|
|
7
|
+
width?: number | undefined;
|
|
8
|
+
/** Draw each page's annotations. Default true. */
|
|
9
|
+
annotations?: boolean | undefined;
|
|
10
|
+
/** Draw plugin marks. Default true. */
|
|
11
|
+
marks?: boolean | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Renders inside each item, after its page number. The item's height is fixed from the
|
|
14
|
+
* page's, so keep what you add out of the flow (position it absolutely) or small.
|
|
15
|
+
*/
|
|
16
|
+
item?: Snippet<[{
|
|
17
|
+
page: number;
|
|
18
|
+
current: boolean;
|
|
19
|
+
}]> | undefined;
|
|
20
|
+
}
|
|
21
|
+
declare const InkThumbnails: import("svelte").Component<InkThumbnailsProps, {}, "">;
|
|
22
|
+
type InkThumbnails = ReturnType<typeof InkThumbnails>;
|
|
23
|
+
export default InkThumbnails;
|
|
24
|
+
//# sourceMappingURL=InkThumbnails.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkThumbnails.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkThumbnails.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAC9C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACjC,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;CACjE;AAsHH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -13,21 +13,12 @@
|
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<script lang="ts">
|
|
16
|
-
import { TOOL_LABELS } from '@inkbar/core/dom'
|
|
17
|
-
import {
|
|
18
|
-
Highlighter,
|
|
19
|
-
MessageSquare,
|
|
20
|
-
PenLine,
|
|
21
|
-
PenTool,
|
|
22
|
-
Signature,
|
|
23
|
-
Square,
|
|
24
|
-
SquareDashedMousePointer,
|
|
25
|
-
TextCursor,
|
|
26
|
-
Type,
|
|
27
|
-
} from '@inkbar/icons'
|
|
16
|
+
import { TOOL_LABELS, withHostTools } from '@inkbar/core/dom'
|
|
17
|
+
import { SquareDashedMousePointer, TextCursor } from '@inkbar/icons'
|
|
28
18
|
import { Toolbar } from 'bits-ui'
|
|
29
19
|
|
|
30
20
|
import { useInkContext } from '../context.js'
|
|
21
|
+
import { useRegisteredTool } from '../hooks/useRegisteredTool.svelte.js'
|
|
31
22
|
import { useTool } from '../hooks/useTool.svelte.js'
|
|
32
23
|
|
|
33
24
|
import { asBitsProps } from './child-snippet.js'
|
|
@@ -38,15 +29,19 @@
|
|
|
38
29
|
const ICONS: Readonly<Partial<Record<ToolId, IconNode>>> = {
|
|
39
30
|
'select-text': TextCursor,
|
|
40
31
|
select: SquareDashedMousePointer,
|
|
41
|
-
highlight: Highlighter,
|
|
42
|
-
'redact-area': Square,
|
|
43
|
-
sketch: PenTool,
|
|
44
|
-
draw: PenLine,
|
|
45
|
-
note: MessageSquare,
|
|
46
|
-
freetext: Type,
|
|
47
|
-
signature: Signature,
|
|
48
32
|
}
|
|
49
33
|
|
|
34
|
+
// A tool nobody registered leaves the button disabled, hidden from assistive tech, unnamed
|
|
35
|
+
// and inert, whatever the host passed.
|
|
36
|
+
const MISSING = {
|
|
37
|
+
disabled: true,
|
|
38
|
+
'aria-hidden': 'true',
|
|
39
|
+
'aria-pressed': undefined,
|
|
40
|
+
'aria-label': undefined,
|
|
41
|
+
title: undefined,
|
|
42
|
+
onclick: undefined,
|
|
43
|
+
} as const
|
|
44
|
+
|
|
50
45
|
let {
|
|
51
46
|
tool,
|
|
52
47
|
label,
|
|
@@ -60,9 +55,10 @@
|
|
|
60
55
|
const ctx = useInkContext('InkToolButton')
|
|
61
56
|
const current = useTool()
|
|
62
57
|
const active = $derived(current.tool === tool)
|
|
63
|
-
|
|
58
|
+
const registered = useRegisteredTool(() => tool)
|
|
59
|
+
/** The registered tool by this id, for the name and icon core has none for. */
|
|
64
60
|
const own = $derived(
|
|
65
|
-
[...(ctx.hostTools ?? []), ...(ctx.pluginHost?.tools ?? [])].find(
|
|
61
|
+
withHostTools([...(ctx.hostTools ?? []), ...(ctx.pluginHost?.tools ?? [])]).find(
|
|
66
62
|
(t) => t.id === tool
|
|
67
63
|
)
|
|
68
64
|
)
|
|
@@ -85,8 +81,11 @@
|
|
|
85
81
|
onclick?.(e)
|
|
86
82
|
if (!e.defaultPrevented) current.setTool(tool)
|
|
87
83
|
}}
|
|
84
|
+
{...registered.current ? {} : MISSING}
|
|
88
85
|
>
|
|
89
|
-
{#if
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
{#if registered.current}
|
|
87
|
+
{#if children}{@render children({ active, label: name })}{:else if icon}<InkIcon
|
|
88
|
+
{icon}
|
|
89
|
+
/>{/if}
|
|
90
|
+
{/if}
|
|
92
91
|
</Toolbar.Button>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InkToolButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolButton.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACpE,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC;
|
|
1
|
+
{"version":3,"file":"InkToolButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolButton.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACpE,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC;AAoFH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { ChildSnippet } from './child-snippet.js'
|
|
3
|
+
import type { Snippet } from 'svelte'
|
|
4
|
+
import type { HTMLButtonAttributes } from 'svelte/elements'
|
|
5
|
+
|
|
6
|
+
export interface InkToolbarButtonProps extends Omit<HTMLButtonAttributes, 'children'> {
|
|
7
|
+
children?: Snippet | undefined
|
|
8
|
+
/** Render your own button: spread `props` on it. */
|
|
9
|
+
child?: ChildSnippet | undefined
|
|
10
|
+
}
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<!--
|
|
14
|
+
Your own button in an `InkToolbar`. It joins the toolbar's arrow-key focus and takes the
|
|
15
|
+
theme's toolbar button styles; what it shows and does is up to you.
|
|
16
|
+
-->
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import { Toolbar } from 'bits-ui'
|
|
19
|
+
|
|
20
|
+
import { asBitsProps } from './child-snippet.js'
|
|
21
|
+
|
|
22
|
+
let { class: className, children, child, ...rest }: InkToolbarButtonProps = $props()
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<Toolbar.Button
|
|
26
|
+
type="button"
|
|
27
|
+
class={['ink-toolbar-button', className]}
|
|
28
|
+
data-ink-toolbar-button=""
|
|
29
|
+
{...child ? { child } : {}}
|
|
30
|
+
{...asBitsProps(rest)}
|
|
31
|
+
>
|
|
32
|
+
{@render children?.()}
|
|
33
|
+
</Toolbar.Button>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChildSnippet } from './child-snippet.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
|
+
export interface InkToolbarButtonProps extends Omit<HTMLButtonAttributes, 'children'> {
|
|
5
|
+
children?: Snippet | undefined;
|
|
6
|
+
/** Render your own button: spread `props` on it. */
|
|
7
|
+
child?: ChildSnippet | undefined;
|
|
8
|
+
}
|
|
9
|
+
declare const InkToolbarButton: import("svelte").Component<InkToolbarButtonProps, {}, "">;
|
|
10
|
+
type InkToolbarButton = ReturnType<typeof InkToolbarButton>;
|
|
11
|
+
export default InkToolbarButton;
|
|
12
|
+
//# sourceMappingURL=InkToolbarButton.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkToolbarButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolbarButton.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IACnF,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,oDAAoD;IACpD,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC;AAwBH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
import { untrack } from 'svelte'
|
|
44
44
|
|
|
45
45
|
import { useInkContext } from '../context.js'
|
|
46
|
+
import { toolIdsOf } from '../hooks/useRegisteredTool.svelte.js'
|
|
46
47
|
|
|
47
48
|
import InkFormAlert from './InkFormAlert.svelte'
|
|
48
49
|
import InkPage from './InkPage.svelte'
|
|
@@ -115,6 +116,7 @@
|
|
|
115
116
|
engine?.destroy()
|
|
116
117
|
engine = null
|
|
117
118
|
builtWith = null
|
|
119
|
+
ctx.setOpenedToolIds(null)
|
|
118
120
|
ctx.setViewport(null, null)
|
|
119
121
|
if (pages.length > 0) pages = []
|
|
120
122
|
// Their fields and the document they came from are gone.
|
|
@@ -134,6 +136,8 @@
|
|
|
134
136
|
build += 1
|
|
135
137
|
const custom = !!annotation
|
|
136
138
|
builtWith = { gap, buffer, custom }
|
|
139
|
+
// The tools this viewer registers, from the same inputs; a later `tools` waits for a rebuild.
|
|
140
|
+
ctx.setOpenedToolIds(toolIdsOf(ctx.hostTools, ctx.pluginHost))
|
|
137
141
|
engine = mountViewer({
|
|
138
142
|
container: el,
|
|
139
143
|
document: doc,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InkViewport.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkViewport.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,SAAS,CAAA;IACvD,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;CACrE;
|
|
1
|
+
{"version":3,"file":"InkViewport.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkViewport.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,SAAS,CAAA;IACvD,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;CACrE;AA2WH,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
select-only combobox of the ARIA pattern, as Reka UI and Radix render it. -->
|
|
60
60
|
<Select.Root type="single" {items} bind:value={() => value, onValueChange}>
|
|
61
61
|
<Select.Trigger
|
|
62
|
-
class={['ink-
|
|
62
|
+
class={['ink-select', 'ink-select-trigger', 'ink-zoom-select', className]}
|
|
63
63
|
data-ink-zoom-select=""
|
|
64
64
|
role="combobox"
|
|
65
65
|
aria-label="Zoom"
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* A record list's markup on core's record list controller, shared by `InkRecordList` and
|
|
4
|
+
* `InkSearchResults`: `createRecordList`'s `div.ink-records` holding `ul.ink-record-list` with
|
|
5
|
+
* one `li.ink-record` card per row, the empty text and a polite live region. The controller
|
|
6
|
+
* holds the rows, the count and the announcement, and handles the clicks, keys, Tab stop,
|
|
7
|
+
* focus and scrolling. Empty, with no empty text, until there is a source.
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
createRecordListController,
|
|
11
|
+
type RecordListController,
|
|
12
|
+
type RecordListRow,
|
|
13
|
+
type RecordListSource,
|
|
14
|
+
} from '@inkbar/core/dom'
|
|
15
|
+
import { untrack, type Snippet } from 'svelte'
|
|
16
|
+
|
|
17
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
source,
|
|
21
|
+
emptyShown = true,
|
|
22
|
+
empty,
|
|
23
|
+
oncount,
|
|
24
|
+
class: className,
|
|
25
|
+
...rest
|
|
26
|
+
}: Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
27
|
+
source: RecordListSource | null
|
|
28
|
+
/** Whether the empty text shows when there are no rows. Default true. */
|
|
29
|
+
emptyShown?: boolean | undefined
|
|
30
|
+
/** Replaces "Nothing here yet.". */
|
|
31
|
+
empty?: Snippet | undefined
|
|
32
|
+
/** Called with the number listed whenever it changes. */
|
|
33
|
+
oncount?: ((count: number) => void) | undefined
|
|
34
|
+
} = $props()
|
|
35
|
+
|
|
36
|
+
/** A focused action button whose row re-renders: where to put focus back if it goes. */
|
|
37
|
+
interface HeldFocus {
|
|
38
|
+
button: HTMLElement
|
|
39
|
+
id: string
|
|
40
|
+
index: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const actionButtons = (row: Element): HTMLElement[] => [
|
|
44
|
+
...row.querySelectorAll<HTMLElement>('.ink-card-actions > button'),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
let root = $state<HTMLDivElement | null>(null)
|
|
48
|
+
let controller = $state.raw<RecordListController | null>(null)
|
|
49
|
+
let rows = $state.raw<readonly RecordListRow[]>([])
|
|
50
|
+
let announcement = $state('')
|
|
51
|
+
const showEmpty = $derived(controller !== null && rows.length === 0 && emptyShown)
|
|
52
|
+
|
|
53
|
+
/** The count last reported, across sources. */
|
|
54
|
+
let reported: number | null = null
|
|
55
|
+
let held: HeldFocus | null = null
|
|
56
|
+
|
|
57
|
+
function report(count: number): void {
|
|
58
|
+
if (count === reported) return
|
|
59
|
+
reported = count
|
|
60
|
+
oncount?.(count)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Before the rows re-render under a focused action whose button goes (its card lost that
|
|
65
|
+
* action), note where it was; the effect below focuses the same place in the card, else the
|
|
66
|
+
* row. A row that goes is the controller's to re-home.
|
|
67
|
+
*/
|
|
68
|
+
function holdActionFocus(el: HTMLElement): void {
|
|
69
|
+
const active = el.ownerDocument.activeElement
|
|
70
|
+
if (!(active instanceof HTMLButtonElement) || !el.contains(active)) return
|
|
71
|
+
const row = active.closest<HTMLElement>('[data-ink-record]')
|
|
72
|
+
const id = row?.dataset.inkRecord
|
|
73
|
+
if (!row || id === undefined) return
|
|
74
|
+
held = { button: active, id, index: actionButtons(row).indexOf(active) }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
$effect(() => {
|
|
78
|
+
const el = root
|
|
79
|
+
const s = source
|
|
80
|
+
if (!el || !s) return
|
|
81
|
+
return untrack(() => {
|
|
82
|
+
const c = createRecordListController(s)
|
|
83
|
+
controller = c
|
|
84
|
+
rows = c.rows.get()
|
|
85
|
+
announcement = c.announcement.get()
|
|
86
|
+
report(c.count.get())
|
|
87
|
+
const stops = [
|
|
88
|
+
c.rows.subscribe((next) => {
|
|
89
|
+
holdActionFocus(el)
|
|
90
|
+
rows = next
|
|
91
|
+
}),
|
|
92
|
+
c.count.subscribe(report),
|
|
93
|
+
c.announcement.subscribe((text) => {
|
|
94
|
+
announcement = text
|
|
95
|
+
}),
|
|
96
|
+
]
|
|
97
|
+
const detach = c.attach(el)
|
|
98
|
+
return () => {
|
|
99
|
+
for (const stop of stops) stop()
|
|
100
|
+
detach()
|
|
101
|
+
c.destroy()
|
|
102
|
+
controller = null
|
|
103
|
+
rows = []
|
|
104
|
+
announcement = ''
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// After the rows have re-rendered.
|
|
110
|
+
$effect(() => {
|
|
111
|
+
void rows
|
|
112
|
+
const el = root
|
|
113
|
+
untrack(() => {
|
|
114
|
+
const focus = held
|
|
115
|
+
held = null
|
|
116
|
+
if (!focus || !el || focus.button.isConnected) return
|
|
117
|
+
const row = [...el.querySelectorAll<HTMLElement>('[data-ink-record]')].find(
|
|
118
|
+
(r) => r.dataset.inkRecord === focus.id
|
|
119
|
+
)
|
|
120
|
+
if (row) (actionButtons(row)[focus.index] ?? row).focus({ preventScroll: true })
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<div
|
|
126
|
+
{...rest}
|
|
127
|
+
bind:this={root}
|
|
128
|
+
class={['ink-records', className]}
|
|
129
|
+
data-ink-records=""
|
|
130
|
+
tabindex="-1"
|
|
131
|
+
>
|
|
132
|
+
<ul class="ink-record-list" role="list" hidden={rows.length === 0}>
|
|
133
|
+
<!-- The controller sets each row's tabindex. -->
|
|
134
|
+
{#each rows as { item, card, selected } (item.id)}
|
|
135
|
+
<li
|
|
136
|
+
class="ink-record"
|
|
137
|
+
data-ink-record={item.id}
|
|
138
|
+
aria-current={selected ? 'true' : undefined}
|
|
139
|
+
data-selected={selected ? '' : undefined}
|
|
140
|
+
>
|
|
141
|
+
<div class={['ink-card', card?.className]}>
|
|
142
|
+
{#if card}
|
|
143
|
+
<p class="ink-card-title">{card.title}</p>
|
|
144
|
+
{#if card.meta !== undefined}
|
|
145
|
+
<!-- prettier-ignore -->
|
|
146
|
+
<p class="ink-card-meta">{#if card.swatch !== undefined}<span class="ink-card-swatch" style:background={card.swatch}></span>{/if}{card.meta}</p>
|
|
147
|
+
{/if}
|
|
148
|
+
{#if card.body !== undefined}
|
|
149
|
+
<p class="ink-card-body">{card.body}</p>
|
|
150
|
+
{/if}
|
|
151
|
+
{#if card.excerpt !== undefined}
|
|
152
|
+
<!-- prettier-ignore -->
|
|
153
|
+
<p class="ink-card-excerpt">{card.excerpt.before}<mark>{card.excerpt.match}</mark>{card.excerpt.after}</p>
|
|
154
|
+
{/if}
|
|
155
|
+
<div class="ink-card-actions">
|
|
156
|
+
{#each Object.keys(card.actions) as label (label)}
|
|
157
|
+
<button type="button" onclick={() => controller?.act(item.id, label)}
|
|
158
|
+
>{label}</button
|
|
159
|
+
>
|
|
160
|
+
{/each}
|
|
161
|
+
</div>
|
|
162
|
+
{/if}
|
|
163
|
+
</div>
|
|
164
|
+
</li>
|
|
165
|
+
{/each}
|
|
166
|
+
</ul>
|
|
167
|
+
<p class="ink-record-list-empty" hidden={!showEmpty}>
|
|
168
|
+
{#if empty}{@render empty()}{:else}Nothing here yet.{/if}
|
|
169
|
+
</p>
|
|
170
|
+
<div class="ink-visually-hidden" aria-live="polite" data-ink-record-status="">
|
|
171
|
+
{announcement}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RecordListSource } from '@inkbar/core/dom';
|
|
2
|
+
import { type Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
5
|
+
source: RecordListSource | null;
|
|
6
|
+
/** Whether the empty text shows when there are no rows. Default true. */
|
|
7
|
+
emptyShown?: boolean | undefined;
|
|
8
|
+
/** Replaces "Nothing here yet.". */
|
|
9
|
+
empty?: Snippet | undefined;
|
|
10
|
+
/** Called with the number listed whenever it changes. */
|
|
11
|
+
oncount?: ((count: number) => void) | undefined;
|
|
12
|
+
};
|
|
13
|
+
declare const RecordCards: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type RecordCards = ReturnType<typeof RecordCards>;
|
|
15
|
+
export default RecordCards;
|
|
16
|
+
//# sourceMappingURL=RecordCards.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecordCards.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/RecordCards.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,EAIH,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAA;AAC3B,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAA;AAE9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AASrD,KAAK,gBAAgB,GAAI,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG;IACzE,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC/B,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAChC,oCAAoC;IACpC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,yDAAyD;IACzD,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;CAChD,CAAC;AA2IJ,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* A sidebar trigger's content: `createSidebar`'s markup, so the theme styles both alike. The
|
|
4
|
+
* rail shows the icon alone (the trigger's `title` carries the label); tabs and stack headers
|
|
5
|
+
* show the label too. The badge is hidden from assistive technology: the trigger's name has it.
|
|
6
|
+
*/
|
|
7
|
+
import { sidebarBadge, type SidebarPanelInfo } from '@inkbar/core/dom'
|
|
8
|
+
|
|
9
|
+
import InkIcon from './InkIcon.svelte'
|
|
10
|
+
|
|
11
|
+
let { panel, rail }: { panel: SidebarPanelInfo; rail: boolean } = $props()
|
|
12
|
+
|
|
13
|
+
const badge = $derived(sidebarBadge(panel.badge))
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
{#if panel.icon}<InkIcon icon={panel.icon} />{/if}{#if !panel.icon || !rail}<span
|
|
17
|
+
class="ink-sidebar-label">{panel.label}</span
|
|
18
|
+
>{/if}<span class="ink-sidebar-badge" aria-hidden="true" hidden={badge === null}
|
|
19
|
+
>{badge}</span
|
|
20
|
+
>
|