@dorsk/tsumikit 0.74.0 → 0.76.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 +46 -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/RichSelect.svelte +583 -0
- package/dist/components/molecules/RichSelect.svelte.d.ts +68 -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/molecules/rich-select-keyboard.d.ts +82 -0
- package/dist/components/molecules/rich-select-keyboard.js +114 -0
- package/dist/components/organisms/ConversationFeed.svelte +24 -1
- package/dist/components/organisms/DataTable.svelte +37 -14
- package/dist/floating.js +26 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -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/dist/styles/reset.css +3 -3
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// `offset` written back). Numbered buttons collapse to prev / "3 / 12" /
|
|
6
6
|
// next when the container is narrower than 24rem.
|
|
7
7
|
import Text from '../atoms/Text.svelte';
|
|
8
|
+
import { playSound } from '../../sound';
|
|
8
9
|
import IconButton from './IconButton.svelte';
|
|
9
10
|
|
|
10
11
|
let {
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
shrink = true,
|
|
23
24
|
block = false,
|
|
24
25
|
label = 'Pagination',
|
|
26
|
+
sound = true,
|
|
25
27
|
class: klass = ''
|
|
26
28
|
}: {
|
|
27
29
|
/** 1-based current page. */
|
|
@@ -46,6 +48,8 @@
|
|
|
46
48
|
/** Full-width block. */
|
|
47
49
|
block?: boolean;
|
|
48
50
|
label?: string;
|
|
51
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
52
|
+
sound?: boolean;
|
|
49
53
|
class?: string;
|
|
50
54
|
} = $props();
|
|
51
55
|
|
|
@@ -96,6 +100,7 @@
|
|
|
96
100
|
function go(p: number) {
|
|
97
101
|
const next = Math.min(count, Math.max(1, p));
|
|
98
102
|
if (next === current) return;
|
|
103
|
+
playSound('click', sound);
|
|
99
104
|
page = next;
|
|
100
105
|
if (offsetMode) offset = (next - 1) * (limit as number);
|
|
101
106
|
onchange?.(next);
|
|
@@ -116,6 +121,7 @@
|
|
|
116
121
|
label="Previous page"
|
|
117
122
|
box={size === 'sm' ? 'sm' : 'md'}
|
|
118
123
|
disabled={current <= 1}
|
|
124
|
+
sound={false}
|
|
119
125
|
onclick={() => go(current - 1)}
|
|
120
126
|
/>
|
|
121
127
|
<ol class="pages">
|
|
@@ -144,6 +150,7 @@
|
|
|
144
150
|
label="Next page"
|
|
145
151
|
box={size === 'sm' ? 'sm' : 'md'}
|
|
146
152
|
disabled={current >= count}
|
|
153
|
+
sound={false}
|
|
147
154
|
onclick={() => go(current + 1)}
|
|
148
155
|
/>
|
|
149
156
|
{#if showRange}
|
|
@@ -181,21 +188,30 @@
|
|
|
181
188
|
.page {
|
|
182
189
|
min-width: var(--box-md);
|
|
183
190
|
height: var(--box-md);
|
|
184
|
-
padding: 0 var(--
|
|
191
|
+
padding: 0 var(--control-px-md);
|
|
185
192
|
border: 1px solid transparent;
|
|
186
|
-
border-radius:
|
|
193
|
+
border-radius: var(--r-md);
|
|
187
194
|
background: transparent;
|
|
188
195
|
color: var(--text-muted);
|
|
189
|
-
font-size: var(--fs-
|
|
196
|
+
font-size: var(--control-fs-md);
|
|
190
197
|
font-variant-numeric: tabular-nums;
|
|
191
198
|
cursor: pointer;
|
|
199
|
+
--page-scale-dur: var(--dur-fast);
|
|
192
200
|
transition:
|
|
193
|
-
background
|
|
194
|
-
color
|
|
201
|
+
background var(--dur-fast) ease,
|
|
202
|
+
border-color var(--dur-fast) ease,
|
|
203
|
+
color var(--dur-fast) ease,
|
|
204
|
+
scale var(--page-scale-dur) var(--ease-out);
|
|
205
|
+
}
|
|
206
|
+
.page:active {
|
|
207
|
+
--page-scale-dur: var(--dur-press);
|
|
208
|
+
scale: var(--press-scale);
|
|
195
209
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
210
|
+
@media (hover: hover) and (pointer: fine) {
|
|
211
|
+
.page:hover {
|
|
212
|
+
background: var(--bg-elevated-2);
|
|
213
|
+
color: var(--text);
|
|
214
|
+
}
|
|
199
215
|
}
|
|
200
216
|
.page.on {
|
|
201
217
|
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
|
@@ -206,7 +222,8 @@
|
|
|
206
222
|
.pagination-sm .page {
|
|
207
223
|
min-width: var(--box-sm);
|
|
208
224
|
height: var(--box-sm);
|
|
209
|
-
|
|
225
|
+
padding: 0 var(--control-px-sm);
|
|
226
|
+
font-size: var(--control-fs-sm);
|
|
210
227
|
}
|
|
211
228
|
.ellipsis {
|
|
212
229
|
display: inline-flex;
|
|
@@ -22,6 +22,8 @@ type $$ComponentProps = {
|
|
|
22
22
|
/** Full-width block. */
|
|
23
23
|
block?: boolean;
|
|
24
24
|
label?: string;
|
|
25
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
26
|
+
sound?: boolean;
|
|
25
27
|
class?: string;
|
|
26
28
|
};
|
|
27
29
|
declare const Pagination: import("svelte").Component<$$ComponentProps, {}, "offset" | "page">;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
14
14
|
import { formatCount, hasCount } from '../../count';
|
|
15
15
|
import { place } from '../../floating';
|
|
16
|
+
import { playSound } from '../../sound';
|
|
16
17
|
import Toggle from './Toggle.svelte';
|
|
17
18
|
import { HOVER_CLOSE_GRACE, HOVER_OPEN_DELAY, createHoverIntent, opensOnHover } from './popover-hover.js';
|
|
18
19
|
|
|
@@ -90,6 +91,8 @@
|
|
|
90
91
|
/** Class / inline style on the floating panel. */
|
|
91
92
|
panelClass?: string;
|
|
92
93
|
panelStyle?: string;
|
|
94
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
95
|
+
sound?: boolean;
|
|
93
96
|
};
|
|
94
97
|
|
|
95
98
|
let {
|
|
@@ -125,6 +128,7 @@
|
|
|
125
128
|
style: styleProp = '',
|
|
126
129
|
panelClass = '',
|
|
127
130
|
panelStyle = '',
|
|
131
|
+
sound = true,
|
|
128
132
|
...rest
|
|
129
133
|
}: Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own = $props();
|
|
130
134
|
|
|
@@ -161,6 +165,10 @@
|
|
|
161
165
|
let opened = $state(false);
|
|
162
166
|
|
|
163
167
|
let shown = false;
|
|
168
|
+
// An `open` that is already true on mount is a sync, not a transition: no sound.
|
|
169
|
+
let quiet = open;
|
|
170
|
+
// Keyboard opens skip the enter animation (`data-instant`).
|
|
171
|
+
let instant = false;
|
|
164
172
|
|
|
165
173
|
$effect(() => {
|
|
166
174
|
if (!panelEl) return;
|
|
@@ -223,12 +231,21 @@
|
|
|
223
231
|
}
|
|
224
232
|
|
|
225
233
|
function onTriggerKeydown(e: KeyboardEvent) {
|
|
226
|
-
if (
|
|
227
|
-
|
|
234
|
+
if (disabled) return;
|
|
235
|
+
const key = e.key;
|
|
236
|
+
if (key === 'ArrowDown' && !open) {
|
|
228
237
|
e.preventDefault();
|
|
229
238
|
hover.cancel();
|
|
230
|
-
|
|
239
|
+
instant = true;
|
|
240
|
+
show();
|
|
241
|
+
return;
|
|
231
242
|
}
|
|
243
|
+
if (key !== 'Enter' && key !== ' ') return;
|
|
244
|
+
instant = !open;
|
|
245
|
+
if (as !== 'a' || href !== undefined) return;
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
hover.cancel();
|
|
248
|
+
toggle();
|
|
232
249
|
}
|
|
233
250
|
|
|
234
251
|
const triggerAttrs = $derived(
|
|
@@ -252,6 +269,13 @@
|
|
|
252
269
|
const firstOpen = !opened;
|
|
253
270
|
opened = true;
|
|
254
271
|
open = true;
|
|
272
|
+
if (panelEl) {
|
|
273
|
+
if (instant) panelEl.dataset.instant = '';
|
|
274
|
+
else delete panelEl.dataset.instant;
|
|
275
|
+
}
|
|
276
|
+
instant = false;
|
|
277
|
+
if (!quiet) playSound('open', sound);
|
|
278
|
+
quiet = false;
|
|
255
279
|
// On the first open the content snippet mounts this tick; measure the
|
|
256
280
|
// panel only once it has, so placement accounts for its real size.
|
|
257
281
|
if (firstOpen) await tick();
|
|
@@ -261,8 +285,10 @@
|
|
|
261
285
|
onopen?.();
|
|
262
286
|
} else {
|
|
263
287
|
open = false;
|
|
288
|
+
instant = false;
|
|
264
289
|
removeEventListener('scroll', reposition, true);
|
|
265
290
|
removeEventListener('resize', reposition);
|
|
291
|
+
playSound('close', sound);
|
|
266
292
|
onclose?.();
|
|
267
293
|
}
|
|
268
294
|
}
|
|
@@ -371,68 +397,96 @@
|
|
|
371
397
|
background: var(--pop-trigger-bg, transparent);
|
|
372
398
|
color: var(--pop-trigger-fg, var(--text));
|
|
373
399
|
font-size: var(--pop-trigger-size, inherit);
|
|
400
|
+
--icon-size: var(--control-icon-md);
|
|
401
|
+
--pop-scale-dur: var(--dur-fast);
|
|
374
402
|
transition:
|
|
375
|
-
background
|
|
376
|
-
border-color
|
|
403
|
+
background-color var(--dur-fast) var(--ease),
|
|
404
|
+
border-color var(--dur-fast) var(--ease),
|
|
405
|
+
color var(--dur-fast) var(--ease),
|
|
406
|
+
scale var(--pop-scale-dur) var(--ease-out);
|
|
407
|
+
}
|
|
408
|
+
.pop-trigger:not(.bare, .trigger-toggle):active:not(:disabled, .is-disabled) {
|
|
409
|
+
--pop-scale-dur: var(--dur-press);
|
|
410
|
+
scale: var(--press-scale-sm);
|
|
411
|
+
}
|
|
412
|
+
.pop-trigger.canonical:active:not(:disabled, .is-disabled) {
|
|
413
|
+
scale: var(--press-scale);
|
|
377
414
|
}
|
|
378
|
-
|
|
379
|
-
|
|
415
|
+
@media (hover: hover) and (pointer: fine) {
|
|
416
|
+
:where(.pop-trigger:not(.bare):hover:not(:disabled)) {
|
|
417
|
+
background: var(--bg-elevated-2);
|
|
418
|
+
}
|
|
419
|
+
.pop-trigger.canonical:hover:not(:disabled) {
|
|
420
|
+
border-color: var(--accent);
|
|
421
|
+
background: var(--surface);
|
|
422
|
+
}
|
|
423
|
+
.pop-trigger.trigger-primary:hover:not(:disabled) {
|
|
424
|
+
background: var(--accent);
|
|
425
|
+
border-color: var(--accent);
|
|
426
|
+
filter: brightness(1.08);
|
|
427
|
+
}
|
|
428
|
+
.pop-trigger.trigger-ghost:hover:not(:disabled) {
|
|
429
|
+
background: var(--bg-elevated-2);
|
|
430
|
+
border-color: transparent;
|
|
431
|
+
}
|
|
432
|
+
.pop-trigger.trigger-danger:hover:not(:disabled) {
|
|
433
|
+
background: color-mix(in srgb, var(--danger) 14%, transparent);
|
|
434
|
+
border-color: var(--danger);
|
|
435
|
+
}
|
|
436
|
+
.pop-trigger.trigger-tone-accent:hover:not(:disabled),
|
|
437
|
+
.pop-trigger.trigger-tone-success:hover:not(:disabled),
|
|
438
|
+
.pop-trigger.trigger-tone-info:hover:not(:disabled),
|
|
439
|
+
.pop-trigger.trigger-tone-warn:hover:not(:disabled),
|
|
440
|
+
.pop-trigger.trigger-tone-danger:hover:not(:disabled) {
|
|
441
|
+
background: color-mix(in srgb, var(--pop-trigger-tone) 14%, transparent);
|
|
442
|
+
border-color: var(--pop-trigger-tone);
|
|
443
|
+
}
|
|
444
|
+
.pop-trigger.trigger-primary.trigger-tone-success:hover:not(:disabled) {
|
|
445
|
+
background: var(--ok);
|
|
446
|
+
border-color: var(--ok);
|
|
447
|
+
filter: brightness(1.08);
|
|
448
|
+
}
|
|
380
449
|
}
|
|
381
450
|
/* Supplying canonical chrome props opts into the same dimensions, variants
|
|
382
451
|
and state tones as Button while keeping this one native trigger element. */
|
|
383
452
|
.pop-trigger.canonical {
|
|
384
453
|
min-width: 0;
|
|
385
454
|
min-height: var(--control-height-default);
|
|
386
|
-
padding: var(--pop-trigger-pad, var(--sp-2) var(--
|
|
455
|
+
padding: var(--pop-trigger-pad, var(--sp-2) var(--btn-px-md));
|
|
387
456
|
border-color: var(--pop-trigger-border, var(--border-strong));
|
|
388
457
|
background: var(--pop-trigger-bg, var(--surface));
|
|
389
458
|
font-weight: var(--fw-medium);
|
|
390
|
-
font-size: var(--pop-trigger-size, var(--fs-
|
|
391
|
-
line-height:
|
|
459
|
+
font-size: var(--pop-trigger-size, var(--control-fs-md));
|
|
460
|
+
line-height: var(--lh-none);
|
|
392
461
|
user-select: none;
|
|
393
462
|
white-space: nowrap;
|
|
394
463
|
}
|
|
395
|
-
.pop-trigger.canonical:hover:not(:disabled) {
|
|
396
|
-
border-color: var(--accent);
|
|
397
|
-
background: var(--surface);
|
|
398
|
-
}
|
|
399
464
|
.pop-trigger.trigger-primary {
|
|
400
465
|
background: var(--pop-trigger-bg, var(--accent));
|
|
401
466
|
border-color: var(--pop-trigger-border, var(--accent));
|
|
402
467
|
color: var(--pop-trigger-fg, var(--text-on-accent));
|
|
403
468
|
font-weight: var(--fw-semibold);
|
|
404
469
|
}
|
|
405
|
-
.pop-trigger.trigger-primary:hover:not(:disabled) {
|
|
406
|
-
background: var(--accent);
|
|
407
|
-
border-color: var(--accent);
|
|
408
|
-
filter: brightness(1.08);
|
|
409
|
-
}
|
|
410
470
|
.pop-trigger.trigger-ghost {
|
|
411
471
|
background: var(--pop-trigger-bg, transparent);
|
|
412
472
|
border-color: var(--pop-trigger-border, transparent);
|
|
413
473
|
}
|
|
414
|
-
.pop-trigger.trigger-ghost:hover:not(:disabled) {
|
|
415
|
-
background: var(--bg-elevated-2);
|
|
416
|
-
border-color: transparent;
|
|
417
|
-
}
|
|
418
474
|
.pop-trigger.trigger-danger {
|
|
419
475
|
color: var(--pop-trigger-fg, var(--danger));
|
|
420
476
|
border-color: var(--pop-trigger-border, color-mix(in srgb, var(--danger) 50%, var(--border)));
|
|
421
477
|
}
|
|
422
|
-
.pop-trigger.trigger-danger:hover:not(:disabled) {
|
|
423
|
-
background: color-mix(in srgb, var(--danger) 14%, transparent);
|
|
424
|
-
border-color: var(--danger);
|
|
425
|
-
}
|
|
426
478
|
.pop-trigger.trigger-sm {
|
|
427
479
|
height: var(--control-height-compact);
|
|
428
480
|
min-height: var(--control-height-compact);
|
|
429
|
-
padding: var(--pop-trigger-pad, var(--sp-1) var(--
|
|
430
|
-
font-size: var(--pop-trigger-size, var(--fs-
|
|
481
|
+
padding: var(--pop-trigger-pad, var(--sp-1) var(--btn-px-sm));
|
|
482
|
+
font-size: var(--pop-trigger-size, var(--control-fs-sm));
|
|
483
|
+
--icon-size: var(--control-icon-sm);
|
|
431
484
|
}
|
|
432
485
|
.pop-trigger.trigger-lg {
|
|
433
486
|
min-height: var(--control-height-large);
|
|
434
|
-
padding: var(--pop-trigger-pad, var(--sp-3) var(--
|
|
435
|
-
font-size: var(--pop-trigger-size, var(--fs-
|
|
487
|
+
padding: var(--pop-trigger-pad, var(--sp-3) var(--btn-px-lg));
|
|
488
|
+
font-size: var(--pop-trigger-size, var(--control-fs-lg));
|
|
489
|
+
--icon-size: var(--control-icon-lg);
|
|
436
490
|
}
|
|
437
491
|
.pop-trigger.trigger-control {
|
|
438
492
|
height: var(--control-height);
|
|
@@ -491,27 +545,14 @@
|
|
|
491
545
|
color: var(--pop-trigger-fg, var(--pop-trigger-tone));
|
|
492
546
|
border-color: var(--pop-trigger-border, color-mix(in srgb, var(--pop-trigger-tone) 50%, var(--border)));
|
|
493
547
|
}
|
|
494
|
-
.pop-trigger.trigger-tone-accent:hover:not(:disabled),
|
|
495
|
-
.pop-trigger.trigger-tone-success:hover:not(:disabled),
|
|
496
|
-
.pop-trigger.trigger-tone-info:hover:not(:disabled),
|
|
497
|
-
.pop-trigger.trigger-tone-warn:hover:not(:disabled),
|
|
498
|
-
.pop-trigger.trigger-tone-danger:hover:not(:disabled) {
|
|
499
|
-
background: color-mix(in srgb, var(--pop-trigger-tone) 14%, transparent);
|
|
500
|
-
border-color: var(--pop-trigger-tone);
|
|
501
|
-
}
|
|
502
548
|
.pop-trigger.trigger-primary.trigger-tone-success {
|
|
503
549
|
background: var(--pop-trigger-bg, var(--ok));
|
|
504
550
|
border-color: var(--pop-trigger-border, var(--ok));
|
|
505
551
|
color: var(--pop-trigger-fg, var(--text-on-success));
|
|
506
552
|
}
|
|
507
|
-
.pop-trigger.trigger-primary.trigger-tone-success:hover:not(:disabled) {
|
|
508
|
-
background: var(--ok);
|
|
509
|
-
border-color: var(--ok);
|
|
510
|
-
filter: brightness(1.08);
|
|
511
|
-
}
|
|
512
553
|
.pop-trigger:disabled,
|
|
513
554
|
.pop-trigger.is-disabled {
|
|
514
|
-
opacity:
|
|
555
|
+
opacity: var(--disabled-opacity);
|
|
515
556
|
cursor: not-allowed;
|
|
516
557
|
}
|
|
517
558
|
.pop-trigger.is-disabled {
|
|
@@ -544,7 +585,7 @@
|
|
|
544
585
|
font-size: var(--fs-xs);
|
|
545
586
|
font-weight: var(--fw-semibold);
|
|
546
587
|
font-variant-numeric: tabular-nums;
|
|
547
|
-
line-height:
|
|
588
|
+
line-height: var(--lh-none);
|
|
548
589
|
white-space: nowrap;
|
|
549
590
|
pointer-events: none;
|
|
550
591
|
}
|
|
@@ -584,15 +625,30 @@
|
|
|
584
625
|
border: 1px solid var(--border-strong);
|
|
585
626
|
border-radius: var(--r-md);
|
|
586
627
|
box-shadow: var(--shadow-md);
|
|
628
|
+
transform-origin: var(--tsu-origin, top left);
|
|
629
|
+
opacity: 0;
|
|
630
|
+
scale: var(--enter-scale);
|
|
631
|
+
transition:
|
|
632
|
+
opacity var(--dur-exit) var(--ease-out),
|
|
633
|
+
scale var(--dur-exit) var(--ease-out),
|
|
634
|
+
translate var(--dur-exit) var(--ease-out),
|
|
635
|
+
display var(--dur-exit) allow-discrete,
|
|
636
|
+
overlay var(--dur-exit) allow-discrete;
|
|
587
637
|
}
|
|
588
|
-
/* Enter animation (skipped under reduced-motion via the global rule). */
|
|
589
638
|
.pop-panel:popover-open {
|
|
590
|
-
|
|
639
|
+
opacity: 1;
|
|
640
|
+
scale: 1;
|
|
641
|
+
translate: 0;
|
|
642
|
+
transition-duration: var(--dur-base);
|
|
591
643
|
}
|
|
592
|
-
@
|
|
593
|
-
|
|
644
|
+
@starting-style {
|
|
645
|
+
.pop-panel:popover-open {
|
|
594
646
|
opacity: 0;
|
|
595
|
-
|
|
647
|
+
scale: var(--enter-scale);
|
|
648
|
+
translate: var(--tsu-shift, 0 calc(-1 * var(--shift-sm)));
|
|
596
649
|
}
|
|
597
650
|
}
|
|
651
|
+
.pop-panel[data-instant] {
|
|
652
|
+
transition-duration: var(--dur-instant);
|
|
653
|
+
}
|
|
598
654
|
</style>
|
|
@@ -75,6 +75,8 @@ type Own = {
|
|
|
75
75
|
/** Class / inline style on the floating panel. */
|
|
76
76
|
panelClass?: string;
|
|
77
77
|
panelStyle?: string;
|
|
78
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
79
|
+
sound?: boolean;
|
|
78
80
|
};
|
|
79
81
|
type $$ComponentProps = Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own;
|
|
80
82
|
declare const Popover: import("svelte").Component<$$ComponentProps, {}, "open">;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { Snippet } from 'svelte';
|
|
8
8
|
import Icon from '../atoms/Icon.svelte';
|
|
9
9
|
import Spinner from '../atoms/Spinner.svelte';
|
|
10
|
+
import { playSound } from '../../sound';
|
|
10
11
|
|
|
11
12
|
let {
|
|
12
13
|
children,
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
pullLabel = 'Pull to refresh',
|
|
17
18
|
releaseLabel = 'Release to refresh',
|
|
18
19
|
disabled = false,
|
|
20
|
+
sound = true,
|
|
19
21
|
class: klass = '',
|
|
20
22
|
...rest
|
|
21
23
|
}: {
|
|
@@ -29,6 +31,8 @@
|
|
|
29
31
|
pullLabel?: string;
|
|
30
32
|
releaseLabel?: string;
|
|
31
33
|
disabled?: boolean;
|
|
34
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
35
|
+
sound?: boolean;
|
|
32
36
|
class?: string;
|
|
33
37
|
[key: string]: unknown;
|
|
34
38
|
} = $props();
|
|
@@ -68,7 +72,9 @@
|
|
|
68
72
|
if (dy < SLOP) return;
|
|
69
73
|
pulling = true;
|
|
70
74
|
}
|
|
75
|
+
const wasReady = ready;
|
|
71
76
|
offset = dy > 0 ? damp(dy) : 0;
|
|
77
|
+
if (ready && !wasReady) playSound('tick', sound);
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
function onpointerup(e: PointerEvent) {
|
|
@@ -125,6 +131,7 @@
|
|
|
125
131
|
class:pulling
|
|
126
132
|
class:pending
|
|
127
133
|
style:--ptr-offset="{offset}px"
|
|
134
|
+
style:--ptr-threshold="{threshold}px"
|
|
128
135
|
style:--ptr-progress={progress}
|
|
129
136
|
aria-busy={pending || undefined}
|
|
130
137
|
{onpointerdown}
|
|
@@ -156,7 +163,7 @@
|
|
|
156
163
|
}
|
|
157
164
|
.ptr-content {
|
|
158
165
|
translate: 0 var(--ptr-offset, 0px);
|
|
159
|
-
transition: translate
|
|
166
|
+
transition: translate var(--dur-exit-slow) var(--ease-out);
|
|
160
167
|
}
|
|
161
168
|
.pulling .ptr-content {
|
|
162
169
|
transition: none;
|
|
@@ -167,11 +174,13 @@
|
|
|
167
174
|
display: flex;
|
|
168
175
|
align-items: center;
|
|
169
176
|
justify-content: center;
|
|
170
|
-
height: var(--ptr-
|
|
171
|
-
|
|
177
|
+
height: var(--ptr-threshold, 72px);
|
|
178
|
+
translate: 0 calc(var(--ptr-offset, 0px) - 100%);
|
|
172
179
|
color: var(--text-muted);
|
|
173
180
|
opacity: var(--ptr-progress, 0);
|
|
174
|
-
transition:
|
|
181
|
+
transition:
|
|
182
|
+
translate var(--dur-exit-slow) var(--ease-out),
|
|
183
|
+
opacity var(--dur-exit-slow) var(--ease-out);
|
|
175
184
|
pointer-events: none;
|
|
176
185
|
}
|
|
177
186
|
.pulling .ptr-indicator {
|
|
@@ -179,7 +188,7 @@
|
|
|
179
188
|
}
|
|
180
189
|
.ptr-indicator :global(.ptr-arrow) {
|
|
181
190
|
rotate: calc(180deg * var(--ptr-progress, 0));
|
|
182
|
-
transition: rotate
|
|
191
|
+
transition: rotate var(--dur-base) var(--ease);
|
|
183
192
|
}
|
|
184
193
|
.pending .ptr-indicator {
|
|
185
194
|
color: var(--accent);
|
|
@@ -193,11 +202,6 @@
|
|
|
193
202
|
white-space: nowrap;
|
|
194
203
|
}
|
|
195
204
|
@media (prefers-reduced-motion: reduce) {
|
|
196
|
-
.ptr-content,
|
|
197
|
-
.ptr-indicator,
|
|
198
|
-
.ptr-indicator :global(.ptr-arrow) {
|
|
199
|
-
transition: none;
|
|
200
|
-
}
|
|
201
205
|
.ptr-indicator :global(.ptr-arrow) {
|
|
202
206
|
rotate: none;
|
|
203
207
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { menuItemCloses, menuItemGlyphs } from './menu-item';
|
|
20
20
|
import { type Bounds, fanLayout, type Point } from '../../floating';
|
|
21
21
|
import { slideSelect } from '../../slide-select';
|
|
22
|
+
import { playSound } from '../../sound';
|
|
22
23
|
import { canonicalTone, type Tone } from '../../tone';
|
|
23
24
|
|
|
24
25
|
type Own = {
|
|
@@ -28,6 +29,8 @@
|
|
|
28
29
|
open?: boolean;
|
|
29
30
|
onopen?: () => void;
|
|
30
31
|
onclose?: () => void;
|
|
32
|
+
/** Play UI sounds when enabled globally; false silences this instance. */
|
|
33
|
+
sound?: boolean;
|
|
31
34
|
/** Replaces the default FAB content. */
|
|
32
35
|
trigger?: Snippet<[{ open: boolean }]>;
|
|
33
36
|
icon?: IconName;
|
|
@@ -66,6 +69,7 @@
|
|
|
66
69
|
status,
|
|
67
70
|
class: klass = '',
|
|
68
71
|
style: styleProp = '',
|
|
72
|
+
sound = true,
|
|
69
73
|
...rest
|
|
70
74
|
}: Omit<HTMLButtonAttributes, keyof Own> & Own = $props();
|
|
71
75
|
|
|
@@ -85,12 +89,16 @@
|
|
|
85
89
|
let active = $state(0);
|
|
86
90
|
let petalPx = 0;
|
|
87
91
|
let shown = false;
|
|
92
|
+
// An `open` already true on mount is a sync, not a transition: no sound.
|
|
93
|
+
let quiet = open;
|
|
88
94
|
let focusOnOpen = false;
|
|
89
95
|
let pressedOpen = false;
|
|
90
96
|
let swallowClick = false;
|
|
91
97
|
let releasedAt = Number.NEGATIVE_INFINITY;
|
|
92
98
|
|
|
93
99
|
const petals = $derived(items.filter((item) => !item.control));
|
|
100
|
+
// Keep the whole fan inside the default budget: base enter + (n-1) × stagger ≤ 300 ms.
|
|
101
|
+
const stagger = $derived(`${Math.min(18, petals.length > 1 ? 120 / (petals.length - 1) : 18)}ms`);
|
|
94
102
|
const ring = $derived(tone === 'neutral' ? 'var(--border-strong)' : `var(--${canonicalTone(tone)})`);
|
|
95
103
|
const forward = $derived(points.length < 2 || points[points.length - 1].x >= points[0].x);
|
|
96
104
|
|
|
@@ -210,6 +218,8 @@
|
|
|
210
218
|
if (!panel || want === shown) return;
|
|
211
219
|
shown = want;
|
|
212
220
|
untrack(() => {
|
|
221
|
+
if (!quiet) playSound(want ? 'open' : 'close', sound);
|
|
222
|
+
quiet = false;
|
|
213
223
|
if (want) {
|
|
214
224
|
show();
|
|
215
225
|
position();
|
|
@@ -332,7 +342,7 @@
|
|
|
332
342
|
{#if trigger}
|
|
333
343
|
{@render trigger({ open })}
|
|
334
344
|
{:else}
|
|
335
|
-
<Icon name={open ? closeIcon : icon}
|
|
345
|
+
<span class="radial-face-glyph" aria-hidden="true"><Icon name={open ? closeIcon : icon} /></span>
|
|
336
346
|
{#if caption}
|
|
337
347
|
<span class="radial-caption">
|
|
338
348
|
{#if typeof caption === 'string'}{caption}{:else}{@render caption()}{/if}
|
|
@@ -388,7 +398,7 @@
|
|
|
388
398
|
</div>
|
|
389
399
|
{/if}
|
|
390
400
|
{/if}
|
|
391
|
-
<div id={menuId} role="menu" aria-label={label} class="radial-menu">
|
|
401
|
+
<div id={menuId} role="menu" aria-label={label} class="radial-menu" style:--radial-stagger={stagger}>
|
|
392
402
|
{#if open}
|
|
393
403
|
{#each petals as item, i (item.id ?? `${i}:${item.label}`)}
|
|
394
404
|
{@const p = points[i]}
|
|
@@ -418,9 +428,9 @@
|
|
|
418
428
|
{:else}
|
|
419
429
|
<span class="radial-glyph" aria-hidden="true">
|
|
420
430
|
{#if glyphs.leading === 'icon' && item.icon}
|
|
421
|
-
<Icon name={item.icon}
|
|
431
|
+
<Icon name={item.icon} />
|
|
422
432
|
{:else if glyphs.leading === 'check'}
|
|
423
|
-
<Icon name={item.pressed ? 'check' : 'minus'}
|
|
433
|
+
<Icon name={item.pressed ? 'check' : 'minus'} />
|
|
424
434
|
{/if}
|
|
425
435
|
</span>
|
|
426
436
|
<span class="radial-label">{item.label}</span>
|
|
@@ -440,7 +450,7 @@
|
|
|
440
450
|
flex-direction: column;
|
|
441
451
|
align-items: center;
|
|
442
452
|
justify-content: center;
|
|
443
|
-
gap:
|
|
453
|
+
gap: var(--sp-px);
|
|
444
454
|
flex: none;
|
|
445
455
|
width: var(--radial-fab-size, 3.75rem);
|
|
446
456
|
height: var(--radial-fab-size, 3.75rem);
|
|
@@ -456,18 +466,31 @@
|
|
|
456
466
|
touch-action: none;
|
|
457
467
|
user-select: none;
|
|
458
468
|
-webkit-touch-callout: none;
|
|
469
|
+
--radial-scale-dur: var(--dur-fast);
|
|
470
|
+
transition:
|
|
471
|
+
background-color var(--dur-fast) var(--ease),
|
|
472
|
+
border-color var(--dur-fast) var(--ease),
|
|
473
|
+
scale var(--radial-scale-dur) var(--ease-out);
|
|
474
|
+
}
|
|
475
|
+
.radial-fab:active:not(:disabled) {
|
|
476
|
+
--radial-scale-dur: var(--dur-press);
|
|
477
|
+
scale: var(--press-scale-sm);
|
|
478
|
+
}
|
|
479
|
+
.radial-face-glyph {
|
|
480
|
+
display: inline-flex;
|
|
481
|
+
font-size: var(--icon-lg);
|
|
459
482
|
}
|
|
460
483
|
.radial-fab:focus-visible {
|
|
461
484
|
outline: var(--focus-ring);
|
|
462
485
|
outline-offset: var(--focus-ring-offset);
|
|
463
486
|
}
|
|
464
487
|
.radial-fab:disabled {
|
|
465
|
-
opacity:
|
|
488
|
+
opacity: var(--disabled-opacity);
|
|
466
489
|
cursor: not-allowed;
|
|
467
490
|
}
|
|
468
491
|
.radial-caption {
|
|
469
492
|
font-size: var(--fs-xs);
|
|
470
|
-
line-height:
|
|
493
|
+
line-height: var(--lh-none);
|
|
471
494
|
color: var(--text-muted);
|
|
472
495
|
white-space: nowrap;
|
|
473
496
|
}
|
|
@@ -515,22 +538,31 @@
|
|
|
515
538
|
flex-direction: column;
|
|
516
539
|
align-items: center;
|
|
517
540
|
justify-content: center;
|
|
518
|
-
gap:
|
|
541
|
+
gap: var(--sp-0-5);
|
|
519
542
|
width: max(var(--radial-petal-size, 3.5rem), var(--touch-target));
|
|
520
543
|
height: max(var(--radial-petal-size, 3.5rem), var(--touch-target));
|
|
521
|
-
padding: 0
|
|
544
|
+
padding: 0 var(--sp-0-5);
|
|
522
545
|
border: 1px solid var(--border-strong);
|
|
523
546
|
border-radius: 50%;
|
|
524
547
|
background: var(--bg-elevated);
|
|
525
548
|
color: var(--text);
|
|
526
549
|
box-shadow: var(--shadow-md);
|
|
527
550
|
font-size: var(--fs-xs);
|
|
528
|
-
line-height:
|
|
551
|
+
line-height: var(--lh-heading);
|
|
529
552
|
translate: -50% -50%;
|
|
530
553
|
pointer-events: auto;
|
|
531
554
|
cursor: pointer;
|
|
532
|
-
|
|
533
|
-
|
|
555
|
+
--radial-scale-dur: var(--dur-fast);
|
|
556
|
+
transition:
|
|
557
|
+
background-color var(--dur-fast) var(--ease),
|
|
558
|
+
border-color var(--dur-fast) var(--ease),
|
|
559
|
+
scale var(--radial-scale-dur) var(--ease-out);
|
|
560
|
+
animation: radial-in var(--dur-base) var(--ease-out) backwards;
|
|
561
|
+
animation-delay: calc(var(--radial-i, 0) * var(--radial-stagger, 18ms));
|
|
562
|
+
}
|
|
563
|
+
.radial-petal:active:not(:disabled) {
|
|
564
|
+
--radial-scale-dur: var(--dur-press);
|
|
565
|
+
scale: var(--press-scale-sm);
|
|
534
566
|
}
|
|
535
567
|
@keyframes radial-in {
|
|
536
568
|
from {
|
|
@@ -540,6 +572,7 @@
|
|
|
540
572
|
}
|
|
541
573
|
.radial-glyph {
|
|
542
574
|
display: inline-flex;
|
|
575
|
+
font-size: var(--icon-md);
|
|
543
576
|
color: var(--text-muted);
|
|
544
577
|
}
|
|
545
578
|
.radial-label {
|
|
@@ -548,11 +581,16 @@
|
|
|
548
581
|
text-overflow: ellipsis;
|
|
549
582
|
white-space: nowrap;
|
|
550
583
|
}
|
|
551
|
-
.radial-petal:hover:not(:disabled),
|
|
552
584
|
.radial-petal.armed {
|
|
553
585
|
background: var(--bg-elevated-2);
|
|
554
586
|
border-color: var(--accent);
|
|
555
587
|
}
|
|
588
|
+
@media (hover: hover) and (pointer: fine) {
|
|
589
|
+
.radial-petal:hover:not(:disabled) {
|
|
590
|
+
background: var(--bg-elevated-2);
|
|
591
|
+
border-color: var(--accent);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
556
594
|
.radial-petal:focus-visible {
|
|
557
595
|
outline: var(--focus-ring);
|
|
558
596
|
outline-offset: var(--focus-ring-offset);
|
|
@@ -568,12 +606,7 @@
|
|
|
568
606
|
border-color: var(--danger);
|
|
569
607
|
}
|
|
570
608
|
.radial-petal:disabled {
|
|
571
|
-
opacity:
|
|
609
|
+
opacity: var(--disabled-opacity);
|
|
572
610
|
cursor: not-allowed;
|
|
573
611
|
}
|
|
574
|
-
@media (prefers-reduced-motion: reduce) {
|
|
575
|
-
.radial-petal {
|
|
576
|
-
animation: none;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
612
|
</style>
|