@dorsk/tsumikit 0.38.1 → 0.40.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/dist/autoresize.d.ts +2 -0
- package/dist/autoresize.js +15 -1
- package/dist/components/atoms/Artwork.svelte +19 -18
- package/dist/components/atoms/Artwork.svelte.d.ts +3 -2
- package/dist/components/atoms/Badge.svelte +60 -26
- package/dist/components/atoms/Badge.svelte.d.ts +14 -3
- package/dist/components/atoms/Button.svelte +30 -12
- package/dist/components/atoms/Button.svelte.d.ts +4 -2
- package/dist/components/atoms/Card.svelte +47 -30
- package/dist/components/atoms/Card.svelte.d.ts +12 -3
- package/dist/components/atoms/Checkbox.svelte +16 -2
- package/dist/components/atoms/Checkbox.svelte.d.ts +2 -0
- package/dist/components/atoms/Dot.svelte +17 -11
- package/dist/components/atoms/Dot.svelte.d.ts +5 -2
- package/dist/components/atoms/Gauge.svelte +19 -18
- package/dist/components/atoms/Gauge.svelte.d.ts +3 -2
- package/dist/components/atoms/Heading.svelte +20 -19
- package/dist/components/atoms/Heading.svelte.d.ts +3 -2
- package/dist/components/atoms/Icon.svelte +36 -13
- package/dist/components/atoms/Icon.svelte.d.ts +20 -2
- package/dist/components/atoms/Input.svelte +17 -3
- package/dist/components/atoms/Input.svelte.d.ts +2 -1
- package/dist/components/atoms/Kbd.svelte +25 -3
- package/dist/components/atoms/Kbd.svelte.d.ts +5 -1
- package/dist/components/atoms/Link.svelte +16 -12
- package/dist/components/atoms/Link.svelte.d.ts +6 -2
- package/dist/components/atoms/Progress.svelte +119 -4
- package/dist/components/atoms/Progress.svelte.d.ts +28 -2
- package/dist/components/atoms/Scrim.svelte +7 -2
- package/dist/components/atoms/Scrim.svelte.d.ts +2 -0
- package/dist/components/atoms/SegmentedProgress.svelte +14 -2
- package/dist/components/atoms/SegmentedProgress.svelte.d.ts +5 -2
- package/dist/components/atoms/Select.svelte +18 -6
- package/dist/components/atoms/Select.svelte.d.ts +3 -3
- package/dist/components/atoms/Skeleton.svelte +12 -11
- package/dist/components/atoms/Skeleton.svelte.d.ts +3 -2
- package/dist/components/atoms/Slider.svelte +45 -3
- package/dist/components/atoms/Slider.svelte.d.ts +7 -0
- package/dist/components/atoms/Spinner.svelte +2 -0
- package/dist/components/atoms/Spinner.svelte.d.ts +2 -0
- package/dist/components/atoms/Switch.svelte +70 -18
- package/dist/components/atoms/Switch.svelte.d.ts +5 -1
- package/dist/components/atoms/Text.svelte +39 -26
- package/dist/components/atoms/Text.svelte.d.ts +7 -3
- package/dist/components/atoms/Textarea.svelte +47 -10
- package/dist/components/atoms/Textarea.svelte.d.ts +10 -3
- package/dist/components/layouts/AppShell.svelte +7 -3
- package/dist/components/layouts/AppShell.svelte.d.ts +2 -0
- package/dist/components/layouts/AutoGrid.svelte +8 -0
- package/dist/components/layouts/AutoGrid.svelte.d.ts +2 -0
- package/dist/components/layouts/Cluster.svelte +62 -1
- package/dist/components/layouts/Cluster.svelte.d.ts +9 -0
- package/dist/components/layouts/NavItem.svelte +22 -1
- package/dist/components/layouts/NavItem.svelte.d.ts +5 -0
- package/dist/components/layouts/NavSection.svelte +4 -1
- package/dist/components/layouts/NavSection.svelte.d.ts +2 -0
- package/dist/components/layouts/ResizablePanel.svelte +14 -3
- package/dist/components/layouts/ResizablePanel.svelte.d.ts +5 -0
- package/dist/components/layouts/Stack.svelte +41 -0
- package/dist/components/layouts/Stack.svelte.d.ts +9 -0
- package/dist/components/layouts/Toolbar.svelte +122 -0
- package/dist/components/layouts/Toolbar.svelte.d.ts +22 -0
- package/dist/components/molecules/AttachmentList.svelte +59 -0
- package/dist/components/molecules/AttachmentList.svelte.d.ts +13 -0
- package/dist/components/molecules/Breadcrumb.svelte +6 -2
- package/dist/components/molecules/Breadcrumb.svelte.d.ts +2 -0
- package/dist/components/molecules/Callout.svelte +10 -4
- package/dist/components/molecules/Callout.svelte.d.ts +1 -1
- package/dist/components/molecules/CapBar.svelte +5 -1
- package/dist/components/molecules/ChatBubble.svelte +158 -0
- package/dist/components/molecules/ChatBubble.svelte.d.ts +26 -0
- package/dist/components/molecules/CodeBlock.svelte +36 -3
- package/dist/components/molecules/CodeBlock.svelte.d.ts +8 -0
- package/dist/components/molecules/Composer.svelte +195 -0
- package/dist/components/molecules/Composer.svelte.d.ts +27 -0
- package/dist/components/molecules/ConfirmModal.svelte +15 -5
- package/dist/components/molecules/ConfirmModal.svelte.d.ts +5 -1
- package/dist/components/molecules/Drawer.svelte +9 -6
- package/dist/components/molecules/Dropzone.svelte +9 -3
- package/dist/components/molecules/Dropzone.svelte.d.ts +2 -0
- package/dist/components/molecules/EmptyState.svelte +9 -5
- package/dist/components/molecules/EmptyState.svelte.d.ts +3 -1
- package/dist/components/molecules/Field.svelte +7 -1
- package/dist/components/molecules/Field.svelte.d.ts +2 -0
- package/dist/components/molecules/FileButton.svelte +2 -1
- package/dist/components/molecules/FileButton.svelte.d.ts +2 -1
- package/dist/components/molecules/FilterInput.svelte +13 -3
- package/dist/components/molecules/FilterInput.svelte.d.ts +4 -1
- package/dist/components/molecules/FontScalePicker.svelte +8 -1
- package/dist/components/molecules/IconButton.svelte +32 -7
- package/dist/components/molecules/IconButton.svelte.d.ts +9 -1
- package/dist/components/molecules/KeyValue.svelte +4 -1
- package/dist/components/molecules/KeyValue.svelte.d.ts +2 -1
- package/dist/components/molecules/Menu.svelte +50 -5
- package/dist/components/molecules/Menu.svelte.d.ts +10 -1
- package/dist/components/molecules/Meter.svelte +91 -0
- package/dist/components/molecules/Meter.svelte.d.ts +29 -0
- package/dist/components/molecules/Metric.svelte +134 -8
- package/dist/components/molecules/Metric.svelte.d.ts +20 -2
- package/dist/components/molecules/Modal.svelte +26 -12
- package/dist/components/molecules/Modal.svelte.d.ts +7 -1
- package/dist/components/molecules/Pagination.svelte +25 -1
- package/dist/components/molecules/Pagination.svelte.d.ts +9 -2
- package/dist/components/molecules/Popover.svelte +16 -5
- package/dist/components/molecules/Popover.svelte.d.ts +5 -0
- package/dist/components/molecules/Prose.svelte +140 -0
- package/dist/components/molecules/Prose.svelte.d.ts +13 -0
- package/dist/components/molecules/RadioGroup.svelte +42 -2
- package/dist/components/molecules/RadioGroup.svelte.d.ts +4 -1
- package/dist/components/molecules/SectionHeader.svelte +5 -2
- package/dist/components/molecules/SectionHeader.svelte.d.ts +1 -1
- package/dist/components/molecules/SegmentedControl.svelte +36 -1
- package/dist/components/molecules/SegmentedControl.svelte.d.ts +11 -1
- package/dist/components/molecules/SelectButton.svelte +64 -4
- package/dist/components/molecules/SelectButton.svelte.d.ts +18 -1
- package/dist/components/molecules/Tabs.svelte +21 -3
- package/dist/components/molecules/Tabs.svelte.d.ts +6 -0
- package/dist/components/molecules/Timestamp.svelte +13 -6
- package/dist/components/molecules/Timestamp.svelte.d.ts +4 -0
- package/dist/components/molecules/Toaster.svelte +54 -38
- package/dist/components/molecules/Toaster.svelte.d.ts +5 -1
- package/dist/components/molecules/Toggle.svelte +2 -1
- package/dist/components/molecules/Toggle.svelte.d.ts +2 -1
- package/dist/components/molecules/Tooltip.svelte +32 -5
- package/dist/components/molecules/Tooltip.svelte.d.ts +9 -0
- package/dist/components/molecules/Truncate.svelte +156 -18
- package/dist/components/molecules/Truncate.svelte.d.ts +17 -1
- package/dist/components/molecules/WorkingDir.svelte +6 -0
- package/dist/components/molecules/WorkingDir.svelte.d.ts +2 -0
- package/dist/components/organisms/DataTable.svelte +38 -10
- package/dist/components/organisms/DataTable.svelte.d.ts +4 -0
- package/dist/components/organisms/FilterSearchBar.svelte +9 -2
- package/dist/components/organisms/FilterSearchBar.svelte.d.ts +4 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.js +8 -1
- package/dist/size.d.ts +2 -0
- package/dist/size.js +1 -0
- package/dist/styles/tokens.css +3 -0
- package/dist/timestamp.d.ts +2 -2
- package/dist/timestamp.js +4 -4
- package/dist/tone.d.ts +1 -1
- package/dist/tone.js +1 -1
- package/dist/truncate.d.ts +13 -1
- package/dist/truncate.js +39 -0
- package/package.json +5 -3
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
onclick,
|
|
28
28
|
badge,
|
|
29
29
|
badgeTone = 'neutral',
|
|
30
|
+
orientation = 'horizontal',
|
|
30
31
|
children,
|
|
32
|
+
class: klass = '',
|
|
31
33
|
...rest
|
|
32
34
|
}: {
|
|
33
35
|
icon?: IconName;
|
|
@@ -48,8 +50,13 @@
|
|
|
48
50
|
badge?: string | number;
|
|
49
51
|
/** Tone for the trailing badge when the item is not active. */
|
|
50
52
|
badgeTone?: BadgeTone;
|
|
53
|
+
/** `vertical` stacks the icon over the label with the badge overlaid on the
|
|
54
|
+
* icon corner (bottom tab bars). */
|
|
55
|
+
orientation?: 'horizontal' | 'vertical';
|
|
51
56
|
children?: Snippet;
|
|
52
57
|
[key: string]: unknown;
|
|
58
|
+
class?: string;
|
|
59
|
+
style?: string;
|
|
53
60
|
} = $props();
|
|
54
61
|
|
|
55
62
|
// Treat 0 / '' as "no badge" so callers can pass a raw count without guarding.
|
|
@@ -61,7 +68,8 @@
|
|
|
61
68
|
data-tsu="NavItem"
|
|
62
69
|
{href}
|
|
63
70
|
type={href ? undefined : 'button'}
|
|
64
|
-
class="nav-item"
|
|
71
|
+
class="nav-item {klass}"
|
|
72
|
+
class:vertical={orientation === 'vertical'}
|
|
65
73
|
class:active
|
|
66
74
|
class:bar={activeStyle === 'bar'}
|
|
67
75
|
title={label}
|
|
@@ -148,6 +156,19 @@
|
|
|
148
156
|
.nav-trail {
|
|
149
157
|
flex: none;
|
|
150
158
|
}
|
|
159
|
+
.nav-item.vertical {
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
gap: 2px;
|
|
163
|
+
padding: var(--sp-1) var(--sp-2);
|
|
164
|
+
font-size: var(--fs-xs);
|
|
165
|
+
text-align: center;
|
|
166
|
+
}
|
|
167
|
+
.nav-item.vertical .nav-trail {
|
|
168
|
+
position: absolute;
|
|
169
|
+
top: 0;
|
|
170
|
+
left: calc(50% + 0.4rem);
|
|
171
|
+
}
|
|
151
172
|
/* Dot is the rail-width stand-in for the badge: hidden at full width. */
|
|
152
173
|
.nav-dot {
|
|
153
174
|
display: none;
|
|
@@ -20,8 +20,13 @@ type $$ComponentProps = {
|
|
|
20
20
|
badge?: string | number;
|
|
21
21
|
/** Tone for the trailing badge when the item is not active. */
|
|
22
22
|
badgeTone?: BadgeTone;
|
|
23
|
+
/** `vertical` stacks the icon over the label with the badge overlaid on the
|
|
24
|
+
* icon corner (bottom tab bars). */
|
|
25
|
+
orientation?: 'horizontal' | 'vertical';
|
|
23
26
|
children?: Snippet;
|
|
24
27
|
[key: string]: unknown;
|
|
28
|
+
class?: string;
|
|
29
|
+
style?: string;
|
|
25
30
|
};
|
|
26
31
|
declare const NavItem: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
27
32
|
type NavItem = ReturnType<typeof NavItem>;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
let {
|
|
11
11
|
label,
|
|
12
12
|
children,
|
|
13
|
+
class: klass = '',
|
|
13
14
|
...rest
|
|
14
15
|
}: {
|
|
15
16
|
/** Optional section heading; rendered uppercase + mono. Omit for an
|
|
@@ -17,10 +18,12 @@
|
|
|
17
18
|
label?: string;
|
|
18
19
|
children?: Snippet;
|
|
19
20
|
[key: string]: unknown;
|
|
21
|
+
class?: string;
|
|
22
|
+
style?: string;
|
|
20
23
|
} = $props();
|
|
21
24
|
</script>
|
|
22
25
|
|
|
23
|
-
<div class="nav-section" data-tsu="NavSection" {...rest}>
|
|
26
|
+
<div class="nav-section {klass}" data-tsu="NavSection" {...rest}>
|
|
24
27
|
{#if label}
|
|
25
28
|
<div class="nav-section-label">{label}</div>
|
|
26
29
|
{/if}
|
|
@@ -5,6 +5,8 @@ type $$ComponentProps = {
|
|
|
5
5
|
label?: string;
|
|
6
6
|
children?: Snippet;
|
|
7
7
|
[key: string]: unknown;
|
|
8
|
+
class?: string;
|
|
9
|
+
style?: string;
|
|
8
10
|
};
|
|
9
11
|
declare const NavSection: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
12
|
type NavSection = ReturnType<typeof NavSection>;
|
|
@@ -26,7 +26,10 @@
|
|
|
26
26
|
onclose,
|
|
27
27
|
scrim,
|
|
28
28
|
fullWidthBelow,
|
|
29
|
-
clampToViewport = true
|
|
29
|
+
clampToViewport = true,
|
|
30
|
+
collapseControl,
|
|
31
|
+
class: klass = '',
|
|
32
|
+
style: styleProp = '',
|
|
30
33
|
}: {
|
|
31
34
|
/** Content shown while the panel is expanded. */
|
|
32
35
|
panel: Snippet;
|
|
@@ -55,6 +58,9 @@
|
|
|
55
58
|
/** Keep the collapse handle in view when the panel scrolls past the
|
|
56
59
|
* viewport, repositioning on scroll/resize via requestAnimationFrame. */
|
|
57
60
|
stickyHandle?: boolean;
|
|
61
|
+
/** Render the edge chevron. Defaults to on inline, off in overlay mode
|
|
62
|
+
* (Escape and the scrim already close a drawer). */
|
|
63
|
+
collapseControl?: boolean;
|
|
58
64
|
/** `inline` shares the row with `children`; `overlay` fixes the panel to
|
|
59
65
|
* its viewport edge as a non-modal drawer above the page. */
|
|
60
66
|
mode?: 'inline' | 'overlay';
|
|
@@ -69,6 +75,8 @@
|
|
|
69
75
|
fullWidthBelow?: string;
|
|
70
76
|
/** Overlay mode: cap the width at the viewport and re-clamp on window resize. */
|
|
71
77
|
clampToViewport?: boolean;
|
|
78
|
+
class?: string;
|
|
79
|
+
style?: string;
|
|
72
80
|
} = $props();
|
|
73
81
|
|
|
74
82
|
let root: HTMLDivElement;
|
|
@@ -83,6 +91,7 @@
|
|
|
83
91
|
let fullBleed = $state(false);
|
|
84
92
|
|
|
85
93
|
const overlay = $derived(mode === 'overlay');
|
|
94
|
+
const showCollapseControl = $derived(collapseControl ?? !overlay);
|
|
86
95
|
const shown = $derived(overlay ? open : !collapsed);
|
|
87
96
|
const showScrim = $derived(overlay && open && (scrim ?? true));
|
|
88
97
|
|
|
@@ -268,14 +277,14 @@
|
|
|
268
277
|
|
|
269
278
|
<div
|
|
270
279
|
bind:this={root}
|
|
271
|
-
class="panel-layout"
|
|
280
|
+
class="panel-layout {klass}"
|
|
272
281
|
class:left={side === 'left'}
|
|
273
282
|
class:right={side === 'right'}
|
|
274
283
|
class:collapsed={!overlay && collapsed}
|
|
275
284
|
class:overlay
|
|
276
285
|
class:full-bleed={fullBleed}
|
|
277
286
|
class:resizing
|
|
278
|
-
style="--panel-width: {currentWidth}px"
|
|
287
|
+
style="--panel-width: {currentWidth}px; {styleProp}"
|
|
279
288
|
data-tsu="ResizablePanel"
|
|
280
289
|
>
|
|
281
290
|
{#if showScrim}
|
|
@@ -319,6 +328,7 @@
|
|
|
319
328
|
></div>
|
|
320
329
|
{/if}
|
|
321
330
|
|
|
331
|
+
{#if showCollapseControl}
|
|
322
332
|
<button
|
|
323
333
|
bind:this={handleEl}
|
|
324
334
|
type="button"
|
|
@@ -333,6 +343,7 @@
|
|
|
333
343
|
>
|
|
334
344
|
<Icon name={toggleIcon} size={14} />
|
|
335
345
|
</button>
|
|
346
|
+
{/if}
|
|
336
347
|
</svelte:element>
|
|
337
348
|
{/if}
|
|
338
349
|
|
|
@@ -27,6 +27,9 @@ type $$ComponentProps = {
|
|
|
27
27
|
/** Keep the collapse handle in view when the panel scrolls past the
|
|
28
28
|
* viewport, repositioning on scroll/resize via requestAnimationFrame. */
|
|
29
29
|
stickyHandle?: boolean;
|
|
30
|
+
/** Render the edge chevron. Defaults to on inline, off in overlay mode
|
|
31
|
+
* (Escape and the scrim already close a drawer). */
|
|
32
|
+
collapseControl?: boolean;
|
|
30
33
|
/** `inline` shares the row with `children`; `overlay` fixes the panel to
|
|
31
34
|
* its viewport edge as a non-modal drawer above the page. */
|
|
32
35
|
mode?: 'inline' | 'overlay';
|
|
@@ -41,6 +44,8 @@ type $$ComponentProps = {
|
|
|
41
44
|
fullWidthBelow?: string;
|
|
42
45
|
/** Overlay mode: cap the width at the viewport and re-clamp on window resize. */
|
|
43
46
|
clampToViewport?: boolean;
|
|
47
|
+
class?: string;
|
|
48
|
+
style?: string;
|
|
44
49
|
};
|
|
45
50
|
declare const ResizablePanel: import("svelte").Component<$$ComponentProps, {}, "open" | "collapsed">;
|
|
46
51
|
type ResizablePanel = ReturnType<typeof ResizablePanel>;
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
gap = 'var(--sp-3)',
|
|
11
11
|
align,
|
|
12
12
|
justify,
|
|
13
|
+
grow = false,
|
|
14
|
+
shrink = true,
|
|
15
|
+
fill = false,
|
|
16
|
+
push,
|
|
13
17
|
class: klass = '',
|
|
14
18
|
children,
|
|
15
19
|
...rest
|
|
@@ -18,7 +22,16 @@
|
|
|
18
22
|
gap?: string;
|
|
19
23
|
align?: string;
|
|
20
24
|
justify?: string;
|
|
25
|
+
/** `flex: 1 1 0` inside a parent row/column. */
|
|
26
|
+
grow?: boolean;
|
|
27
|
+
/** `false` pins the stack (`flex: none`). */
|
|
28
|
+
shrink?: boolean;
|
|
29
|
+
/** `height: 100%` to fill a flex parent. */
|
|
30
|
+
fill?: boolean;
|
|
31
|
+
/** Auto margin pushing the stack to the start/end of its parent. */
|
|
32
|
+
push?: 'start' | 'end';
|
|
21
33
|
class?: string;
|
|
34
|
+
style?: string;
|
|
22
35
|
children?: Snippet;
|
|
23
36
|
[key: string]: unknown;
|
|
24
37
|
} = $props();
|
|
@@ -28,6 +41,11 @@
|
|
|
28
41
|
this={as}
|
|
29
42
|
data-tsu="Stack"
|
|
30
43
|
class="stack-c {klass}"
|
|
44
|
+
class:grow
|
|
45
|
+
class:no-shrink={!shrink}
|
|
46
|
+
class:fill
|
|
47
|
+
class:push-start={push === 'start'}
|
|
48
|
+
class:push-end={push === 'end'}
|
|
31
49
|
style:gap
|
|
32
50
|
style:align-items={align}
|
|
33
51
|
style:justify-content={justify}
|
|
@@ -42,4 +60,27 @@
|
|
|
42
60
|
flex-direction: column;
|
|
43
61
|
min-width: 0;
|
|
44
62
|
}
|
|
63
|
+
.stack-c > :global([data-grow]) {
|
|
64
|
+
flex: 1 1 0;
|
|
65
|
+
min-height: 0;
|
|
66
|
+
}
|
|
67
|
+
.stack-c > :global([data-shrink='false']) {
|
|
68
|
+
flex: none;
|
|
69
|
+
}
|
|
70
|
+
.grow {
|
|
71
|
+
flex: 1 1 0;
|
|
72
|
+
min-height: 0;
|
|
73
|
+
}
|
|
74
|
+
.no-shrink {
|
|
75
|
+
flex: none;
|
|
76
|
+
}
|
|
77
|
+
.fill {
|
|
78
|
+
height: 100%;
|
|
79
|
+
}
|
|
80
|
+
.push-start {
|
|
81
|
+
margin-block-end: auto;
|
|
82
|
+
}
|
|
83
|
+
.push-end {
|
|
84
|
+
margin-block-start: auto;
|
|
85
|
+
}
|
|
45
86
|
</style>
|
|
@@ -4,7 +4,16 @@ type $$ComponentProps = {
|
|
|
4
4
|
gap?: string;
|
|
5
5
|
align?: string;
|
|
6
6
|
justify?: string;
|
|
7
|
+
/** `flex: 1 1 0` inside a parent row/column. */
|
|
8
|
+
grow?: boolean;
|
|
9
|
+
/** `false` pins the stack (`flex: none`). */
|
|
10
|
+
shrink?: boolean;
|
|
11
|
+
/** `height: 100%` to fill a flex parent. */
|
|
12
|
+
fill?: boolean;
|
|
13
|
+
/** Auto margin pushing the stack to the start/end of its parent. */
|
|
14
|
+
push?: 'start' | 'end';
|
|
7
15
|
class?: string;
|
|
16
|
+
style?: string;
|
|
8
17
|
children?: Snippet;
|
|
9
18
|
[key: string]: unknown;
|
|
10
19
|
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Horizontal action bar. Below `collapseBelow` (measured on the bar's own box)
|
|
3
|
+
// every child marked `data-overflow` is hidden and a `…` button opens a Menu
|
|
4
|
+
// with `items` (or the `overflow` snippet in a Popover) that stands in for
|
|
5
|
+
// them. `sticky` pins the bar under `stickyOffset`.
|
|
6
|
+
import type { Snippet } from 'svelte';
|
|
7
|
+
import IconButton from '../molecules/IconButton.svelte';
|
|
8
|
+
import Menu, { type MenuItem } from '../molecules/Menu.svelte';
|
|
9
|
+
import Popover from '../molecules/Popover.svelte';
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
children,
|
|
13
|
+
items,
|
|
14
|
+
overflow,
|
|
15
|
+
collapseBelow,
|
|
16
|
+
sticky = false,
|
|
17
|
+
stickyOffset = '0px',
|
|
18
|
+
density = 'default',
|
|
19
|
+
overflowLabel = 'More',
|
|
20
|
+
label,
|
|
21
|
+
class: klass = '',
|
|
22
|
+
style: styleProp = ''
|
|
23
|
+
}: {
|
|
24
|
+
children: Snippet;
|
|
25
|
+
/** Menu rows standing in for the collapsed `data-overflow` children. */
|
|
26
|
+
items?: MenuItem[];
|
|
27
|
+
/** Free-form panel content instead of `items`. */
|
|
28
|
+
overflow?: Snippet;
|
|
29
|
+
/** Width (px/em/rem) under which `data-overflow` children collapse. */
|
|
30
|
+
collapseBelow?: string;
|
|
31
|
+
sticky?: boolean;
|
|
32
|
+
stickyOffset?: string;
|
|
33
|
+
density?: 'compact' | 'default';
|
|
34
|
+
overflowLabel?: string;
|
|
35
|
+
/** Accessible name of the bar (`role="toolbar"`). */
|
|
36
|
+
label?: string;
|
|
37
|
+
class?: string;
|
|
38
|
+
style?: string;
|
|
39
|
+
} = $props();
|
|
40
|
+
|
|
41
|
+
let el = $state<HTMLDivElement | null>(null);
|
|
42
|
+
let collapsed = $state(false);
|
|
43
|
+
|
|
44
|
+
function toPx(length: string, node: HTMLElement): number {
|
|
45
|
+
const n = Number.parseFloat(length);
|
|
46
|
+
const fontSize = (x: Element) => Number.parseFloat(getComputedStyle(x).fontSize);
|
|
47
|
+
if (length.endsWith('rem')) return n * fontSize(document.documentElement);
|
|
48
|
+
if (length.endsWith('em')) return n * fontSize(node);
|
|
49
|
+
return n;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (!collapseBelow || !el) {
|
|
54
|
+
collapsed = false;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const node = el;
|
|
58
|
+
const limit = toPx(collapseBelow, node);
|
|
59
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
60
|
+
collapsed = entry.contentRect.width < limit;
|
|
61
|
+
});
|
|
62
|
+
ro.observe(node);
|
|
63
|
+
return () => ro.disconnect();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const hasOverflow = $derived(!!items?.length || !!overflow);
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<div
|
|
70
|
+
bind:this={el}
|
|
71
|
+
data-tsu="Toolbar"
|
|
72
|
+
role="toolbar"
|
|
73
|
+
aria-label={label}
|
|
74
|
+
class="toolbar density-{density} {klass}"
|
|
75
|
+
class:sticky
|
|
76
|
+
class:collapsed
|
|
77
|
+
data-collapsed={collapsed || undefined}
|
|
78
|
+
style="--toolbar-top: {stickyOffset}; {styleProp}"
|
|
79
|
+
>
|
|
80
|
+
{@render children()}
|
|
81
|
+
{#if collapsed && hasOverflow}
|
|
82
|
+
{#if items?.length}
|
|
83
|
+
<Menu label={overflowLabel} {items} placement="bottom-end" box="sm">
|
|
84
|
+
{#snippet trigger()}<IconButton icon="more" label={overflowLabel} box="sm" />{/snippet}
|
|
85
|
+
</Menu>
|
|
86
|
+
{:else if overflow}
|
|
87
|
+
<Popover label={overflowLabel} placement="bottom-end" box="sm">
|
|
88
|
+
{#snippet trigger()}<IconButton icon="more" label={overflowLabel} box="sm" />{/snippet}
|
|
89
|
+
<div class="overflow-panel">{@render overflow()}</div>
|
|
90
|
+
</Popover>
|
|
91
|
+
{/if}
|
|
92
|
+
{/if}
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<style>
|
|
96
|
+
.toolbar {
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
flex-wrap: nowrap;
|
|
100
|
+
gap: var(--sp-2);
|
|
101
|
+
min-width: 0;
|
|
102
|
+
container-type: inline-size;
|
|
103
|
+
}
|
|
104
|
+
.density-compact {
|
|
105
|
+
gap: var(--sp-1);
|
|
106
|
+
}
|
|
107
|
+
.sticky {
|
|
108
|
+
position: sticky;
|
|
109
|
+
top: var(--toolbar-top, 0);
|
|
110
|
+
z-index: calc(var(--z-header) - 1);
|
|
111
|
+
background: var(--bg);
|
|
112
|
+
}
|
|
113
|
+
.collapsed :global([data-overflow]) {
|
|
114
|
+
display: none;
|
|
115
|
+
}
|
|
116
|
+
.overflow-panel {
|
|
117
|
+
display: flex;
|
|
118
|
+
flex-direction: column;
|
|
119
|
+
gap: var(--sp-1);
|
|
120
|
+
padding: var(--sp-1);
|
|
121
|
+
}
|
|
122
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type MenuItem } from '../molecules/Menu.svelte';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
children: Snippet;
|
|
5
|
+
/** Menu rows standing in for the collapsed `data-overflow` children. */
|
|
6
|
+
items?: MenuItem[];
|
|
7
|
+
/** Free-form panel content instead of `items`. */
|
|
8
|
+
overflow?: Snippet;
|
|
9
|
+
/** Width (px/em/rem) under which `data-overflow` children collapse. */
|
|
10
|
+
collapseBelow?: string;
|
|
11
|
+
sticky?: boolean;
|
|
12
|
+
stickyOffset?: string;
|
|
13
|
+
density?: 'compact' | 'default';
|
|
14
|
+
overflowLabel?: string;
|
|
15
|
+
/** Accessible name of the bar (`role="toolbar"`). */
|
|
16
|
+
label?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
style?: string;
|
|
19
|
+
};
|
|
20
|
+
declare const Toolbar: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
21
|
+
type Toolbar = ReturnType<typeof Toolbar>;
|
|
22
|
+
export default Toolbar;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Removable file chips for a composer or upload form.
|
|
3
|
+
import Badge from '../atoms/Badge.svelte';
|
|
4
|
+
|
|
5
|
+
export type Attachment = { name: string; size?: number };
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
files,
|
|
9
|
+
onremove,
|
|
10
|
+
class: klass = '',
|
|
11
|
+
style: styleProp = ''
|
|
12
|
+
}: {
|
|
13
|
+
files: (File | Attachment)[];
|
|
14
|
+
onremove?: (index: number) => void;
|
|
15
|
+
class?: string;
|
|
16
|
+
style?: string;
|
|
17
|
+
} = $props();
|
|
18
|
+
|
|
19
|
+
function fmt(size: number | undefined): string {
|
|
20
|
+
if (size === undefined) return '';
|
|
21
|
+
if (size < 1024) return `${size} B`;
|
|
22
|
+
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
|
|
23
|
+
return `${(size / 1024 / 1024).toFixed(1)} MB`;
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
{#if files.length}
|
|
28
|
+
<ul data-tsu="AttachmentList" class="attachments {klass}" style={styleProp}>
|
|
29
|
+
{#each files as f, i (`${f.name}-${i}`)}
|
|
30
|
+
<li>
|
|
31
|
+
<Badge
|
|
32
|
+
mono
|
|
33
|
+
truncate
|
|
34
|
+
maxWidth="16rem"
|
|
35
|
+
removable={!!onremove}
|
|
36
|
+
onremove={() => onremove?.(i)}
|
|
37
|
+
title={f.name}
|
|
38
|
+
>
|
|
39
|
+
{f.name}{#if f.size !== undefined}<span class="size">{fmt(f.size)}</span>{/if}
|
|
40
|
+
</Badge>
|
|
41
|
+
</li>
|
|
42
|
+
{/each}
|
|
43
|
+
</ul>
|
|
44
|
+
{/if}
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
.attachments {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-wrap: wrap;
|
|
50
|
+
gap: var(--sp-1);
|
|
51
|
+
margin: 0;
|
|
52
|
+
padding: 0;
|
|
53
|
+
list-style: none;
|
|
54
|
+
}
|
|
55
|
+
.size {
|
|
56
|
+
margin-inline-start: var(--sp-1);
|
|
57
|
+
color: var(--text-faint);
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Attachment = {
|
|
2
|
+
name: string;
|
|
3
|
+
size?: number;
|
|
4
|
+
};
|
|
5
|
+
type $$ComponentProps = {
|
|
6
|
+
files: (File | Attachment)[];
|
|
7
|
+
onremove?: (index: number) => void;
|
|
8
|
+
class?: string;
|
|
9
|
+
style?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const AttachmentList: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type AttachmentList = ReturnType<typeof AttachmentList>;
|
|
13
|
+
export default AttachmentList;
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
items,
|
|
19
19
|
char,
|
|
20
20
|
separator,
|
|
21
|
-
maxItems = 0
|
|
21
|
+
maxItems = 0,
|
|
22
|
+
class: klass = '',
|
|
23
|
+
style: styleProp = '',
|
|
22
24
|
}: {
|
|
23
25
|
items: BreadcrumbItem[];
|
|
24
26
|
/** Separator character; ignored when the `separator` snippet is set. */
|
|
@@ -27,6 +29,8 @@
|
|
|
27
29
|
/** Collapse the middle to an ellipsis when the trail is longer than this
|
|
28
30
|
* (0 = never collapse). Keeps the first item and the last `maxItems - 1`. */
|
|
29
31
|
maxItems?: number;
|
|
32
|
+
class?: string;
|
|
33
|
+
style?: string;
|
|
30
34
|
} = $props();
|
|
31
35
|
|
|
32
36
|
type Crumb = BreadcrumbItem | { ellipsis: true };
|
|
@@ -38,7 +42,7 @@
|
|
|
38
42
|
);
|
|
39
43
|
</script>
|
|
40
44
|
|
|
41
|
-
<nav aria-label="breadcrumb" data-tsu="Breadcrumb">
|
|
45
|
+
<nav aria-label="breadcrumb" data-tsu="Breadcrumb" class={klass} style={styleProp}>
|
|
42
46
|
<ol class="crumbs">
|
|
43
47
|
{#each shown as item, i (i)}
|
|
44
48
|
<li class="crumb">
|
|
@@ -11,6 +11,8 @@ type $$ComponentProps = {
|
|
|
11
11
|
/** Collapse the middle to an ellipsis when the trail is longer than this
|
|
12
12
|
* (0 = never collapse). Keeps the first item and the last `maxItems - 1`. */
|
|
13
13
|
maxItems?: number;
|
|
14
|
+
class?: string;
|
|
15
|
+
style?: string;
|
|
14
16
|
};
|
|
15
17
|
declare const Breadcrumb: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
16
18
|
type Breadcrumb = ReturnType<typeof Breadcrumb>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { canonicalTone, type Tone } from '../../tone';
|
|
2
3
|
// Inline message banner — the one way to say "heads up" inside a page:
|
|
3
4
|
// tone-tinted Card with a leading glyph, optional title, body, right-aligned
|
|
4
5
|
// actions and an optional dismiss button. `busy` swaps the glyph for a
|
|
@@ -10,12 +11,13 @@
|
|
|
10
11
|
import Spinner from '../atoms/Spinner.svelte';
|
|
11
12
|
import IconButton from './IconButton.svelte';
|
|
12
13
|
|
|
13
|
-
type Tone = 'neutral' | 'ok' | 'warn' | 'danger' | 'info';
|
|
14
14
|
|
|
15
15
|
const DEFAULT_ICON: Record<Tone, IconName> = {
|
|
16
16
|
neutral: 'info',
|
|
17
17
|
info: 'info',
|
|
18
18
|
ok: 'check',
|
|
19
|
+
success: 'check',
|
|
20
|
+
accent: 'info',
|
|
19
21
|
warn: 'warning',
|
|
20
22
|
danger: 'warning'
|
|
21
23
|
};
|
|
@@ -45,15 +47,16 @@
|
|
|
45
47
|
actions?: Snippet;
|
|
46
48
|
[key: string]: unknown;
|
|
47
49
|
} = $props();
|
|
50
|
+
const t = $derived(canonicalTone(tone));
|
|
48
51
|
|
|
49
52
|
let glyph = $derived(icon ?? DEFAULT_ICON[tone]);
|
|
50
53
|
</script>
|
|
51
54
|
|
|
52
55
|
<Card
|
|
53
|
-
{
|
|
56
|
+
tone={t}
|
|
54
57
|
padding="none"
|
|
55
|
-
role={
|
|
56
|
-
class="callout callout-{
|
|
58
|
+
role={t === 'danger' ? 'alert' : 'status'}
|
|
59
|
+
class="callout callout-{t} {klass}"
|
|
57
60
|
data-tsu="Callout"
|
|
58
61
|
{...rest}
|
|
59
62
|
>
|
|
@@ -99,6 +102,9 @@
|
|
|
99
102
|
:global(.callout-info) {
|
|
100
103
|
--callout-tone: var(--info);
|
|
101
104
|
}
|
|
105
|
+
:global(.callout-accent) {
|
|
106
|
+
--callout-tone: var(--accent);
|
|
107
|
+
}
|
|
102
108
|
|
|
103
109
|
.callout-icon {
|
|
104
110
|
display: inline-flex;
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
const tone = $derived(capTone(value, cap, warnAt));
|
|
59
59
|
const readoutText = $derived(hint ? `${value}% · ${hint}` : `${value}%`);
|
|
60
60
|
const tip = $derived(tooltip ?? `cap ${cap}% — drag the bar`);
|
|
61
|
+
const readoutTitle = $derived(readout === undefined ? readoutText : typeof readout === 'string' ? readout : undefined);
|
|
61
62
|
const ariaLabel = $derived(typeof label === 'string' ? `${label} cap` : 'cap');
|
|
62
63
|
|
|
63
64
|
let trackEl = $state<HTMLDivElement | null>(null);
|
|
@@ -142,7 +143,7 @@
|
|
|
142
143
|
onkeyup={keyUp}
|
|
143
144
|
></div>
|
|
144
145
|
</div>
|
|
145
|
-
<div class="readout">
|
|
146
|
+
<div class="readout" title={readoutTitle}>
|
|
146
147
|
{#if readout === undefined}{readoutText}{:else if typeof readout === 'string'}{readout}{:else}{@render readout()}{/if}
|
|
147
148
|
</div>
|
|
148
149
|
{#if caption}
|
|
@@ -233,6 +234,9 @@
|
|
|
233
234
|
opacity: 0.6;
|
|
234
235
|
}
|
|
235
236
|
.readout {
|
|
237
|
+
min-width: 0;
|
|
238
|
+
overflow: hidden;
|
|
239
|
+
text-overflow: ellipsis;
|
|
236
240
|
text-align: right;
|
|
237
241
|
font-variant-numeric: tabular-nums;
|
|
238
242
|
color: var(--text-muted);
|