@dorsk/tsumikit 0.38.0 → 0.39.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 +33 -13
- package/dist/components/atoms/Icon.svelte.d.ts +17 -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 +4 -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/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 +9 -4
- 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 +14 -3
- 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,14 +1,18 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { ControlSize } from '../../size';
|
|
2
3
|
// Canonical on/off toggle (role="switch") — a pill track with a sliding knob.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
4
|
+
// `checked` is bindable (clicking toggles it) and `onclick` still fires.
|
|
5
|
+
// `label` is the accessible name; `labelVisible` renders it beside the track.
|
|
5
6
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
6
7
|
import { getFieldContext } from '../../field-context';
|
|
7
8
|
|
|
8
9
|
let {
|
|
9
|
-
checked = false,
|
|
10
|
+
checked = $bindable(false),
|
|
10
11
|
invalid = false,
|
|
11
12
|
label,
|
|
13
|
+
labelVisible = false,
|
|
14
|
+
size = 'md',
|
|
15
|
+
onclick,
|
|
12
16
|
id,
|
|
13
17
|
'aria-describedby': ariaDescribedby,
|
|
14
18
|
'aria-invalid': ariaInvalid,
|
|
@@ -18,6 +22,9 @@
|
|
|
18
22
|
checked?: boolean;
|
|
19
23
|
invalid?: boolean;
|
|
20
24
|
label: string;
|
|
25
|
+
/** Render `label` as visible text beside the track. */
|
|
26
|
+
labelVisible?: boolean;
|
|
27
|
+
size?: ControlSize;
|
|
21
28
|
id?: string;
|
|
22
29
|
'aria-describedby'?: string | null;
|
|
23
30
|
'aria-invalid'?: HTMLButtonAttributes['aria-invalid'];
|
|
@@ -25,23 +32,42 @@
|
|
|
25
32
|
|
|
26
33
|
const field = getFieldContext();
|
|
27
34
|
const isInvalid = $derived(invalid || !!field?.invalid);
|
|
35
|
+
|
|
36
|
+
function toggle(e: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
|
37
|
+
onclick?.(e);
|
|
38
|
+
if (e.defaultPrevented) return;
|
|
39
|
+
checked = !checked;
|
|
40
|
+
}
|
|
28
41
|
</script>
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
{#snippet control()}
|
|
44
|
+
<button
|
|
45
|
+
data-tsu="Switch"
|
|
46
|
+
{...rest}
|
|
47
|
+
type="button"
|
|
48
|
+
class="switch {klass}"
|
|
49
|
+
class:on={checked}
|
|
50
|
+
class:switch-sm={size === 'sm'}
|
|
51
|
+
role="switch"
|
|
52
|
+
aria-checked={checked}
|
|
53
|
+
id={id ?? field?.id}
|
|
54
|
+
aria-describedby={ariaDescribedby ?? field?.describedBy}
|
|
55
|
+
aria-invalid={ariaInvalid ?? (isInvalid ? 'true' : undefined)}
|
|
56
|
+
aria-label={labelVisible ? undefined : label}
|
|
57
|
+
onclick={toggle}
|
|
58
|
+
>
|
|
59
|
+
<span class="knob"></span>
|
|
60
|
+
</button>
|
|
61
|
+
{/snippet}
|
|
62
|
+
|
|
63
|
+
{#if labelVisible}
|
|
64
|
+
<label class="switch-row" class:switch-row-sm={size === 'sm'}>
|
|
65
|
+
{@render control()}
|
|
66
|
+
<span class="switch-label">{label}</span>
|
|
67
|
+
</label>
|
|
68
|
+
{:else}
|
|
69
|
+
{@render control()}
|
|
70
|
+
{/if}
|
|
45
71
|
|
|
46
72
|
<style>
|
|
47
73
|
.switch {
|
|
@@ -76,6 +102,10 @@
|
|
|
76
102
|
transform: translateX(1.15rem);
|
|
77
103
|
background: var(--text-on-accent);
|
|
78
104
|
}
|
|
105
|
+
.switch:focus-visible {
|
|
106
|
+
outline: var(--focus-ring);
|
|
107
|
+
outline-offset: var(--focus-ring-offset);
|
|
108
|
+
}
|
|
79
109
|
.switch:disabled {
|
|
80
110
|
opacity: 0.45;
|
|
81
111
|
cursor: not-allowed;
|
|
@@ -83,4 +113,26 @@
|
|
|
83
113
|
.switch[aria-invalid='true']:not(.on) {
|
|
84
114
|
border-color: var(--danger);
|
|
85
115
|
}
|
|
116
|
+
.switch-sm {
|
|
117
|
+
width: 2rem;
|
|
118
|
+
height: 1.2rem;
|
|
119
|
+
}
|
|
120
|
+
.switch-sm .knob {
|
|
121
|
+
width: 0.9rem;
|
|
122
|
+
height: 0.9rem;
|
|
123
|
+
}
|
|
124
|
+
.switch-sm.on .knob {
|
|
125
|
+
transform: translateX(0.8rem);
|
|
126
|
+
}
|
|
127
|
+
.switch-row {
|
|
128
|
+
display: inline-flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: var(--sp-2);
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
font-size: var(--fs-sm);
|
|
133
|
+
color: var(--text);
|
|
134
|
+
}
|
|
135
|
+
.switch-row-sm {
|
|
136
|
+
font-size: var(--fs-xs);
|
|
137
|
+
}
|
|
86
138
|
</style>
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import type { ControlSize } from '../../size';
|
|
1
2
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
2
3
|
type $$ComponentProps = HTMLButtonAttributes & {
|
|
3
4
|
checked?: boolean;
|
|
4
5
|
invalid?: boolean;
|
|
5
6
|
label: string;
|
|
7
|
+
/** Render `label` as visible text beside the track. */
|
|
8
|
+
labelVisible?: boolean;
|
|
9
|
+
size?: ControlSize;
|
|
6
10
|
id?: string;
|
|
7
11
|
'aria-describedby'?: string | null;
|
|
8
12
|
'aria-invalid'?: HTMLButtonAttributes['aria-invalid'];
|
|
9
13
|
};
|
|
10
|
-
declare const Switch: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
declare const Switch: import("svelte").Component<$$ComponentProps, {}, "checked">;
|
|
11
15
|
type Switch = ReturnType<typeof Switch>;
|
|
12
16
|
export default Switch;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { canonicalTone, type Tone } from '../../tone';
|
|
2
4
|
// Text primitive: the ONLY place body/label/caption/code text and its bearing
|
|
3
5
|
// elements (<p>/<span>/<label>/<div> of pure text) are emitted. `variant` picks
|
|
4
6
|
// a token preset; `tone`/`weight`/`size` override individual axes; bare <Text>
|
|
@@ -9,39 +11,21 @@
|
|
|
9
11
|
|
|
10
12
|
type Size = 'xs' | 'sm' | 'base' | 'md' | 'lg' | 'xl' | '2xl';
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
as = 'span',
|
|
14
|
-
variant,
|
|
15
|
-
tone = 'inherit',
|
|
16
|
-
weight,
|
|
17
|
-
size,
|
|
18
|
-
numeric = false,
|
|
19
|
-
truncate = false,
|
|
20
|
-
italic = false,
|
|
21
|
-
nowrap = false,
|
|
22
|
-
wrap = 'normal',
|
|
23
|
-
uppercase = false,
|
|
24
|
-
leading,
|
|
25
|
-
measure,
|
|
26
|
-
grow = false,
|
|
27
|
-
scale = true,
|
|
28
|
-
block = false,
|
|
29
|
-
class: klass = '',
|
|
30
|
-
children,
|
|
31
|
-
...rest
|
|
32
|
-
}: {
|
|
14
|
+
type Own = {
|
|
33
15
|
as?: 'span' | 'p' | 'div' | 'label';
|
|
34
16
|
// body: default reading text · label: form-label · caption: small meta ·
|
|
35
17
|
// code: monospace · eyebrow: small uppercase muted kicker. Omit for inline
|
|
36
18
|
// glue that should inherit its parent.
|
|
37
19
|
variant?: 'body' | 'label' | 'caption' | 'code' | 'eyebrow';
|
|
38
20
|
// `ok` and `success` are aliases.
|
|
39
|
-
tone?: 'inherit' | 'default' | 'muted' | 'faint' |
|
|
21
|
+
tone?: 'inherit' | 'default' | 'muted' | 'faint' | Tone;
|
|
40
22
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
41
23
|
size?: Size;
|
|
42
24
|
// Tabular figures: digits share a fixed advance width so counts/percentages
|
|
43
25
|
// don't jitter as they change. Use for counters, timers, metrics.
|
|
44
26
|
numeric?: boolean;
|
|
27
|
+
/** Monospace family without the `code` variant's chip styling. */
|
|
28
|
+
mono?: boolean;
|
|
45
29
|
truncate?: boolean;
|
|
46
30
|
italic?: boolean;
|
|
47
31
|
nowrap?: boolean;
|
|
@@ -56,10 +40,33 @@
|
|
|
56
40
|
block?: boolean;
|
|
57
41
|
class?: string;
|
|
58
42
|
children?: Snippet;
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
};
|
|
44
|
+
let {
|
|
45
|
+
as = 'span',
|
|
46
|
+
variant,
|
|
47
|
+
tone = 'inherit',
|
|
48
|
+
weight,
|
|
49
|
+
size,
|
|
50
|
+
numeric = false,
|
|
51
|
+
mono = false,
|
|
52
|
+
truncate = false,
|
|
53
|
+
italic = false,
|
|
54
|
+
nowrap = false,
|
|
55
|
+
wrap = 'normal',
|
|
56
|
+
uppercase = false,
|
|
57
|
+
leading,
|
|
58
|
+
measure,
|
|
59
|
+
grow = false,
|
|
60
|
+
scale = true,
|
|
61
|
+
block = false,
|
|
62
|
+
class: klass = '',
|
|
63
|
+
children,
|
|
64
|
+
...rest
|
|
65
|
+
}: Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own = $props();
|
|
61
66
|
|
|
62
|
-
const toneClass = $derived(
|
|
67
|
+
const toneClass = $derived(
|
|
68
|
+
tone === 'neutral' ? 'default' : canonicalTone(tone) === 'ok' ? 'success' : canonicalTone(tone)
|
|
69
|
+
);
|
|
63
70
|
const styleAttr = $derived(measure ? `max-width: ${measure}` : undefined);
|
|
64
71
|
</script>
|
|
65
72
|
|
|
@@ -68,7 +75,7 @@
|
|
|
68
75
|
data-tsu="Text"
|
|
69
76
|
class="text {variant ? `v-${variant}` : ''} tone-{toneClass} {weight ? `fw-${weight}` : ''} {size
|
|
70
77
|
? `fs-${size}`
|
|
71
|
-
: ''} {numeric ? 'numeric' : ''} {truncate ? 'truncate' : ''} {leading ? `lh-${leading}` : ''} {wrap !==
|
|
78
|
+
: ''} {numeric ? 'numeric' : ''} {mono ? 'mono' : ''} {truncate ? 'truncate' : ''} {leading ? `lh-${leading}` : ''} {wrap !==
|
|
72
79
|
'normal'
|
|
73
80
|
? `wrap-${wrap}`
|
|
74
81
|
: ''} {klass}"
|
|
@@ -116,6 +123,9 @@
|
|
|
116
123
|
letter-spacing: 0.04em;
|
|
117
124
|
}
|
|
118
125
|
/* Tone (colour) — overrides variant colour. */
|
|
126
|
+
.mono {
|
|
127
|
+
font-family: var(--font-mono);
|
|
128
|
+
}
|
|
119
129
|
.tone-default {
|
|
120
130
|
color: var(--text);
|
|
121
131
|
}
|
|
@@ -134,6 +144,9 @@
|
|
|
134
144
|
.tone-danger {
|
|
135
145
|
color: var(--danger);
|
|
136
146
|
}
|
|
147
|
+
.tone-info {
|
|
148
|
+
color: var(--info);
|
|
149
|
+
}
|
|
137
150
|
.tone-accent {
|
|
138
151
|
color: var(--accent);
|
|
139
152
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import { type Tone } from '../../tone';
|
|
1
3
|
import type { Snippet } from 'svelte';
|
|
2
4
|
type Size = 'xs' | 'sm' | 'base' | 'md' | 'lg' | 'xl' | '2xl';
|
|
3
|
-
type
|
|
5
|
+
type Own = {
|
|
4
6
|
as?: 'span' | 'p' | 'div' | 'label';
|
|
5
7
|
variant?: 'body' | 'label' | 'caption' | 'code' | 'eyebrow';
|
|
6
|
-
tone?: 'inherit' | 'default' | 'muted' | 'faint' |
|
|
8
|
+
tone?: 'inherit' | 'default' | 'muted' | 'faint' | Tone;
|
|
7
9
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
8
10
|
size?: Size;
|
|
9
11
|
numeric?: boolean;
|
|
12
|
+
/** Monospace family without the `code` variant's chip styling. */
|
|
13
|
+
mono?: boolean;
|
|
10
14
|
truncate?: boolean;
|
|
11
15
|
italic?: boolean;
|
|
12
16
|
nowrap?: boolean;
|
|
@@ -19,8 +23,8 @@ type $$ComponentProps = {
|
|
|
19
23
|
block?: boolean;
|
|
20
24
|
class?: string;
|
|
21
25
|
children?: Snippet;
|
|
22
|
-
[key: string]: unknown;
|
|
23
26
|
};
|
|
27
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own;
|
|
24
28
|
declare const Text: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
25
29
|
type Text = ReturnType<typeof Text>;
|
|
26
30
|
export default Text;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { ControlSize } from '../../size';
|
|
2
3
|
// Multi-line text input primitive. Owns its styling from theme tokens;
|
|
3
4
|
// supports `bind:value` and passes through every native <textarea> attribute
|
|
4
5
|
// and event (Svelte 5 events are props, so `...rest` forwards them). `mono`
|
|
@@ -10,10 +11,9 @@
|
|
|
10
11
|
// handle on the top or bottom edge, styled like the Modal/AppShell grips: a
|
|
11
12
|
// centered pill that's a thicker portion of the border.
|
|
12
13
|
//
|
|
13
|
-
// With `autoresize`,
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// no sense alongside content-driven sizing, so it's suppressed.)
|
|
14
|
+
// With `autoresize`, the handle (either edge) sets a manual *floor*
|
|
15
|
+
// (min-height) rather than a fixed height: the textarea still grows with
|
|
16
|
+
// content, but dragging reserves extra space. `rows` is the initial floor.
|
|
17
17
|
//
|
|
18
18
|
// `onsubmit` fires with the value on Enter (`submitOn="enter"`, Shift+Enter
|
|
19
19
|
// still inserts a newline) or Ctrl/Meta+Enter (`submitOn="mod-enter"`, the
|
|
@@ -25,10 +25,16 @@
|
|
|
25
25
|
type Props = HTMLTextareaAttributes & {
|
|
26
26
|
mono?: boolean;
|
|
27
27
|
autoresize?: boolean;
|
|
28
|
-
size?:
|
|
28
|
+
size?: ControlSize;
|
|
29
|
+
/** Fill the free space of a flex row (`flex: 1 1 0`). */
|
|
30
|
+
grow?: boolean;
|
|
31
|
+
/** `false` pins the box (`flex: none`). */
|
|
32
|
+
shrink?: boolean;
|
|
33
|
+
/** Full-width block. */
|
|
34
|
+
block?: boolean;
|
|
29
35
|
/** Manual resize handle edge, or 'none' to disable. Defaults to a bottom
|
|
30
|
-
* handle. With `autoresize
|
|
31
|
-
*
|
|
36
|
+
* handle. With `autoresize` it drags a min-height floor (the textarea
|
|
37
|
+
* still grows with content). */
|
|
32
38
|
resize?: 'none' | 'top' | 'bottom';
|
|
33
39
|
/** Error state: danger border + aria-invalid (also styles if a consumer
|
|
34
40
|
* sets aria-invalid directly). */
|
|
@@ -50,6 +56,9 @@
|
|
|
50
56
|
mono = false,
|
|
51
57
|
autoresize = false,
|
|
52
58
|
size = 'md',
|
|
59
|
+
grow = false,
|
|
60
|
+
shrink = true,
|
|
61
|
+
block = false,
|
|
53
62
|
resize = 'bottom',
|
|
54
63
|
invalid = false,
|
|
55
64
|
maxHeight,
|
|
@@ -70,8 +79,7 @@
|
|
|
70
79
|
|
|
71
80
|
$effect(() => warnUnlabelled(el, 'Textarea'));
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
const handleEdge = $derived(autoresize ? (resize === 'top' ? 'top' : 'none') : resize);
|
|
82
|
+
const handleEdge = $derived(resize);
|
|
75
83
|
const showHandle = $derived(handleEdge !== 'none');
|
|
76
84
|
const submitMode = $derived(submitOn === 'none' && onsubmit ? 'mod-enter' : submitOn);
|
|
77
85
|
|
|
@@ -136,13 +144,21 @@
|
|
|
136
144
|
}
|
|
137
145
|
</script>
|
|
138
146
|
|
|
139
|
-
<div
|
|
147
|
+
<div
|
|
148
|
+
class="textarea-wrap"
|
|
149
|
+
class:dragging
|
|
150
|
+
class:grow={grow}
|
|
151
|
+
class:no-shrink={!shrink}
|
|
152
|
+
class:block={block}
|
|
153
|
+
data-tsu="Textarea"
|
|
154
|
+
>
|
|
140
155
|
{#if autoresize}
|
|
141
156
|
<textarea
|
|
142
157
|
bind:this={el}
|
|
143
158
|
class="textarea {klass}"
|
|
144
159
|
class:mono
|
|
145
160
|
class:textarea-sm={size === 'sm'}
|
|
161
|
+
class:textarea-lg={size === 'lg'}
|
|
146
162
|
class:capped={!!maxHeight}
|
|
147
163
|
style:max-height={maxHeight}
|
|
148
164
|
bind:value
|
|
@@ -159,6 +175,7 @@
|
|
|
159
175
|
class="textarea {klass}"
|
|
160
176
|
class:mono
|
|
161
177
|
class:textarea-sm={size === 'sm'}
|
|
178
|
+
class:textarea-lg={size === 'lg'}
|
|
162
179
|
class:capped={!!maxHeight}
|
|
163
180
|
style:max-height={maxHeight}
|
|
164
181
|
bind:value
|
|
@@ -184,6 +201,17 @@
|
|
|
184
201
|
</div>
|
|
185
202
|
|
|
186
203
|
<style>
|
|
204
|
+
.grow {
|
|
205
|
+
flex: 1 1 0;
|
|
206
|
+
min-width: 0;
|
|
207
|
+
}
|
|
208
|
+
.no-shrink {
|
|
209
|
+
flex: none;
|
|
210
|
+
}
|
|
211
|
+
.block {
|
|
212
|
+
display: flex;
|
|
213
|
+
width: 100%;
|
|
214
|
+
}
|
|
187
215
|
.textarea-wrap {
|
|
188
216
|
position: relative;
|
|
189
217
|
display: flex;
|
|
@@ -212,6 +240,10 @@
|
|
|
212
240
|
outline: none;
|
|
213
241
|
border-color: var(--accent);
|
|
214
242
|
}
|
|
243
|
+
.textarea:focus-visible {
|
|
244
|
+
outline: var(--focus-ring);
|
|
245
|
+
outline-offset: var(--focus-ring-offset);
|
|
246
|
+
}
|
|
215
247
|
.textarea.capped {
|
|
216
248
|
overflow-y: auto;
|
|
217
249
|
}
|
|
@@ -220,6 +252,11 @@
|
|
|
220
252
|
font-size: var(--fs-sm);
|
|
221
253
|
min-height: 2rem;
|
|
222
254
|
}
|
|
255
|
+
.textarea-lg {
|
|
256
|
+
padding: var(--sp-3) var(--sp-4);
|
|
257
|
+
font-size: var(--fs-base);
|
|
258
|
+
min-height: var(--control-height-large);
|
|
259
|
+
}
|
|
223
260
|
.textarea[aria-invalid='true'],
|
|
224
261
|
.textarea[aria-invalid='true']:focus {
|
|
225
262
|
border-color: var(--danger);
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
import type { ControlSize } from '../../size';
|
|
1
2
|
import type { HTMLTextareaAttributes } from 'svelte/elements';
|
|
2
3
|
type Props = HTMLTextareaAttributes & {
|
|
3
4
|
mono?: boolean;
|
|
4
5
|
autoresize?: boolean;
|
|
5
|
-
size?:
|
|
6
|
+
size?: ControlSize;
|
|
7
|
+
/** Fill the free space of a flex row (`flex: 1 1 0`). */
|
|
8
|
+
grow?: boolean;
|
|
9
|
+
/** `false` pins the box (`flex: none`). */
|
|
10
|
+
shrink?: boolean;
|
|
11
|
+
/** Full-width block. */
|
|
12
|
+
block?: boolean;
|
|
6
13
|
/** Manual resize handle edge, or 'none' to disable. Defaults to a bottom
|
|
7
|
-
* handle. With `autoresize
|
|
8
|
-
*
|
|
14
|
+
* handle. With `autoresize` it drags a min-height floor (the textarea
|
|
15
|
+
* still grows with content). */
|
|
9
16
|
resize?: 'none' | 'top' | 'bottom';
|
|
10
17
|
/** Error state: danger border + aria-invalid (also styles if a consumer
|
|
11
18
|
* sets aria-invalid directly). */
|
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
sidebarWidthKey,
|
|
28
28
|
layout = 'header-top',
|
|
29
29
|
stickySidebar = false,
|
|
30
|
-
sidebarPadding = 'md'
|
|
30
|
+
sidebarPadding = 'md',
|
|
31
|
+
class: klass = '',
|
|
32
|
+
style: styleProp = '',
|
|
31
33
|
}: {
|
|
32
34
|
header?: Snippet;
|
|
33
35
|
sidebar?: Snippet;
|
|
@@ -52,6 +54,8 @@
|
|
|
52
54
|
stickySidebar?: boolean;
|
|
53
55
|
/** Inner padding of the sidebar; `'none'` when the nav owns its gutters. */
|
|
54
56
|
sidebarPadding?: 'none' | 'sm' | 'md';
|
|
57
|
+
class?: string;
|
|
58
|
+
style?: string;
|
|
55
59
|
} = $props();
|
|
56
60
|
|
|
57
61
|
let open = $state(false);
|
|
@@ -127,11 +131,11 @@
|
|
|
127
131
|
<svelte:window onkeydown={(e) => e.key === 'Escape' && (open = false)} />
|
|
128
132
|
|
|
129
133
|
<div
|
|
130
|
-
class="shell"
|
|
134
|
+
class="shell {klass}"
|
|
131
135
|
class:dragging
|
|
132
136
|
class:sidebar-full={layout === 'sidebar-full'}
|
|
133
137
|
class:sticky-sidebar={stickySidebar}
|
|
134
|
-
style="--shell-sidebar-w: {widthCss}"
|
|
138
|
+
style="--shell-sidebar-w: {widthCss}; {styleProp}"
|
|
135
139
|
data-tsu="AppShell"
|
|
136
140
|
>
|
|
137
141
|
<header class="shell-header">
|
|
@@ -23,6 +23,8 @@ type $$ComponentProps = {
|
|
|
23
23
|
stickySidebar?: boolean;
|
|
24
24
|
/** Inner padding of the sidebar; `'none'` when the nav owns its gutters. */
|
|
25
25
|
sidebarPadding?: 'none' | 'sm' | 'md';
|
|
26
|
+
class?: string;
|
|
27
|
+
style?: string;
|
|
26
28
|
};
|
|
27
29
|
declare const AppShell: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
28
30
|
type AppShell = ReturnType<typeof AppShell>;
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
fill = false,
|
|
29
29
|
gap = 'var(--sp-4)',
|
|
30
30
|
maxCols,
|
|
31
|
+
template,
|
|
31
32
|
align = 'start',
|
|
32
33
|
justify,
|
|
33
34
|
class: klass = '',
|
|
@@ -45,6 +46,8 @@
|
|
|
45
46
|
fill?: boolean;
|
|
46
47
|
gap?: string;
|
|
47
48
|
maxCols?: number;
|
|
49
|
+
/** Explicit `grid-template-columns` (e.g. `1fr 1fr auto`); bypasses `min`/`max`. */
|
|
50
|
+
template?: string;
|
|
48
51
|
/** Cross-axis alignment of items in their row (align-items). Defaults to `start`. */
|
|
49
52
|
align?: 'start' | 'center' | 'end' | 'stretch';
|
|
50
53
|
/** Inline (main-axis) distribution of tracks (justify-content). Defaults to
|
|
@@ -70,6 +73,7 @@
|
|
|
70
73
|
`--ag-mode: ${mode}`,
|
|
71
74
|
maxCols != null ? `--ag-gap: ${gap}` : null,
|
|
72
75
|
maxCols != null ? `--ag-cols: ${maxCols}` : null,
|
|
76
|
+
template ? `grid-template-columns: ${template}` : null,
|
|
73
77
|
`gap: ${gap}`,
|
|
74
78
|
`align-items: ${align}`,
|
|
75
79
|
justifyValue ? `justify-content: ${justifyValue}` : null
|
|
@@ -11,6 +11,8 @@ type $$ComponentProps = {
|
|
|
11
11
|
fill?: boolean;
|
|
12
12
|
gap?: string;
|
|
13
13
|
maxCols?: number;
|
|
14
|
+
/** Explicit `grid-template-columns` (e.g. `1fr 1fr auto`); bypasses `min`/`max`. */
|
|
15
|
+
template?: string;
|
|
14
16
|
/** Cross-axis alignment of items in their row (align-items). Defaults to `start`. */
|
|
15
17
|
align?: 'start' | 'center' | 'end' | 'stretch';
|
|
16
18
|
/** Inline (main-axis) distribution of tracks (justify-content). Defaults to
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
wrap = true,
|
|
28
28
|
size,
|
|
29
29
|
grow = false,
|
|
30
|
+
shrink = true,
|
|
31
|
+
fill = false,
|
|
32
|
+
push,
|
|
33
|
+
wrapAt,
|
|
30
34
|
stackAt,
|
|
31
35
|
class: klass = '',
|
|
32
36
|
children,
|
|
@@ -39,18 +43,56 @@
|
|
|
39
43
|
wrap?: boolean;
|
|
40
44
|
size?: 'sm' | 'md' | 'lg';
|
|
41
45
|
grow?: boolean;
|
|
46
|
+
/** `false` pins the cluster (`flex: none`) inside a parent row. */
|
|
47
|
+
shrink?: boolean;
|
|
48
|
+
/** `height: 100%` to fill a flex parent. */
|
|
49
|
+
fill?: boolean;
|
|
50
|
+
/** Auto margin that pushes the cluster to the start/end of its parent. */
|
|
51
|
+
push?: 'start' | 'end';
|
|
52
|
+
/** Width (px/em/rem) under which the row starts wrapping (wraps always when unset and `wrap`). */
|
|
53
|
+
wrapAt?: string;
|
|
42
54
|
stackAt?: 'xs' | 'sm' | 'md' | 'lg';
|
|
43
55
|
class?: string;
|
|
56
|
+
style?: string;
|
|
44
57
|
children?: Snippet;
|
|
45
58
|
[key: string]: unknown;
|
|
46
59
|
} = $props();
|
|
60
|
+
|
|
61
|
+
let el = $state<HTMLElement | null>(null);
|
|
62
|
+
let narrow = $state(false);
|
|
63
|
+
function toPx(length: string, node: HTMLElement): number {
|
|
64
|
+
const n = Number.parseFloat(length);
|
|
65
|
+
const fontSize = (x: Element) => Number.parseFloat(getComputedStyle(x).fontSize);
|
|
66
|
+
if (length.endsWith('rem')) return n * fontSize(document.documentElement);
|
|
67
|
+
if (length.endsWith('em')) return n * fontSize(node);
|
|
68
|
+
return n;
|
|
69
|
+
}
|
|
70
|
+
$effect(() => {
|
|
71
|
+
if (!wrapAt || !el) {
|
|
72
|
+
narrow = false;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const node = el;
|
|
76
|
+
const limit = toPx(wrapAt, node);
|
|
77
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
78
|
+
narrow = entry.contentRect.width < limit;
|
|
79
|
+
});
|
|
80
|
+
ro.observe(node);
|
|
81
|
+
return () => ro.disconnect();
|
|
82
|
+
});
|
|
83
|
+
const wrapping = $derived(wrapAt ? narrow : wrap);
|
|
47
84
|
</script>
|
|
48
85
|
|
|
49
86
|
<svelte:element
|
|
50
87
|
this={as}
|
|
88
|
+
bind:this={el}
|
|
51
89
|
data-tsu="Cluster"
|
|
52
90
|
class="cluster-c {klass}"
|
|
53
91
|
class:cluster-grow={grow}
|
|
92
|
+
class:no-shrink={!shrink}
|
|
93
|
+
class:fill
|
|
94
|
+
class:push-start={push === 'start'}
|
|
95
|
+
class:push-end={push === 'end'}
|
|
54
96
|
class:cluster-stack={stackAt !== undefined}
|
|
55
97
|
class:stack-xs={stackAt === 'xs'}
|
|
56
98
|
class:stack-sm={stackAt === 'sm'}
|
|
@@ -59,7 +101,7 @@
|
|
|
59
101
|
style:gap
|
|
60
102
|
style:align-items={align}
|
|
61
103
|
style:justify-content={justify}
|
|
62
|
-
style:flex-wrap={
|
|
104
|
+
style:flex-wrap={wrapping ? 'wrap' : 'nowrap'}
|
|
63
105
|
style:--control-height={size ? CONTROL_TIER[size] : undefined}
|
|
64
106
|
{...rest}
|
|
65
107
|
>
|
|
@@ -74,6 +116,25 @@
|
|
|
74
116
|
flex: 1 1 0;
|
|
75
117
|
min-width: 0;
|
|
76
118
|
}
|
|
119
|
+
.cluster-c > :global([data-grow]) {
|
|
120
|
+
flex: 1 1 0;
|
|
121
|
+
min-width: 0;
|
|
122
|
+
}
|
|
123
|
+
.cluster-c > :global([data-shrink='false']) {
|
|
124
|
+
flex: none;
|
|
125
|
+
}
|
|
126
|
+
.no-shrink {
|
|
127
|
+
flex: none;
|
|
128
|
+
}
|
|
129
|
+
.fill {
|
|
130
|
+
height: 100%;
|
|
131
|
+
}
|
|
132
|
+
.push-start {
|
|
133
|
+
margin-inline-end: auto;
|
|
134
|
+
}
|
|
135
|
+
.push-end {
|
|
136
|
+
margin-inline-start: auto;
|
|
137
|
+
}
|
|
77
138
|
.cluster-stack {
|
|
78
139
|
container-type: inline-size;
|
|
79
140
|
}
|
|
@@ -7,8 +7,17 @@ type $$ComponentProps = {
|
|
|
7
7
|
wrap?: boolean;
|
|
8
8
|
size?: 'sm' | 'md' | 'lg';
|
|
9
9
|
grow?: boolean;
|
|
10
|
+
/** `false` pins the cluster (`flex: none`) inside a parent row. */
|
|
11
|
+
shrink?: boolean;
|
|
12
|
+
/** `height: 100%` to fill a flex parent. */
|
|
13
|
+
fill?: boolean;
|
|
14
|
+
/** Auto margin that pushes the cluster to the start/end of its parent. */
|
|
15
|
+
push?: 'start' | 'end';
|
|
16
|
+
/** Width (px/em/rem) under which the row starts wrapping (wraps always when unset and `wrap`). */
|
|
17
|
+
wrapAt?: string;
|
|
10
18
|
stackAt?: 'xs' | 'sm' | 'md' | 'lg';
|
|
11
19
|
class?: string;
|
|
20
|
+
style?: string;
|
|
12
21
|
children?: Snippet;
|
|
13
22
|
[key: string]: unknown;
|
|
14
23
|
};
|