@dorsk/tsumikit 0.22.0 → 0.24.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 +151 -8
- package/dist/components/atoms/Button.svelte +121 -12
- package/dist/components/atoms/Button.svelte.d.ts +4 -0
- package/dist/components/atoms/Scrim.svelte +81 -0
- package/dist/components/atoms/Scrim.svelte.d.ts +14 -0
- package/dist/components/layouts/Cluster.svelte +33 -0
- package/dist/components/layouts/Cluster.svelte.d.ts +1 -0
- package/dist/components/layouts/ResizablePanel.svelte +238 -120
- package/dist/components/layouts/ResizablePanel.svelte.d.ts +24 -8
- package/dist/components/layouts/resizable-panel-frame.d.ts +70 -0
- package/dist/components/layouts/resizable-panel-frame.js +161 -0
- package/dist/components/molecules/CopyButton.svelte +11 -2
- package/dist/components/molecules/CopyButton.svelte.d.ts +4 -0
- package/dist/components/molecules/FileButton.svelte +32 -3
- package/dist/components/molecules/FileButton.svelte.d.ts +4 -0
- package/dist/components/molecules/IconButton.svelte +34 -6
- package/dist/components/molecules/IconButton.svelte.d.ts +3 -0
- package/dist/components/molecules/Popover.svelte +32 -2
- package/dist/components/molecules/Popover.svelte.d.ts +5 -0
- package/dist/components/molecules/SelectButton.svelte +6 -1
- package/dist/components/molecules/SelectButton.svelte.d.ts +2 -0
- package/dist/components/molecules/ThemePicker.svelte +11 -13
- package/dist/components/organisms/DataTable.svelte +210 -10
- package/dist/components/organisms/DataTable.svelte.d.ts +27 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/stores/theme.svelte.d.ts +21 -5
- package/dist/stores/theme.svelte.js +48 -22
- package/dist/styles/app.css +7 -297
- package/dist/styles/reset.css +97 -0
- package/dist/styles/syntax.css +94 -0
- package/dist/styles/themes.css +729 -0
- package/dist/styles/tokens.css +207 -0
- package/dist/styles/utilities.css +111 -0
- package/dist/styles/variables.css +5 -915
- package/package.json +7 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
+
export type RowTone = 'neutral' | 'ok' | 'warn' | 'danger' | 'info';
|
|
3
|
+
|
|
2
4
|
export interface Column<T> {
|
|
3
5
|
/** Key into the row, or an arbitrary id when paired with a cell snippet. */
|
|
4
6
|
key: string;
|
|
@@ -10,6 +12,12 @@
|
|
|
10
12
|
/** Header becomes a sort toggle. Sorts by the displayed value unless an
|
|
11
13
|
* `onsort` handler is supplied (then the caller controls ordering). */
|
|
12
14
|
sortable?: boolean;
|
|
15
|
+
/** One-line cells clipped with an ellipsis (pair with `layout="fixed"`). */
|
|
16
|
+
truncate?: boolean;
|
|
17
|
+
nowrap?: boolean;
|
|
18
|
+
/** Drop the column when the table's own box is narrower than the breakpoint
|
|
19
|
+
* (sm 30rem, md 48rem, lg 64rem). */
|
|
20
|
+
hideBelow?: 'sm' | 'md' | 'lg';
|
|
13
21
|
}
|
|
14
22
|
</script>
|
|
15
23
|
|
|
@@ -30,7 +38,19 @@
|
|
|
30
38
|
onsort,
|
|
31
39
|
cellSnippets = {},
|
|
32
40
|
empty = 'No data.',
|
|
33
|
-
stickyHeader = false
|
|
41
|
+
stickyHeader = false,
|
|
42
|
+
stickyOffset,
|
|
43
|
+
layout = 'auto',
|
|
44
|
+
hideHeader = false,
|
|
45
|
+
size = 'md',
|
|
46
|
+
rowTone,
|
|
47
|
+
rowClass,
|
|
48
|
+
rowActions,
|
|
49
|
+
rowActionsLabel = 'Actions',
|
|
50
|
+
loading = false,
|
|
51
|
+
loadingLabel = 'Loading…',
|
|
52
|
+
onloadmore,
|
|
53
|
+
loadMoreLabel = 'Load more'
|
|
34
54
|
}: {
|
|
35
55
|
columns: Column<T>[];
|
|
36
56
|
rows: T[];
|
|
@@ -41,13 +61,34 @@
|
|
|
41
61
|
* table only reflects the indicator and emits; it does not reorder rows. */
|
|
42
62
|
onsort?: (key: string, dir: 'asc' | 'desc') => void;
|
|
43
63
|
cellSnippets?: Record<string, Snippet<[T]>>;
|
|
44
|
-
empty?: string;
|
|
64
|
+
empty?: string | Snippet;
|
|
45
65
|
stickyHeader?: boolean;
|
|
66
|
+
/** Distance from the scroll container's top for the sticky header, e.g.
|
|
67
|
+
* `var(--header-h)` when the page header overlaps. Defaults to 0. */
|
|
68
|
+
stickyOffset?: string;
|
|
69
|
+
/** `fixed` makes declared column widths authoritative (needed for truncate). */
|
|
70
|
+
layout?: 'auto' | 'fixed';
|
|
71
|
+
/** Visually hides the header; it stays in the DOM for assistive tech. */
|
|
72
|
+
hideHeader?: boolean;
|
|
73
|
+
size?: 'sm' | 'md';
|
|
74
|
+
/** Per-row semantic tone, rendered as a left accent bar + `data-tone`. */
|
|
75
|
+
rowTone?: (row: T) => RowTone | undefined;
|
|
76
|
+
rowClass?: (row: T) => string | undefined;
|
|
77
|
+
/** Trailing actions cell revealed on hover/focus (always visible on touch). */
|
|
78
|
+
rowActions?: Snippet<[T]>;
|
|
79
|
+
rowActionsLabel?: string;
|
|
80
|
+
loading?: boolean;
|
|
81
|
+
loadingLabel?: string;
|
|
82
|
+
/** Renders a footer "load more" button that calls this. */
|
|
83
|
+
onloadmore?: () => void;
|
|
84
|
+
loadMoreLabel?: string;
|
|
46
85
|
} = $props();
|
|
47
86
|
|
|
48
87
|
let sortKey = $state<string | null>(null);
|
|
49
88
|
let sortDir = $state<'asc' | 'desc'>('asc');
|
|
50
89
|
|
|
90
|
+
const colCount = $derived(columns.length + (rowActions ? 1 : 0));
|
|
91
|
+
|
|
51
92
|
function display(col: Column<T>, row: T): unknown {
|
|
52
93
|
if (col.get) return col.get(row);
|
|
53
94
|
return (row as Record<string, unknown>)[col.key];
|
|
@@ -80,13 +121,21 @@
|
|
|
80
121
|
});
|
|
81
122
|
</script>
|
|
82
123
|
|
|
83
|
-
<div class="dt-scroll" data-tsu="DataTable">
|
|
84
|
-
<table
|
|
85
|
-
|
|
124
|
+
<div class="dt-scroll" data-tsu="DataTable" data-size={size} aria-busy={loading || undefined}>
|
|
125
|
+
<table
|
|
126
|
+
class="dt"
|
|
127
|
+
class:sticky={stickyHeader}
|
|
128
|
+
class:fixed={layout === 'fixed'}
|
|
129
|
+
class:head-hidden={hideHeader}
|
|
130
|
+
class:sm={size === 'sm'}
|
|
131
|
+
style:--dt-sticky-offset={stickyOffset}
|
|
132
|
+
>
|
|
133
|
+
<thead data-part="head">
|
|
86
134
|
<tr>
|
|
87
135
|
{#each columns as col (col.key)}
|
|
88
136
|
<th
|
|
89
137
|
scope="col"
|
|
138
|
+
data-hide-below={col.hideBelow}
|
|
90
139
|
style:width={col.width}
|
|
91
140
|
style:text-align={col.align ?? 'left'}
|
|
92
141
|
aria-sort={col.sortable
|
|
@@ -109,17 +158,31 @@
|
|
|
109
158
|
{/if}
|
|
110
159
|
</th>
|
|
111
160
|
{/each}
|
|
161
|
+
{#if rowActions}
|
|
162
|
+
<th scope="col" class="dt-actions-head"><span class="sr-only">{rowActionsLabel}</span></th>
|
|
163
|
+
{/if}
|
|
112
164
|
</tr>
|
|
113
165
|
</thead>
|
|
114
166
|
<tbody>
|
|
115
|
-
{#if sortedRows.length === 0}
|
|
116
|
-
<tr>
|
|
117
|
-
<td class="dt-empty" colspan={
|
|
167
|
+
{#if sortedRows.length === 0 && !loading}
|
|
168
|
+
<tr data-part="empty">
|
|
169
|
+
<td class="dt-empty" colspan={colCount}>
|
|
170
|
+
{#if typeof empty === 'string'}
|
|
171
|
+
{empty}
|
|
172
|
+
{:else}
|
|
173
|
+
{@render empty()}
|
|
174
|
+
{/if}
|
|
175
|
+
</td>
|
|
118
176
|
</tr>
|
|
119
177
|
{:else}
|
|
120
178
|
{#each sortedRows as row (rowKey(row))}
|
|
179
|
+
{@const tone = rowTone?.(row)}
|
|
121
180
|
<tr
|
|
181
|
+
data-part="row"
|
|
182
|
+
data-tone={tone}
|
|
183
|
+
class={rowClass?.(row)}
|
|
122
184
|
class:clickable={!!onrowclick}
|
|
185
|
+
class:toned={!!tone && tone !== 'neutral'}
|
|
123
186
|
tabindex={onrowclick ? 0 : undefined}
|
|
124
187
|
role={onrowclick ? 'button' : undefined}
|
|
125
188
|
onclick={onrowclick ? () => onrowclick(row) : undefined}
|
|
@@ -133,7 +196,13 @@
|
|
|
133
196
|
: undefined}
|
|
134
197
|
>
|
|
135
198
|
{#each columns as col (col.key)}
|
|
136
|
-
<td
|
|
199
|
+
<td
|
|
200
|
+
data-part="cell"
|
|
201
|
+
data-hide-below={col.hideBelow}
|
|
202
|
+
class:truncate={col.truncate}
|
|
203
|
+
class:nowrap={col.nowrap}
|
|
204
|
+
style:text-align={col.align ?? 'left'}
|
|
205
|
+
>
|
|
137
206
|
{#if cellSnippets[col.key]}
|
|
138
207
|
{@render cellSnippets[col.key](row)}
|
|
139
208
|
{:else}
|
|
@@ -141,10 +210,29 @@
|
|
|
141
210
|
{/if}
|
|
142
211
|
</td>
|
|
143
212
|
{/each}
|
|
213
|
+
{#if rowActions}
|
|
214
|
+
<td data-part="actions" class="dt-actions">
|
|
215
|
+
{@render rowActions(row)}
|
|
216
|
+
</td>
|
|
217
|
+
{/if}
|
|
144
218
|
</tr>
|
|
145
219
|
{/each}
|
|
146
220
|
{/if}
|
|
221
|
+
{#if loading}
|
|
222
|
+
<tr data-part="loading">
|
|
223
|
+
<td class="dt-empty" colspan={colCount}>{loadingLabel}</td>
|
|
224
|
+
</tr>
|
|
225
|
+
{/if}
|
|
147
226
|
</tbody>
|
|
227
|
+
{#if onloadmore && !loading}
|
|
228
|
+
<tfoot>
|
|
229
|
+
<tr>
|
|
230
|
+
<td class="dt-more" colspan={colCount}>
|
|
231
|
+
<button type="button" class="dt-more-btn" onclick={onloadmore}>{loadMoreLabel}</button>
|
|
232
|
+
</td>
|
|
233
|
+
</tr>
|
|
234
|
+
</tfoot>
|
|
235
|
+
{/if}
|
|
148
236
|
</table>
|
|
149
237
|
</div>
|
|
150
238
|
|
|
@@ -155,6 +243,7 @@
|
|
|
155
243
|
-webkit-overflow-scrolling: touch;
|
|
156
244
|
border: 1px solid var(--border);
|
|
157
245
|
border-radius: var(--r-lg);
|
|
246
|
+
container-type: inline-size;
|
|
158
247
|
}
|
|
159
248
|
.dt {
|
|
160
249
|
width: 100%;
|
|
@@ -162,11 +251,21 @@
|
|
|
162
251
|
font-size: var(--fs-sm);
|
|
163
252
|
background: var(--surface);
|
|
164
253
|
}
|
|
254
|
+
.dt.fixed {
|
|
255
|
+
table-layout: fixed;
|
|
256
|
+
}
|
|
257
|
+
.dt.sm {
|
|
258
|
+
font-size: var(--fs-xs);
|
|
259
|
+
}
|
|
165
260
|
th,
|
|
166
261
|
td {
|
|
167
262
|
padding: var(--sp-2) var(--sp-3);
|
|
168
263
|
border-bottom: 1px solid var(--border);
|
|
169
264
|
}
|
|
265
|
+
.dt.sm th,
|
|
266
|
+
.dt.sm td {
|
|
267
|
+
padding: var(--sp-1) var(--sp-2);
|
|
268
|
+
}
|
|
170
269
|
th {
|
|
171
270
|
font-size: var(--fs-xs);
|
|
172
271
|
font-weight: var(--fw-semibold);
|
|
@@ -178,9 +277,23 @@
|
|
|
178
277
|
}
|
|
179
278
|
.dt.sticky th {
|
|
180
279
|
position: sticky;
|
|
181
|
-
top: 0;
|
|
280
|
+
top: var(--dt-sticky-offset, 0);
|
|
182
281
|
z-index: 1;
|
|
183
282
|
}
|
|
283
|
+
/* Header stays in flow (so widths still apply) but paints nothing. */
|
|
284
|
+
.dt.head-hidden th {
|
|
285
|
+
height: 0;
|
|
286
|
+
padding: 0;
|
|
287
|
+
border: 0;
|
|
288
|
+
line-height: 0;
|
|
289
|
+
overflow: hidden;
|
|
290
|
+
clip-path: inset(50%);
|
|
291
|
+
}
|
|
292
|
+
.dt.head-hidden th > * {
|
|
293
|
+
display: block;
|
|
294
|
+
height: 0;
|
|
295
|
+
overflow: hidden;
|
|
296
|
+
}
|
|
184
297
|
/* Sort toggle: a bare button that inherits the th's type styling. */
|
|
185
298
|
.dt-sort {
|
|
186
299
|
display: inline-flex;
|
|
@@ -218,9 +331,96 @@
|
|
|
218
331
|
background: var(--bg-elevated-2);
|
|
219
332
|
outline: none;
|
|
220
333
|
}
|
|
334
|
+
td.truncate {
|
|
335
|
+
white-space: nowrap;
|
|
336
|
+
overflow: hidden;
|
|
337
|
+
text-overflow: ellipsis;
|
|
338
|
+
max-width: 0;
|
|
339
|
+
}
|
|
340
|
+
td.nowrap {
|
|
341
|
+
white-space: nowrap;
|
|
342
|
+
}
|
|
343
|
+
tr[data-tone='ok'] {
|
|
344
|
+
--dt-tone: var(--ok);
|
|
345
|
+
}
|
|
346
|
+
tr[data-tone='warn'] {
|
|
347
|
+
--dt-tone: var(--warn);
|
|
348
|
+
}
|
|
349
|
+
tr[data-tone='danger'] {
|
|
350
|
+
--dt-tone: var(--danger);
|
|
351
|
+
}
|
|
352
|
+
tr[data-tone='info'] {
|
|
353
|
+
--dt-tone: var(--info);
|
|
354
|
+
}
|
|
355
|
+
tr.toned td:first-child {
|
|
356
|
+
box-shadow: inset 3px 0 0 var(--dt-tone);
|
|
357
|
+
}
|
|
358
|
+
.dt-actions-head,
|
|
359
|
+
.dt-actions {
|
|
360
|
+
width: 1%;
|
|
361
|
+
white-space: nowrap;
|
|
362
|
+
text-align: right;
|
|
363
|
+
}
|
|
364
|
+
.dt-actions {
|
|
365
|
+
opacity: 0;
|
|
366
|
+
transition: opacity 0.12s var(--ease);
|
|
367
|
+
}
|
|
368
|
+
tr:hover .dt-actions,
|
|
369
|
+
tr:focus-within .dt-actions {
|
|
370
|
+
opacity: 1;
|
|
371
|
+
}
|
|
372
|
+
@media (pointer: coarse) {
|
|
373
|
+
.dt-actions {
|
|
374
|
+
opacity: 1;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
@container (max-width: 30rem) {
|
|
378
|
+
[data-hide-below='sm'] {
|
|
379
|
+
display: none;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
@container (max-width: 48rem) {
|
|
383
|
+
[data-hide-below='md'] {
|
|
384
|
+
display: none;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
@container (max-width: 64rem) {
|
|
388
|
+
[data-hide-below='lg'] {
|
|
389
|
+
display: none;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
221
392
|
.dt-empty {
|
|
222
393
|
text-align: center;
|
|
223
394
|
color: var(--text-faint);
|
|
224
395
|
padding: var(--sp-8);
|
|
225
396
|
}
|
|
397
|
+
.dt-more {
|
|
398
|
+
text-align: center;
|
|
399
|
+
padding: var(--sp-2);
|
|
400
|
+
border-top: 1px solid var(--border);
|
|
401
|
+
}
|
|
402
|
+
.dt-more-btn {
|
|
403
|
+
border: 0;
|
|
404
|
+
background: none;
|
|
405
|
+
padding: var(--sp-1) var(--sp-3);
|
|
406
|
+
font: inherit;
|
|
407
|
+
color: var(--accent);
|
|
408
|
+
cursor: pointer;
|
|
409
|
+
border-radius: var(--r-md, 6px);
|
|
410
|
+
}
|
|
411
|
+
.dt-more-btn:hover,
|
|
412
|
+
.dt-more-btn:focus-visible {
|
|
413
|
+
background: var(--bg-elevated-2);
|
|
414
|
+
}
|
|
415
|
+
.sr-only {
|
|
416
|
+
position: absolute;
|
|
417
|
+
width: 1px;
|
|
418
|
+
height: 1px;
|
|
419
|
+
padding: 0;
|
|
420
|
+
margin: -1px;
|
|
421
|
+
overflow: hidden;
|
|
422
|
+
clip: rect(0, 0, 0, 0);
|
|
423
|
+
white-space: nowrap;
|
|
424
|
+
border: 0;
|
|
425
|
+
}
|
|
226
426
|
</style>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type RowTone = 'neutral' | 'ok' | 'warn' | 'danger' | 'info';
|
|
1
2
|
export interface Column<T> {
|
|
2
3
|
/** Key into the row, or an arbitrary id when paired with a cell snippet. */
|
|
3
4
|
key: string;
|
|
@@ -9,6 +10,12 @@ export interface Column<T> {
|
|
|
9
10
|
/** Header becomes a sort toggle. Sorts by the displayed value unless an
|
|
10
11
|
* `onsort` handler is supplied (then the caller controls ordering). */
|
|
11
12
|
sortable?: boolean;
|
|
13
|
+
/** One-line cells clipped with an ellipsis (pair with `layout="fixed"`). */
|
|
14
|
+
truncate?: boolean;
|
|
15
|
+
nowrap?: boolean;
|
|
16
|
+
/** Drop the column when the table's own box is narrower than the breakpoint
|
|
17
|
+
* (sm 30rem, md 48rem, lg 64rem). */
|
|
18
|
+
hideBelow?: 'sm' | 'md' | 'lg';
|
|
12
19
|
}
|
|
13
20
|
import type { Snippet } from 'svelte';
|
|
14
21
|
declare function $$render<T>(): {
|
|
@@ -22,8 +29,27 @@ declare function $$render<T>(): {
|
|
|
22
29
|
* table only reflects the indicator and emits; it does not reorder rows. */
|
|
23
30
|
onsort?: (key: string, dir: "asc" | "desc") => void;
|
|
24
31
|
cellSnippets?: Record<string, Snippet<[T]>>;
|
|
25
|
-
empty?: string;
|
|
32
|
+
empty?: string | Snippet;
|
|
26
33
|
stickyHeader?: boolean;
|
|
34
|
+
/** Distance from the scroll container's top for the sticky header, e.g.
|
|
35
|
+
* `var(--header-h)` when the page header overlaps. Defaults to 0. */
|
|
36
|
+
stickyOffset?: string;
|
|
37
|
+
/** `fixed` makes declared column widths authoritative (needed for truncate). */
|
|
38
|
+
layout?: "auto" | "fixed";
|
|
39
|
+
/** Visually hides the header; it stays in the DOM for assistive tech. */
|
|
40
|
+
hideHeader?: boolean;
|
|
41
|
+
size?: "sm" | "md";
|
|
42
|
+
/** Per-row semantic tone, rendered as a left accent bar + `data-tone`. */
|
|
43
|
+
rowTone?: (row: T) => RowTone | undefined;
|
|
44
|
+
rowClass?: (row: T) => string | undefined;
|
|
45
|
+
/** Trailing actions cell revealed on hover/focus (always visible on touch). */
|
|
46
|
+
rowActions?: Snippet<[T]>;
|
|
47
|
+
rowActionsLabel?: string;
|
|
48
|
+
loading?: boolean;
|
|
49
|
+
loadingLabel?: string;
|
|
50
|
+
/** Renders a footer "load more" button that calls this. */
|
|
51
|
+
onloadmore?: () => void;
|
|
52
|
+
loadMoreLabel?: string;
|
|
27
53
|
};
|
|
28
54
|
exports: {};
|
|
29
55
|
bindings: "";
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { default as Icon } from './components/atoms/Icon.svelte';
|
|
|
11
11
|
export { default as Input } from './components/atoms/Input.svelte';
|
|
12
12
|
export { default as Link } from './components/atoms/Link.svelte';
|
|
13
13
|
export { default as Progress } from './components/atoms/Progress.svelte';
|
|
14
|
+
export { default as Scrim } from './components/atoms/Scrim.svelte';
|
|
14
15
|
export { default as SegmentedProgress, type ProgressSegment, } from './components/atoms/SegmentedProgress.svelte';
|
|
15
16
|
export { default as Select } from './components/atoms/Select.svelte';
|
|
16
17
|
export { default as Slider } from './components/atoms/Slider.svelte';
|
|
@@ -25,6 +26,7 @@ export { default as Container } from './components/layouts/Container.svelte';
|
|
|
25
26
|
export { default as NavItem } from './components/layouts/NavItem.svelte';
|
|
26
27
|
export { default as NavSection } from './components/layouts/NavSection.svelte';
|
|
27
28
|
export { default as ResizablePanel } from './components/layouts/ResizablePanel.svelte';
|
|
29
|
+
export { type ResizeHandleParams, resizeHandle, } from './components/layouts/resizable-panel-frame.js';
|
|
28
30
|
export { default as Stack } from './components/layouts/Stack.svelte';
|
|
29
31
|
export { type AccordionItem, default as Accordion, } from './components/molecules/Accordion.svelte';
|
|
30
32
|
export { type BreadcrumbItem, default as Breadcrumb, } from './components/molecules/Breadcrumb.svelte';
|
|
@@ -53,7 +55,7 @@ export { default as Toaster } from './components/molecules/Toaster.svelte';
|
|
|
53
55
|
export { default as Toggle } from './components/molecules/Toggle.svelte';
|
|
54
56
|
export { default as Tooltip } from './components/molecules/Tooltip.svelte';
|
|
55
57
|
export { default as Truncate } from './components/molecules/Truncate.svelte';
|
|
56
|
-
export { type Column, default as DataTable } from './components/organisms/DataTable.svelte';
|
|
58
|
+
export { type Column, default as DataTable, type RowTone, } from './components/organisms/DataTable.svelte';
|
|
57
59
|
export { default as FilterSearchBar } from './components/organisms/FilterSearchBar.svelte';
|
|
58
60
|
export { type AndNode, activeToken, compilePredicate, defaultOperator, type ExprNode, type FieldDef, type FieldType, type FilterNode, filters, findField, freeText, type LeafNode, type NotNode, OPERATORS, type Operator, type OperatorId, type OrNode, operatorByCode, operatorById, operatorsFor, parse, type Query, type QueryNode, resolveValues, type Schema, type Suggestion, type SuggestKind, type SuggestState, serialize, serializeFilter, suggest, type TextNode, toSql, type ValueContext, type ValueOption, type ValueProvider, walk, } from './query';
|
|
59
61
|
export { fontScale, SCALE_LEVELS, type ScaleLevel } from './stores/fontscale.svelte';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export { default as Icon } from './components/atoms/Icon.svelte';
|
|
|
16
16
|
export { default as Input } from './components/atoms/Input.svelte';
|
|
17
17
|
export { default as Link } from './components/atoms/Link.svelte';
|
|
18
18
|
export { default as Progress } from './components/atoms/Progress.svelte';
|
|
19
|
+
export { default as Scrim } from './components/atoms/Scrim.svelte';
|
|
19
20
|
export { default as SegmentedProgress, } from './components/atoms/SegmentedProgress.svelte';
|
|
20
21
|
export { default as Select } from './components/atoms/Select.svelte';
|
|
21
22
|
export { default as Slider } from './components/atoms/Slider.svelte';
|
|
@@ -32,6 +33,7 @@ export { default as Container } from './components/layouts/Container.svelte';
|
|
|
32
33
|
export { default as NavItem } from './components/layouts/NavItem.svelte';
|
|
33
34
|
export { default as NavSection } from './components/layouts/NavSection.svelte';
|
|
34
35
|
export { default as ResizablePanel } from './components/layouts/ResizablePanel.svelte';
|
|
36
|
+
export { resizeHandle, } from './components/layouts/resizable-panel-frame.js';
|
|
35
37
|
export { default as Stack } from './components/layouts/Stack.svelte';
|
|
36
38
|
export { default as Accordion, } from './components/molecules/Accordion.svelte';
|
|
37
39
|
export { default as Breadcrumb, } from './components/molecules/Breadcrumb.svelte';
|
|
@@ -64,7 +66,7 @@ export { default as Toggle } from './components/molecules/Toggle.svelte';
|
|
|
64
66
|
export { default as Tooltip } from './components/molecules/Tooltip.svelte';
|
|
65
67
|
export { default as Truncate } from './components/molecules/Truncate.svelte';
|
|
66
68
|
// ---- organisms ----
|
|
67
|
-
export { default as DataTable } from './components/organisms/DataTable.svelte';
|
|
69
|
+
export { default as DataTable, } from './components/organisms/DataTable.svelte';
|
|
68
70
|
export { default as FilterSearchBar } from './components/organisms/FilterSearchBar.svelte';
|
|
69
71
|
// ---- query core (headless: schema / parser / AST / suggest / compilers) ----
|
|
70
72
|
export { activeToken, compilePredicate, defaultOperator, filters, findField, freeText, OPERATORS, operatorByCode, operatorById, operatorsFor, parse, resolveValues, serialize, serializeFilter, suggest, toSql, walk, } from './query';
|
|
@@ -145,17 +145,33 @@ export declare const THEMES: readonly [{
|
|
|
145
145
|
}];
|
|
146
146
|
export type Mode = (typeof THEMES)[number]['id'];
|
|
147
147
|
export type ThemeMode = (typeof THEMES)[number]['mode'];
|
|
148
|
-
type
|
|
148
|
+
export type ThemeId = Mode | (string & {});
|
|
149
|
+
export interface ThemeDef {
|
|
150
|
+
id: ThemeId;
|
|
151
|
+
label: string;
|
|
152
|
+
mode: ThemeMode;
|
|
153
|
+
icon?: string;
|
|
154
|
+
themeColor?: string;
|
|
155
|
+
}
|
|
149
156
|
declare class Theme {
|
|
150
|
-
current:
|
|
157
|
+
current: ThemeId;
|
|
158
|
+
readonly fallbackIcon = "\u25C8";
|
|
159
|
+
private registered;
|
|
160
|
+
private fallback;
|
|
161
|
+
private saved;
|
|
151
162
|
constructor();
|
|
163
|
+
get all(): readonly ThemeDef[];
|
|
164
|
+
has(id: string | null | undefined): id is ThemeId;
|
|
165
|
+
register(defs: ThemeDef | ThemeDef[]): void;
|
|
166
|
+
setDefault(id: ThemeId): void;
|
|
167
|
+
private resolve;
|
|
152
168
|
private apply;
|
|
153
|
-
get option():
|
|
169
|
+
get option(): ThemeDef;
|
|
154
170
|
get label(): string;
|
|
155
171
|
get icon(): string;
|
|
156
|
-
get next():
|
|
172
|
+
get next(): ThemeDef;
|
|
157
173
|
toggle(): void;
|
|
158
|
-
set(mode:
|
|
174
|
+
set(mode: ThemeId): void;
|
|
159
175
|
}
|
|
160
176
|
export declare const theme: Theme;
|
|
161
177
|
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { browser } from '../env';
|
|
2
|
-
// Theme registry
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// `mode` groups the theme into the picker's
|
|
2
|
+
// Theme registry. Built-ins live in THEMES (+ one [data-theme="id"] block in
|
|
3
|
+
// styles/themes.css); consumers append their own with theme.register() and ship
|
|
4
|
+
// the matching block in their own stylesheet. `themeColor` drives the mobile
|
|
5
|
+
// browser-chrome <meta theme-color>; `mode` groups the theme into the picker's
|
|
6
|
+
// light/dark sections.
|
|
6
7
|
const KEY = 'tsumikit-theme';
|
|
7
8
|
export const THEMES = [
|
|
8
9
|
// ── Light ── bright, paper-white surfaces
|
|
@@ -51,50 +52,75 @@ export const THEMES = [
|
|
|
51
52
|
{ id: 'monokai', label: 'Monokai', icon: '✸', themeColor: '#272822', mode: 'dark' },
|
|
52
53
|
{ id: 'amoled', label: 'AMOLED (high contrast)', icon: '◼', themeColor: '#000000', mode: 'dark' },
|
|
53
54
|
];
|
|
54
|
-
const
|
|
55
|
-
function isMode(value) {
|
|
56
|
-
return THEMES.some((t) => t.id === value);
|
|
57
|
-
}
|
|
58
|
-
function optionFor(mode) {
|
|
59
|
-
return THEMES.find((t) => t.id === mode) ?? THEMES[0];
|
|
60
|
-
}
|
|
55
|
+
const FALLBACK_ICON = '◈';
|
|
61
56
|
class Theme {
|
|
62
57
|
current = $state('dark');
|
|
58
|
+
fallbackIcon = FALLBACK_ICON;
|
|
59
|
+
registered = $state([]);
|
|
60
|
+
fallback = 'dark';
|
|
61
|
+
saved = null;
|
|
63
62
|
constructor() {
|
|
64
63
|
if (browser) {
|
|
65
|
-
|
|
66
|
-
this.
|
|
67
|
-
this.apply();
|
|
64
|
+
this.saved = localStorage.getItem(KEY);
|
|
65
|
+
this.resolve();
|
|
68
66
|
}
|
|
69
67
|
}
|
|
68
|
+
get all() {
|
|
69
|
+
const byId = new Map();
|
|
70
|
+
for (const t of THEMES)
|
|
71
|
+
byId.set(t.id, t);
|
|
72
|
+
for (const t of this.registered)
|
|
73
|
+
byId.set(t.id, t);
|
|
74
|
+
return [...byId.values()];
|
|
75
|
+
}
|
|
76
|
+
has(id) {
|
|
77
|
+
return id != null && this.all.some((t) => t.id === id);
|
|
78
|
+
}
|
|
79
|
+
register(defs) {
|
|
80
|
+
const list = Array.isArray(defs) ? defs : [defs];
|
|
81
|
+
this.registered = [...this.registered.filter((r) => !list.some((d) => d.id === r.id)), ...list];
|
|
82
|
+
this.resolve();
|
|
83
|
+
}
|
|
84
|
+
setDefault(id) {
|
|
85
|
+
this.fallback = id;
|
|
86
|
+
this.resolve();
|
|
87
|
+
}
|
|
88
|
+
resolve() {
|
|
89
|
+
this.current = this.has(this.saved) ? this.saved : this.fallback;
|
|
90
|
+
this.apply();
|
|
91
|
+
}
|
|
70
92
|
apply() {
|
|
71
93
|
if (!browser)
|
|
72
94
|
return;
|
|
73
95
|
document.documentElement.setAttribute('data-theme', this.current);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
96
|
+
const color = this.option.themeColor;
|
|
97
|
+
if (color)
|
|
98
|
+
document
|
|
99
|
+
.querySelector('meta[name="theme-color"]')
|
|
100
|
+
?.setAttribute('content', color);
|
|
77
101
|
}
|
|
78
102
|
get option() {
|
|
79
|
-
return
|
|
103
|
+
return this.all.find((t) => t.id === this.current) ?? this.all[0];
|
|
80
104
|
}
|
|
81
105
|
get label() {
|
|
82
106
|
return this.option.label;
|
|
83
107
|
}
|
|
84
108
|
get icon() {
|
|
85
|
-
return this.option.icon;
|
|
109
|
+
return this.option.icon ?? FALLBACK_ICON;
|
|
86
110
|
}
|
|
87
111
|
get next() {
|
|
88
|
-
const
|
|
89
|
-
|
|
112
|
+
const all = this.all;
|
|
113
|
+
const i = all.findIndex((t) => t.id === this.current);
|
|
114
|
+
return all[(i + 1) % all.length];
|
|
90
115
|
}
|
|
91
116
|
toggle() {
|
|
92
117
|
this.set(this.next.id);
|
|
93
118
|
}
|
|
94
119
|
set(mode) {
|
|
120
|
+
this.saved = mode;
|
|
95
121
|
this.current = mode;
|
|
96
122
|
if (browser)
|
|
97
|
-
localStorage.setItem(KEY,
|
|
123
|
+
localStorage.setItem(KEY, mode);
|
|
98
124
|
this.apply();
|
|
99
125
|
}
|
|
100
126
|
}
|