@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
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
class?: string;
|
|
3
|
+
style?: string;
|
|
4
|
+
};
|
|
5
|
+
declare const Toaster: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
2
6
|
type Toaster = ReturnType<typeof Toaster>;
|
|
3
7
|
export default Toaster;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { ControlSize } from '../../size';
|
|
2
3
|
// Toggle chip — a muted pill/chip that lights up (tinted) when `pressed`.
|
|
3
4
|
// The "on" tint is driven by the `--toggle-accent` CSS var (default accent),
|
|
4
5
|
// so callers recolor it per-use (e.g. warm for behavior, role color for
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
pressed?: boolean;
|
|
27
28
|
pill?: boolean;
|
|
28
29
|
struck?: boolean;
|
|
29
|
-
size?:
|
|
30
|
+
size?: ControlSize;
|
|
30
31
|
// Share a flex row with siblings (flex: 1 1 0).
|
|
31
32
|
grow?: boolean;
|
|
32
33
|
// `shrink={false}` keeps the chip at its intrinsic width (flex: none).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type { ControlSize } from '../../size';
|
|
1
2
|
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
4
|
type $$ComponentProps = HTMLButtonAttributes & {
|
|
4
5
|
pressed?: boolean;
|
|
5
6
|
pill?: boolean;
|
|
6
7
|
struck?: boolean;
|
|
7
|
-
size?:
|
|
8
|
+
size?: ControlSize;
|
|
8
9
|
grow?: boolean;
|
|
9
10
|
shrink?: boolean;
|
|
10
11
|
children?: Snippet;
|
|
@@ -24,7 +24,13 @@
|
|
|
24
24
|
placement = 'top',
|
|
25
25
|
delay = 200,
|
|
26
26
|
closeDelay = 250,
|
|
27
|
-
trigger
|
|
27
|
+
trigger,
|
|
28
|
+
cursor = true,
|
|
29
|
+
inline = false,
|
|
30
|
+
width,
|
|
31
|
+
maxWidth,
|
|
32
|
+
class: klass = '',
|
|
33
|
+
style: styleProp = '',
|
|
28
34
|
}: {
|
|
29
35
|
/** Plain-text bubble. Ignored when `content` is provided. */
|
|
30
36
|
text?: string;
|
|
@@ -36,6 +42,15 @@
|
|
|
36
42
|
/** Grace period (ms) before closing — lets the pointer travel into the panel. */
|
|
37
43
|
closeDelay?: number;
|
|
38
44
|
trigger: Snippet;
|
|
45
|
+
/** `cursor: help` on the trigger wrapper (default). */
|
|
46
|
+
cursor?: boolean;
|
|
47
|
+
/** `flex: none` on the wrapper so a flex row cannot squeeze the trigger. */
|
|
48
|
+
inline?: boolean;
|
|
49
|
+
/** Panel width / max-width (default max 16rem). */
|
|
50
|
+
width?: string;
|
|
51
|
+
maxWidth?: string;
|
|
52
|
+
class?: string;
|
|
53
|
+
style?: string;
|
|
39
54
|
} = $props();
|
|
40
55
|
|
|
41
56
|
const id = `tip-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -82,10 +97,14 @@
|
|
|
82
97
|
// hover/focus listeners (attached via the DOM, so the wrapper stays a plain,
|
|
83
98
|
// role-less container — the interactive element is the trigger inside).
|
|
84
99
|
function tooltip(node: HTMLElement) {
|
|
85
|
-
|
|
100
|
+
let target = (node.matches(FOCUSABLE) ? node : node.querySelector(FOCUSABLE)) as
|
|
86
101
|
| HTMLElement
|
|
87
102
|
| null;
|
|
88
|
-
target
|
|
103
|
+
if (!target) {
|
|
104
|
+
node.tabIndex = 0;
|
|
105
|
+
target = node;
|
|
106
|
+
}
|
|
107
|
+
target.setAttribute('aria-describedby', id);
|
|
89
108
|
const onkey = (e: KeyboardEvent) => e.key === 'Escape' && hideNow();
|
|
90
109
|
node.addEventListener('mouseenter', show);
|
|
91
110
|
node.addEventListener('mouseleave', scheduleHide);
|
|
@@ -94,7 +113,7 @@
|
|
|
94
113
|
node.addEventListener('keydown', onkey);
|
|
95
114
|
return {
|
|
96
115
|
destroy() {
|
|
97
|
-
target
|
|
116
|
+
target.removeAttribute('aria-describedby');
|
|
98
117
|
node.removeEventListener('mouseenter', show);
|
|
99
118
|
node.removeEventListener('mouseleave', scheduleHide);
|
|
100
119
|
node.removeEventListener('focusin', show);
|
|
@@ -125,7 +144,7 @@
|
|
|
125
144
|
}
|
|
126
145
|
</script>
|
|
127
146
|
|
|
128
|
-
<span class="tip-wrap" data-tsu="Tooltip" bind:this={wrapEl} use:tooltip>
|
|
147
|
+
<span class="tip-wrap {klass}" class:help={cursor} class:inline style={styleProp} data-tsu="Tooltip" bind:this={wrapEl} use:tooltip>
|
|
129
148
|
{@render trigger()}
|
|
130
149
|
</span>
|
|
131
150
|
|
|
@@ -136,6 +155,8 @@
|
|
|
136
155
|
popover="manual"
|
|
137
156
|
class="tip"
|
|
138
157
|
class:rich={!!content}
|
|
158
|
+
style:width
|
|
159
|
+
style:max-width={maxWidth}
|
|
139
160
|
use:panel
|
|
140
161
|
>
|
|
141
162
|
{#if content}{@render content()}{:else}{text}{/if}
|
|
@@ -145,6 +166,12 @@
|
|
|
145
166
|
.tip-wrap {
|
|
146
167
|
display: inline-flex;
|
|
147
168
|
}
|
|
169
|
+
.tip-wrap.inline {
|
|
170
|
+
flex: none;
|
|
171
|
+
}
|
|
172
|
+
.tip-wrap.help:not(:has(a, button, input, select, textarea)) {
|
|
173
|
+
cursor: help;
|
|
174
|
+
}
|
|
148
175
|
.tip {
|
|
149
176
|
position: fixed;
|
|
150
177
|
margin: 0;
|
|
@@ -10,6 +10,15 @@ type $$ComponentProps = {
|
|
|
10
10
|
/** Grace period (ms) before closing — lets the pointer travel into the panel. */
|
|
11
11
|
closeDelay?: number;
|
|
12
12
|
trigger: Snippet;
|
|
13
|
+
/** `cursor: help` on the trigger wrapper (default). */
|
|
14
|
+
cursor?: boolean;
|
|
15
|
+
/** `flex: none` on the wrapper so a flex row cannot squeeze the trigger. */
|
|
16
|
+
inline?: boolean;
|
|
17
|
+
/** Panel width / max-width (default max 16rem). */
|
|
18
|
+
width?: string;
|
|
19
|
+
maxWidth?: string;
|
|
20
|
+
class?: string;
|
|
21
|
+
style?: string;
|
|
13
22
|
};
|
|
14
23
|
declare const Tooltip: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
15
24
|
type Tooltip = ReturnType<typeof Tooltip>;
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
// fits, it renders as a bare inline span with no tooltip wiring at all — so
|
|
6
6
|
// you only pay for the Tooltip when something is actually hidden.
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
import {
|
|
8
|
+
// `fit` swaps the character budget for a measured one: the host width is
|
|
9
|
+
// observed and the longest candidate that fits is shown (mode `path`
|
|
10
|
+
// abbreviates fish-style). `copyable` copies the full text on click.
|
|
11
|
+
import { copyToClipboard } from '../../clipboard';
|
|
12
|
+
import { pathCandidates, truncate, type TruncateMode } from '../../truncate';
|
|
12
13
|
import Tooltip from './Tooltip.svelte';
|
|
13
14
|
|
|
14
15
|
let {
|
|
@@ -16,34 +17,135 @@
|
|
|
16
17
|
max,
|
|
17
18
|
mode = 'end',
|
|
18
19
|
ellipsis = '…',
|
|
20
|
+
separator = '/',
|
|
21
|
+
minLeaf = 3,
|
|
22
|
+
keepFirst = 0,
|
|
23
|
+
fit = false,
|
|
24
|
+
mono = false,
|
|
25
|
+
copyable = false,
|
|
26
|
+
copiedLabel = 'Copied',
|
|
27
|
+
oncopy,
|
|
28
|
+
grow = false,
|
|
19
29
|
tooltip = true,
|
|
20
|
-
placement = 'top'
|
|
30
|
+
placement = 'top',
|
|
31
|
+
class: klass = '',
|
|
32
|
+
style: styleProp = '',
|
|
21
33
|
}: {
|
|
22
34
|
text: string;
|
|
23
|
-
|
|
35
|
+
/** Character budget. Ignored when `fit` is set. */
|
|
36
|
+
max?: number;
|
|
24
37
|
mode?: TruncateMode;
|
|
25
38
|
ellipsis?: string;
|
|
39
|
+
/** `path` mode options. */
|
|
40
|
+
separator?: string;
|
|
41
|
+
minLeaf?: number;
|
|
42
|
+
keepFirst?: number;
|
|
43
|
+
/** Measure the host width and show the longest candidate that fits. */
|
|
44
|
+
fit?: boolean;
|
|
45
|
+
mono?: boolean;
|
|
46
|
+
/** Click copies the full text; announces via a live region. */
|
|
47
|
+
copyable?: boolean;
|
|
48
|
+
copiedLabel?: string;
|
|
49
|
+
oncopy?: (ok: boolean) => void;
|
|
50
|
+
/** `flex: 1 1 0; min-width: 0` so the host claims a flex row's free space. */
|
|
51
|
+
grow?: boolean;
|
|
26
52
|
/** Reveal the full text on hover/focus when truncated. Default true. */
|
|
27
53
|
tooltip?: boolean;
|
|
28
54
|
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
55
|
+
class?: string;
|
|
56
|
+
style?: string;
|
|
29
57
|
} = $props();
|
|
30
58
|
|
|
31
|
-
const
|
|
59
|
+
const pathOpts = $derived({ ellipsis, separator, minLeaf, keepFirst });
|
|
60
|
+
|
|
61
|
+
const candidates = $derived.by(() => {
|
|
62
|
+
if (mode === 'path') return pathCandidates(text, pathOpts);
|
|
63
|
+
const out: string[] = [];
|
|
64
|
+
for (let m = [...text].length; m >= 2; m--) {
|
|
65
|
+
const c = truncate(text, { max: m, mode, ellipsis });
|
|
66
|
+
if (out[out.length - 1] !== c) out.push(c);
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let rail = $state<HTMLSpanElement | null>(null);
|
|
72
|
+
let probe = $state<HTMLSpanElement | null>(null);
|
|
73
|
+
let avail = $state(Number.POSITIVE_INFINITY);
|
|
74
|
+
let chPx = $state(8);
|
|
75
|
+
|
|
76
|
+
$effect(() => {
|
|
77
|
+
if (!fit || !rail) return;
|
|
78
|
+
const el = rail;
|
|
79
|
+
const measure = () => {
|
|
80
|
+
avail = el.clientWidth;
|
|
81
|
+
if (probe) chPx = probe.getBoundingClientRect().width / 10 || chPx;
|
|
82
|
+
};
|
|
83
|
+
measure();
|
|
84
|
+
const ro = new ResizeObserver(measure);
|
|
85
|
+
ro.observe(el);
|
|
86
|
+
return () => ro.disconnect();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const shown = $derived.by(() => {
|
|
90
|
+
if (fit) {
|
|
91
|
+
const budget = Math.max(0, avail - 1);
|
|
92
|
+
return candidates.find((c) => [...c].length * chPx <= budget) ?? candidates[candidates.length - 1];
|
|
93
|
+
}
|
|
94
|
+
if (max === undefined) return text;
|
|
95
|
+
return truncate(text, { max, mode, ...pathOpts });
|
|
96
|
+
});
|
|
32
97
|
const isTruncated = $derived(shown !== text);
|
|
98
|
+
|
|
99
|
+
let status = $state('');
|
|
100
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
101
|
+
async function copy(e: MouseEvent) {
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
const ok = await copyToClipboard(text);
|
|
104
|
+
oncopy?.(ok);
|
|
105
|
+
status = ok ? copiedLabel : 'Copy failed';
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
timer = setTimeout(() => {
|
|
108
|
+
status = '';
|
|
109
|
+
}, 1500);
|
|
110
|
+
}
|
|
33
111
|
</script>
|
|
34
112
|
|
|
35
|
-
{#
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
113
|
+
{#snippet inner(focusable: boolean)}
|
|
114
|
+
{#if copyable}
|
|
115
|
+
<button type="button" class="trunc copy {klass}" class:mono style={styleProp} data-tsu="Truncate" title={text} onclick={copy}>
|
|
116
|
+
{shown}
|
|
117
|
+
</button>
|
|
118
|
+
<span class="sr-only" role="status" aria-live="polite">{status}</span>
|
|
119
|
+
{:else if focusable}
|
|
120
|
+
<!-- tabindex makes the shortened text reachable by keyboard so the
|
|
121
|
+
full-text tooltip (wired by Tooltip onto the first focusable child)
|
|
122
|
+
isn't mouse-only; the span is otherwise non-interactive. -->
|
|
123
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
124
|
+
<span class="trunc {klass}" class:mono style={styleProp} data-tsu="Truncate" tabindex="0">{shown}</span>
|
|
125
|
+
{:else}
|
|
126
|
+
<span class="trunc {klass}" class:mono style={styleProp} data-tsu="Truncate">{shown}</span>
|
|
127
|
+
{/if}
|
|
128
|
+
{/snippet}
|
|
129
|
+
|
|
130
|
+
{#snippet body()}
|
|
131
|
+
{#if isTruncated && tooltip}
|
|
132
|
+
<Tooltip {text} {placement}>
|
|
133
|
+
{#snippet trigger()}{@render inner(true)}{/snippet}
|
|
134
|
+
</Tooltip>
|
|
135
|
+
{:else}
|
|
136
|
+
{@render inner(false)}
|
|
137
|
+
{/if}
|
|
138
|
+
{/snippet}
|
|
139
|
+
|
|
140
|
+
{#if fit}
|
|
141
|
+
<span class="rail" class:grow bind:this={rail}>
|
|
142
|
+
{@render body()}
|
|
143
|
+
<span class="probe" bind:this={probe} aria-hidden="true">{'0'.repeat(10)}</span>
|
|
144
|
+
</span>
|
|
145
|
+
{:else if grow}
|
|
146
|
+
<span class="rail grow">{@render body()}</span>
|
|
45
147
|
{:else}
|
|
46
|
-
|
|
148
|
+
{@render body()}
|
|
47
149
|
{/if}
|
|
48
150
|
|
|
49
151
|
<style>
|
|
@@ -51,5 +153,41 @@
|
|
|
51
153
|
/* No layout of its own — it shortens the string, not the box. The cursor
|
|
52
154
|
hint signals there's more to reveal. */
|
|
53
155
|
cursor: default;
|
|
156
|
+
white-space: nowrap;
|
|
157
|
+
}
|
|
158
|
+
.mono {
|
|
159
|
+
font-family: var(--font-mono);
|
|
160
|
+
}
|
|
161
|
+
.copy {
|
|
162
|
+
border: 0;
|
|
163
|
+
padding: 0;
|
|
164
|
+
background: none;
|
|
165
|
+
color: inherit;
|
|
166
|
+
font: inherit;
|
|
167
|
+
cursor: copy;
|
|
168
|
+
}
|
|
169
|
+
.rail {
|
|
170
|
+
display: inline-flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
min-width: 0;
|
|
173
|
+
max-width: 100%;
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
}
|
|
176
|
+
.grow {
|
|
177
|
+
flex: 1 1 0;
|
|
178
|
+
}
|
|
179
|
+
.probe {
|
|
180
|
+
position: absolute;
|
|
181
|
+
visibility: hidden;
|
|
182
|
+
white-space: pre;
|
|
183
|
+
pointer-events: none;
|
|
184
|
+
}
|
|
185
|
+
.sr-only {
|
|
186
|
+
position: absolute;
|
|
187
|
+
width: 1px;
|
|
188
|
+
height: 1px;
|
|
189
|
+
overflow: hidden;
|
|
190
|
+
clip: rect(0, 0, 0, 0);
|
|
191
|
+
white-space: nowrap;
|
|
54
192
|
}
|
|
55
193
|
</style>
|
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
import { type TruncateMode } from '../../truncate';
|
|
2
2
|
type $$ComponentProps = {
|
|
3
3
|
text: string;
|
|
4
|
-
|
|
4
|
+
/** Character budget. Ignored when `fit` is set. */
|
|
5
|
+
max?: number;
|
|
5
6
|
mode?: TruncateMode;
|
|
6
7
|
ellipsis?: string;
|
|
8
|
+
/** `path` mode options. */
|
|
9
|
+
separator?: string;
|
|
10
|
+
minLeaf?: number;
|
|
11
|
+
keepFirst?: number;
|
|
12
|
+
/** Measure the host width and show the longest candidate that fits. */
|
|
13
|
+
fit?: boolean;
|
|
14
|
+
mono?: boolean;
|
|
15
|
+
/** Click copies the full text; announces via a live region. */
|
|
16
|
+
copyable?: boolean;
|
|
17
|
+
copiedLabel?: string;
|
|
18
|
+
oncopy?: (ok: boolean) => void;
|
|
19
|
+
/** `flex: 1 1 0; min-width: 0` so the host claims a flex row's free space. */
|
|
20
|
+
grow?: boolean;
|
|
7
21
|
/** Reveal the full text on hover/focus when truncated. Default true. */
|
|
8
22
|
tooltip?: boolean;
|
|
9
23
|
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
24
|
+
class?: string;
|
|
25
|
+
style?: string;
|
|
10
26
|
};
|
|
11
27
|
declare const Truncate: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
28
|
type Truncate = ReturnType<typeof Truncate>;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
minLeaf = 18,
|
|
14
14
|
mono = true,
|
|
15
15
|
full = false,
|
|
16
|
+
shrink = false,
|
|
16
17
|
icon = 'folder',
|
|
17
18
|
title,
|
|
18
19
|
copy = false,
|
|
@@ -27,6 +28,8 @@
|
|
|
27
28
|
mono?: boolean;
|
|
28
29
|
/** Skip the fit algorithm: render the whole path and size to content. */
|
|
29
30
|
full?: boolean;
|
|
31
|
+
/** Size to the path when it fits; keep truncating when it does not. */
|
|
32
|
+
shrink?: boolean;
|
|
30
33
|
icon?: IconName;
|
|
31
34
|
/** Defaults to the full path. */
|
|
32
35
|
title?: string;
|
|
@@ -64,6 +67,8 @@
|
|
|
64
67
|
return () => ro.disconnect();
|
|
65
68
|
});
|
|
66
69
|
|
|
70
|
+
const fullWidth = $derived(shrink && !full ? Math.ceil(candidates[0].length * chPx + chrome + 2) : undefined);
|
|
71
|
+
|
|
67
72
|
const shown = $derived.by(() => {
|
|
68
73
|
if (full) return normalized;
|
|
69
74
|
const budget = Math.max(0, avail - chrome - 1);
|
|
@@ -87,6 +92,7 @@
|
|
|
87
92
|
class="rail {klass}"
|
|
88
93
|
class:full
|
|
89
94
|
bind:this={rail}
|
|
95
|
+
style:max-width={fullWidth ? `${fullWidth}px` : undefined}
|
|
90
96
|
{style}
|
|
91
97
|
>
|
|
92
98
|
<Badge
|
|
@@ -6,6 +6,8 @@ type $$ComponentProps = {
|
|
|
6
6
|
mono?: boolean;
|
|
7
7
|
/** Skip the fit algorithm: render the whole path and size to content. */
|
|
8
8
|
full?: boolean;
|
|
9
|
+
/** Size to the path when it fits; keep truncating when it does not. */
|
|
10
|
+
shrink?: boolean;
|
|
9
11
|
icon?: IconName;
|
|
10
12
|
/** Defaults to the full path. */
|
|
11
13
|
title?: string;
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
// Generic, accessible data table. Columns are typed against the row type; a
|
|
29
29
|
// column renders row[key] by default, a `get` accessor, or a custom
|
|
30
30
|
// `cellSnippets[key]` snippet for full control (badges, actions…). Sticky
|
|
31
|
-
// header, empty state, optional row click (
|
|
32
|
-
//
|
|
31
|
+
// header, empty state, optional row click (the table becomes a grid whose
|
|
32
|
+
// rows are focusable and Enter/Space-activatable; rows stay rows for AT). Horizontal scroll is contained so it never
|
|
33
33
|
// breaks the page layout on mobile.
|
|
34
34
|
import type { Snippet } from 'svelte';
|
|
35
35
|
|
|
@@ -55,7 +55,10 @@
|
|
|
55
55
|
onloadmore,
|
|
56
56
|
loadMoreLabel = 'Load more',
|
|
57
57
|
responsive = 'scroll',
|
|
58
|
-
stackBelow = '48rem'
|
|
58
|
+
stackBelow = '48rem',
|
|
59
|
+
class: klass = '',
|
|
60
|
+
style: styleProp = '',
|
|
61
|
+
tableClass = '',
|
|
59
62
|
}: {
|
|
60
63
|
columns: Column<T>[];
|
|
61
64
|
rows: T[];
|
|
@@ -92,10 +95,25 @@
|
|
|
92
95
|
responsive?: 'scroll' | 'stack';
|
|
93
96
|
/** Width (px/em/rem) under which `stack` mode kicks in. */
|
|
94
97
|
stackBelow?: string;
|
|
98
|
+
class?: string;
|
|
99
|
+
style?: string;
|
|
100
|
+
/** Class on the inner <table>. */
|
|
101
|
+
tableClass?: string;
|
|
95
102
|
} = $props();
|
|
96
103
|
|
|
97
104
|
let wrapEl = $state<HTMLDivElement | null>(null);
|
|
98
105
|
let stacked = $state(false);
|
|
106
|
+
let actionsW = $state(0);
|
|
107
|
+
|
|
108
|
+
function measureActions(node: HTMLElement) {
|
|
109
|
+
const update = () => {
|
|
110
|
+
actionsW = Math.max(actionsW, Math.ceil(node.getBoundingClientRect().width));
|
|
111
|
+
};
|
|
112
|
+
update();
|
|
113
|
+
const observer = new ResizeObserver(update);
|
|
114
|
+
observer.observe(node);
|
|
115
|
+
return { destroy: () => observer.disconnect() };
|
|
116
|
+
}
|
|
99
117
|
|
|
100
118
|
function toPx(length: string, el: HTMLElement): number {
|
|
101
119
|
const n = Number.parseFloat(length);
|
|
@@ -158,7 +176,8 @@
|
|
|
158
176
|
|
|
159
177
|
<div
|
|
160
178
|
bind:this={wrapEl}
|
|
161
|
-
class="dt-scroll"
|
|
179
|
+
class="dt-scroll {klass}"
|
|
180
|
+
style={styleProp}
|
|
162
181
|
class:dt-stack={responsive === 'stack'}
|
|
163
182
|
data-tsu="DataTable"
|
|
164
183
|
data-size={size}
|
|
@@ -166,12 +185,14 @@
|
|
|
166
185
|
aria-busy={loading || undefined}
|
|
167
186
|
>
|
|
168
187
|
<table
|
|
169
|
-
class="dt"
|
|
188
|
+
class="dt {tableClass}"
|
|
170
189
|
class:sticky={stickyHeader}
|
|
171
190
|
class:fixed={layout === 'fixed'}
|
|
172
191
|
class:head-hidden={hideHeader}
|
|
173
192
|
class:sm={size === 'sm'}
|
|
174
193
|
style:--dt-sticky-offset={stickyOffset}
|
|
194
|
+
style:--dt-actions-w={actionsW ? `calc(${actionsW}px + 2 * var(--sp-3))` : undefined}
|
|
195
|
+
role={onrowclick ? 'grid' : undefined}
|
|
175
196
|
>
|
|
176
197
|
<thead data-part="head">
|
|
177
198
|
<tr>
|
|
@@ -227,7 +248,6 @@
|
|
|
227
248
|
class:clickable={!!onrowclick}
|
|
228
249
|
class:toned={!!tone && tone !== 'neutral'}
|
|
229
250
|
tabindex={onrowclick ? 0 : undefined}
|
|
230
|
-
role={onrowclick ? 'button' : undefined}
|
|
231
251
|
onclick={onrowclick ? () => onrowclick(row) : undefined}
|
|
232
252
|
onkeydown={onrowclick
|
|
233
253
|
? (e) => {
|
|
@@ -257,7 +277,7 @@
|
|
|
257
277
|
{/each}
|
|
258
278
|
{#if rowActions}
|
|
259
279
|
<td data-part="actions" class="dt-actions">
|
|
260
|
-
{@render rowActions(row)}
|
|
280
|
+
<span class="dt-actions-inner" use:measureActions>{@render rowActions(row)}</span>
|
|
261
281
|
</td>
|
|
262
282
|
{/if}
|
|
263
283
|
</tr>
|
|
@@ -414,6 +434,12 @@
|
|
|
414
434
|
opacity: 0;
|
|
415
435
|
transition: opacity 0.12s var(--ease);
|
|
416
436
|
}
|
|
437
|
+
.dt-actions-inner {
|
|
438
|
+
display: inline-flex;
|
|
439
|
+
align-items: center;
|
|
440
|
+
gap: var(--sp-1);
|
|
441
|
+
white-space: nowrap;
|
|
442
|
+
}
|
|
417
443
|
tr:hover .dt-actions,
|
|
418
444
|
tr:focus-within .dt-actions {
|
|
419
445
|
opacity: 1;
|
|
@@ -444,7 +470,7 @@
|
|
|
444
470
|
[data-stacked] {
|
|
445
471
|
overflow-x: visible;
|
|
446
472
|
}
|
|
447
|
-
[data-stacked] thead
|
|
473
|
+
[data-stacked] thead {
|
|
448
474
|
position: absolute;
|
|
449
475
|
width: 1px;
|
|
450
476
|
height: 1px;
|
|
@@ -460,7 +486,8 @@
|
|
|
460
486
|
[data-stacked] td {
|
|
461
487
|
display: block;
|
|
462
488
|
}
|
|
463
|
-
[data-stacked] tr
|
|
489
|
+
[data-stacked] tbody tr,
|
|
490
|
+
[data-stacked] tfoot tr {
|
|
464
491
|
display: flex;
|
|
465
492
|
flex-wrap: wrap;
|
|
466
493
|
align-items: baseline;
|
|
@@ -487,7 +514,8 @@
|
|
|
487
514
|
box-shadow: inset 3px 0 0 var(--dt-tone);
|
|
488
515
|
}
|
|
489
516
|
[data-stacked] td[data-role='title'] {
|
|
490
|
-
flex: 1 1
|
|
517
|
+
flex: 1 1 auto;
|
|
518
|
+
min-width: min(100%, 12rem);
|
|
491
519
|
order: -2;
|
|
492
520
|
font-weight: var(--fw-semibold);
|
|
493
521
|
font-size: var(--fs-md);
|
|
@@ -58,6 +58,10 @@ declare function $$render<T>(): {
|
|
|
58
58
|
responsive?: "scroll" | "stack";
|
|
59
59
|
/** Width (px/em/rem) under which `stack` mode kicks in. */
|
|
60
60
|
stackBelow?: string;
|
|
61
|
+
class?: string;
|
|
62
|
+
style?: string;
|
|
63
|
+
/** Class on the inner <table>. */
|
|
64
|
+
tableClass?: string;
|
|
61
65
|
};
|
|
62
66
|
exports: {};
|
|
63
67
|
bindings: "";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { ControlSize } from '../../size';
|
|
2
3
|
// ───────────────────────────────────────────────────────────────────────
|
|
3
4
|
// FilterSearchBar organism — a YouTrack-style structured search bar.
|
|
4
5
|
//
|
|
@@ -31,7 +32,9 @@
|
|
|
31
32
|
showHotkey = false,
|
|
32
33
|
grow = false,
|
|
33
34
|
onchange,
|
|
34
|
-
onsubmit
|
|
35
|
+
onsubmit,
|
|
36
|
+
class: klass = '',
|
|
37
|
+
style: styleProp = '',
|
|
35
38
|
}: {
|
|
36
39
|
schema: Schema;
|
|
37
40
|
/** The raw textual query (two-way bindable). */
|
|
@@ -46,7 +49,7 @@
|
|
|
46
49
|
*/
|
|
47
50
|
autoQuote?: boolean;
|
|
48
51
|
/** Forwarded to FilterInput. */
|
|
49
|
-
size?:
|
|
52
|
+
size?: ControlSize;
|
|
50
53
|
shape?: 'square' | 'pill';
|
|
51
54
|
surface?: 'base' | 'raised' | 'sunken';
|
|
52
55
|
hotkey?: string;
|
|
@@ -56,6 +59,8 @@
|
|
|
56
59
|
onchange?: (query: Query, raw: string) => void;
|
|
57
60
|
/** Fires on Enter / clear / chip-remove with the raw query string. */
|
|
58
61
|
onsubmit?: (value: string) => void;
|
|
62
|
+
class?: string;
|
|
63
|
+
style?: string;
|
|
59
64
|
} = $props();
|
|
60
65
|
|
|
61
66
|
function labelFor(fieldName: string): string {
|
|
@@ -76,6 +81,8 @@
|
|
|
76
81
|
{grow}
|
|
77
82
|
{onchange}
|
|
78
83
|
{onsubmit}
|
|
84
|
+
class={klass}
|
|
85
|
+
style={styleProp}
|
|
79
86
|
>
|
|
80
87
|
{#snippet below({ filters: chips, text, remove })}
|
|
81
88
|
{#if showChips && (chips.length || text)}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ControlSize } from '../../size';
|
|
1
2
|
import type { Query } from '../../query/ast';
|
|
2
3
|
import { type Schema } from '../../query/schema';
|
|
3
4
|
type $$ComponentProps = {
|
|
@@ -14,7 +15,7 @@ type $$ComponentProps = {
|
|
|
14
15
|
*/
|
|
15
16
|
autoQuote?: boolean;
|
|
16
17
|
/** Forwarded to FilterInput. */
|
|
17
|
-
size?:
|
|
18
|
+
size?: ControlSize;
|
|
18
19
|
shape?: 'square' | 'pill';
|
|
19
20
|
surface?: 'base' | 'raised' | 'sunken';
|
|
20
21
|
hotkey?: string;
|
|
@@ -24,6 +25,8 @@ type $$ComponentProps = {
|
|
|
24
25
|
onchange?: (query: Query, raw: string) => void;
|
|
25
26
|
/** Fires on Enter / clear / chip-remove with the raw query string. */
|
|
26
27
|
onsubmit?: (value: string) => void;
|
|
28
|
+
class?: string;
|
|
29
|
+
style?: string;
|
|
27
30
|
};
|
|
28
31
|
declare const FilterSearchBar: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
29
32
|
type FilterSearchBar = ReturnType<typeof FilterSearchBar>;
|