@dorsk/tsumikit 0.80.2 → 0.81.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/components/atoms/Artwork.svelte +37 -10
- package/dist/components/atoms/Badge.svelte +1 -1
- package/dist/components/atoms/Button.svelte +1 -1
- package/dist/components/atoms/Input.svelte +1 -1
- package/dist/components/layouts/NavSection.svelte +1 -1
- package/dist/components/molecules/FilterInput.svelte +17 -9
- package/dist/components/molecules/Metric.svelte +3 -3
- package/dist/components/molecules/SegmentedControl.svelte +4 -4
- package/dist/styles/tokens.css +6 -0
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
-
// Cover / thumbnail tile: a
|
|
4
|
-
//
|
|
5
|
-
// comes from `size`, height from `aspect`; `status`
|
|
6
|
-
// the bottom-right corner.
|
|
3
|
+
// Cover / thumbnail tile: a seeded gradient with initials (or an icon) that a
|
|
4
|
+
// lazy <img> fades in over once it loads; the gradient stays when there is no
|
|
5
|
+
// source or it fails. Width comes from `size`, height from `aspect`; `status`
|
|
6
|
+
// overlays a Dot/Badge in the bottom-right corner.
|
|
7
7
|
import { untrack, type Snippet } from 'svelte';
|
|
8
8
|
import { artworkGradient, initials as initialsOf } from '../../artwork';
|
|
9
9
|
import Icon from './Icon.svelte';
|
|
@@ -53,11 +53,13 @@
|
|
|
53
53
|
|
|
54
54
|
const source = $derived(`${src ?? ''} ${srcset ?? ''}`);
|
|
55
55
|
let failedSource = $state<string | null>(null);
|
|
56
|
+
let loadedSource = $state<string | null>(null);
|
|
56
57
|
|
|
57
58
|
const key = $derived(seed ?? alt);
|
|
58
59
|
const showImage = $derived(Boolean(src) && failedSource !== source);
|
|
60
|
+
const loaded = $derived(showImage && loadedSource === source);
|
|
59
61
|
const tile = $derived(showImage ? undefined : alt.trim() ? 'named' : 'decorative');
|
|
60
|
-
const gradient = $derived(
|
|
62
|
+
const gradient = $derived(artworkGradient(key));
|
|
61
63
|
const text = $derived(fallback === 'initials' ? initialsOf(key) : '');
|
|
62
64
|
|
|
63
65
|
function fail() {
|
|
@@ -66,11 +68,20 @@
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
function watchLoad(img: HTMLImageElement) {
|
|
71
|
+
const load = () => {
|
|
72
|
+
loadedSource = source;
|
|
73
|
+
};
|
|
69
74
|
img.addEventListener('error', fail);
|
|
75
|
+
img.addEventListener('load', load);
|
|
70
76
|
untrack(() => {
|
|
71
|
-
if (img.complete
|
|
77
|
+
if (!img.complete) return;
|
|
78
|
+
if (img.naturalWidth === 0) fail();
|
|
79
|
+
else load();
|
|
72
80
|
});
|
|
73
|
-
return () =>
|
|
81
|
+
return () => {
|
|
82
|
+
img.removeEventListener('error', fail);
|
|
83
|
+
img.removeEventListener('load', load);
|
|
84
|
+
};
|
|
74
85
|
}
|
|
75
86
|
</script>
|
|
76
87
|
|
|
@@ -86,13 +97,24 @@
|
|
|
86
97
|
aria-hidden={tile === 'decorative' ? 'true' : undefined}
|
|
87
98
|
{...rest}
|
|
88
99
|
>
|
|
89
|
-
{#if
|
|
90
|
-
<img {src} {srcset} {sizes} {alt} class="img fit-{fit}" loading="lazy" decoding="async" {@attach watchLoad} />
|
|
91
|
-
{:else if fallback === 'initials' && text}
|
|
100
|
+
{#if fallback === 'initials' && text}
|
|
92
101
|
<span class="initials" aria-hidden="true">{text}</span>
|
|
93
102
|
{:else if fallback === 'icon'}
|
|
94
103
|
<span class="glyph" aria-hidden="true"><Icon name={icon} /></span>
|
|
95
104
|
{/if}
|
|
105
|
+
{#if showImage}
|
|
106
|
+
<img
|
|
107
|
+
{src}
|
|
108
|
+
{srcset}
|
|
109
|
+
{sizes}
|
|
110
|
+
{alt}
|
|
111
|
+
class="img fit-{fit}"
|
|
112
|
+
class:loaded
|
|
113
|
+
loading="lazy"
|
|
114
|
+
decoding="async"
|
|
115
|
+
{@attach watchLoad}
|
|
116
|
+
/>
|
|
117
|
+
{/if}
|
|
96
118
|
{#if status}
|
|
97
119
|
<span class="status">{@render status()}</span>
|
|
98
120
|
{/if}
|
|
@@ -145,6 +167,11 @@
|
|
|
145
167
|
width: 100%;
|
|
146
168
|
height: 100%;
|
|
147
169
|
display: block;
|
|
170
|
+
opacity: 0;
|
|
171
|
+
transition: opacity var(--dur-release) var(--ease);
|
|
172
|
+
}
|
|
173
|
+
.img.loaded {
|
|
174
|
+
opacity: 1;
|
|
148
175
|
}
|
|
149
176
|
.fit-cover {
|
|
150
177
|
object-fit: cover;
|
|
@@ -239,13 +239,18 @@
|
|
|
239
239
|
const pos = el.selectionStart ?? value.length;
|
|
240
240
|
const id = ++reqId;
|
|
241
241
|
const next = key ? await suggestSingle(schema, key, value) : await suggest(schema, value, pos);
|
|
242
|
-
if (id !== reqId
|
|
242
|
+
if (id !== reqId || document.activeElement !== el) return;
|
|
243
243
|
menu = next;
|
|
244
244
|
active = 0;
|
|
245
245
|
keyboardNavigation = false;
|
|
246
246
|
open = !!next && next.items.length > 0;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
function close() {
|
|
250
|
+
++reqId;
|
|
251
|
+
open = false;
|
|
252
|
+
}
|
|
253
|
+
|
|
249
254
|
function accept(i: number) {
|
|
250
255
|
if (!menu || !el) return;
|
|
251
256
|
const s = menu.items[i];
|
|
@@ -269,7 +274,7 @@
|
|
|
269
274
|
el.focus();
|
|
270
275
|
el.setSelectionRange(caret, caret);
|
|
271
276
|
if (s.advance) refresh();
|
|
272
|
-
else
|
|
277
|
+
else close();
|
|
273
278
|
});
|
|
274
279
|
}
|
|
275
280
|
|
|
@@ -282,7 +287,7 @@
|
|
|
282
287
|
const exit = closingQuoteExit(value, pos);
|
|
283
288
|
if (exit !== null) {
|
|
284
289
|
e.preventDefault();
|
|
285
|
-
|
|
290
|
+
close();
|
|
286
291
|
setCaret(exit);
|
|
287
292
|
return;
|
|
288
293
|
}
|
|
@@ -299,6 +304,7 @@
|
|
|
299
304
|
}
|
|
300
305
|
if (!open || !menu) {
|
|
301
306
|
if (e.key === 'Enter') submit();
|
|
307
|
+
else if (e.key === 'Escape') close();
|
|
302
308
|
return;
|
|
303
309
|
}
|
|
304
310
|
switch (e.key) {
|
|
@@ -319,13 +325,13 @@
|
|
|
319
325
|
break;
|
|
320
326
|
case 'Escape':
|
|
321
327
|
e.preventDefault();
|
|
322
|
-
|
|
328
|
+
close();
|
|
323
329
|
break;
|
|
324
330
|
}
|
|
325
331
|
}
|
|
326
332
|
|
|
327
333
|
function submit() {
|
|
328
|
-
|
|
334
|
+
close();
|
|
329
335
|
onsubmit?.(value);
|
|
330
336
|
}
|
|
331
337
|
|
|
@@ -399,7 +405,9 @@
|
|
|
399
405
|
}}
|
|
400
406
|
onblur={() => {
|
|
401
407
|
focused = false;
|
|
402
|
-
setTimeout(() =>
|
|
408
|
+
setTimeout(() => {
|
|
409
|
+
if (document.activeElement !== el) close();
|
|
410
|
+
}, 120);
|
|
403
411
|
}}
|
|
404
412
|
{onkeydown}
|
|
405
413
|
/>
|
|
@@ -488,7 +496,7 @@
|
|
|
488
496
|
font-size: var(--control-fs-sm);
|
|
489
497
|
}
|
|
490
498
|
.fi--pill .fi__bar {
|
|
491
|
-
border-radius: var(--r-
|
|
499
|
+
border-radius: var(--r-control);
|
|
492
500
|
padding-inline: var(--sp-4);
|
|
493
501
|
}
|
|
494
502
|
.surface-raised .fi__bar {
|
|
@@ -540,7 +548,7 @@
|
|
|
540
548
|
border: 0;
|
|
541
549
|
background: transparent;
|
|
542
550
|
color: var(--text);
|
|
543
|
-
font-family: var(--font-
|
|
551
|
+
font-family: var(--font-numeric);
|
|
544
552
|
font-size: var(--control-fs-md);
|
|
545
553
|
outline: none;
|
|
546
554
|
min-width: 0;
|
|
@@ -656,7 +664,7 @@
|
|
|
656
664
|
}
|
|
657
665
|
.fi__opt-code {
|
|
658
666
|
margin-left: auto;
|
|
659
|
-
font-family: var(--font-
|
|
667
|
+
font-family: var(--font-numeric);
|
|
660
668
|
font-size: var(--fs-xs);
|
|
661
669
|
color: var(--text-muted);
|
|
662
670
|
background: var(--bg-elevated-2);
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
text-transform: uppercase;
|
|
188
188
|
letter-spacing: 0.06em;
|
|
189
189
|
color: var(--text-muted);
|
|
190
|
-
font-family: var(--font-
|
|
190
|
+
font-family: var(--font-label);
|
|
191
191
|
line-height: var(--lh-snug);
|
|
192
192
|
}
|
|
193
193
|
.metric-chip {
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
display: flex;
|
|
208
208
|
align-items: baseline;
|
|
209
209
|
gap: var(--sp-1);
|
|
210
|
-
font-family: var(--font-
|
|
210
|
+
font-family: var(--font-numeric);
|
|
211
211
|
line-height: var(--lh-none);
|
|
212
212
|
}
|
|
213
213
|
.metric-num {
|
|
@@ -274,7 +274,7 @@
|
|
|
274
274
|
flex-wrap: wrap;
|
|
275
275
|
align-items: baseline;
|
|
276
276
|
gap: var(--sp-1) var(--sp-3);
|
|
277
|
-
font-family: var(--font-
|
|
277
|
+
font-family: var(--font-numeric);
|
|
278
278
|
font-size: var(--fs-xs);
|
|
279
279
|
color: var(--text-muted);
|
|
280
280
|
}
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
padding: var(--sp-0-5);
|
|
250
250
|
background: var(--track-fill);
|
|
251
251
|
border: var(--edge-width) solid var(--track-edge);
|
|
252
|
-
border-radius: var(--r-
|
|
252
|
+
border-radius: var(--r-control);
|
|
253
253
|
max-width: 100%;
|
|
254
254
|
min-width: 0;
|
|
255
255
|
}
|
|
@@ -325,7 +325,7 @@
|
|
|
325
325
|
left: 0;
|
|
326
326
|
width: 0;
|
|
327
327
|
height: 0;
|
|
328
|
-
border-radius: var(--r-
|
|
328
|
+
border-radius: var(--r-control);
|
|
329
329
|
background: var(--thumb-fill);
|
|
330
330
|
box-shadow: var(--thumb-shadow);
|
|
331
331
|
transform-origin: top left;
|
|
@@ -346,7 +346,7 @@
|
|
|
346
346
|
height: 100%;
|
|
347
347
|
padding: 0 var(--control-px-md);
|
|
348
348
|
border: 0;
|
|
349
|
-
border-radius: var(--r-
|
|
349
|
+
border-radius: var(--r-control);
|
|
350
350
|
background: transparent;
|
|
351
351
|
color: var(--text-muted);
|
|
352
352
|
font-size: var(--control-fs-md);
|
|
@@ -370,7 +370,7 @@
|
|
|
370
370
|
}
|
|
371
371
|
/* Capped at half a row: one row stays a pill, extra rows square the corners. */
|
|
372
372
|
.seg-wrap:not(.seg-icon) {
|
|
373
|
-
border-radius: calc(var(--seg-h) / 2);
|
|
373
|
+
border-radius: min(var(--r-control), calc(var(--seg-h) / 2));
|
|
374
374
|
}
|
|
375
375
|
/* Icon-only segments are squares; corners stay concentric with the track. */
|
|
376
376
|
.seg-icon .seg-item,
|
package/dist/styles/tokens.css
CHANGED
|
@@ -113,6 +113,10 @@
|
|
|
113
113
|
--font-mono:
|
|
114
114
|
ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, Consolas,
|
|
115
115
|
monospace;
|
|
116
|
+
/* Non-code UI text: counts and stat values (--font-numeric), eyebrow labels
|
|
117
|
+
and shortcut hints (--font-label). Code-ish slots keep --font-mono. */
|
|
118
|
+
--font-numeric: var(--font-mono);
|
|
119
|
+
--font-label: var(--font-mono);
|
|
116
120
|
|
|
117
121
|
/* Text-scale factor (CCT-305). The UI font control multiplies ONLY the
|
|
118
122
|
font-size tokens by this — not the root font-size — so bumping the size up
|
|
@@ -171,6 +175,8 @@
|
|
|
171
175
|
--r-md: 10px;
|
|
172
176
|
--r-lg: 14px;
|
|
173
177
|
--r-pill: 999px;
|
|
178
|
+
/* Segmented tracks and pill-shaped filter bars; squarish apps set --r-md. */
|
|
179
|
+
--r-control: var(--r-pill);
|
|
174
180
|
|
|
175
181
|
/* ---- Elevation ---- */
|
|
176
182
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
|
package/package.json
CHANGED