@dorsk/tsumikit 0.54.0 → 0.56.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 +19 -1
- package/dist/anchor.d.ts +10 -0
- package/dist/anchor.js +1 -0
- package/dist/components/molecules/FilterInput.svelte +73 -30
- package/dist/components/molecules/FilterInput.svelte.d.ts +16 -4
- package/dist/components/molecules/Menu.svelte +7 -0
- package/dist/components/molecules/Menu.svelte.d.ts +6 -0
- package/dist/components/molecules/Popover.svelte +36 -31
- package/dist/components/molecules/Popover.svelte.d.ts +3 -1
- package/dist/components/molecules/Tabs.svelte +14 -3
- package/dist/components/molecules/Tabs.svelte.d.ts +9 -1
- package/dist/components/molecules/ThemePicker.svelte +7 -2
- package/dist/components/molecules/ThemePicker.svelte.d.ts +3 -1
- package/dist/components/organisms/FilterSearchBar.svelte +7 -2
- package/dist/components/organisms/FilterSearchBar.svelte.d.ts +3 -1
- package/dist/index.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -222,6 +222,23 @@ renders, and the trigger belongs to `Popover`, so `.toolbar .my-trigger` compile
|
|
|
222
222
|
to `.toolbar.svelte-x .my-trigger.svelte-x` and matches nothing. Use `style` and
|
|
223
223
|
the properties above instead.
|
|
224
224
|
|
|
225
|
+
### Anchoring to a component
|
|
226
|
+
|
|
227
|
+
Components that render a single addressable element forward rest props onto it,
|
|
228
|
+
so `data-journey`, `data-testid` and analytics attributes land where you expect.
|
|
229
|
+
`Popover` puts them on its **trigger** (so `Menu`, `ThemePicker` and
|
|
230
|
+
`FontScalePicker` forward through to it), `Tabs` on its root — with a per-tab
|
|
231
|
+
`attrs` bag on each `TabItem` — `FilterInput` on its root, and `FilterSearchBar`
|
|
232
|
+
forwards onto that. `Menu` items take an `attrs` bag. Components that forward
|
|
233
|
+
into another component take `AnchorAttributes` (`data-*` only), which cannot
|
|
234
|
+
collide with the child's own props; the rest take full `HTMLAttributes`. The
|
|
235
|
+
component's own `role`, `aria-*`, `class` and keyboard wiring are applied after
|
|
236
|
+
the spread and always win, so an anchor can never break behaviour; `class` in a
|
|
237
|
+
rest/`attrs` bag is therefore dropped — use `class` / `triggerClass`.
|
|
238
|
+
|
|
239
|
+
Never target the private `data-tsu="…"` markers: they are internal, unguarded by
|
|
240
|
+
any test, and change without a major.
|
|
241
|
+
|
|
225
242
|
## Components
|
|
226
243
|
|
|
227
244
|
**Atoms:** Text, Heading, Button, Input (`icon` inset leading glyph,
|
|
@@ -241,7 +258,8 @@ always/hover/none, `align`), Icon (open registry — pass a `children` snippet f
|
|
|
241
258
|
any custom SVG).
|
|
242
259
|
|
|
243
260
|
**Molecules:** Field (`grow`), IconButton, SelectButton, Toggle, OptionButton, Modal,
|
|
244
|
-
Popover, Menu (items take a free-form trailing `tag` + `tagTone`, or a `tag` snippet
|
|
261
|
+
Popover, Menu (items take a free-form trailing `tag` + `tagTone`, or a `tag` snippet,
|
|
262
|
+
and an `attrs` object for `data-*`/test ids on the row),
|
|
245
263
|
Tabs, RadioGroup (`variant="rows"`: bordered rows, per-option `note`/`description`,
|
|
246
264
|
`action(option)` trailing control that never toggles, `below(option)` inline panel),
|
|
247
265
|
Tooltip, Accordion, CopyButton, FileButton,
|
package/dist/anchor.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `data-*` attributes for addressing a component later — tour anchors, test ids.
|
|
3
|
+
*
|
|
4
|
+
* Components rendering their own element take a full `HTMLAttributes` rest
|
|
5
|
+
* instead; this narrower bag is for those that forward into another component,
|
|
6
|
+
* where a full attribute type collides with the child's narrower props.
|
|
7
|
+
*/
|
|
8
|
+
export type AnchorAttributes = {
|
|
9
|
+
[key: `data-${string}`]: string | number | boolean | null | undefined;
|
|
10
|
+
};
|
package/dist/anchor.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import type { FilterNode, Query } from '../../query/ast';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* The reactive parse context handed to the `inline` and `below`
|
|
6
|
-
* host can render arbitrary Svelte nodes (badges, custom
|
|
7
|
-
*
|
|
5
|
+
* The reactive parse context handed to the `inline`, `display` and `below`
|
|
6
|
+
* snippets so a host can render arbitrary Svelte nodes (badges, custom
|
|
7
|
+
* components, chips) inside the bar, over the input or below it — all
|
|
8
|
+
* derived from the same value.
|
|
8
9
|
*/
|
|
9
10
|
export interface FilterInputContext {
|
|
10
11
|
/** The parsed AST for the current value. */
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
// `ValueProvider`s). No `fetch` lives in here; the bar owns the QUERY string.
|
|
39
40
|
// ───────────────────────────────────────────────────────────────────────
|
|
40
41
|
import type { Snippet } from 'svelte';
|
|
42
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
41
43
|
import Icon, { type IconName } from '../atoms/Icon.svelte';
|
|
42
44
|
import { filters, freeText } from '../../query/ast';
|
|
43
45
|
import { autoQuoteEdit, backspaceEmptyQuotes, closingQuoteExit } from '../../query/edit';
|
|
@@ -67,10 +69,14 @@
|
|
|
67
69
|
onchange,
|
|
68
70
|
onsubmit,
|
|
69
71
|
inline,
|
|
72
|
+
display,
|
|
70
73
|
below,
|
|
71
74
|
class: klass = '',
|
|
72
75
|
style: styleProp = '',
|
|
73
|
-
|
|
76
|
+
...rest
|
|
77
|
+
}: Omit<HTMLAttributes<HTMLDivElement>, keyof Own> & Own = $props();
|
|
78
|
+
|
|
79
|
+
type Own = {
|
|
74
80
|
schema: Schema;
|
|
75
81
|
/**
|
|
76
82
|
* Single-key mode: the name (or alias) of the ONE schema field being
|
|
@@ -120,6 +126,15 @@
|
|
|
120
126
|
* inline (no below-bar chips). Receives the reactive parse context.
|
|
121
127
|
*/
|
|
122
128
|
inline?: Snippet<[FilterInputContext]>;
|
|
129
|
+
/**
|
|
130
|
+
* Rendering of the value shown over the input while the field is blurred
|
|
131
|
+
* and non-empty — a compact path, a formatted timestamp, a resolved id.
|
|
132
|
+
* Focusing the input (click, Tab, `hotkey`, `context.focus()`) reveals the
|
|
133
|
+
* raw text again. It is decoration, never a control: the overlay is
|
|
134
|
+
* `aria-hidden`, takes no pointer events and adds no tab stop, so the
|
|
135
|
+
* input stays the single focus target.
|
|
136
|
+
*/
|
|
137
|
+
display?: Snippet<[FilterInputContext]>;
|
|
123
138
|
/**
|
|
124
139
|
* Content rendered below the bar (e.g. removable chips). FilterSearchBar
|
|
125
140
|
* passes its chip row here; single-field hosts simply omit it.
|
|
@@ -127,7 +142,7 @@
|
|
|
127
142
|
below?: Snippet<[FilterInputContext]>;
|
|
128
143
|
class?: string;
|
|
129
144
|
style?: string;
|
|
130
|
-
}
|
|
145
|
+
};
|
|
131
146
|
|
|
132
147
|
const field = getFieldContext();
|
|
133
148
|
let el = $state<HTMLInputElement | null>(null);
|
|
@@ -177,6 +192,8 @@
|
|
|
177
192
|
focus: () => el?.focus()
|
|
178
193
|
});
|
|
179
194
|
|
|
195
|
+
const showDisplay = $derived(!!display && !focused && !!value);
|
|
196
|
+
|
|
180
197
|
// Emit the AST whenever the parse result changes.
|
|
181
198
|
$effect(() => {
|
|
182
199
|
onchange?.(ast, value);
|
|
@@ -335,6 +352,7 @@
|
|
|
335
352
|
</script>
|
|
336
353
|
|
|
337
354
|
<div
|
|
355
|
+
{...rest}
|
|
338
356
|
class="fi {klass}"
|
|
339
357
|
style={styleProp}
|
|
340
358
|
class:fi--sm={size === 'sm'}
|
|
@@ -347,31 +365,37 @@
|
|
|
347
365
|
<div class="fi__bar">
|
|
348
366
|
{#if icon}<span class="fi__icon"><Icon name={icon} label="Search" /></span>{/if}
|
|
349
367
|
{#if inline}{@render inline(ctx)}{/if}
|
|
350
|
-
<
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
368
|
+
<span class="fi__field">
|
|
369
|
+
<input
|
|
370
|
+
bind:this={el}
|
|
371
|
+
bind:value
|
|
372
|
+
class="fi__input"
|
|
373
|
+
class:fi__input--masked={showDisplay}
|
|
374
|
+
id={id ?? field?.id}
|
|
375
|
+
aria-describedby={ariaDescribedby ?? field?.describedBy}
|
|
376
|
+
aria-invalid={ariaInvalid ?? (field?.invalid ? 'true' : undefined)}
|
|
377
|
+
spellcheck="false"
|
|
378
|
+
autocomplete="off"
|
|
379
|
+
placeholder={hint}
|
|
380
|
+
{oninput}
|
|
381
|
+
onclick={refresh}
|
|
382
|
+
onkeyup={(e) => {
|
|
383
|
+
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') refresh();
|
|
384
|
+
}}
|
|
385
|
+
onfocus={() => {
|
|
386
|
+
focused = true;
|
|
387
|
+
refresh();
|
|
388
|
+
}}
|
|
389
|
+
onblur={() => {
|
|
390
|
+
focused = false;
|
|
391
|
+
setTimeout(() => (open = false), 120);
|
|
392
|
+
}}
|
|
393
|
+
{onkeydown}
|
|
394
|
+
/>
|
|
395
|
+
{#if display && showDisplay}
|
|
396
|
+
<span class="fi__display" aria-hidden="true">{@render display(ctx)}</span>
|
|
397
|
+
{/if}
|
|
398
|
+
</span>
|
|
375
399
|
{#if showHotkey && hotkey && !focused && !value}
|
|
376
400
|
<kbd class="fi__kbd" aria-hidden="true">{hotkey}</kbd>
|
|
377
401
|
{/if}
|
|
@@ -474,6 +498,22 @@
|
|
|
474
498
|
display: flex;
|
|
475
499
|
color: var(--text-faint);
|
|
476
500
|
}
|
|
501
|
+
.fi__field {
|
|
502
|
+
position: relative;
|
|
503
|
+
display: flex;
|
|
504
|
+
flex: 1;
|
|
505
|
+
min-width: 0;
|
|
506
|
+
}
|
|
507
|
+
.fi__display {
|
|
508
|
+
position: absolute;
|
|
509
|
+
inset: 0;
|
|
510
|
+
display: flex;
|
|
511
|
+
align-items: center;
|
|
512
|
+
gap: var(--sp-2);
|
|
513
|
+
min-width: 0;
|
|
514
|
+
overflow: hidden;
|
|
515
|
+
pointer-events: none;
|
|
516
|
+
}
|
|
477
517
|
.fi__input {
|
|
478
518
|
flex: 1;
|
|
479
519
|
border: 0;
|
|
@@ -487,6 +527,9 @@
|
|
|
487
527
|
.fi__input::placeholder {
|
|
488
528
|
color: var(--text-faint);
|
|
489
529
|
}
|
|
530
|
+
.fi__input--masked {
|
|
531
|
+
color: transparent;
|
|
532
|
+
}
|
|
490
533
|
.fi__clear {
|
|
491
534
|
display: flex;
|
|
492
535
|
border: 0;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { FilterNode, Query } from '../../query/ast';
|
|
2
2
|
/**
|
|
3
|
-
* The reactive parse context handed to the `inline` and `below`
|
|
4
|
-
* host can render arbitrary Svelte nodes (badges, custom
|
|
5
|
-
*
|
|
3
|
+
* The reactive parse context handed to the `inline`, `display` and `below`
|
|
4
|
+
* snippets so a host can render arbitrary Svelte nodes (badges, custom
|
|
5
|
+
* components, chips) inside the bar, over the input or below it — all
|
|
6
|
+
* derived from the same value.
|
|
6
7
|
*/
|
|
7
8
|
export interface FilterInputContext {
|
|
8
9
|
/** The parsed AST for the current value. */
|
|
@@ -20,9 +21,10 @@ export interface FilterInputContext {
|
|
|
20
21
|
}
|
|
21
22
|
import type { ControlSize } from '../../size';
|
|
22
23
|
import type { Snippet } from 'svelte';
|
|
24
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
23
25
|
import { type IconName } from '../atoms/Icon.svelte';
|
|
24
26
|
import { type Schema } from '../../query/schema';
|
|
25
|
-
type
|
|
27
|
+
type Own = {
|
|
26
28
|
schema: Schema;
|
|
27
29
|
/**
|
|
28
30
|
* Single-key mode: the name (or alias) of the ONE schema field being
|
|
@@ -72,6 +74,15 @@ type $$ComponentProps = {
|
|
|
72
74
|
* inline (no below-bar chips). Receives the reactive parse context.
|
|
73
75
|
*/
|
|
74
76
|
inline?: Snippet<[FilterInputContext]>;
|
|
77
|
+
/**
|
|
78
|
+
* Rendering of the value shown over the input while the field is blurred
|
|
79
|
+
* and non-empty — a compact path, a formatted timestamp, a resolved id.
|
|
80
|
+
* Focusing the input (click, Tab, `hotkey`, `context.focus()`) reveals the
|
|
81
|
+
* raw text again. It is decoration, never a control: the overlay is
|
|
82
|
+
* `aria-hidden`, takes no pointer events and adds no tab stop, so the
|
|
83
|
+
* input stays the single focus target.
|
|
84
|
+
*/
|
|
85
|
+
display?: Snippet<[FilterInputContext]>;
|
|
75
86
|
/**
|
|
76
87
|
* Content rendered below the bar (e.g. removable chips). FilterSearchBar
|
|
77
88
|
* passes its chip row here; single-field hosts simply omit it.
|
|
@@ -80,6 +91,7 @@ type $$ComponentProps = {
|
|
|
80
91
|
class?: string;
|
|
81
92
|
style?: string;
|
|
82
93
|
};
|
|
94
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, keyof Own> & Own;
|
|
83
95
|
declare const FilterInput: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
84
96
|
type FilterInput = ReturnType<typeof FilterInput>;
|
|
85
97
|
export default FilterInput;
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
pressed?: boolean;
|
|
13
13
|
/** Custom row content (a rename Input, a slider…) replacing icon/label/tag. */
|
|
14
14
|
content?: import('svelte').Snippet<[MenuItem]>;
|
|
15
|
+
/**
|
|
16
|
+
* Extra attributes for the rendered row — `data-*`, `title`, `aria-describedby`…
|
|
17
|
+
* The row's own semantics (`role`, `aria-checked`, `disabled`, `class`, `onclick`)
|
|
18
|
+
* are applied after and win.
|
|
19
|
+
*/
|
|
20
|
+
attrs?: import('svelte/elements').HTMLButtonAttributes;
|
|
15
21
|
}
|
|
16
22
|
</script>
|
|
17
23
|
|
|
@@ -165,6 +171,7 @@
|
|
|
165
171
|
{#each items as item (item.label)}
|
|
166
172
|
<button
|
|
167
173
|
type="button"
|
|
174
|
+
{...item.attrs}
|
|
168
175
|
role={item.pressed === undefined ? 'menuitem' : 'menuitemcheckbox'}
|
|
169
176
|
aria-checked={item.pressed === undefined ? undefined : item.pressed}
|
|
170
177
|
class="menu-item"
|
|
@@ -11,6 +11,12 @@ export interface MenuItem {
|
|
|
11
11
|
pressed?: boolean;
|
|
12
12
|
/** Custom row content (a rename Input, a slider…) replacing icon/label/tag. */
|
|
13
13
|
content?: import('svelte').Snippet<[MenuItem]>;
|
|
14
|
+
/**
|
|
15
|
+
* Extra attributes for the rendered row — `data-*`, `title`, `aria-describedby`…
|
|
16
|
+
* The row's own semantics (`role`, `aria-checked`, `disabled`, `class`, `onclick`)
|
|
17
|
+
* are applied after and win.
|
|
18
|
+
*/
|
|
19
|
+
attrs?: import('svelte/elements').HTMLButtonAttributes;
|
|
14
20
|
}
|
|
15
21
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
16
22
|
import Popover from './Popover.svelte';
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// ships beyond Chromium; the popover semantics above are the hard part and
|
|
11
11
|
// are broadly supported today.)
|
|
12
12
|
import { tick, type Snippet } from 'svelte';
|
|
13
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
13
14
|
import { place } from '../../floating';
|
|
14
15
|
import { HOVER_CLOSE_GRACE, HOVER_OPEN_DELAY, createHoverIntent, opensOnHover } from './popover-hover.js';
|
|
15
16
|
|
|
@@ -20,36 +21,7 @@
|
|
|
20
21
|
type PanelRole = 'dialog' | 'menu' | 'listbox' | 'group';
|
|
21
22
|
type HasPopup = 'menu' | 'dialog' | 'listbox' | true;
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
placement = 'bottom-start',
|
|
25
|
-
gap = 6,
|
|
26
|
-
label,
|
|
27
|
-
trigger,
|
|
28
|
-
children,
|
|
29
|
-
triggerClass = '',
|
|
30
|
-
bare = false,
|
|
31
|
-
variant,
|
|
32
|
-
tone = 'none',
|
|
33
|
-
size,
|
|
34
|
-
box,
|
|
35
|
-
pill = false,
|
|
36
|
-
control = false,
|
|
37
|
-
block = false,
|
|
38
|
-
hitArea = 'auto',
|
|
39
|
-
disabled = false,
|
|
40
|
-
openOn = 'click',
|
|
41
|
-
hoverDelay = HOVER_OPEN_DELAY,
|
|
42
|
-
as = 'button',
|
|
43
|
-
href,
|
|
44
|
-
role = 'dialog',
|
|
45
|
-
haspopup = 'dialog',
|
|
46
|
-
onopen,
|
|
47
|
-
onclose,
|
|
48
|
-
class: klass = '',
|
|
49
|
-
style: styleProp = '',
|
|
50
|
-
panelClass = '',
|
|
51
|
-
panelStyle = '',
|
|
52
|
-
}: {
|
|
24
|
+
type Own = {
|
|
53
25
|
placement?: Placement;
|
|
54
26
|
gap?: number;
|
|
55
27
|
/** Accessible name for the trigger. */
|
|
@@ -106,7 +78,39 @@
|
|
|
106
78
|
/** Class / inline style on the floating panel. */
|
|
107
79
|
panelClass?: string;
|
|
108
80
|
panelStyle?: string;
|
|
109
|
-
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
let {
|
|
84
|
+
placement = 'bottom-start',
|
|
85
|
+
gap = 6,
|
|
86
|
+
label,
|
|
87
|
+
trigger,
|
|
88
|
+
children,
|
|
89
|
+
triggerClass = '',
|
|
90
|
+
bare = false,
|
|
91
|
+
variant,
|
|
92
|
+
tone = 'none',
|
|
93
|
+
size,
|
|
94
|
+
box,
|
|
95
|
+
pill = false,
|
|
96
|
+
control = false,
|
|
97
|
+
block = false,
|
|
98
|
+
hitArea = 'auto',
|
|
99
|
+
disabled = false,
|
|
100
|
+
openOn = 'click',
|
|
101
|
+
hoverDelay = HOVER_OPEN_DELAY,
|
|
102
|
+
as = 'button',
|
|
103
|
+
href,
|
|
104
|
+
role = 'dialog',
|
|
105
|
+
haspopup = 'dialog',
|
|
106
|
+
onopen,
|
|
107
|
+
onclose,
|
|
108
|
+
class: klass = '',
|
|
109
|
+
style: styleProp = '',
|
|
110
|
+
panelClass = '',
|
|
111
|
+
panelStyle = '',
|
|
112
|
+
...rest
|
|
113
|
+
}: Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own = $props();
|
|
110
114
|
|
|
111
115
|
const canonicalChrome = $derived(
|
|
112
116
|
variant !== undefined || tone !== 'none' || size !== undefined || control || block
|
|
@@ -219,6 +223,7 @@
|
|
|
219
223
|
<svelte:element
|
|
220
224
|
this={as}
|
|
221
225
|
bind:this={triggerEl}
|
|
226
|
+
{...rest}
|
|
222
227
|
data-tsu="Popover"
|
|
223
228
|
class="pop-trigger {triggerClass} {klass}"
|
|
224
229
|
style={styleProp}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
3
|
type Placement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
|
|
3
4
|
type TriggerVariant = 'default' | 'primary' | 'ghost' | 'danger';
|
|
4
5
|
type TriggerTone = 'none' | 'accent' | 'success' | 'info' | 'warn' | 'danger';
|
|
5
6
|
type TriggerSize = 'sm' | 'md' | 'lg';
|
|
6
7
|
type PanelRole = 'dialog' | 'menu' | 'listbox' | 'group';
|
|
7
8
|
type HasPopup = 'menu' | 'dialog' | 'listbox' | true;
|
|
8
|
-
type
|
|
9
|
+
type Own = {
|
|
9
10
|
placement?: Placement;
|
|
10
11
|
gap?: number;
|
|
11
12
|
/** Accessible name for the trigger. */
|
|
@@ -65,6 +66,7 @@ type $$ComponentProps = {
|
|
|
65
66
|
panelClass?: string;
|
|
66
67
|
panelStyle?: string;
|
|
67
68
|
};
|
|
69
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own;
|
|
68
70
|
declare const Popover: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
69
71
|
type Popover = ReturnType<typeof Popover>;
|
|
70
72
|
export default Popover;
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
/** Trailing count badge. */
|
|
9
9
|
count?: number | string;
|
|
10
|
+
/**
|
|
11
|
+
* Extra attributes for this tab's button — `data-*`, `title`…
|
|
12
|
+
* The tab's own semantics (`role`, `aria-*`, `id`, `class`, `disabled`,
|
|
13
|
+
* `tabindex`, `onclick`) are applied after and win.
|
|
14
|
+
*/
|
|
15
|
+
attrs?: import('svelte/elements').HTMLButtonAttributes;
|
|
10
16
|
}
|
|
11
17
|
</script>
|
|
12
18
|
|
|
@@ -18,6 +24,7 @@
|
|
|
18
24
|
// the Tab order. `value` is bindable; `panel` is a snippet that receives the
|
|
19
25
|
// active id so the caller renders the matching content.
|
|
20
26
|
import type { Snippet } from 'svelte';
|
|
27
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
21
28
|
import Icon from '../atoms/Icon.svelte';
|
|
22
29
|
|
|
23
30
|
let {
|
|
@@ -29,7 +36,10 @@
|
|
|
29
36
|
style: styleProp = '',
|
|
30
37
|
panelClass = '',
|
|
31
38
|
panelPadding = 'md',
|
|
32
|
-
|
|
39
|
+
...rest
|
|
40
|
+
}: Omit<HTMLAttributes<HTMLDivElement>, keyof Own> & Own = $props();
|
|
41
|
+
|
|
42
|
+
type Own = {
|
|
33
43
|
tabs: TabItem[];
|
|
34
44
|
value?: string;
|
|
35
45
|
label?: string;
|
|
@@ -38,7 +48,7 @@
|
|
|
38
48
|
style?: string;
|
|
39
49
|
panelClass?: string;
|
|
40
50
|
panelPadding?: 'none' | 'sm' | 'md';
|
|
41
|
-
}
|
|
51
|
+
};
|
|
42
52
|
|
|
43
53
|
// Default to the first selectable tab when no value is supplied.
|
|
44
54
|
$effect(() => {
|
|
@@ -84,11 +94,12 @@
|
|
|
84
94
|
}
|
|
85
95
|
</script>
|
|
86
96
|
|
|
87
|
-
<div class="tabs {klass}" style={styleProp} data-tsu="Tabs">
|
|
97
|
+
<div {...rest} class="tabs {klass}" style={styleProp} data-tsu="Tabs">
|
|
88
98
|
<div bind:this={listEl} role="tablist" aria-label={label} tabindex="-1" class="tablist" {onkeydown}>
|
|
89
99
|
{#each tabs as t (t.id)}
|
|
90
100
|
<button
|
|
91
101
|
type="button"
|
|
102
|
+
{...t.attrs}
|
|
92
103
|
role="tab"
|
|
93
104
|
id="{baseId}-tab-{t.id}"
|
|
94
105
|
aria-selected={value === t.id}
|
|
@@ -6,9 +6,16 @@ export interface TabItem {
|
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
/** Trailing count badge. */
|
|
8
8
|
count?: number | string;
|
|
9
|
+
/**
|
|
10
|
+
* Extra attributes for this tab's button — `data-*`, `title`…
|
|
11
|
+
* The tab's own semantics (`role`, `aria-*`, `id`, `class`, `disabled`,
|
|
12
|
+
* `tabindex`, `onclick`) are applied after and win.
|
|
13
|
+
*/
|
|
14
|
+
attrs?: import('svelte/elements').HTMLButtonAttributes;
|
|
9
15
|
}
|
|
10
16
|
import type { Snippet } from 'svelte';
|
|
11
|
-
type
|
|
17
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
18
|
+
type Own = {
|
|
12
19
|
tabs: TabItem[];
|
|
13
20
|
value?: string;
|
|
14
21
|
label?: string;
|
|
@@ -18,6 +25,7 @@ type $$ComponentProps = {
|
|
|
18
25
|
panelClass?: string;
|
|
19
26
|
panelPadding?: 'none' | 'sm' | 'md';
|
|
20
27
|
};
|
|
28
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, keyof Own> & Own;
|
|
21
29
|
declare const Tabs: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
22
30
|
type Tabs = ReturnType<typeof Tabs>;
|
|
23
31
|
export default Tabs;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// resolved once at :root and would not re-scope). The root default theme
|
|
7
7
|
// has no [data-theme] block, so its swatch carries the :root values.
|
|
8
8
|
import type { ComponentProps } from 'svelte';
|
|
9
|
+
import type { AnchorAttributes } from '../../anchor';
|
|
9
10
|
import Popover from './Popover.svelte';
|
|
10
11
|
import { type ThemeDef, theme } from '../../stores/theme.svelte';
|
|
11
12
|
import { AUTO_THEME } from '../../theme-mode';
|
|
@@ -34,7 +35,10 @@
|
|
|
34
35
|
darkLabel = 'Dark',
|
|
35
36
|
class: klass = '',
|
|
36
37
|
style: styleProp = '',
|
|
37
|
-
|
|
38
|
+
...rest
|
|
39
|
+
}: AnchorAttributes & TriggerChrome & Own = $props();
|
|
40
|
+
|
|
41
|
+
type Own = {
|
|
38
42
|
/** Offer an "auto" row that follows `prefers-color-scheme`, remembering
|
|
39
43
|
* one light and one dark theme. */
|
|
40
44
|
auto?: boolean;
|
|
@@ -44,7 +48,7 @@
|
|
|
44
48
|
darkLabel?: string;
|
|
45
49
|
class?: string;
|
|
46
50
|
style?: string;
|
|
47
|
-
}
|
|
51
|
+
};
|
|
48
52
|
|
|
49
53
|
let hovered = $state<ThemeDef | null>(null);
|
|
50
54
|
let hoveredAuto = $state(false);
|
|
@@ -74,6 +78,7 @@
|
|
|
74
78
|
{/snippet}
|
|
75
79
|
|
|
76
80
|
<Popover
|
|
81
|
+
{...rest}
|
|
77
82
|
label={title}
|
|
78
83
|
{placement}
|
|
79
84
|
{box}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import type { AnchorAttributes } from '../../anchor';
|
|
2
3
|
import Popover from './Popover.svelte';
|
|
3
4
|
type TriggerChrome = Pick<ComponentProps<typeof Popover>, 'variant' | 'tone' | 'size' | 'box' | 'pill' | 'control' | 'block' | 'bare' | 'hitArea' | 'placement' | 'disabled'>;
|
|
4
|
-
type
|
|
5
|
+
type Own = {
|
|
5
6
|
/** Offer an "auto" row that follows `prefers-color-scheme`, remembering
|
|
6
7
|
* one light and one dark theme. */
|
|
7
8
|
auto?: boolean;
|
|
@@ -12,6 +13,7 @@ type $$ComponentProps = TriggerChrome & {
|
|
|
12
13
|
class?: string;
|
|
13
14
|
style?: string;
|
|
14
15
|
};
|
|
16
|
+
type $$ComponentProps = AnchorAttributes & TriggerChrome & Own;
|
|
15
17
|
declare const ThemePicker: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
16
18
|
type ThemePicker = ReturnType<typeof ThemePicker>;
|
|
17
19
|
export default ThemePicker;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// and forwards onchange/onsubmit. Everything project-specific is INJECTED via
|
|
15
15
|
// `schema` (with per-field async `ValueProvider`s).
|
|
16
16
|
// ───────────────────────────────────────────────────────────────────────
|
|
17
|
+
import type { AnchorAttributes } from '../../anchor';
|
|
17
18
|
import Badge from '../atoms/Badge.svelte';
|
|
18
19
|
import FilterInput from '../molecules/FilterInput.svelte';
|
|
19
20
|
import type { Query } from '../../query/ast';
|
|
@@ -35,7 +36,10 @@
|
|
|
35
36
|
onsubmit,
|
|
36
37
|
class: klass = '',
|
|
37
38
|
style: styleProp = '',
|
|
38
|
-
|
|
39
|
+
...rest
|
|
40
|
+
}: AnchorAttributes & Own = $props();
|
|
41
|
+
|
|
42
|
+
type Own = {
|
|
39
43
|
schema: Schema;
|
|
40
44
|
/** The raw textual query (two-way bindable). */
|
|
41
45
|
value?: string;
|
|
@@ -61,7 +65,7 @@
|
|
|
61
65
|
onsubmit?: (value: string) => void;
|
|
62
66
|
class?: string;
|
|
63
67
|
style?: string;
|
|
64
|
-
}
|
|
68
|
+
};
|
|
65
69
|
|
|
66
70
|
function labelFor(fieldName: string): string {
|
|
67
71
|
return findField(schema, fieldName)?.label ?? fieldName;
|
|
@@ -69,6 +73,7 @@
|
|
|
69
73
|
</script>
|
|
70
74
|
|
|
71
75
|
<FilterInput
|
|
76
|
+
{...rest}
|
|
72
77
|
{schema}
|
|
73
78
|
bind:value
|
|
74
79
|
{placeholder}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ControlSize } from '../../size';
|
|
2
|
+
import type { AnchorAttributes } from '../../anchor';
|
|
2
3
|
import type { Query } from '../../query/ast';
|
|
3
4
|
import { type Schema } from '../../query/schema';
|
|
4
|
-
type
|
|
5
|
+
type Own = {
|
|
5
6
|
schema: Schema;
|
|
6
7
|
/** The raw textual query (two-way bindable). */
|
|
7
8
|
value?: string;
|
|
@@ -28,6 +29,7 @@ type $$ComponentProps = {
|
|
|
28
29
|
class?: string;
|
|
29
30
|
style?: string;
|
|
30
31
|
};
|
|
32
|
+
type $$ComponentProps = AnchorAttributes & Own;
|
|
31
33
|
declare const FilterSearchBar: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
32
34
|
type FilterSearchBar = ReturnType<typeof FilterSearchBar>;
|
|
33
35
|
export default FilterSearchBar;
|
package/dist/index.d.ts
CHANGED
package/package.json
CHANGED