@dorsk/tsumikit 0.62.0 → 0.63.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/components/atoms/Icon.svelte +1 -0
- package/dist/components/atoms/Icon.svelte.d.ts +1 -0
- package/dist/components/layouts/ResizablePanel.svelte +7 -3
- package/dist/components/layouts/Toolbar.svelte +5 -5
- package/dist/components/molecules/Drawer.svelte +1 -1
- package/dist/components/molecules/Menu.svelte +3 -0
- package/dist/components/molecules/Menu.svelte.d.ts +1 -1
- package/dist/components/molecules/Popover.svelte +53 -5
- package/dist/components/molecules/Popover.svelte.d.ts +6 -2
- package/dist/components/molecules/Toggle.svelte +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -206,6 +206,7 @@ sanctioned escape hatch for everything else.
|
|
|
206
206
|
| `GitRef` | `--git-ref-tone`, `--git-ref-max-width` |
|
|
207
207
|
| `EmptyState` | `--empty-tone` |
|
|
208
208
|
| `Combobox` | `--cb-bg`, `--cb-border`, `--cb-radius`, `--cb-active-bg`, `--cb-min-width`, `--cb-max-height` |
|
|
209
|
+
| `Toggle` | `--toggle-accent`, `--toggle-size` |
|
|
209
210
|
| `Popover` | `--pop-trigger-bg`, `--pop-trigger-fg`, `--pop-trigger-border`, `--pop-trigger-radius`, `--pop-trigger-size`, `--pop-trigger-pad`, `--pop-box`, `--pop-count-bg`, `--pop-count-fg`, `--pop-count-size` |
|
|
210
211
|
|
|
211
212
|
`tests/css-custom-property-contract.test.js` reads this table and fails if a
|
|
@@ -763,6 +764,21 @@ triggers, and `SegmentedControl`. Each renders an exact
|
|
|
763
764
|
original ghost icon-button default. Use `control` on `Button` or `Popover` when
|
|
764
765
|
the roomier shared `--control-height` composer contract is required.
|
|
765
766
|
|
|
767
|
+
`variant="toggle"` renders the trigger as a `Toggle` chip — the component
|
|
768
|
+
itself, not a copy of its chrome — so a popover trigger sitting next to a
|
|
769
|
+
`Toggle` in a toolbar shares one box. `size` (`sm` default, `md`), `pill`,
|
|
770
|
+
`pressed` and `tone` behave as they do on `Toggle`; a `Toggle`'s height comes
|
|
771
|
+
from `--toggle-size`, never from its content, so an emoji, an `Icon` and text
|
|
772
|
+
all leave the chip the same height.
|
|
773
|
+
|
|
774
|
+
```svelte
|
|
775
|
+
<Toggle pressed={auto} onclick={toggleAuto}><Icon name="zap" size={12} /> Auto</Toggle>
|
|
776
|
+
<Popover label="Pins" variant="toggle" pressed={pinned.length > 0} count={pinned.length}>
|
|
777
|
+
{#snippet trigger()}Pins{/snippet}
|
|
778
|
+
{#snippet children()}…{/snippet}
|
|
779
|
+
</Popover>
|
|
780
|
+
```
|
|
781
|
+
|
|
766
782
|
`Toolbar` implements the keyboard contract its `role="toolbar"` announces: the
|
|
767
783
|
bar is a **single tab stop**, and `←`/`→` (plus `Home`/`End`) move between the
|
|
768
784
|
controls that are actually visible — collapsed `data-overflow` children drop out
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
sun: '<circle cx="12" cy="12" r="4" /><path d="M12 2v2" /><path d="M12 20v2" /><path d="m4.93 4.93 1.41 1.41" /><path d="m17.66 17.66 1.41 1.41" /><path d="M2 12h2" /><path d="M20 12h2" /><path d="m6.34 17.66-1.41 1.41" /><path d="m19.07 4.93-1.41 1.41" />',
|
|
96
96
|
moon: '<path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401" />',
|
|
97
97
|
flame: '<path d="M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4" />',
|
|
98
|
+
zap: '<path d="M13 2 4.093 12.688a1 1 0 0 0 .764 1.642H11l-1 7.67L18.907 11.31a1 1 0 0 0-.764-1.642H13z" />',
|
|
98
99
|
loader: '<path d="M21 12a9 9 0 1 1-6.219-8.56" />',
|
|
99
100
|
|
|
100
101
|
// — circled status —
|
|
@@ -78,6 +78,7 @@ declare const ICONS: {
|
|
|
78
78
|
readonly sun: "<circle cx=\"12\" cy=\"12\" r=\"4\" /><path d=\"M12 2v2\" /><path d=\"M12 20v2\" /><path d=\"m4.93 4.93 1.41 1.41\" /><path d=\"m17.66 17.66 1.41 1.41\" /><path d=\"M2 12h2\" /><path d=\"M20 12h2\" /><path d=\"m6.34 17.66-1.41 1.41\" /><path d=\"m19.07 4.93-1.41 1.41\" />";
|
|
79
79
|
readonly moon: "<path d=\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\" />";
|
|
80
80
|
readonly flame: "<path d=\"M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4\" />";
|
|
81
|
+
readonly zap: "<path d=\"M13 2 4.093 12.688a1 1 0 0 0 .764 1.642H11l-1 7.67L18.907 11.31a1 1 0 0 0-.764-1.642H13z\" />";
|
|
81
82
|
readonly loader: "<path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />";
|
|
82
83
|
readonly info: "<circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />";
|
|
83
84
|
readonly warning: "<path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" />";
|
|
@@ -525,7 +525,9 @@
|
|
|
525
525
|
|
|
526
526
|
/* Overlay mode: a fixed non-modal drawer on the viewport edge. The page
|
|
527
527
|
(`.main`) flows as normal underneath; the handle overhang is a viewport
|
|
528
|
-
edge, so the container clamp does not apply.
|
|
528
|
+
edge, so the container clamp does not apply. Widths clamp against the
|
|
529
|
+
containing block, never `100vw`: the reset's `scrollbar-gutter: stable`
|
|
530
|
+
makes `100vw` wider than the viewport and clips the panel's leading edge. */
|
|
529
531
|
.panel-layout.overlay {
|
|
530
532
|
display: block;
|
|
531
533
|
container: none;
|
|
@@ -535,7 +537,7 @@
|
|
|
535
537
|
inset-block: 0;
|
|
536
538
|
left: 0;
|
|
537
539
|
z-index: var(--z-drawer);
|
|
538
|
-
width: min(var(--panel-current-width),
|
|
540
|
+
width: min(var(--panel-current-width), 100%);
|
|
539
541
|
box-shadow: var(--shadow-md);
|
|
540
542
|
outline: none;
|
|
541
543
|
animation: panel-slide-left 0.18s var(--ease);
|
|
@@ -549,7 +551,9 @@
|
|
|
549
551
|
--handle-shift: -6px;
|
|
550
552
|
}
|
|
551
553
|
.overlay.full-bleed .panel {
|
|
552
|
-
|
|
554
|
+
left: 0;
|
|
555
|
+
right: 0;
|
|
556
|
+
width: auto;
|
|
553
557
|
border: 0;
|
|
554
558
|
box-shadow: none;
|
|
555
559
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// controls actually visible, the `…` trigger included. Children are arbitrary
|
|
10
10
|
// snippet content, so the ring is read from the DOM rather than a list prop.
|
|
11
11
|
import type { Snippet } from 'svelte';
|
|
12
|
-
import
|
|
12
|
+
import Icon from '../atoms/Icon.svelte';
|
|
13
13
|
import Menu, { type MenuItem } from '../molecules/Menu.svelte';
|
|
14
14
|
import Popover from '../molecules/Popover.svelte';
|
|
15
15
|
import {
|
|
@@ -161,12 +161,12 @@
|
|
|
161
161
|
{@render children()}
|
|
162
162
|
{#if collapsed && hasOverflow}
|
|
163
163
|
{#if items?.length}
|
|
164
|
-
<Menu label={overflowLabel} {items} placement="bottom-end" box="sm">
|
|
165
|
-
{#snippet trigger()}<
|
|
164
|
+
<Menu label={overflowLabel} title={overflowLabel} {items} placement="bottom-end" box="sm">
|
|
165
|
+
{#snippet trigger()}<Icon name="more" size={18} />{/snippet}
|
|
166
166
|
</Menu>
|
|
167
167
|
{:else if overflow}
|
|
168
|
-
<Popover label={overflowLabel} placement="bottom-end" box="sm">
|
|
169
|
-
{#snippet trigger()}<
|
|
168
|
+
<Popover label={overflowLabel} title={overflowLabel} placement="bottom-end" box="sm">
|
|
169
|
+
{#snippet trigger()}<Icon name="more" size={18} />{/snippet}
|
|
170
170
|
<div class="overflow-panel">{@render overflow()}</div>
|
|
171
171
|
</Popover>
|
|
172
172
|
{/if}
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
| 'control'
|
|
53
53
|
| 'block'
|
|
54
54
|
| 'triggerClass'
|
|
55
|
+
| 'title'
|
|
55
56
|
| 'bare'
|
|
56
57
|
| 'hitArea'
|
|
57
58
|
| 'disabled'
|
|
@@ -74,6 +75,7 @@
|
|
|
74
75
|
control,
|
|
75
76
|
block,
|
|
76
77
|
triggerClass,
|
|
78
|
+
title,
|
|
77
79
|
bare,
|
|
78
80
|
hitArea,
|
|
79
81
|
disabled,
|
|
@@ -148,6 +150,7 @@
|
|
|
148
150
|
{control}
|
|
149
151
|
{block}
|
|
150
152
|
{triggerClass}
|
|
153
|
+
{title}
|
|
151
154
|
class={klass}
|
|
152
155
|
style={styleProp}
|
|
153
156
|
{panelClass}
|
|
@@ -27,7 +27,7 @@ export interface MenuItem {
|
|
|
27
27
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
28
28
|
import Popover from './Popover.svelte';
|
|
29
29
|
type PopoverProps = ComponentProps<typeof Popover>;
|
|
30
|
-
type TriggerChrome = Pick<PopoverProps, 'variant' | 'tone' | 'size' | 'box' | 'pill' | 'control' | 'block' | 'triggerClass' | 'bare' | 'hitArea' | 'disabled' | 'gap' | 'onopen' | 'onclose'>;
|
|
30
|
+
type TriggerChrome = Pick<PopoverProps, 'variant' | 'tone' | 'size' | 'box' | 'pill' | 'control' | 'block' | 'triggerClass' | 'title' | 'bare' | 'hitArea' | 'disabled' | 'gap' | 'onopen' | 'onclose'>;
|
|
31
31
|
type $$ComponentProps = {
|
|
32
32
|
label: string;
|
|
33
33
|
items: MenuItem[];
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
14
14
|
import { formatCount, hasCount } from '../../count';
|
|
15
15
|
import { place } from '../../floating';
|
|
16
|
+
import Toggle from './Toggle.svelte';
|
|
16
17
|
import { HOVER_CLOSE_GRACE, HOVER_OPEN_DELAY, createHoverIntent, opensOnHover } from './popover-hover.js';
|
|
17
18
|
|
|
18
19
|
type Placement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
|
|
19
|
-
type TriggerVariant = 'default' | 'primary' | 'ghost' | 'danger';
|
|
20
|
+
type TriggerVariant = 'default' | 'primary' | 'ghost' | 'danger' | 'toggle';
|
|
20
21
|
type TriggerTone = 'none' | 'accent' | 'success' | 'info' | 'warn' | 'danger';
|
|
21
22
|
type TriggerSize = 'sm' | 'md' | 'lg';
|
|
22
23
|
type PanelRole = 'dialog' | 'menu' | 'listbox' | 'group';
|
|
@@ -42,8 +43,12 @@
|
|
|
42
43
|
* you fully own (pair with `triggerClass`). */
|
|
43
44
|
bare?: boolean;
|
|
44
45
|
/** Button-compatible trigger chrome. Omitting every chrome prop preserves
|
|
45
|
-
* the original ghost icon-button trigger.
|
|
46
|
+
* the original ghost icon-button trigger. `toggle` renders the trigger as
|
|
47
|
+
* the `Toggle` chip itself, so a trigger and a Toggle side by side share
|
|
48
|
+
* one box — `size`, `pill` and `pressed` behave as they do on `Toggle`. */
|
|
46
49
|
variant?: TriggerVariant;
|
|
50
|
+
/** `variant="toggle"` only: paint the chip in its "on" tint. */
|
|
51
|
+
pressed?: boolean;
|
|
47
52
|
tone?: TriggerTone;
|
|
48
53
|
size?: TriggerSize;
|
|
49
54
|
/** Shared square box scale (`--box-xs/sm/md/lg`) for an icon-only trigger,
|
|
@@ -96,6 +101,7 @@
|
|
|
96
101
|
triggerClass = '',
|
|
97
102
|
bare = false,
|
|
98
103
|
variant,
|
|
104
|
+
pressed = false,
|
|
99
105
|
tone = 'none',
|
|
100
106
|
size,
|
|
101
107
|
box,
|
|
@@ -122,12 +128,29 @@
|
|
|
122
128
|
...rest
|
|
123
129
|
}: Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own = $props();
|
|
124
130
|
|
|
131
|
+
// The Toggle chip is not restyled here, it is rendered: the trigger *is* a
|
|
132
|
+
// `Toggle`, so the two cannot drift apart. A link trigger keeps the canonical
|
|
133
|
+
// chrome — `Toggle` is always a button.
|
|
134
|
+
const toggleChrome = $derived(variant === 'toggle' && as === 'button');
|
|
125
135
|
const canonicalChrome = $derived(
|
|
126
|
-
variant !== undefined || tone !== 'none' || size !== undefined || control || block
|
|
136
|
+
!toggleChrome && (variant !== undefined || tone !== 'none' || size !== undefined || control || block)
|
|
127
137
|
);
|
|
128
138
|
const counted = $derived(hasCount(count));
|
|
129
139
|
const countText = $derived(hasCount(count) ? formatCount(count, countMax) : '');
|
|
130
140
|
const triggerName = $derived(counted ? `${label}, ${count}` : label);
|
|
141
|
+
const toneVar = $derived(
|
|
142
|
+
{ none: undefined, accent: 'var(--accent)', success: 'var(--ok)', info: 'var(--info)', warn: 'var(--warn)', danger: 'var(--danger)' }[
|
|
143
|
+
tone
|
|
144
|
+
]
|
|
145
|
+
);
|
|
146
|
+
// The count pill is absolutely positioned against the chip, which therefore
|
|
147
|
+
// needs to be its containing block; Popover's scoped CSS cannot reach an
|
|
148
|
+
// element rendered by `Toggle`, so the two declarations ride inline.
|
|
149
|
+
const toggleStyle = $derived(
|
|
150
|
+
[counted ? 'position:relative' : '', toneVar ? `--toggle-accent:${toneVar}` : '', styleProp]
|
|
151
|
+
.filter(Boolean)
|
|
152
|
+
.join(';')
|
|
153
|
+
);
|
|
131
154
|
|
|
132
155
|
const id = `pop-${Math.random().toString(36).slice(2, 8)}`;
|
|
133
156
|
let triggerEl = $state<HTMLElement | null>(null);
|
|
@@ -241,7 +264,30 @@
|
|
|
241
264
|
}
|
|
242
265
|
</script>
|
|
243
266
|
|
|
244
|
-
|
|
267
|
+
{#if toggleChrome}
|
|
268
|
+
<Toggle
|
|
269
|
+
{...rest}
|
|
270
|
+
{...triggerAttrs}
|
|
271
|
+
{pressed}
|
|
272
|
+
{pill}
|
|
273
|
+
size={size === 'md' ? 'md' : 'sm'}
|
|
274
|
+
data-tsu="Popover"
|
|
275
|
+
class="pop-trigger trigger-toggle {triggerClass} {klass}"
|
|
276
|
+
style={toggleStyle}
|
|
277
|
+
aria-label={triggerName}
|
|
278
|
+
aria-haspopup={haspopup}
|
|
279
|
+
aria-expanded={open}
|
|
280
|
+
onpointerenter={onPointerEnter}
|
|
281
|
+
onpointerleave={onPointerLeave}
|
|
282
|
+
{@attach (node) => {
|
|
283
|
+
triggerEl = node;
|
|
284
|
+
}}
|
|
285
|
+
>
|
|
286
|
+
{@render trigger()}
|
|
287
|
+
{#if counted}<span class="pop-count count-sm" aria-hidden="true">{countText}</span>{/if}
|
|
288
|
+
</Toggle>
|
|
289
|
+
{:else}
|
|
290
|
+
<svelte:element
|
|
245
291
|
this={as}
|
|
246
292
|
bind:this={triggerEl}
|
|
247
293
|
{...rest}
|
|
@@ -280,6 +326,7 @@
|
|
|
280
326
|
{@render trigger()}
|
|
281
327
|
{#if counted}<span class="pop-count" aria-hidden="true">{countText}</span>{/if}
|
|
282
328
|
</svelte:element>
|
|
329
|
+
{/if}
|
|
283
330
|
|
|
284
331
|
<div
|
|
285
332
|
bind:this={panelEl}
|
|
@@ -495,7 +542,8 @@
|
|
|
495
542
|
pointer-events: none;
|
|
496
543
|
}
|
|
497
544
|
.trigger-sm .pop-count,
|
|
498
|
-
.bare .pop-count
|
|
545
|
+
.bare .pop-count,
|
|
546
|
+
.pop-count.count-sm {
|
|
499
547
|
--pop-count-size: 1rem;
|
|
500
548
|
font-size: calc(var(--fs-xs) * 0.85);
|
|
501
549
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
3
|
type Placement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
|
|
4
|
-
type TriggerVariant = 'default' | 'primary' | 'ghost' | 'danger';
|
|
4
|
+
type TriggerVariant = 'default' | 'primary' | 'ghost' | 'danger' | 'toggle';
|
|
5
5
|
type TriggerTone = 'none' | 'accent' | 'success' | 'info' | 'warn' | 'danger';
|
|
6
6
|
type TriggerSize = 'sm' | 'md' | 'lg';
|
|
7
7
|
type PanelRole = 'dialog' | 'menu' | 'listbox' | 'group';
|
|
@@ -28,8 +28,12 @@ type Own = {
|
|
|
28
28
|
* you fully own (pair with `triggerClass`). */
|
|
29
29
|
bare?: boolean;
|
|
30
30
|
/** Button-compatible trigger chrome. Omitting every chrome prop preserves
|
|
31
|
-
* the original ghost icon-button trigger.
|
|
31
|
+
* the original ghost icon-button trigger. `toggle` renders the trigger as
|
|
32
|
+
* the `Toggle` chip itself, so a trigger and a Toggle side by side share
|
|
33
|
+
* one box — `size`, `pill` and `pressed` behave as they do on `Toggle`. */
|
|
32
34
|
variant?: TriggerVariant;
|
|
35
|
+
/** `variant="toggle"` only: paint the chip in its "on" tint. */
|
|
36
|
+
pressed?: boolean;
|
|
33
37
|
tone?: TriggerTone;
|
|
34
38
|
size?: TriggerSize;
|
|
35
39
|
/** Shared square box scale (`--box-xs/sm/md/lg`) for an icon-only trigger,
|
|
@@ -54,16 +54,20 @@
|
|
|
54
54
|
</button>
|
|
55
55
|
|
|
56
56
|
<style>
|
|
57
|
+
/* The height is pinned to the `--toggle-size` box, never to the line box:
|
|
58
|
+
an emoji, a 12px Icon and plain text all leave the chip the same height. */
|
|
57
59
|
.toggle {
|
|
58
60
|
display: inline-flex;
|
|
59
61
|
align-items: center;
|
|
60
62
|
justify-content: center;
|
|
61
63
|
gap: 4px;
|
|
62
|
-
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
height: var(--toggle-size, var(--box-xs));
|
|
66
|
+
padding: 0 var(--sp-2);
|
|
63
67
|
border-radius: var(--r-sm);
|
|
64
68
|
font-size: var(--fs-xs);
|
|
65
69
|
font-weight: var(--fw-medium);
|
|
66
|
-
line-height: 1
|
|
70
|
+
line-height: 1;
|
|
67
71
|
background: var(--bg-elevated-2);
|
|
68
72
|
color: var(--text-muted);
|
|
69
73
|
border: 1px solid var(--border);
|
|
@@ -82,7 +86,7 @@
|
|
|
82
86
|
border-radius: var(--r-pill);
|
|
83
87
|
}
|
|
84
88
|
.toggle.md {
|
|
85
|
-
|
|
89
|
+
height: var(--toggle-size, var(--control-height-compact));
|
|
86
90
|
font-size: var(--fs-sm);
|
|
87
91
|
}
|
|
88
92
|
.toggle.grow {
|
package/package.json
CHANGED