@spaethtech/svelte-ui 0.14.0 → 0.15.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/.claude/skills/svelte-ui/SKILL.md +14 -11
- package/dist/components/Carousel/Carousel.svelte +147 -0
- package/dist/components/Carousel/Carousel.svelte.d.ts +38 -0
- package/dist/components/Carousel/index.d.ts +1 -0
- package/dist/components/Carousel/index.js +1 -0
- package/dist/components/Combobox/Combobox.svelte +62 -31
- package/dist/components/Combobox/Combobox.svelte.d.ts +1 -1
- package/dist/components/CommandPalette/CommandPalette.svelte +203 -0
- package/dist/components/CommandPalette/CommandPalette.svelte.d.ts +23 -0
- package/dist/components/CommandPalette/index.d.ts +2 -0
- package/dist/components/CommandPalette/index.js +1 -0
- package/dist/components/ContextMenu/ContextMenu.svelte +58 -0
- package/dist/components/ContextMenu/ContextMenu.svelte.d.ts +16 -0
- package/dist/components/ContextMenu/index.d.ts +1 -0
- package/dist/components/ContextMenu/index.js +1 -0
- package/dist/components/EmptyState/EmptyState.svelte +68 -0
- package/dist/components/EmptyState/EmptyState.svelte.d.ts +19 -0
- package/dist/components/EmptyState/index.d.ts +1 -0
- package/dist/components/EmptyState/index.js +1 -0
- package/dist/components/Kbd/Kbd.svelte +53 -0
- package/dist/components/Kbd/Kbd.svelte.d.ts +15 -0
- package/dist/components/Kbd/index.d.ts +1 -0
- package/dist/components/Kbd/index.js +1 -0
- package/dist/components/Pagination/Pagination.svelte +58 -43
- package/dist/components/Pagination/Pagination.svelte.d.ts +7 -2
- package/dist/components/Rating.svelte +169 -30
- package/dist/components/Rating.svelte.d.ts +22 -4
- package/dist/components/ScrollArea/ScrollArea.svelte +71 -0
- package/dist/components/ScrollArea/ScrollArea.svelte.d.ts +15 -0
- package/dist/components/ScrollArea/index.d.ts +1 -0
- package/dist/components/ScrollArea/index.js +1 -0
- package/dist/components/Slider/Slider.svelte +5 -3
- package/dist/components/Splitter/Splitter.svelte +127 -0
- package/dist/components/Splitter/Splitter.svelte.d.ts +18 -0
- package/dist/components/Splitter/index.d.ts +1 -0
- package/dist/components/Splitter/index.js +1 -0
- package/dist/components/Stat/Stat.svelte +95 -0
- package/dist/components/Stat/Stat.svelte.d.ts +21 -0
- package/dist/components/Stat/index.d.ts +1 -0
- package/dist/components/Stat/index.js +1 -0
- package/dist/components/Timeline/Timeline.svelte +98 -0
- package/dist/components/Timeline/Timeline.svelte.d.ts +21 -0
- package/dist/components/Timeline/index.d.ts +2 -0
- package/dist/components/Timeline/index.js +1 -0
- package/dist/components/TokenInput/TokenInput.svelte +12 -6
- package/dist/components/Tree/Tree.svelte +178 -0
- package/dist/components/Tree/Tree.svelte.d.ts +26 -0
- package/dist/components/Tree/index.d.ts +2 -0
- package/dist/components/Tree/index.js +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +10 -1
- package/docs/components.md +125 -22
- package/docs/usage.md +180 -13
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Stat } from "./Stat.svelte";
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
/**
|
|
3
|
+
* Timeline — a vertical sequence of events: a marker (dot or icon) on a connecting line, with a
|
|
4
|
+
* title / time / description per item. Per-item `variant` colours its marker. `--ui-*` tokens.
|
|
5
|
+
* See Timeline.spec.md.
|
|
6
|
+
*/
|
|
7
|
+
-->
|
|
8
|
+
<script lang="ts" module>
|
|
9
|
+
import type { Snippet } from "svelte";
|
|
10
|
+
import type { Variant } from "../../types/variants.js";
|
|
11
|
+
export type TimelineItem = {
|
|
12
|
+
title: string;
|
|
13
|
+
time?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
icon?: Snippet;
|
|
16
|
+
variant?: Variant;
|
|
17
|
+
content?: Snippet;
|
|
18
|
+
};
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import { variantToken } from "../../types/variants.js";
|
|
23
|
+
import type { Size } from "../../types/sizes.js";
|
|
24
|
+
import { responsiveClasses, type Responsive } from "../../types/responsive.js";
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
items,
|
|
28
|
+
variant = "primary",
|
|
29
|
+
size = "md",
|
|
30
|
+
class: cls = "",
|
|
31
|
+
}: {
|
|
32
|
+
items: TimelineItem[];
|
|
33
|
+
variant?: Variant;
|
|
34
|
+
size?: Responsive<Size>;
|
|
35
|
+
class?: string;
|
|
36
|
+
} = $props();
|
|
37
|
+
|
|
38
|
+
const dotSize: Record<Size, string> = { sm: "w-2.5 h-2.5", md: "w-3 h-3", lg: "w-3.5 h-3.5" };
|
|
39
|
+
const iconBox: Record<Size, string> = {
|
|
40
|
+
sm: "w-6 h-6 [&_svg]:w-3.5 [&_svg]:h-3.5",
|
|
41
|
+
md: "w-8 h-8 [&_svg]:w-4 [&_svg]:h-4",
|
|
42
|
+
lg: "w-9 h-9 [&_svg]:w-5 [&_svg]:h-5",
|
|
43
|
+
};
|
|
44
|
+
const titleSize: Record<Size, string> = { sm: "text-sm", md: "text-base", lg: "text-lg" };
|
|
45
|
+
const tokenFor = (item: TimelineItem) => variantToken[item.variant ?? variant];
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<ol class="flex flex-col {cls}">
|
|
49
|
+
{#each items as item, i (i)}
|
|
50
|
+
{@const last = i === items.length - 1}
|
|
51
|
+
<li class="flex gap-3">
|
|
52
|
+
<!-- Marker + connecting line. -->
|
|
53
|
+
<div class="flex flex-col items-center">
|
|
54
|
+
{#if item.icon}
|
|
55
|
+
<span
|
|
56
|
+
class="inline-flex shrink-0 items-center justify-center rounded-full {responsiveClasses(
|
|
57
|
+
size,
|
|
58
|
+
iconBox,
|
|
59
|
+
)}"
|
|
60
|
+
style="background-color: color-mix(in srgb, var({tokenFor(item)}) 16%, transparent); color: color-mix(in srgb, var({tokenFor(item)}) var(--ui-tint-strong), var(--ui-color-text));"
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
>
|
|
63
|
+
{@render item.icon()}
|
|
64
|
+
</span>
|
|
65
|
+
{:else}
|
|
66
|
+
<span
|
|
67
|
+
class="mt-1.5 shrink-0 rounded-full {responsiveClasses(size, dotSize)}"
|
|
68
|
+
style="background-color: var({tokenFor(item)});"
|
|
69
|
+
aria-hidden="true"
|
|
70
|
+
></span>
|
|
71
|
+
{/if}
|
|
72
|
+
{#if !last}
|
|
73
|
+
<span class="my-1 w-px flex-1 [background-color:var(--ui-border-color)]" aria-hidden="true"></span>
|
|
74
|
+
{/if}
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<!-- Content. -->
|
|
78
|
+
<div class="min-w-0 flex-1 pb-6">
|
|
79
|
+
<div class="flex flex-wrap items-baseline gap-x-2">
|
|
80
|
+
<span class="font-medium [color:var(--ui-color-text)] {responsiveClasses(size, titleSize)}">
|
|
81
|
+
{item.title}
|
|
82
|
+
</span>
|
|
83
|
+
{#if item.time}
|
|
84
|
+
<span class="text-xs [color:color-mix(in_srgb,var(--ui-color-text)_50%,transparent)]">
|
|
85
|
+
{item.time}
|
|
86
|
+
</span>
|
|
87
|
+
{/if}
|
|
88
|
+
</div>
|
|
89
|
+
{#if item.description}
|
|
90
|
+
<p class="mt-0.5 text-sm [color:color-mix(in_srgb,var(--ui-color-text)_65%,transparent)]">
|
|
91
|
+
{item.description}
|
|
92
|
+
</p>
|
|
93
|
+
{/if}
|
|
94
|
+
{#if item.content}<div class="mt-2">{@render item.content()}</div>{/if}
|
|
95
|
+
</div>
|
|
96
|
+
</li>
|
|
97
|
+
{/each}
|
|
98
|
+
</ol>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { Variant } from "../../types/variants.js";
|
|
3
|
+
export type TimelineItem = {
|
|
4
|
+
title: string;
|
|
5
|
+
time?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
icon?: Snippet;
|
|
8
|
+
variant?: Variant;
|
|
9
|
+
content?: Snippet;
|
|
10
|
+
};
|
|
11
|
+
import type { Size } from "../../types/sizes.js";
|
|
12
|
+
import { type Responsive } from "../../types/responsive.js";
|
|
13
|
+
type $$ComponentProps = {
|
|
14
|
+
items: TimelineItem[];
|
|
15
|
+
variant?: Variant;
|
|
16
|
+
size?: Responsive<Size>;
|
|
17
|
+
class?: string;
|
|
18
|
+
};
|
|
19
|
+
declare const Timeline: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
20
|
+
type Timeline = ReturnType<typeof Timeline>;
|
|
21
|
+
export default Timeline;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Timeline } from "./Timeline.svelte";
|
|
@@ -62,14 +62,19 @@
|
|
|
62
62
|
: `color-mix(in srgb, var(${token}) var(--ui-tint-border-hover), transparent)`,
|
|
63
63
|
);
|
|
64
64
|
|
|
65
|
+
// Vertical padding is kept small enough that a single token row (chip + 1px border box + padding)
|
|
66
|
+
// never exceeds the tier's `min-h`, so the field height is CONSTANT whether empty or holding tokens.
|
|
67
|
+
// Multi-row wrapping still grows the field; row spacing comes from the flex `gap`, not padding.
|
|
65
68
|
const fieldSize: Record<Size, string> = {
|
|
66
|
-
sm: "min-h-6 px-1.5 py-0
|
|
67
|
-
md: "min-h-8 px-2 py-
|
|
68
|
-
lg: "min-h-10 px-2.5 py-
|
|
69
|
+
sm: "min-h-6 px-1.5 py-0 gap-1 text-xs",
|
|
70
|
+
md: "min-h-8 px-2 py-[2px] gap-1.5 text-sm",
|
|
71
|
+
lg: "min-h-10 px-2.5 py-[2px] gap-2 text-base",
|
|
69
72
|
};
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
const baseSize = $derived<Size>(typeof size === "string" ? size : "md");
|
|
74
|
+
const chipSize = $derived<Size>(baseSize === "lg" ? "md" : "sm");
|
|
75
|
+
// A default `sm` chip is 24px — exactly the sm FIELD height, so it can't fit inside the border box.
|
|
76
|
+
// Only in the sm field, cap the chip to 20px (still fits the min-h with room to center).
|
|
77
|
+
const chipHeightClass = $derived(baseSize === "sm" ? "!h-5" : "");
|
|
73
78
|
|
|
74
79
|
let draft = $state("");
|
|
75
80
|
|
|
@@ -115,6 +120,7 @@
|
|
|
115
120
|
text={v}
|
|
116
121
|
variant={variant === "ghost" ? "secondary" : variant}
|
|
117
122
|
size={chipSize}
|
|
123
|
+
class={chipHeightClass}
|
|
118
124
|
removable
|
|
119
125
|
{disabled}
|
|
120
126
|
onremove={() => removeAt(i)}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
/**
|
|
3
|
+
* Tree — a hierarchical, expandable tree view (ARIA `tree`). Data-driven nodes; expand/collapse +
|
|
4
|
+
* single-select with roving-tabindex keyboard nav (↑/↓ move, →/← expand/collapse, Enter/Space select).
|
|
5
|
+
* Shared `variant`/`size` axes, `--ui-*` tokens. See Tree.spec.md.
|
|
6
|
+
*/
|
|
7
|
+
-->
|
|
8
|
+
<script lang="ts" module>
|
|
9
|
+
import type { Snippet } from "svelte";
|
|
10
|
+
export type TreeNode = {
|
|
11
|
+
label: string;
|
|
12
|
+
/** Stable key; falls back to the index path when omitted. */
|
|
13
|
+
id?: string;
|
|
14
|
+
icon?: Snippet;
|
|
15
|
+
children?: TreeNode[];
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
};
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import IconChevronRight from "~icons/mdi/chevron-right";
|
|
22
|
+
import type { Variant } from "../../types/variants.js";
|
|
23
|
+
import { variantToken } from "../../types/variants.js";
|
|
24
|
+
import type { Size } from "../../types/sizes.js";
|
|
25
|
+
import { responsiveClasses, type Responsive } from "../../types/responsive.js";
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
items,
|
|
29
|
+
expanded = $bindable([]),
|
|
30
|
+
selected = $bindable(""),
|
|
31
|
+
onselect,
|
|
32
|
+
variant = "primary",
|
|
33
|
+
size = "md",
|
|
34
|
+
class: cls = "",
|
|
35
|
+
}: {
|
|
36
|
+
items: TreeNode[];
|
|
37
|
+
/** Keys of expanded nodes (bindable). */
|
|
38
|
+
expanded?: string[];
|
|
39
|
+
/** Key of the selected node (bindable). */
|
|
40
|
+
selected?: string;
|
|
41
|
+
onselect?: (node: TreeNode, key: string) => void;
|
|
42
|
+
variant?: Variant;
|
|
43
|
+
size?: Responsive<Size>;
|
|
44
|
+
class?: string;
|
|
45
|
+
} = $props();
|
|
46
|
+
|
|
47
|
+
const token = $derived(variantToken[variant]);
|
|
48
|
+
const rowH: Record<Size, string> = { sm: "h-7 text-xs", md: "h-8 text-sm", lg: "h-10 text-base" };
|
|
49
|
+
const iconCls: Record<Size, string> = {
|
|
50
|
+
sm: "[&_svg]:w-3.5 [&_svg]:h-3.5",
|
|
51
|
+
md: "[&_svg]:w-4 [&_svg]:h-4",
|
|
52
|
+
lg: "[&_svg]:w-5 [&_svg]:h-5",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type Flat = { key: string; node: TreeNode; level: number; hasChildren: boolean; open: boolean };
|
|
56
|
+
// Flattened list of VISIBLE nodes (children shown only under expanded parents) — drives rendering,
|
|
57
|
+
// roving tabindex, and arrow-key navigation.
|
|
58
|
+
const flat = $derived.by<Flat[]>(() => {
|
|
59
|
+
const out: Flat[] = [];
|
|
60
|
+
const walk = (nodes: TreeNode[], level: number, prefix: string) => {
|
|
61
|
+
nodes.forEach((node, i) => {
|
|
62
|
+
const key = node.id ?? `${prefix}${i}`;
|
|
63
|
+
const hasChildren = !!node.children?.length;
|
|
64
|
+
const open = expanded.includes(key);
|
|
65
|
+
out.push({ key, node, level, hasChildren, open });
|
|
66
|
+
if (hasChildren && open) walk(node.children!, level + 1, `${key}>`);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
walk(items, 0, "");
|
|
70
|
+
return out;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
let rowRefs = $state<Record<string, HTMLElement | undefined>>({});
|
|
74
|
+
let activeKey = $state("");
|
|
75
|
+
const tabKey = $derived(
|
|
76
|
+
flat.some((f) => f.key === activeKey) ? activeKey : (flat.find((f) => !f.node.disabled)?.key ?? ""),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const toggle = (key: string) =>
|
|
80
|
+
(expanded = expanded.includes(key) ? expanded.filter((k) => k !== key) : [...expanded, key]);
|
|
81
|
+
function selectNode(f: Flat) {
|
|
82
|
+
if (f.node.disabled) return;
|
|
83
|
+
selected = f.key;
|
|
84
|
+
onselect?.(f.node, f.key);
|
|
85
|
+
if (f.hasChildren) toggle(f.key);
|
|
86
|
+
}
|
|
87
|
+
const focusKey = (key: string) => {
|
|
88
|
+
activeKey = key;
|
|
89
|
+
rowRefs[key]?.focus();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function onKeydown(e: KeyboardEvent) {
|
|
93
|
+
const idx = flat.findIndex((f) => f.key === tabKey);
|
|
94
|
+
if (idx < 0) return;
|
|
95
|
+
const f = flat[idx];
|
|
96
|
+
switch (e.key) {
|
|
97
|
+
case "ArrowDown":
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
if (idx < flat.length - 1) focusKey(flat[idx + 1].key);
|
|
100
|
+
break;
|
|
101
|
+
case "ArrowUp":
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
if (idx > 0) focusKey(flat[idx - 1].key);
|
|
104
|
+
break;
|
|
105
|
+
case "ArrowRight":
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
if (f.hasChildren && !f.open) toggle(f.key);
|
|
108
|
+
else if (f.hasChildren && flat[idx + 1]) focusKey(flat[idx + 1].key);
|
|
109
|
+
break;
|
|
110
|
+
case "ArrowLeft":
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
if (f.hasChildren && f.open) toggle(f.key);
|
|
113
|
+
else {
|
|
114
|
+
// move to parent (previous node at a lower level)
|
|
115
|
+
for (let j = idx - 1; j >= 0; j--)
|
|
116
|
+
if (flat[j].level < f.level) {
|
|
117
|
+
focusKey(flat[j].key);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
case "Home":
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
focusKey(flat[0].key);
|
|
125
|
+
break;
|
|
126
|
+
case "End":
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
focusKey(flat[flat.length - 1].key);
|
|
129
|
+
break;
|
|
130
|
+
case "Enter":
|
|
131
|
+
case " ":
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
selectNode(f);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
</script>
|
|
138
|
+
|
|
139
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
140
|
+
<ul role="tree" class="flex flex-col {responsiveClasses(size, iconCls)} {cls}" onkeydown={onKeydown}>
|
|
141
|
+
{#each flat as f (f.key)}
|
|
142
|
+
{@const active = selected === f.key}
|
|
143
|
+
<li role="none" class="list-none">
|
|
144
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
145
|
+
<div
|
|
146
|
+
bind:this={rowRefs[f.key]}
|
|
147
|
+
role="treeitem"
|
|
148
|
+
aria-level={f.level + 1}
|
|
149
|
+
aria-selected={active}
|
|
150
|
+
aria-expanded={f.hasChildren ? f.open : undefined}
|
|
151
|
+
aria-disabled={f.node.disabled || undefined}
|
|
152
|
+
tabindex={f.key === tabKey ? 0 : -1}
|
|
153
|
+
onclick={() => selectNode(f)}
|
|
154
|
+
onfocus={() => (activeKey = f.key)}
|
|
155
|
+
style="padding-inline-start: {f.level * 1.25 + 0.5}rem; {active
|
|
156
|
+
? `background-color: color-mix(in srgb, var(${token}) 16%, transparent);`
|
|
157
|
+
: ''}"
|
|
158
|
+
class="group flex items-center gap-1.5 rounded-[var(--ui-border-radius)] pr-2 transition-colors {responsiveClasses(
|
|
159
|
+
size,
|
|
160
|
+
rowH,
|
|
161
|
+
)} {f.node.disabled
|
|
162
|
+
? 'opacity-50 cursor-not-allowed'
|
|
163
|
+
: 'cursor-pointer hover:[background-color:var(--ui-color-hover)]'} focus-visible:[outline:2px_solid_color-mix(in_srgb,var(--ui-color-text)_70%,transparent)] focus-visible:[outline-offset:-2px]"
|
|
164
|
+
>
|
|
165
|
+
{#if f.hasChildren}
|
|
166
|
+
<span
|
|
167
|
+
class="inline-flex shrink-0 items-center justify-center transition-transform duration-150 {f.open
|
|
168
|
+
? 'rotate-90'
|
|
169
|
+
: ''}"><IconChevronRight /></span>
|
|
170
|
+
{:else}
|
|
171
|
+
<span class="inline-block w-4 shrink-0"></span>
|
|
172
|
+
{/if}
|
|
173
|
+
{#if f.node.icon}<span class="inline-flex shrink-0">{@render f.node.icon()}</span>{/if}
|
|
174
|
+
<span class="truncate [color:var(--ui-color-text)]">{f.node.label}</span>
|
|
175
|
+
</div>
|
|
176
|
+
</li>
|
|
177
|
+
{/each}
|
|
178
|
+
</ul>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
export type TreeNode = {
|
|
3
|
+
label: string;
|
|
4
|
+
/** Stable key; falls back to the index path when omitted. */
|
|
5
|
+
id?: string;
|
|
6
|
+
icon?: Snippet;
|
|
7
|
+
children?: TreeNode[];
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
};
|
|
10
|
+
import type { Variant } from "../../types/variants.js";
|
|
11
|
+
import type { Size } from "../../types/sizes.js";
|
|
12
|
+
import { type Responsive } from "../../types/responsive.js";
|
|
13
|
+
type $$ComponentProps = {
|
|
14
|
+
items: TreeNode[];
|
|
15
|
+
/** Keys of expanded nodes (bindable). */
|
|
16
|
+
expanded?: string[];
|
|
17
|
+
/** Key of the selected node (bindable). */
|
|
18
|
+
selected?: string;
|
|
19
|
+
onselect?: (node: TreeNode, key: string) => void;
|
|
20
|
+
variant?: Variant;
|
|
21
|
+
size?: Responsive<Size>;
|
|
22
|
+
class?: string;
|
|
23
|
+
};
|
|
24
|
+
declare const Tree: import("svelte").Component<$$ComponentProps, {}, "selected" | "expanded">;
|
|
25
|
+
type Tree = ReturnType<typeof Tree>;
|
|
26
|
+
export default Tree;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Tree } from "./Tree.svelte";
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { default as Alert } from "./components/Alert.svelte";
|
|
|
2
2
|
export { default as Banner } from "./components/Banner.svelte";
|
|
3
3
|
export { default as Badge } from "./components/Badge/Badge.svelte";
|
|
4
4
|
export { Chip } from "./components/Chip/index.js";
|
|
5
|
+
export { Kbd } from "./components/Kbd/index.js";
|
|
6
|
+
export { EmptyState } from "./components/EmptyState/index.js";
|
|
5
7
|
export { Avatar, AvatarGroup } from "./components/Avatar/index.js";
|
|
6
8
|
export type { AvatarShape, AvatarStatus, AvatarItem } from "./components/Avatar/index.js";
|
|
7
9
|
export { default as Button } from "./components/Button.svelte";
|
|
@@ -10,6 +12,9 @@ export { default as Card } from "./components/Card.svelte";
|
|
|
10
12
|
export { default as CardHeader } from "./components/CardHeader.svelte";
|
|
11
13
|
export { default as CardBody } from "./components/CardBody.svelte";
|
|
12
14
|
export { default as CardFooter } from "./components/CardFooter.svelte";
|
|
15
|
+
export { Stat } from "./components/Stat/index.js";
|
|
16
|
+
export { Timeline } from "./components/Timeline/index.js";
|
|
17
|
+
export type { TimelineItem } from "./components/Timeline/index.js";
|
|
13
18
|
export { default as Toaster } from "./components/Toast/Toaster.svelte";
|
|
14
19
|
export { toast, getToasts, dismissToast, type ToastVariant, type ToastItem, } from "./components/Toast/toast.svelte.js";
|
|
15
20
|
export { default as Dialog } from "./components/Dialog.svelte";
|
|
@@ -38,13 +43,16 @@ export { default as DatePicker } from "./components/DatePicker.svelte";
|
|
|
38
43
|
export { default as TimeSpinner } from "./components/TimeSpinner.svelte";
|
|
39
44
|
export { default as TimePicker } from "./components/TimePicker.svelte";
|
|
40
45
|
export { default as TimeRangeInput } from "./components/TimeRangeInput.svelte";
|
|
41
|
-
export { DateRangePicker } from "./components/DateRangePicker/index.js";
|
|
42
46
|
export { FileUpload } from "./components/FileUpload/index.js";
|
|
43
|
-
export type { RangePreset } from "./components/DateRangePicker/index.js";
|
|
44
47
|
export { default as ThemeSelector } from "./components/ThemeSelector.svelte";
|
|
45
48
|
export { default as ThemeToggle } from "./components/ThemeToggle.svelte";
|
|
46
49
|
export { default as Popup } from "./components/Popup.svelte";
|
|
47
50
|
export { default as Menu } from "./components/Menu.svelte";
|
|
51
|
+
export { ContextMenu } from "./components/ContextMenu/index.js";
|
|
52
|
+
export { CommandPalette } from "./components/CommandPalette/index.js";
|
|
53
|
+
export type { Command } from "./components/CommandPalette/index.js";
|
|
54
|
+
export { Tree } from "./components/Tree/index.js";
|
|
55
|
+
export type { TreeNode } from "./components/Tree/index.js";
|
|
48
56
|
export type { MenuItem } from "./data/table/types.js";
|
|
49
57
|
export { ButtonGroup } from "./components/ButtonGroup/index.js";
|
|
50
58
|
export type { ButtonGroupItem, ButtonGroupSelect } from "./components/ButtonGroup/index.js";
|
|
@@ -54,6 +62,9 @@ export { default as Radio } from "./components/Radio.svelte";
|
|
|
54
62
|
export { default as FieldGroup } from "./components/FieldGroup.svelte";
|
|
55
63
|
export { default as Grid } from "./components/Grid.svelte";
|
|
56
64
|
export { Divider } from "./components/Divider/index.js";
|
|
65
|
+
export { ScrollArea } from "./components/ScrollArea/index.js";
|
|
66
|
+
export { Splitter } from "./components/Splitter/index.js";
|
|
67
|
+
export { Carousel } from "./components/Carousel/index.js";
|
|
57
68
|
export type { Columns, Gap } from "./components/field-group.js";
|
|
58
69
|
export { Stepper } from "./components/Stepper/index.js";
|
|
59
70
|
export type { StepItem } from "./components/Stepper/index.js";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ export { default as Alert } from "./components/Alert.svelte";
|
|
|
3
3
|
export { default as Banner } from "./components/Banner.svelte";
|
|
4
4
|
export { default as Badge } from "./components/Badge/Badge.svelte";
|
|
5
5
|
export { Chip } from "./components/Chip/index.js";
|
|
6
|
+
export { Kbd } from "./components/Kbd/index.js";
|
|
7
|
+
export { EmptyState } from "./components/EmptyState/index.js";
|
|
6
8
|
export { Avatar, AvatarGroup } from "./components/Avatar/index.js";
|
|
7
9
|
export { default as Button } from "./components/Button.svelte";
|
|
8
10
|
export { default as ButtonDropdown } from "./components/ButtonDropdown.svelte";
|
|
@@ -10,6 +12,8 @@ export { default as Card } from "./components/Card.svelte";
|
|
|
10
12
|
export { default as CardHeader } from "./components/CardHeader.svelte";
|
|
11
13
|
export { default as CardBody } from "./components/CardBody.svelte";
|
|
12
14
|
export { default as CardFooter } from "./components/CardFooter.svelte";
|
|
15
|
+
export { Stat } from "./components/Stat/index.js";
|
|
16
|
+
export { Timeline } from "./components/Timeline/index.js";
|
|
13
17
|
// Toast notifications — `toast.*` pushes from anywhere; mount <Toaster /> once at the app root.
|
|
14
18
|
export { default as Toaster } from "./components/Toast/Toaster.svelte";
|
|
15
19
|
export { toast, getToasts, dismissToast, } from "./components/Toast/toast.svelte.js";
|
|
@@ -40,7 +44,6 @@ export { default as DatePicker } from "./components/DatePicker.svelte";
|
|
|
40
44
|
export { default as TimeSpinner } from "./components/TimeSpinner.svelte";
|
|
41
45
|
export { default as TimePicker } from "./components/TimePicker.svelte";
|
|
42
46
|
export { default as TimeRangeInput } from "./components/TimeRangeInput.svelte";
|
|
43
|
-
export { DateRangePicker } from "./components/DateRangePicker/index.js";
|
|
44
47
|
export { FileUpload } from "./components/FileUpload/index.js";
|
|
45
48
|
// Theme Components
|
|
46
49
|
export { default as ThemeSelector } from "./components/ThemeSelector.svelte";
|
|
@@ -48,6 +51,9 @@ export { default as ThemeToggle } from "./components/ThemeToggle.svelte";
|
|
|
48
51
|
// Utility Components
|
|
49
52
|
export { default as Popup } from "./components/Popup.svelte";
|
|
50
53
|
export { default as Menu } from "./components/Menu.svelte";
|
|
54
|
+
export { ContextMenu } from "./components/ContextMenu/index.js";
|
|
55
|
+
export { CommandPalette } from "./components/CommandPalette/index.js";
|
|
56
|
+
export { Tree } from "./components/Tree/index.js";
|
|
51
57
|
export { ButtonGroup } from "./components/ButtonGroup/index.js";
|
|
52
58
|
export { default as Checkbox } from "./components/Checkbox.svelte";
|
|
53
59
|
export { default as Toggle } from "./components/Toggle.svelte";
|
|
@@ -55,6 +61,9 @@ export { default as Radio } from "./components/Radio.svelte";
|
|
|
55
61
|
export { default as FieldGroup } from "./components/FieldGroup.svelte";
|
|
56
62
|
export { default as Grid } from "./components/Grid.svelte";
|
|
57
63
|
export { Divider } from "./components/Divider/index.js";
|
|
64
|
+
export { ScrollArea } from "./components/ScrollArea/index.js";
|
|
65
|
+
export { Splitter } from "./components/Splitter/index.js";
|
|
66
|
+
export { Carousel } from "./components/Carousel/index.js";
|
|
58
67
|
export { Stepper } from "./components/Stepper/index.js";
|
|
59
68
|
export { default as Disclosure } from "./components/Disclosure.svelte";
|
|
60
69
|
export { default as Accordion } from "./components/Accordion.svelte";
|
package/docs/components.md
CHANGED
|
@@ -122,20 +122,6 @@ Drag-and-drop + click file field with thumbnails, remove, and optional progress.
|
|
|
122
122
|
`<input type=file>`; labelled remove ×s
|
|
123
123
|
- **Composition**: `Button` + `Progress`; FieldChrome for label/error
|
|
124
124
|
|
|
125
|
-
### DateRangePicker
|
|
126
|
-
|
|
127
|
-
Start–end date range with a presets rail; composes `Calendar mode="range"` + `Input` + `Popup`.
|
|
128
|
-
|
|
129
|
-
- **Location**: `src/lib/components/DateRangePicker/DateRangePicker.svelte`
|
|
130
|
-
- **Axes**: `variant`, `size`
|
|
131
|
-
- **Props**: `start`/`end` (bindable ISO), `min`/`max`, `presets` (`RangePreset[]`; default set, `[]`
|
|
132
|
-
hides the rail), `format`, `placeholder`, plus FieldChrome (`label`/`aside`/`error`/`description`/
|
|
133
|
-
`id`/`required`), `class`
|
|
134
|
-
- **Behavior**: readonly `Input` trigger shows the formatted range; the popup pairs a presets column
|
|
135
|
-
(`Button` ghost) with the range calendar; a preset or the second calendar pick closes it
|
|
136
|
-
- **Note**: `DatePicker range` covers a bare range (object value, no presets); this is the
|
|
137
|
-
presets-first, `bind:start`/`bind:end` variant
|
|
138
|
-
|
|
139
125
|
### Slider
|
|
140
126
|
|
|
141
127
|
Draggable range input — a single value or a `[min, max]` pair.
|
|
@@ -285,10 +271,15 @@ independent. Propagates `size`/`variant`/`disabled` to children.
|
|
|
285
271
|
|
|
286
272
|
### Rating
|
|
287
273
|
|
|
288
|
-
|
|
274
|
+
Star rating in two modes: a read-only display, or an interactive picker.
|
|
289
275
|
|
|
290
276
|
- **Location**: `src/lib/components/Rating.svelte`
|
|
291
|
-
- **
|
|
277
|
+
- **Display mode** (default): `average` (0–10 score, shown as 5 stars = average/2), `count` (votes).
|
|
278
|
+
- **Interactive mode**: `interactive` + `value` (bindable, 0..`max`). Click a star, hover to preview,
|
|
279
|
+
or focus and use ←/→ · ↑/↓ · Home/End. `allowHalf` enables half-star selection; `onchange`,
|
|
280
|
+
`readonly`, `disabled`. Rendered as a `role="slider"` (same a11y pattern as `Slider`).
|
|
281
|
+
- **Shared**: `variant` (filled-star tint; default gold), `size`, `showValue` (numeric readout —
|
|
282
|
+
defaults on in display mode, off interactive), `label` (slider a11y label).
|
|
292
283
|
|
|
293
284
|
## Specialized Input Components
|
|
294
285
|
|
|
@@ -399,13 +390,19 @@ dialog (operators, casts, dynamic `now()` dates, and a worked examples table). T
|
|
|
399
390
|
Standalone page navigator — the reusable extraction of `DataTable`'s footer pager.
|
|
400
391
|
|
|
401
392
|
- **Location**: `src/lib/components/Pagination/Pagination.svelte`
|
|
402
|
-
- **Axes**: `variant` (
|
|
393
|
+
- **Axes**: `variant` (tints the band **surface + button hover**, like DataTable's footer — NOT a
|
|
394
|
+
filled button; omit for a neutral band, `ghost` for a transparent one), `size` (scales band, Select,
|
|
395
|
+
buttons, and the page field)
|
|
403
396
|
- **Props**: `page` (bindable), `total`, `perPage` (bindable), `perPageOptions` (→ rows-per-page
|
|
404
|
-
`Select`), `
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
397
|
+
`Select` on the left), `showEdges` (first/last, default **true**), `showTotal` (range text, default
|
|
398
|
+
**true**), `borderless` (drop the band border/surface), `disabled`, `class`
|
|
399
|
+
- **Layout**: a tinted, bordered band (matches `DataTable`'s footer) — per-page `Select` left; range
|
|
400
|
+
text + a compact `First · Prev · [page]/N · Next · Last` stepper right. The page indicator is an
|
|
401
|
+
**editable `NumberInput`** (min 1, max N, clamped) — type a page + Enter to jump; ↑/↓ step it.
|
|
402
|
+
- **Behavior**: prev/next/first/last disable at the ends; choosing `perPage` resets to page 1; `page`
|
|
403
|
+
clamps to range; jump commits on change (Enter/blur)
|
|
404
|
+
- **Accessibility**: `<nav aria-label="Pagination">`; the jump field is `aria-label="Page number"`
|
|
405
|
+
- **Composition**: `Button` + `Select` + `NumberInput` only (no raw `<button>`/`<input>`)
|
|
409
406
|
|
|
410
407
|
## Overlays
|
|
411
408
|
|
|
@@ -617,3 +614,109 @@ src/lib/components/ComponentName/
|
|
|
617
614
|
```
|
|
618
615
|
src/lib/components/ComponentName.svelte
|
|
619
616
|
```
|
|
617
|
+
|
|
618
|
+
### Kbd
|
|
619
|
+
|
|
620
|
+
Keyboard-key display (`<kbd>` keycap).
|
|
621
|
+
|
|
622
|
+
- **Location**: `src/lib/components/Kbd/Kbd.svelte`
|
|
623
|
+
- **Axes**: `size`
|
|
624
|
+
- **Props**: `keys` (`string[]` combo, joined with `+`), `text`/`children` (single key), `class`
|
|
625
|
+
- **Theming**: `--ui-color-surface` fill + `--ui-border-color` border with a shallow inset bottom shadow
|
|
626
|
+
|
|
627
|
+
### EmptyState
|
|
628
|
+
|
|
629
|
+
Zero-data / empty placeholder — icon + title + description + optional action.
|
|
630
|
+
|
|
631
|
+
- **Location**: `src/lib/components/EmptyState/EmptyState.svelte`
|
|
632
|
+
- **Axes**: `variant` (icon-badge accent), `size`
|
|
633
|
+
- **Props**: `title`, `description`, `icon` (snippet; default inbox), `action` (CTA snippet),
|
|
634
|
+
`children`, `class`
|
|
635
|
+
|
|
636
|
+
### Stat
|
|
637
|
+
|
|
638
|
+
Metric/KPI card — label, value, delta, icon. Built on `Card`.
|
|
639
|
+
|
|
640
|
+
- **Location**: `src/lib/components/Stat/Stat.svelte`
|
|
641
|
+
- **Axes**: `variant` (icon-badge accent), `size` (value scale)
|
|
642
|
+
- **Props**: `label`, `value`, `delta` (number → arrow; string → as-is), `deltaLabel`, `invertDelta`
|
|
643
|
+
(negative is good), `icon` (snippet), `class`
|
|
644
|
+
- **Theming**: rising delta success / falling danger / flat muted; icon badge tints from the variant
|
|
645
|
+
|
|
646
|
+
### Timeline
|
|
647
|
+
|
|
648
|
+
Vertical event sequence — marker + connecting line + title/time/description.
|
|
649
|
+
|
|
650
|
+
- **Location**: `src/lib/components/Timeline/Timeline.svelte`
|
|
651
|
+
- **Axes**: `variant` (default marker accent; per-item override), `size`
|
|
652
|
+
- **Props**: `items` (`TimelineItem[]` = `{ title, time?, description?, icon?, variant?, content? }`), `class`
|
|
653
|
+
- **Theming**: icon marker = variant token @16% badge; dot = solid token; connector `--ui-border-color`
|
|
654
|
+
|
|
655
|
+
### ContextMenu
|
|
656
|
+
|
|
657
|
+
Right-click menu over any content; opens `Menu` at the pointer.
|
|
658
|
+
|
|
659
|
+
- **Location**: `src/lib/components/ContextMenu/ContextMenu.svelte`
|
|
660
|
+
- **Props**: `items` (`MenuItem[]`), `size`, `variant`, `disabled` (leaves the native menu), `class`,
|
|
661
|
+
`children` (the right-clickable content)
|
|
662
|
+
- **Composition**: `Menu` anchored to a 0-size point at the pointer; flips/shifts at edges
|
|
663
|
+
|
|
664
|
+
### Tree
|
|
665
|
+
|
|
666
|
+
Hierarchical, expandable tree view (ARIA `tree`).
|
|
667
|
+
|
|
668
|
+
- **Location**: `src/lib/components/Tree/Tree.svelte`
|
|
669
|
+
- **Axes**: `variant` (selected-row tint), `size`
|
|
670
|
+
- **Props**: `items` (`TreeNode[]` = `{ label, id?, icon?, children?, disabled? }`), `expanded`
|
|
671
|
+
(bindable `string[]`), `selected` (bindable key), `onselect`, `class`
|
|
672
|
+
- **Keyboard**: roving tabindex — ↑/↓ move, → expand/into child, ← collapse/to parent, Enter/Space
|
|
673
|
+
select, Home/End
|
|
674
|
+
- **Accessibility**: `role="tree"`/`treeitem` with `aria-level`/`-expanded`/`-selected`/`-disabled`
|
|
675
|
+
|
|
676
|
+
### ScrollArea
|
|
677
|
+
|
|
678
|
+
Scroll container with a themed thin scrollbar.
|
|
679
|
+
|
|
680
|
+
- **Location**: `src/lib/components/ScrollArea/ScrollArea.svelte`
|
|
681
|
+
- **Props**: `orientation` (`vertical`/`horizontal`/`both`), `maxHeight`/`height` (CSS length), `variant`
|
|
682
|
+
(thumb accent; default neutral text-tint), `class`, `children`
|
|
683
|
+
- **Theming**: thumb = accent @28% (hover @45%), transparent track, rounded; Firefox `scrollbar-color`/
|
|
684
|
+
`-width:thin` + WebKit `::-webkit-scrollbar` both styled
|
|
685
|
+
|
|
686
|
+
### Splitter
|
|
687
|
+
|
|
688
|
+
Two resizable panes with a draggable divider.
|
|
689
|
+
|
|
690
|
+
- **Location**: `src/lib/components/Splitter/Splitter.svelte`
|
|
691
|
+
- **Props**: `start`/`end` (snippets), `orientation` (`horizontal`/`vertical`), `size` (bindable %),
|
|
692
|
+
`min`/`max`/`step`, `variant` (handle accent), `disabled`, `class`
|
|
693
|
+
- **Interaction**: drag the handle or focus it and use ←/→ (or ↑/↓) + Home/End
|
|
694
|
+
- **Accessibility**: WAI-ARIA window-splitter — `role="separator"`, `aria-orientation`,
|
|
695
|
+
`aria-valuenow`/`min`/`max`, focusable
|
|
696
|
+
|
|
697
|
+
### Carousel
|
|
698
|
+
|
|
699
|
+
Sliding, one-at-a-time viewport with controls + dot indicators.
|
|
700
|
+
|
|
701
|
+
- **Location**: `src/lib/components/Carousel/Carousel.svelte`
|
|
702
|
+
- **Props**: `items` (`T[]`) + `slide` (`Snippet<[item, index]>`), `index` (bindable), `loop`,
|
|
703
|
+
`autoplay` (ms; pauses on hover/focus), `controls`, `indicators`, `variant`, `ariaLabel`, `class`
|
|
704
|
+
- **Interaction**: prev/next `Button`s, clickable dots, ←/→ keys; `loop` wraps or clamps at ends
|
|
705
|
+
- **Accessibility**: `role="region"` carousel + per-slide `role="group"` (`aria-label="N of M"`, hidden
|
|
706
|
+
when off); dots are `<button>`s with `aria-current`
|
|
707
|
+
|
|
708
|
+
### CommandPalette
|
|
709
|
+
|
|
710
|
+
⌘K/Ctrl+K fuzzy command launcher — a top-centered modal overlay with a search field + filtered,
|
|
711
|
+
grouped, keyboard-navigable command list.
|
|
712
|
+
|
|
713
|
+
- **Location**: `src/lib/components/CommandPalette/CommandPalette.svelte`
|
|
714
|
+
- **Props**: `open` (bindable), `commands` (`Command[]` = `{ label, keywords?, group?, icon?,
|
|
715
|
+
shortcut?, disabled?, onrun }`), `hotkey` (default `"mod+k"`; `mod` = ⌘ on macOS / Ctrl elsewhere;
|
|
716
|
+
`""` disables), `placeholder`, `empty` (no-match text), `onopen`
|
|
717
|
+
- **Interaction**: hotkey toggles open; typing filters (label + keywords); ↑/↓ move (wrapping), Enter
|
|
718
|
+
runs the highlighted command, Esc / backdrop close; shortcuts render as `Kbd`
|
|
719
|
+
- **Behavior**: portals to `<body>`; on open resets query/highlight, locks scroll, focuses input; on
|
|
720
|
+
close restores focus. Disabled commands are inert.
|
|
721
|
+
- **Composition**: `Input` (search) + `Kbd` (shortcuts); `role="dialog"`/`combobox`/`listbox` wiring
|
|
722
|
+
- **Exports**: `CommandPalette` + the `Command` type
|