@spaethtech/svelte-ui 0.12.0 → 0.13.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 +13 -4
- package/dist/components/Avatar/Avatar.svelte +118 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +20 -0
- package/dist/components/Avatar/AvatarGroup.svelte +83 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +24 -0
- package/dist/components/Avatar/index.d.ts +4 -0
- package/dist/components/Avatar/index.js +2 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.svelte +131 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.svelte.d.ts +19 -0
- package/dist/components/Breadcrumbs/index.d.ts +2 -0
- package/dist/components/Breadcrumbs/index.js +1 -0
- package/dist/components/ButtonGroup/ButtonGroup.svelte.d.ts +1 -1
- package/dist/components/Chip/Chip.svelte +139 -0
- package/dist/components/Chip/Chip.svelte.d.ts +22 -0
- package/dist/components/Chip/index.d.ts +1 -0
- package/dist/components/Chip/index.js +1 -0
- package/dist/components/Divider/Divider.svelte +48 -0
- package/dist/components/Divider/Divider.svelte.d.ts +11 -0
- package/dist/components/Divider/index.d.ts +1 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/Pagination/Pagination.svelte +147 -0
- package/dist/components/Pagination/Pagination.svelte.d.ts +20 -0
- package/dist/components/Pagination/index.d.ts +1 -0
- package/dist/components/Pagination/index.js +1 -0
- package/dist/components/Popup.svelte +12 -1
- package/dist/components/Popup.svelte.d.ts +6 -1
- package/dist/components/SideBarMenu/SideBarMenu.svelte +160 -76
- package/dist/index.d.ts +7 -0
- package/dist/index.js +5 -0
- package/dist/positioning/anchored.d.ts +4 -0
- package/dist/positioning/anchored.js +19 -4
- package/docs/components.md +64 -0
- package/docs/usage.md +109 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
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
|
+
export { Chip } from "./components/Chip/index.js";
|
|
5
|
+
export { Avatar, AvatarGroup } from "./components/Avatar/index.js";
|
|
6
|
+
export type { AvatarShape, AvatarStatus, AvatarItem } from "./components/Avatar/index.js";
|
|
4
7
|
export { default as Button } from "./components/Button.svelte";
|
|
5
8
|
export { default as ButtonDropdown } from "./components/ButtonDropdown.svelte";
|
|
6
9
|
export { default as Card } from "./components/Card.svelte";
|
|
@@ -45,14 +48,18 @@ export { default as Toggle } from "./components/Toggle.svelte";
|
|
|
45
48
|
export { default as Radio } from "./components/Radio.svelte";
|
|
46
49
|
export { default as FieldGroup } from "./components/FieldGroup.svelte";
|
|
47
50
|
export { default as Grid } from "./components/Grid.svelte";
|
|
51
|
+
export { Divider } from "./components/Divider/index.js";
|
|
48
52
|
export type { Columns, Gap } from "./components/field-group.js";
|
|
49
53
|
export { default as Disclosure } from "./components/Disclosure.svelte";
|
|
50
54
|
export { default as Accordion } from "./components/Accordion.svelte";
|
|
51
55
|
export { default as SideBarMenu } from "./components/SideBarMenu/SideBarMenu.svelte";
|
|
52
56
|
export type { SideBarMenuItem, SideBarMenuBadge, SideBarMenuBadgeVariant, SideBarMenuApi, SideBarMenuMode, SideBarMenuActiveRule, } from "./components/SideBarMenu/SideBarMenu.svelte";
|
|
57
|
+
export { Breadcrumbs } from "./components/Breadcrumbs/index.js";
|
|
58
|
+
export type { BreadcrumbItem } from "./components/Breadcrumbs/index.js";
|
|
53
59
|
export { default as TabStrip } from "./components/TabStrip/TabStrip.svelte";
|
|
54
60
|
export type { TabDefinition } from "./components/TabStrip/TabStrip.svelte";
|
|
55
61
|
export { default as DataTable } from "./components/DataTable.svelte";
|
|
62
|
+
export { Pagination } from "./components/Pagination/index.js";
|
|
56
63
|
export { default as Query } from "./components/Query.svelte";
|
|
57
64
|
export { anchored, computePlacement, resolveBoundary } from "./positioning/anchored.js";
|
|
58
65
|
export type { Side, Align, Boundary, PlaceOptions, Placement, AnchoredParams, } from "./positioning/anchored.js";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
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
|
+
export { Chip } from "./components/Chip/index.js";
|
|
6
|
+
export { Avatar, AvatarGroup } from "./components/Avatar/index.js";
|
|
5
7
|
export { default as Button } from "./components/Button.svelte";
|
|
6
8
|
export { default as ButtonDropdown } from "./components/ButtonDropdown.svelte";
|
|
7
9
|
export { default as Card } from "./components/Card.svelte";
|
|
@@ -48,13 +50,16 @@ export { default as Toggle } from "./components/Toggle.svelte";
|
|
|
48
50
|
export { default as Radio } from "./components/Radio.svelte";
|
|
49
51
|
export { default as FieldGroup } from "./components/FieldGroup.svelte";
|
|
50
52
|
export { default as Grid } from "./components/Grid.svelte";
|
|
53
|
+
export { Divider } from "./components/Divider/index.js";
|
|
51
54
|
export { default as Disclosure } from "./components/Disclosure.svelte";
|
|
52
55
|
export { default as Accordion } from "./components/Accordion.svelte";
|
|
53
56
|
// Navigation Components
|
|
54
57
|
export { default as SideBarMenu } from "./components/SideBarMenu/SideBarMenu.svelte";
|
|
58
|
+
export { Breadcrumbs } from "./components/Breadcrumbs/index.js";
|
|
55
59
|
export { default as TabStrip } from "./components/TabStrip/TabStrip.svelte";
|
|
56
60
|
// Data table (pairs with the `svelte-ui/data` headless layer)
|
|
57
61
|
export { default as DataTable } from "./components/DataTable.svelte";
|
|
62
|
+
export { Pagination } from "./components/Pagination/index.js";
|
|
58
63
|
export { default as Query } from "./components/Query.svelte";
|
|
59
64
|
// Positioning engine (shared by Popup and tooltips)
|
|
60
65
|
export { anchored, computePlacement, resolveBoundary } from "./positioning/anchored.js";
|
|
@@ -21,6 +21,10 @@ export interface PlaceOptions {
|
|
|
21
21
|
margin?: number;
|
|
22
22
|
/** Resolve the boundary rect the placement must stay within. Default the viewport. */
|
|
23
23
|
boundary?: Boundary;
|
|
24
|
+
/** When >0, round the cross-axis *shift* (the amount the box is shoved to stay in the boundary) UP
|
|
25
|
+
* to a whole multiple of this many px. Lets a row-based menu shove by whole rows so it stays in
|
|
26
|
+
* phase with an adjacent list. Default 0 (free-px shift). */
|
|
27
|
+
snap?: number;
|
|
24
28
|
}
|
|
25
29
|
export interface Placement {
|
|
26
30
|
x: number;
|
|
@@ -25,7 +25,7 @@ export function resolveBoundary(boundary = "viewport") {
|
|
|
25
25
|
* vertically per `align`. Flips to the opposite side, then scans, when the preferred side won't fit.
|
|
26
26
|
*/
|
|
27
27
|
export function computePlacement(anchor, size, opts = {}) {
|
|
28
|
-
const { side = "bottom", align = "start", offset = 4, margin = 8 } = opts;
|
|
28
|
+
const { side = "bottom", align = "start", offset = 4, margin = 8, snap = 0 } = opts;
|
|
29
29
|
const b = resolveBoundary(opts.boundary);
|
|
30
30
|
const { width: fw, height: fh } = size;
|
|
31
31
|
const fits = (s) => {
|
|
@@ -75,9 +75,24 @@ export function computePlacement(anchor, size, opts = {}) {
|
|
|
75
75
|
? anchor.bottom - fh
|
|
76
76
|
: anchor.top;
|
|
77
77
|
}
|
|
78
|
-
// Clamp into the boundary (shift), so the box never spills past the edges.
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
// Clamp into the boundary (shift), so the box never spills past the edges. The CROSS axis (the one
|
|
79
|
+
// perpendicular to the chosen side) can `snap` its shift to whole steps; the main axis never snaps.
|
|
80
|
+
const clampSnap = (v, min, max) => {
|
|
81
|
+
const c = Math.min(Math.max(v, min), max);
|
|
82
|
+
if (snap > 0 && c !== v) {
|
|
83
|
+
// Shoved by (c - v); round the magnitude UP to a whole step, keep direction, re-clamp.
|
|
84
|
+
const shoved = v + Math.sign(c - v) * Math.ceil(Math.abs(c - v) / snap) * snap;
|
|
85
|
+
return Math.min(Math.max(shoved, min), max);
|
|
86
|
+
}
|
|
87
|
+
return c;
|
|
88
|
+
};
|
|
89
|
+
const crossVertical = chosen === "left" || chosen === "right";
|
|
90
|
+
x = crossVertical
|
|
91
|
+
? Math.min(Math.max(x, b.left + margin), b.right - margin - fw)
|
|
92
|
+
: clampSnap(x, b.left + margin, b.right - margin - fw);
|
|
93
|
+
y = crossVertical
|
|
94
|
+
? clampSnap(y, b.top + margin, b.bottom - margin - fh)
|
|
95
|
+
: Math.min(Math.max(y, b.top + margin), b.bottom - margin - fh);
|
|
81
96
|
return { x, y, side: chosen, maxHeight };
|
|
82
97
|
}
|
|
83
98
|
/**
|
package/docs/components.md
CHANGED
|
@@ -182,6 +182,16 @@ per-child prop). Works with any content. Uniform cells only (for variable widths
|
|
|
182
182
|
- **Props**: `columns` (`number | { base, md, … }`, 1–6, default `1`), `gap` (`number | { x, y }`,
|
|
183
183
|
default `4`), `class`, `children`. Shares `gridColsClass`/`gapClass` with `FieldGroup`.
|
|
184
184
|
|
|
185
|
+
### Divider
|
|
186
|
+
|
|
187
|
+
A thin separating rule.
|
|
188
|
+
|
|
189
|
+
- **Location**: `src/lib/components/Divider/Divider.svelte`
|
|
190
|
+
- **Props**: `orientation` (`horizontal`/`vertical`), `label`/`children` (inline label, horizontal),
|
|
191
|
+
`labelAlign` (`start`/`center`/`end`), `class`. No `variant`/`size`.
|
|
192
|
+
- **Accessibility**: `role="separator"` + `aria-orientation` (plain horizontal uses `<hr>`)
|
|
193
|
+
- **Theming**: line = `--ui-border-color`; label muted. Vertical stretches to the row via `self-stretch`.
|
|
194
|
+
|
|
185
195
|
### Disclosure
|
|
186
196
|
|
|
187
197
|
A single expand/collapse section — a `<Button>` header (title + rotating chevron) over a content
|
|
@@ -260,6 +270,19 @@ snaps to canonical on blur.
|
|
|
260
270
|
|
|
261
271
|
## Navigation
|
|
262
272
|
|
|
273
|
+
### Breadcrumbs
|
|
274
|
+
|
|
275
|
+
Navigation trail (root → current); composes `Button` (links) + `Menu` (overflow).
|
|
276
|
+
|
|
277
|
+
- **Location**: `src/lib/components/Breadcrumbs/Breadcrumbs.svelte`
|
|
278
|
+
- **Axes**: `size`
|
|
279
|
+
- **Props**: `items` (`BreadcrumbItem[]` = `{ label, href?, onclick?, icon? }`; last = current page),
|
|
280
|
+
`separator` (Snippet | string, default chevron), `maxItems` (collapse middle → `⋯` Menu; needs ≥3),
|
|
281
|
+
`size`, `class`
|
|
282
|
+
- **Accessibility**: `<nav aria-label="Breadcrumb">` + `<ol>`; current crumb `aria-current="page"` and
|
|
283
|
+
not a link; ellipsis button has `aria-haspopup`/`aria-expanded`
|
|
284
|
+
- **Composition**: link crumbs are `Button variant="ghost"`; overflow crumbs live in a `Menu`
|
|
285
|
+
|
|
263
286
|
### TabStrip
|
|
264
287
|
|
|
265
288
|
A horizontal tab bar (renders as a `tablist` of buttons, or a `navigation` of links when any tab has
|
|
@@ -301,6 +324,19 @@ dialog (operators, casts, dynamic `now()` dates, and a worked examples table). T
|
|
|
301
324
|
- **Note**: URL sync is intentionally not built in — observe `dataset.applied` and sync yourself.
|
|
302
325
|
Built on `Input` + `Button` + `Popup` + `Dialog`.
|
|
303
326
|
|
|
327
|
+
### Pagination
|
|
328
|
+
|
|
329
|
+
Standalone page navigator — the reusable extraction of `DataTable`'s footer pager.
|
|
330
|
+
|
|
331
|
+
- **Location**: `src/lib/components/Pagination/Pagination.svelte`
|
|
332
|
+
- **Axes**: `variant` (active page fill), `size`
|
|
333
|
+
- **Props**: `page` (bindable), `total`, `perPage` (bindable), `perPageOptions` (→ rows-per-page
|
|
334
|
+
`Select`), `siblingCount`/`boundaryCount`, `showEdges` (first/last), `showTotal`, `disabled`, `class`
|
|
335
|
+
- **Behavior**: numbered buttons with ellipsis for long ranges (active = filled, rest `ghost`);
|
|
336
|
+
prev/next/first/last disable at the ends; choosing `perPage` resets to page 1; `page` clamps to range
|
|
337
|
+
- **Accessibility**: `<nav aria-label="Pagination">`; active page `aria-current="page"`
|
|
338
|
+
- **Composition**: `Button` + `Select` only (no raw `<button>`)
|
|
339
|
+
|
|
304
340
|
## Overlays
|
|
305
341
|
|
|
306
342
|
### Dialog
|
|
@@ -390,6 +426,20 @@ Shared placement engine — a Svelte action plus `computePlacement` / `resolveBo
|
|
|
390
426
|
|
|
391
427
|
## Display / Theme
|
|
392
428
|
|
|
429
|
+
### Avatar / AvatarGroup
|
|
430
|
+
|
|
431
|
+
Person/entity likeness with a graceful fallback chain; `AvatarGroup` stacks with a `+N` overflow.
|
|
432
|
+
|
|
433
|
+
- **Location**: `src/lib/components/Avatar/Avatar.svelte`, `AvatarGroup.svelte`
|
|
434
|
+
- **Axes**: `size` (`sm`/`md`/`lg` = 32/40/48px), `variant` (fallback fill)
|
|
435
|
+
- **Avatar props**: `src`, `name` (initials + alt), `alt`, `icon` (snippet), `shape`
|
|
436
|
+
(`circle`/`square`), `status` (`online`/`offline`/`busy`/`away`), `class`. Fallback order:
|
|
437
|
+
`src` (until load error) → `name` initials → `icon` → default glyph.
|
|
438
|
+
- **AvatarGroup props**: `items` (`AvatarItem[]` = `{ src?, name?, alt?, variant?, icon? }`), `max`
|
|
439
|
+
(→ `+N` chip), `size`/`shape`/`variant` defaults, or `children` (hand-authored, no overflow), `class`
|
|
440
|
+
- **Theming**: fallback fill = variant token @20% + strong token text; status dot = semantic token;
|
|
441
|
+
overlap ring = `--ui-color-background`. No per-component tokens.
|
|
442
|
+
|
|
393
443
|
### Spinner
|
|
394
444
|
|
|
395
445
|
Indeterminate loading ring — the primitive `Button`'s `loading` state and the loading family build on.
|
|
@@ -426,6 +476,20 @@ Colour-neutral content placeholder for loading states.
|
|
|
426
476
|
- **Theming**: neutral text-tint base + CSS shimmer; corners use `--ui-border-radius`. Compose several
|
|
427
477
|
to sketch a card/list row.
|
|
428
478
|
|
|
479
|
+
### Chip
|
|
480
|
+
|
|
481
|
+
Interactive pill — the interactive counterpart to `Badge` (display-only).
|
|
482
|
+
|
|
483
|
+
- **Location**: `src/lib/components/Chip/Chip.svelte`
|
|
484
|
+
- **Axes**: `variant`, `size` (`sm`/`md`/`lg` = 24/32/40px)
|
|
485
|
+
- **Props**: `text`/`children`, `icon` (leading), `selected` (toggle → filled), `removable` +
|
|
486
|
+
`onremove` (× ; also Backspace/Delete when focused), `onclick` (→ `role="button"` + `aria-pressed`),
|
|
487
|
+
`href` (→ `<a>`), `disabled`, `rounded` (pill, default), `class`
|
|
488
|
+
- **HTML**: root is a `span`/`a` (never a `<button>`) so a removable chip's × can be a real nested
|
|
489
|
+
`<button>` without invalid nesting
|
|
490
|
+
- **Theming**: selected = variant token fill + white text; unselected = `--ui-tint-soft` fill +
|
|
491
|
+
`--ui-tint-border` border + `--ui-tint-strong` text (same vocabulary as `Badge`). No per-component tokens.
|
|
492
|
+
|
|
429
493
|
### Badge
|
|
430
494
|
|
|
431
495
|
Status indicator / label.
|
package/docs/usage.md
CHANGED
|
@@ -631,6 +631,115 @@ An edge-anchored panel that slides in — same modal mechanics as `Dialog`, pinn
|
|
|
631
631
|
</Badge>
|
|
632
632
|
```
|
|
633
633
|
|
|
634
|
+
### Divider
|
|
635
|
+
|
|
636
|
+
A thin separating rule — horizontal (optionally labelled) or vertical (toolbars).
|
|
637
|
+
|
|
638
|
+
```svelte
|
|
639
|
+
<script>
|
|
640
|
+
import { Divider } from "@spaethtech/svelte-ui";
|
|
641
|
+
</script>
|
|
642
|
+
|
|
643
|
+
<Divider />
|
|
644
|
+
<Divider label="OR" />
|
|
645
|
+
<Divider label="Recent" labelAlign="start" />
|
|
646
|
+
|
|
647
|
+
<!-- Vertical, inside a flex row -->
|
|
648
|
+
<div class="flex items-center gap-3 h-8">
|
|
649
|
+
<span>Edit</span>
|
|
650
|
+
<Divider orientation="vertical" />
|
|
651
|
+
<span>Share</span>
|
|
652
|
+
</div>
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
### Pagination
|
|
656
|
+
|
|
657
|
+
A standalone page navigator (numbered buttons + ellipsis, prev/next, optional per-page + total).
|
|
658
|
+
|
|
659
|
+
```svelte
|
|
660
|
+
<script>
|
|
661
|
+
import { Pagination } from "@spaethtech/svelte-ui";
|
|
662
|
+
let page = $state(1);
|
|
663
|
+
let perPage = $state(10);
|
|
664
|
+
</script>
|
|
665
|
+
|
|
666
|
+
<Pagination bind:page bind:perPage total={247} perPageOptions={[10, 25, 50, 100]} showTotal />
|
|
667
|
+
|
|
668
|
+
<!-- Compact: numbers only, with first/last controls -->
|
|
669
|
+
<Pagination bind:page total={200} showEdges />
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### Breadcrumbs
|
|
673
|
+
|
|
674
|
+
A navigation trail; the last item is the current page. `maxItems` collapses the middle into a `⋯` menu.
|
|
675
|
+
|
|
676
|
+
```svelte
|
|
677
|
+
<script>
|
|
678
|
+
import { Breadcrumbs } from "@spaethtech/svelte-ui";
|
|
679
|
+
|
|
680
|
+
const trail = [
|
|
681
|
+
{ label: "Home", href: "/" },
|
|
682
|
+
{ label: "Components", href: "/components" },
|
|
683
|
+
{ label: "Breadcrumbs" }, // last = current page (not a link)
|
|
684
|
+
];
|
|
685
|
+
</script>
|
|
686
|
+
|
|
687
|
+
<Breadcrumbs items={trail} />
|
|
688
|
+
|
|
689
|
+
<!-- Collapse a long trail; custom separator -->
|
|
690
|
+
<Breadcrumbs items={trail} maxItems={4} separator="/" />
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### Chip
|
|
694
|
+
|
|
695
|
+
An interactive pill — filter/choice tokens, removable tags, clickable labels.
|
|
696
|
+
|
|
697
|
+
```svelte
|
|
698
|
+
<script>
|
|
699
|
+
import { Chip } from "@spaethtech/svelte-ui";
|
|
700
|
+
|
|
701
|
+
const FILTERS = ["All", "Active", "Archived"];
|
|
702
|
+
let active = $state("Active");
|
|
703
|
+
|
|
704
|
+
let tags = $state(["design", "svelte", "ui"]);
|
|
705
|
+
const remove = (t) => (tags = tags.filter((x) => x !== t));
|
|
706
|
+
</script>
|
|
707
|
+
|
|
708
|
+
<!-- Filter / choice: selected fills with the variant (consumer owns which is on) -->
|
|
709
|
+
{#each FILTERS as f}
|
|
710
|
+
<Chip text={f} selected={active === f} variant="primary" onclick={() => (active = f)} />
|
|
711
|
+
{/each}
|
|
712
|
+
|
|
713
|
+
<!-- Removable tags: the × fires onremove (Backspace/Delete on a focused chip also removes) -->
|
|
714
|
+
{#each tags as t (t)}
|
|
715
|
+
<Chip text={t} removable onremove={() => remove(t)} />
|
|
716
|
+
{/each}
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### Avatar
|
|
720
|
+
|
|
721
|
+
A likeness with a fallback chain (image → initials → icon → glyph). `AvatarGroup` stacks with `+N`.
|
|
722
|
+
|
|
723
|
+
```svelte
|
|
724
|
+
<script>
|
|
725
|
+
import { Avatar, AvatarGroup } from "@spaethtech/svelte-ui";
|
|
726
|
+
|
|
727
|
+
const team = [
|
|
728
|
+
{ name: "Jane Doe", src: "/1.jpg" },
|
|
729
|
+
{ name: "Ravi Patel", src: "/2.jpg" },
|
|
730
|
+
{ name: "Mia Chen" }, // no src → initials "MC"
|
|
731
|
+
];
|
|
732
|
+
</script>
|
|
733
|
+
|
|
734
|
+
<Avatar src="/jane.jpg" name="Jane Doe" />
|
|
735
|
+
<Avatar name="Jane Doe" variant="primary" /> <!-- initials fallback -->
|
|
736
|
+
<Avatar /> <!-- default glyph -->
|
|
737
|
+
<Avatar name="Jane Doe" status="online" /> <!-- status dot -->
|
|
738
|
+
|
|
739
|
+
<!-- Overlapped group; extras beyond max collapse into a +N chip -->
|
|
740
|
+
<AvatarGroup items={team} max={4} />
|
|
741
|
+
```
|
|
742
|
+
|
|
634
743
|
### TextArea
|
|
635
744
|
|
|
636
745
|
```svelte
|