@dorsk/tsumikit 0.74.0 → 0.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -1
- package/dist/autoresize.d.ts +2 -0
- package/dist/autoresize.js +8 -1
- package/dist/components/atoms/Artwork.svelte +12 -5
- package/dist/components/atoms/Avatar.svelte +1 -1
- package/dist/components/atoms/Badge.svelte +59 -23
- package/dist/components/atoms/Button.svelte +130 -96
- package/dist/components/atoms/Button.svelte.d.ts +2 -0
- package/dist/components/atoms/Card.svelte +10 -4
- package/dist/components/atoms/Checkbox.svelte +36 -9
- package/dist/components/atoms/Checkbox.svelte.d.ts +2 -0
- package/dist/components/atoms/Gauge.svelte +20 -14
- package/dist/components/atoms/Heading.svelte +1 -1
- package/dist/components/atoms/Icon.svelte +1 -0
- package/dist/components/atoms/Input.svelte +67 -13
- package/dist/components/atoms/Kbd.svelte +3 -3
- package/dist/components/atoms/Link.svelte +7 -2
- package/dist/components/atoms/Progress.svelte +4 -2
- package/dist/components/atoms/Scrim.svelte +3 -8
- package/dist/components/atoms/SegmentedProgress.svelte +5 -4
- package/dist/components/atoms/Select.svelte +58 -27
- package/dist/components/atoms/Slider.svelte +79 -22
- package/dist/components/atoms/Slider.svelte.d.ts +2 -0
- package/dist/components/atoms/Swatch.svelte +19 -8
- package/dist/components/atoms/Switch.svelte +34 -21
- package/dist/components/atoms/Switch.svelte.d.ts +2 -0
- package/dist/components/atoms/Text.svelte +1 -1
- package/dist/components/atoms/Textarea.svelte +34 -12
- package/dist/components/atoms/TimeDisplay.svelte +4 -2
- package/dist/components/layouts/AppShell.svelte +26 -15
- package/dist/components/layouts/MasterDetail.svelte +23 -21
- package/dist/components/layouts/NavBar.svelte +11 -8
- package/dist/components/layouts/NavItem.svelte +15 -7
- package/dist/components/layouts/NavSection.svelte +1 -1
- package/dist/components/layouts/ResizablePanel.svelte +37 -16
- package/dist/components/layouts/TabBar.svelte +24 -14
- package/dist/components/layouts/TabBar.svelte.d.ts +2 -0
- package/dist/components/molecules/Accordion.svelte +4 -0
- package/dist/components/molecules/Accordion.svelte.d.ts +2 -0
- package/dist/components/molecules/AttachmentList.svelte +14 -11
- package/dist/components/molecules/Breadcrumb.svelte +13 -9
- package/dist/components/molecules/Callout.svelte +3 -3
- package/dist/components/molecules/CapBar.svelte +23 -22
- package/dist/components/molecules/Carousel.svelte +27 -17
- package/dist/components/molecules/ChatBubble.svelte +2 -2
- package/dist/components/molecules/CodeBlock.svelte +2 -2
- package/dist/components/molecules/Combobox.svelte +30 -1
- package/dist/components/molecules/Combobox.svelte.d.ts +4 -0
- package/dist/components/molecules/Composer.svelte +2 -2
- package/dist/components/molecules/ConfirmModal.svelte +4 -0
- package/dist/components/molecules/ConfirmModal.svelte.d.ts +2 -0
- package/dist/components/molecules/CopyButton.svelte +46 -2
- package/dist/components/molecules/CopyButton.svelte.d.ts +2 -0
- package/dist/components/molecules/Disclosure.svelte +23 -12
- package/dist/components/molecules/Disclosure.svelte.d.ts +2 -0
- package/dist/components/molecules/Drawer.svelte +43 -23
- package/dist/components/molecules/Drawer.svelte.d.ts +2 -0
- package/dist/components/molecules/Dropzone.svelte +24 -8
- package/dist/components/molecules/EmojiPicker.svelte +27 -8
- package/dist/components/molecules/EmojiPicker.svelte.d.ts +2 -0
- package/dist/components/molecules/EmptyState.svelte +5 -5
- package/dist/components/molecules/Fieldset.svelte +8 -13
- package/dist/components/molecules/FileButton.svelte +57 -31
- package/dist/components/molecules/FileButton.svelte.d.ts +2 -0
- package/dist/components/molecules/FilterInput.svelte +51 -11
- package/dist/components/molecules/FontScalePicker.svelte +20 -9
- package/dist/components/molecules/GitRef.svelte +9 -7
- package/dist/components/molecules/GlyphStack.svelte +38 -14
- package/dist/components/molecules/IconButton.svelte +6 -3
- package/dist/components/molecules/IconButton.svelte.d.ts +2 -0
- package/dist/components/molecules/InputGroup.svelte +13 -12
- package/dist/components/molecules/LoadMore.svelte +6 -1
- package/dist/components/molecules/LoadMore.svelte.d.ts +2 -0
- package/dist/components/molecules/Menu.svelte +33 -7
- package/dist/components/molecules/Menu.svelte.d.ts +2 -0
- package/dist/components/molecules/Metric.svelte +11 -9
- package/dist/components/molecules/Modal.svelte +76 -26
- package/dist/components/molecules/Modal.svelte.d.ts +2 -0
- package/dist/components/molecules/OptionButton.svelte +28 -10
- package/dist/components/molecules/OptionButton.svelte.d.ts +2 -0
- package/dist/components/molecules/Pagination.svelte +26 -9
- package/dist/components/molecules/Pagination.svelte.d.ts +2 -0
- package/dist/components/molecules/Popover.svelte +107 -51
- package/dist/components/molecules/Popover.svelte.d.ts +2 -0
- package/dist/components/molecules/Prose.svelte +1 -1
- package/dist/components/molecules/PullToRefresh.svelte +14 -10
- package/dist/components/molecules/PullToRefresh.svelte.d.ts +2 -0
- package/dist/components/molecules/RadialMenu.svelte +52 -19
- package/dist/components/molecules/RadialMenu.svelte.d.ts +2 -0
- package/dist/components/molecules/RadioGroup.svelte +38 -23
- package/dist/components/molecules/RadioGroup.svelte.d.ts +2 -0
- package/dist/components/molecules/SectionHeader.svelte +13 -8
- package/dist/components/molecules/SeekSlider.svelte +38 -27
- package/dist/components/molecules/SeekSlider.svelte.d.ts +2 -0
- package/dist/components/molecules/SegmentedControl.svelte +139 -86
- package/dist/components/molecules/SegmentedControl.svelte.d.ts +2 -0
- package/dist/components/molecules/SelectButton.svelte +21 -6
- package/dist/components/molecules/SelectButton.svelte.d.ts +2 -0
- package/dist/components/molecules/SortableList.svelte +10 -11
- package/dist/components/molecules/SoundSettings.svelte +161 -0
- package/dist/components/molecules/SoundSettings.svelte.d.ts +17 -0
- package/dist/components/molecules/SplitButton.svelte +30 -3
- package/dist/components/molecules/SplitButton.svelte.d.ts +2 -0
- package/dist/components/molecules/SwipeRow.svelte +18 -15
- package/dist/components/molecules/SwipeRow.svelte.d.ts +2 -0
- package/dist/components/molecules/Tabs.svelte +92 -10
- package/dist/components/molecules/Tabs.svelte.d.ts +2 -0
- package/dist/components/molecules/ThemePicker.svelte +25 -10
- package/dist/components/molecules/Timestamp.svelte +12 -7
- package/dist/components/molecules/Toaster.svelte +65 -14
- package/dist/components/molecules/Toaster.svelte.d.ts +2 -0
- package/dist/components/molecules/Toggle.svelte +36 -13
- package/dist/components/molecules/Toggle.svelte.d.ts +2 -0
- package/dist/components/molecules/Tooltip.svelte +41 -10
- package/dist/components/molecules/Tooltip.svelte.d.ts +1 -1
- package/dist/components/molecules/VolumeSlider.svelte +34 -14
- package/dist/components/molecules/VolumeSlider.svelte.d.ts +2 -0
- package/dist/components/molecules/WorkingDir.svelte +1 -0
- package/dist/components/organisms/ConversationFeed.svelte +4 -0
- package/dist/components/organisms/DataTable.svelte +37 -14
- package/dist/floating.js +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/sound-synth.js +17 -12
- package/dist/stores/toast.svelte.d.ts +9 -0
- package/dist/stores/toast.svelte.js +45 -9
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// <button> + scoped styles rather than specializing the Button atom.
|
|
11
11
|
import type { Snippet } from 'svelte';
|
|
12
12
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
13
|
+
import { playSound } from '../../sound';
|
|
13
14
|
|
|
14
15
|
let {
|
|
15
16
|
pressed = false,
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
shrink = true,
|
|
21
22
|
type = 'button',
|
|
22
23
|
disabled = false,
|
|
24
|
+
sound = true,
|
|
25
|
+
onclick,
|
|
23
26
|
class: klass = '',
|
|
24
27
|
children,
|
|
25
28
|
...rest
|
|
@@ -32,8 +35,17 @@
|
|
|
32
35
|
grow?: boolean;
|
|
33
36
|
// `shrink={false}` keeps the chip at its intrinsic width (flex: none).
|
|
34
37
|
shrink?: boolean;
|
|
38
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
39
|
+
sound?: boolean;
|
|
35
40
|
children?: Snippet;
|
|
36
41
|
} = $props();
|
|
42
|
+
|
|
43
|
+
function click(e: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
|
44
|
+
const next = !pressed;
|
|
45
|
+
onclick?.(e);
|
|
46
|
+
if (e.defaultPrevented) return;
|
|
47
|
+
playSound(next ? 'toggle-on' : 'toggle-off', sound);
|
|
48
|
+
}
|
|
37
49
|
</script>
|
|
38
50
|
|
|
39
51
|
<button
|
|
@@ -41,6 +53,7 @@
|
|
|
41
53
|
{...rest}
|
|
42
54
|
{type}
|
|
43
55
|
{disabled}
|
|
56
|
+
onclick={click}
|
|
44
57
|
class="toggle {klass}"
|
|
45
58
|
class:pill
|
|
46
59
|
class:struck
|
|
@@ -60,34 +73,38 @@
|
|
|
60
73
|
display: inline-flex;
|
|
61
74
|
align-items: center;
|
|
62
75
|
justify-content: center;
|
|
63
|
-
gap:
|
|
76
|
+
gap: var(--sp-1);
|
|
64
77
|
box-sizing: border-box;
|
|
65
|
-
height: var(--toggle-size, var(--
|
|
66
|
-
padding: 0 var(--
|
|
78
|
+
height: var(--toggle-size, var(--control-sm));
|
|
79
|
+
padding: 0 var(--control-px-sm);
|
|
67
80
|
border-radius: var(--r-sm);
|
|
68
|
-
font-size: var(--fs-
|
|
81
|
+
font-size: var(--control-fs-sm);
|
|
69
82
|
font-weight: var(--fw-medium);
|
|
70
|
-
line-height:
|
|
83
|
+
line-height: var(--lh-none);
|
|
84
|
+
--icon-size: var(--control-icon-sm);
|
|
71
85
|
background: var(--bg-elevated-2);
|
|
72
86
|
color: var(--text-muted);
|
|
73
87
|
border: 1px solid var(--border);
|
|
74
88
|
white-space: nowrap;
|
|
75
89
|
user-select: none;
|
|
76
90
|
transition:
|
|
77
|
-
background
|
|
78
|
-
border-color
|
|
79
|
-
color
|
|
91
|
+
background var(--dur-fast) var(--ease),
|
|
92
|
+
border-color var(--dur-fast) var(--ease),
|
|
93
|
+
color var(--dur-fast) var(--ease),
|
|
94
|
+
scale var(--dur-fast) var(--ease-out);
|
|
80
95
|
}
|
|
81
96
|
.toggle:disabled {
|
|
82
|
-
opacity:
|
|
97
|
+
opacity: var(--disabled-opacity);
|
|
83
98
|
cursor: not-allowed;
|
|
84
99
|
}
|
|
85
100
|
.toggle.pill {
|
|
86
101
|
border-radius: var(--r-pill);
|
|
87
102
|
}
|
|
88
103
|
.toggle.md {
|
|
89
|
-
height: var(--toggle-size, var(--control-
|
|
90
|
-
|
|
104
|
+
height: var(--toggle-size, var(--control-md));
|
|
105
|
+
padding: 0 var(--control-px-md);
|
|
106
|
+
font-size: var(--control-fs-md);
|
|
107
|
+
--icon-size: var(--control-icon-md);
|
|
91
108
|
}
|
|
92
109
|
.toggle.grow {
|
|
93
110
|
flex: 1 1 0;
|
|
@@ -96,8 +113,14 @@
|
|
|
96
113
|
.toggle.no-shrink {
|
|
97
114
|
flex: none;
|
|
98
115
|
}
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
@media (hover: hover) and (pointer: fine) {
|
|
117
|
+
.toggle:hover:not(:disabled) {
|
|
118
|
+
border-color: var(--border-strong);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
.toggle:active:not(:disabled) {
|
|
122
|
+
scale: var(--press-scale);
|
|
123
|
+
transition-duration: var(--dur-press);
|
|
101
124
|
}
|
|
102
125
|
.toggle.on {
|
|
103
126
|
--tc: var(--toggle-accent, var(--accent));
|
|
@@ -8,6 +8,8 @@ type $$ComponentProps = HTMLButtonAttributes & {
|
|
|
8
8
|
size?: ControlSize;
|
|
9
9
|
grow?: boolean;
|
|
10
10
|
shrink?: boolean;
|
|
11
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
12
|
+
sound?: boolean;
|
|
11
13
|
children?: Snippet;
|
|
12
14
|
};
|
|
13
15
|
declare const Toggle: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
let lastClosed = -Infinity;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
1
5
|
<script lang="ts">
|
|
2
6
|
// Lightweight tooltip. Shows on hover (after a short delay) and on keyboard
|
|
3
7
|
// focus of the trigger; hides on blur / mouseleave / Escape (anywhere). The trigger is
|
|
@@ -15,7 +19,7 @@
|
|
|
15
19
|
// by `closeDelay` and cancelled when the pointer (or focus) enters the panel,
|
|
16
20
|
// so users can move in to select/copy text or click a button. Note the
|
|
17
21
|
// role="tooltip" caveat: `aria-describedby` flattens the panel to its text.
|
|
18
|
-
import type
|
|
22
|
+
import { flushSync, type Snippet } from 'svelte';
|
|
19
23
|
import { place } from '../../floating';
|
|
20
24
|
|
|
21
25
|
let {
|
|
@@ -53,6 +57,8 @@
|
|
|
53
57
|
style?: string;
|
|
54
58
|
} = $props();
|
|
55
59
|
|
|
60
|
+
// A tooltip closed moments ago makes the next one open at once, unanimated.
|
|
61
|
+
const WARM_MS = 300;
|
|
56
62
|
const id = `tip-${Math.random().toString(36).slice(2, 8)}`;
|
|
57
63
|
let wrapEl = $state<HTMLElement | null>(null);
|
|
58
64
|
let tipEl = $state<HTMLElement | null>(null);
|
|
@@ -68,23 +74,32 @@
|
|
|
68
74
|
if (e.key === 'Escape') hideNow();
|
|
69
75
|
}
|
|
70
76
|
|
|
77
|
+
let instant = $state(false);
|
|
71
78
|
function show() {
|
|
72
79
|
clearTimeout(hideTimer); // pointer came back within the grace period
|
|
73
80
|
clearTimeout(showTimer);
|
|
74
|
-
|
|
81
|
+
const warm = performance.now() - lastClosed < WARM_MS;
|
|
82
|
+
const reveal = () => {
|
|
75
83
|
if (!tipEl || tipEl.matches(':popover-open')) return; // re-entry guard
|
|
84
|
+
instant = warm;
|
|
85
|
+
flushSync();
|
|
76
86
|
tipEl.showPopover(); // top layer — displayed before we measure it
|
|
77
87
|
reposition();
|
|
78
88
|
addEventListener('scroll', reposition, true);
|
|
79
89
|
addEventListener('resize', reposition);
|
|
80
90
|
document.addEventListener('keydown', onEscape);
|
|
81
|
-
}
|
|
91
|
+
};
|
|
92
|
+
if (warm) reveal();
|
|
93
|
+
else showTimer = setTimeout(reveal, delay);
|
|
82
94
|
}
|
|
83
95
|
function hideNow() {
|
|
84
96
|
clearTimeout(showTimer);
|
|
85
97
|
clearTimeout(hideTimer);
|
|
86
98
|
try {
|
|
87
|
-
if (tipEl?.matches(':popover-open'))
|
|
99
|
+
if (tipEl?.matches(':popover-open')) {
|
|
100
|
+
tipEl.hidePopover();
|
|
101
|
+
lastClosed = performance.now();
|
|
102
|
+
}
|
|
88
103
|
} catch {}
|
|
89
104
|
removeEventListener('scroll', reposition, true);
|
|
90
105
|
removeEventListener('resize', reposition);
|
|
@@ -164,6 +179,7 @@
|
|
|
164
179
|
popover="manual"
|
|
165
180
|
class="tip"
|
|
166
181
|
class:rich={!!content}
|
|
182
|
+
data-instant={instant ? '' : undefined}
|
|
167
183
|
style:width
|
|
168
184
|
style:max-width={maxWidth}
|
|
169
185
|
use:panel
|
|
@@ -194,9 +210,16 @@
|
|
|
194
210
|
border-radius: var(--r-sm);
|
|
195
211
|
box-shadow: var(--shadow-md);
|
|
196
212
|
font-size: var(--fs-xs);
|
|
197
|
-
line-height:
|
|
213
|
+
line-height: var(--lh-snug);
|
|
198
214
|
white-space: normal;
|
|
199
215
|
pointer-events: none; /* non-interactive: never steals hover/clicks */
|
|
216
|
+
transform-origin: var(--tsu-origin, bottom center);
|
|
217
|
+
opacity: 0;
|
|
218
|
+
scale: var(--tip-scale);
|
|
219
|
+
transition:
|
|
220
|
+
opacity var(--dur-exit-fast) var(--ease-out),
|
|
221
|
+
display var(--dur-exit-fast) allow-discrete,
|
|
222
|
+
overlay var(--dur-exit-fast) allow-discrete;
|
|
200
223
|
}
|
|
201
224
|
/* Rich panel: interactive + selectable so the user can move the pointer in
|
|
202
225
|
to copy an id or click a button (the close grace period allows the trip). */
|
|
@@ -207,14 +230,22 @@
|
|
|
207
230
|
padding: var(--sp-2);
|
|
208
231
|
border-radius: var(--r-md);
|
|
209
232
|
}
|
|
210
|
-
/* Fade/scale in when shown (skipped under reduced-motion via the global rule). */
|
|
211
233
|
.tip:popover-open {
|
|
212
|
-
|
|
234
|
+
opacity: 1;
|
|
235
|
+
scale: 1;
|
|
236
|
+
transition:
|
|
237
|
+
opacity var(--dur-fast) var(--ease-out),
|
|
238
|
+
scale var(--dur-fast) var(--ease-out),
|
|
239
|
+
display var(--dur-fast) allow-discrete,
|
|
240
|
+
overlay var(--dur-fast) allow-discrete;
|
|
213
241
|
}
|
|
214
|
-
@
|
|
215
|
-
|
|
242
|
+
@starting-style {
|
|
243
|
+
.tip:popover-open {
|
|
216
244
|
opacity: 0;
|
|
217
|
-
|
|
245
|
+
scale: var(--tip-scale);
|
|
218
246
|
}
|
|
219
247
|
}
|
|
248
|
+
.tip[data-instant] {
|
|
249
|
+
transition-duration: var(--dur-instant);
|
|
250
|
+
}
|
|
220
251
|
</style>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
9
9
|
import type { IconName } from '../atoms/Icon.svelte';
|
|
10
10
|
import IconButton from './IconButton.svelte';
|
|
11
|
+
import { playSound } from '../../sound';
|
|
11
12
|
|
|
12
13
|
type Own = {
|
|
13
14
|
/** Level 0–1. */
|
|
@@ -22,6 +23,8 @@
|
|
|
22
23
|
label?: string;
|
|
23
24
|
muteLabel?: string;
|
|
24
25
|
unmuteLabel?: string;
|
|
26
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
27
|
+
sound?: boolean;
|
|
25
28
|
oninput?: (value: number) => void;
|
|
26
29
|
onchange?: (value: number) => void;
|
|
27
30
|
onmutechange?: (muted: boolean) => void;
|
|
@@ -40,6 +43,7 @@
|
|
|
40
43
|
label = 'Volume',
|
|
41
44
|
muteLabel = 'Mute',
|
|
42
45
|
unmuteLabel = 'Unmute',
|
|
46
|
+
sound = true,
|
|
43
47
|
oninput,
|
|
44
48
|
onchange,
|
|
45
49
|
onmutechange,
|
|
@@ -59,6 +63,7 @@
|
|
|
59
63
|
muted ? 'volume-mute' : level === 0 ? 'volume' : level < 0.5 ? 'volume-low' : 'volume-high'
|
|
60
64
|
);
|
|
61
65
|
const valueText = $derived(muted ? 'Muted' : `${pct}%`);
|
|
66
|
+
const discrete = $derived(1 / step <= 20);
|
|
62
67
|
|
|
63
68
|
function setMuted(m: boolean) {
|
|
64
69
|
if (m === muted) return;
|
|
@@ -75,7 +80,8 @@
|
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
function commit(v: number) {
|
|
78
|
-
|
|
83
|
+
const next = apply(v);
|
|
84
|
+
onchange?.(next);
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
function fracAt(el: HTMLElement, e: PointerEvent) {
|
|
@@ -117,6 +123,7 @@
|
|
|
117
123
|
|
|
118
124
|
function onpointermove(e: PointerEvent) {
|
|
119
125
|
if (pointerId !== e.pointerId) return;
|
|
126
|
+
if (discrete) playSound('tick', sound);
|
|
120
127
|
apply(fracAt(e.currentTarget as HTMLElement, e));
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -167,6 +174,7 @@
|
|
|
167
174
|
}
|
|
168
175
|
if (next === null) return;
|
|
169
176
|
e.preventDefault();
|
|
177
|
+
playSound('tick', sound);
|
|
170
178
|
commit(next);
|
|
171
179
|
}
|
|
172
180
|
|
|
@@ -176,6 +184,7 @@
|
|
|
176
184
|
const d = e.deltaY || e.deltaX;
|
|
177
185
|
if (!d) return;
|
|
178
186
|
e.preventDefault();
|
|
187
|
+
playSound('tick', sound);
|
|
179
188
|
commit(level + (d < 0 ? step : -step));
|
|
180
189
|
};
|
|
181
190
|
el.addEventListener('wheel', onwheel, { passive: false });
|
|
@@ -199,6 +208,7 @@
|
|
|
199
208
|
box="sm"
|
|
200
209
|
size={size === 'sm' ? 16 : 18}
|
|
201
210
|
{disabled}
|
|
211
|
+
{sound}
|
|
202
212
|
onclick={() => setMuted(!muted)}
|
|
203
213
|
data-part="mute"
|
|
204
214
|
/>
|
|
@@ -237,6 +247,7 @@
|
|
|
237
247
|
--vol-thumb: 10px;
|
|
238
248
|
--vol-length: 5rem;
|
|
239
249
|
--vol-accent: var(--slider-accent, var(--accent));
|
|
250
|
+
--vol-halo: 0 0 0 4px color-mix(in srgb, var(--vol-accent) 20%, transparent);
|
|
240
251
|
display: inline-flex;
|
|
241
252
|
align-items: center;
|
|
242
253
|
gap: var(--sp-1);
|
|
@@ -281,8 +292,8 @@
|
|
|
281
292
|
}
|
|
282
293
|
}
|
|
283
294
|
.vol-slab[aria-disabled='true'] {
|
|
284
|
-
cursor:
|
|
285
|
-
opacity:
|
|
295
|
+
cursor: not-allowed;
|
|
296
|
+
opacity: var(--disabled-opacity);
|
|
286
297
|
}
|
|
287
298
|
.vol-slab:focus-visible {
|
|
288
299
|
outline: var(--focus-ring);
|
|
@@ -340,28 +351,37 @@
|
|
|
340
351
|
top: 0;
|
|
341
352
|
width: var(--vol-thumb);
|
|
342
353
|
height: var(--vol-thumb);
|
|
343
|
-
border-radius:
|
|
354
|
+
border-radius: var(--r-pill);
|
|
344
355
|
background: var(--vol-accent);
|
|
345
356
|
border: 2px solid var(--bg);
|
|
346
357
|
box-shadow: var(--shadow-sm);
|
|
347
|
-
transform: translate(-50%, -50%)
|
|
358
|
+
transform: translate(-50%, -50%);
|
|
359
|
+
scale: 0.6;
|
|
348
360
|
opacity: 0;
|
|
349
361
|
transition:
|
|
350
|
-
opacity
|
|
351
|
-
|
|
362
|
+
opacity var(--dur-fast) var(--ease),
|
|
363
|
+
scale var(--dur-press) var(--ease-out),
|
|
364
|
+
box-shadow var(--dur-fast) var(--ease);
|
|
352
365
|
}
|
|
353
|
-
.vol-slab:hover .vol-thumb,
|
|
354
366
|
.vol-slab:focus-visible .vol-thumb,
|
|
355
367
|
.vol--dragging .vol-thumb {
|
|
356
368
|
opacity: 1;
|
|
357
|
-
|
|
369
|
+
scale: 1;
|
|
358
370
|
}
|
|
359
|
-
.vol--
|
|
360
|
-
|
|
371
|
+
.vol--dragging .vol-thumb {
|
|
372
|
+
scale: 1.15;
|
|
361
373
|
}
|
|
362
|
-
@media (
|
|
363
|
-
.vol-thumb {
|
|
364
|
-
|
|
374
|
+
@media (hover: hover) and (pointer: fine) {
|
|
375
|
+
.vol-slab:not([aria-disabled='true']):hover .vol-thumb {
|
|
376
|
+
opacity: 1;
|
|
377
|
+
scale: 1;
|
|
378
|
+
box-shadow: var(--shadow-sm), var(--vol-halo);
|
|
365
379
|
}
|
|
380
|
+
.vol--dragging .vol-thumb {
|
|
381
|
+
scale: 1.15;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
.vol--muted .vol-fill {
|
|
385
|
+
background: var(--text-faint);
|
|
366
386
|
}
|
|
367
387
|
</style>
|
|
@@ -18,6 +18,8 @@ type Own = {
|
|
|
18
18
|
label?: string;
|
|
19
19
|
muteLabel?: string;
|
|
20
20
|
unmuteLabel?: string;
|
|
21
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
22
|
+
sound?: boolean;
|
|
21
23
|
oninput?: (value: number) => void;
|
|
22
24
|
onchange?: (value: number) => void;
|
|
23
25
|
onmutechange?: (muted: boolean) => void;
|
|
@@ -254,8 +254,13 @@
|
|
|
254
254
|
{:else if col.sortable}
|
|
255
255
|
<button type="button" class="dt-sort" onclick={() => toggleSort(col)}>
|
|
256
256
|
<span>{col.label}</span>
|
|
257
|
-
<span
|
|
258
|
-
|
|
257
|
+
<span
|
|
258
|
+
class="dt-arrow"
|
|
259
|
+
aria-hidden="true"
|
|
260
|
+
class:active={sortKey === col.key}
|
|
261
|
+
class:desc={sortKey === col.key && sortDir === 'desc'}
|
|
262
|
+
>
|
|
263
|
+
{sortKey === col.key ? '↑' : '↕'}
|
|
259
264
|
</span>
|
|
260
265
|
</button>
|
|
261
266
|
{:else}
|
|
@@ -425,31 +430,47 @@
|
|
|
425
430
|
color: inherit;
|
|
426
431
|
cursor: pointer;
|
|
427
432
|
}
|
|
428
|
-
.dt-sort:hover {
|
|
429
|
-
color: var(--text);
|
|
430
|
-
}
|
|
431
433
|
.dt-arrow {
|
|
434
|
+
display: inline-block;
|
|
432
435
|
opacity: 0.4;
|
|
433
436
|
font-size: 0.9em;
|
|
437
|
+
transition:
|
|
438
|
+
rotate var(--dur-base) var(--ease-out),
|
|
439
|
+
opacity var(--dur-fast) var(--ease);
|
|
434
440
|
}
|
|
435
441
|
.dt-arrow.active {
|
|
436
442
|
opacity: 1;
|
|
437
443
|
color: var(--accent);
|
|
438
444
|
}
|
|
445
|
+
.dt-arrow.desc {
|
|
446
|
+
rotate: 180deg;
|
|
447
|
+
}
|
|
439
448
|
tbody tr:last-child td {
|
|
440
449
|
border-bottom: none;
|
|
441
450
|
}
|
|
442
451
|
tr.clickable {
|
|
443
452
|
cursor: pointer;
|
|
444
|
-
transition: background
|
|
453
|
+
transition: background var(--dur-fast) var(--ease);
|
|
445
454
|
}
|
|
446
|
-
tr.clickable:hover,
|
|
447
455
|
tr.clickable:focus-visible {
|
|
448
456
|
background: var(--bg-elevated-2);
|
|
449
|
-
}
|
|
450
|
-
tr.clickable:focus-visible {
|
|
451
457
|
outline: var(--focus-ring);
|
|
452
|
-
outline-offset: -
|
|
458
|
+
outline-offset: var(--focus-ring-offset-inset);
|
|
459
|
+
}
|
|
460
|
+
@media (hover: hover) and (pointer: fine) {
|
|
461
|
+
.dt-sort:hover {
|
|
462
|
+
color: var(--text);
|
|
463
|
+
}
|
|
464
|
+
tr.clickable:hover {
|
|
465
|
+
background: var(--bg-elevated-2);
|
|
466
|
+
transition-duration: var(--dur-instant);
|
|
467
|
+
}
|
|
468
|
+
tr:hover .dt-actions {
|
|
469
|
+
opacity: 1;
|
|
470
|
+
}
|
|
471
|
+
.dt-more-btn:hover {
|
|
472
|
+
background: var(--bg-elevated-2);
|
|
473
|
+
}
|
|
453
474
|
}
|
|
454
475
|
td.truncate {
|
|
455
476
|
white-space: nowrap;
|
|
@@ -487,7 +508,7 @@
|
|
|
487
508
|
}
|
|
488
509
|
.dt-actions {
|
|
489
510
|
opacity: 0;
|
|
490
|
-
transition: opacity
|
|
511
|
+
transition: opacity var(--dur-fast) var(--ease);
|
|
491
512
|
}
|
|
492
513
|
.dt-actions-inner {
|
|
493
514
|
display: inline-flex;
|
|
@@ -495,7 +516,6 @@
|
|
|
495
516
|
gap: var(--sp-1);
|
|
496
517
|
white-space: nowrap;
|
|
497
518
|
}
|
|
498
|
-
tr:hover .dt-actions,
|
|
499
519
|
tr:focus-within .dt-actions {
|
|
500
520
|
opacity: 1;
|
|
501
521
|
}
|
|
@@ -503,6 +523,10 @@
|
|
|
503
523
|
.dt-actions {
|
|
504
524
|
opacity: 1;
|
|
505
525
|
}
|
|
526
|
+
/* The last row's checkbox slab would otherwise add a scroll range. */
|
|
527
|
+
tbody tr:last-child td :global(.checkbox::before) {
|
|
528
|
+
inset-block-end: 0;
|
|
529
|
+
}
|
|
506
530
|
/* Sort slab: inline-end stays at 0 so it never reaches the next header
|
|
507
531
|
cell, and the inline-start reach stays inside the cell's own padding. */
|
|
508
532
|
.dt-sort {
|
|
@@ -664,9 +688,8 @@
|
|
|
664
688
|
font: inherit;
|
|
665
689
|
color: var(--accent);
|
|
666
690
|
cursor: pointer;
|
|
667
|
-
border-radius: var(--r-md
|
|
691
|
+
border-radius: var(--r-md);
|
|
668
692
|
}
|
|
669
|
-
.dt-more-btn:hover,
|
|
670
693
|
.dt-more-btn:focus-visible {
|
|
671
694
|
background: var(--bg-elevated-2);
|
|
672
695
|
}
|
package/dist/floating.js
CHANGED
|
@@ -56,7 +56,33 @@ export function placeAt(t, floating, placement = 'bottom-start', gap = 6) {
|
|
|
56
56
|
left = clamp(left, gap, vw - f.width - gap);
|
|
57
57
|
floating.style.top = `${Math.round(top)}px`;
|
|
58
58
|
floating.style.left = `${Math.round(left)}px`;
|
|
59
|
+
const resolved = side === 'top' || side === 'bottom'
|
|
60
|
+
? top + f.height / 2 < t.top + t.height / 2
|
|
61
|
+
? 'top'
|
|
62
|
+
: 'bottom'
|
|
63
|
+
: left + f.width / 2 < t.left + t.width / 2
|
|
64
|
+
? 'left'
|
|
65
|
+
: 'right';
|
|
66
|
+
const cx = Math.round(clamp(t.left + t.width / 2 - left, 0, f.width));
|
|
67
|
+
const cy = Math.round(clamp(t.top + t.height / 2 - top, 0, f.height));
|
|
68
|
+
floating.dataset.side = resolved;
|
|
69
|
+
floating.style.setProperty('--tsu-origin', ORIGIN[resolved](cx, cy));
|
|
70
|
+
floating.style.setProperty('--tsu-shift', SHIFT[resolved]);
|
|
59
71
|
}
|
|
72
|
+
// Enter motion grows out of the trigger: the origin sits on the trigger-facing
|
|
73
|
+
// edge, level with the trigger centre, and the panel starts shifted towards it.
|
|
74
|
+
const ORIGIN = {
|
|
75
|
+
top: (cx) => `${cx}px 100%`,
|
|
76
|
+
bottom: (cx) => `${cx}px 0`,
|
|
77
|
+
left: (_cx, cy) => `100% ${cy}px`,
|
|
78
|
+
right: (_cx, cy) => `0 ${cy}px`,
|
|
79
|
+
};
|
|
80
|
+
const SHIFT = {
|
|
81
|
+
top: '0 var(--shift-sm)',
|
|
82
|
+
bottom: '0 calc(-1 * var(--shift-sm))',
|
|
83
|
+
left: 'var(--shift-sm) 0',
|
|
84
|
+
right: 'calc(-1 * var(--shift-sm)) 0',
|
|
85
|
+
};
|
|
60
86
|
/** Angles (degrees) of `count` petals spread evenly from `from` to `to`. */
|
|
61
87
|
export function fanAngles(count, from, to) {
|
|
62
88
|
if (count <= 1)
|
package/dist/index.d.ts
CHANGED
|
@@ -90,6 +90,7 @@ export { default as SeekSlider } from './components/molecules/SeekSlider.svelte'
|
|
|
90
90
|
export { default as SegmentedControl, type SegmentOption, } from './components/molecules/SegmentedControl.svelte';
|
|
91
91
|
export { default as SelectButton } from './components/molecules/SelectButton.svelte';
|
|
92
92
|
export { default as SortableList, SORTABLE_LIST_MESSAGES, type SortableListMessages, type SortableListMove, type SortableListRowContext, } from './components/molecules/SortableList.svelte';
|
|
93
|
+
export { default as SoundSettings } from './components/molecules/SoundSettings.svelte';
|
|
93
94
|
export { default as SplitButton } from './components/molecules/SplitButton.svelte';
|
|
94
95
|
export { default as StatusBar } from './components/molecules/StatusBar.svelte';
|
|
95
96
|
export { default as SwipeRow, type SwipeAction } from './components/molecules/SwipeRow.svelte';
|
package/dist/index.js
CHANGED
|
@@ -97,6 +97,7 @@ export { default as SeekSlider } from './components/molecules/SeekSlider.svelte'
|
|
|
97
97
|
export { default as SegmentedControl, } from './components/molecules/SegmentedControl.svelte';
|
|
98
98
|
export { default as SelectButton } from './components/molecules/SelectButton.svelte';
|
|
99
99
|
export { default as SortableList, SORTABLE_LIST_MESSAGES, } from './components/molecules/SortableList.svelte';
|
|
100
|
+
export { default as SoundSettings } from './components/molecules/SoundSettings.svelte';
|
|
100
101
|
export { default as SplitButton } from './components/molecules/SplitButton.svelte';
|
|
101
102
|
export { default as StatusBar } from './components/molecules/StatusBar.svelte';
|
|
102
103
|
export { default as SwipeRow } from './components/molecules/SwipeRow.svelte';
|
package/dist/sound-synth.js
CHANGED
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
const PALETTE = {
|
|
2
2
|
click: [
|
|
3
|
-
['n',
|
|
4
|
-
['sine', 620, 380, 0, 0.04, 0.
|
|
3
|
+
['n', 3000, 0, 0, 0.012, 0.18],
|
|
4
|
+
['sine', 620, 380, 0, 0.04, 0.22],
|
|
5
5
|
],
|
|
6
6
|
'toggle-on': [
|
|
7
|
-
['n',
|
|
8
|
-
['
|
|
7
|
+
['n', 2600, 0, 0, 0.012, 0.3],
|
|
8
|
+
['sine', 520, 520, 0, 0.03, 0.14],
|
|
9
|
+
['triangle', 660, 880, 0.035, 0.05, 0.3],
|
|
9
10
|
],
|
|
10
11
|
'toggle-off': [
|
|
11
|
-
['n',
|
|
12
|
-
['
|
|
12
|
+
['n', 2600, 0, 0, 0.012, 0.3],
|
|
13
|
+
['sine', 520, 520, 0, 0.03, 0.14],
|
|
14
|
+
['triangle', 880, 620, 0.035, 0.05, 0.3],
|
|
13
15
|
],
|
|
14
16
|
open: [['sine', 420, 760, 0, 0.09, 0.25]],
|
|
15
17
|
close: [['sine', 700, 380, 0, 0.08, 0.25]],
|
|
16
18
|
success: [
|
|
17
|
-
['sine',
|
|
18
|
-
['sine',
|
|
19
|
+
['sine', 698, 698, 0, 0.055, 0.25],
|
|
20
|
+
['sine', 1047, 1047, 0.058, 0.085, 0.2],
|
|
19
21
|
],
|
|
20
22
|
error: [
|
|
21
|
-
['triangle', 330, 300, 0, 0.
|
|
22
|
-
['triangle', 262, 240, 0.
|
|
23
|
+
['triangle', 330, 300, 0, 0.065, 0.28],
|
|
24
|
+
['triangle', 262, 240, 0.06, 0.08, 0.28],
|
|
25
|
+
],
|
|
26
|
+
tick: [
|
|
27
|
+
['n', 3800, 0, 0, 0.008, 0.3],
|
|
28
|
+
['sine', 2400, 1900, 0, 0.012, 0.14],
|
|
23
29
|
],
|
|
24
|
-
tick: [['n', 5200, 0, 0, 0.006, 0.25]],
|
|
25
30
|
};
|
|
26
31
|
let ctx;
|
|
27
32
|
let out;
|
|
@@ -51,7 +56,7 @@ export function play(name, volume) {
|
|
|
51
56
|
const env = c.createGain();
|
|
52
57
|
const s = t + at;
|
|
53
58
|
env.gain.setValueAtTime(0.0001, s);
|
|
54
|
-
env.gain.exponentialRampToValueAtTime(g, s + 0.
|
|
59
|
+
env.gain.exponentialRampToValueAtTime(g, s + 0.004);
|
|
55
60
|
env.gain.exponentialRampToValueAtTime(0.0001, s + d);
|
|
56
61
|
env.connect(out);
|
|
57
62
|
let src;
|
|
@@ -13,6 +13,8 @@ export interface Toast {
|
|
|
13
13
|
action?: ToastAction;
|
|
14
14
|
/** True while `action.run` is in flight; the Toaster shows the button loading. */
|
|
15
15
|
pending: boolean;
|
|
16
|
+
/** Dismissed: the Toaster plays the exit while the store holds it for `TOAST_EXIT_MS`. */
|
|
17
|
+
leaving: boolean;
|
|
16
18
|
}
|
|
17
19
|
export interface ToastOptions {
|
|
18
20
|
tone?: ToastToneInput;
|
|
@@ -22,14 +24,21 @@ export interface ToastOptions {
|
|
|
22
24
|
export declare const TOAST_MS = 4000;
|
|
23
25
|
/** Toasts with an action linger longer: the action is only reachable while visible. */
|
|
24
26
|
export declare const ACTION_TOAST_MS = 7000;
|
|
27
|
+
/** Matches `--dur-exit-slow`; the removal never waits on `transitionend` alone. */
|
|
28
|
+
export declare const TOAST_EXIT_MS = 200;
|
|
25
29
|
declare class Toasts {
|
|
26
30
|
#private;
|
|
27
31
|
items: Toast[];
|
|
28
32
|
show(message: string, opts?: ToastOptions): number;
|
|
33
|
+
/** Hold the auto-dismiss (pointer over the toast); `resume` restarts the remaining time. */
|
|
34
|
+
pause(id: number): void;
|
|
35
|
+
resume(id: number): void;
|
|
29
36
|
ok(message: string, duration?: number, action?: ToastAction): number;
|
|
30
37
|
error(message: string, duration?: number, action?: ToastAction): number;
|
|
31
38
|
info(message: string, duration?: number, action?: ToastAction): number;
|
|
32
39
|
dismiss(id: number): void;
|
|
40
|
+
/** Drop a toast at once, skipping the exit. */
|
|
41
|
+
remove(id: number): void;
|
|
33
42
|
/** Run a toast's action once: the toast stays (button loading) until run settles, then dismisses. */
|
|
34
43
|
act(id: number): Promise<void>;
|
|
35
44
|
}
|