@heyramzi/ui 2.3.0 → 2.5.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/COMPONENTS.md
CHANGED
|
@@ -76,7 +76,7 @@ Generic pieces built on the primitives, imported from `@heyramzi/ui/components/<
|
|
|
76
76
|
- `section-card` — SectionCard, a card with a fixed-height header, an h3 title, optional actions and a line under it
|
|
77
77
|
- `section-header` — SectionHeader, a section label with a count chip, a line of description and actions
|
|
78
78
|
- `field-row` — FieldRow, a labelled control with its action button at the control's height
|
|
79
|
-
- `table-view` — TableView, a read-only table from columns and rows: numeric columns, footer row, custom cell; opt-in: `bare`, sortable heads (`column.sortable`, bind `sort`; the caller orders `rows`), `rowHref` (a real link over the row), `onRowClick`, `selectable` + bind `selected`, `rowMenu` (context-menu items), `group` + `groupHeader` + `collapsible`, `subRow` + `expanded`, `loading` (skeleton rows in the frame), `empty`, `trailing`, `head`, and per-column `hideBelow`, `sticky`, `interactive`, `class`
|
|
79
|
+
- `table-view` — TableView, a read-only table from columns and rows: numeric columns, footer row, custom cell; opt-in: `bare`, sortable heads (`column.sortable`, bind `sort`; the caller orders `rows`), `rowHref` (a real link over the row; `onRowLink` opens in place on a plain click), `onRowClick`, `selectable` + bind `selected`, `rowMenu` (context-menu items), `group` (a `null` key sits with no header) + `groupHeader` + `collapsible`, `subRow` + `expanded`, `loading` (skeleton rows in the frame), `empty`, `trailing`, `head`, and per-column `hideBelow`, `sticky`, `interactive`, `class`
|
|
80
80
|
- `segmented` — AnimatedTabs (pill or underline), SegmentedFilter, SegmentedNav, SectionRuns, one bordered track with a sliding pill, a badge note and an overflow menu
|
|
81
81
|
- `theme` — ThemeProvider, mode, userPrefersMode, setMode, toggleMode, resetMode: the one light and dark system (mode-watcher, optional peer). Mount `<ThemeProvider initial onChange themeColors modeStorageKey />` once; flip with `toggleMode()`, read `mode.current`. Pair with `theme-toggle`'s `revealTheme(event, toggleMode)` for the wipe
|
|
82
82
|
- `star-rating-input` — StarRatingInput
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyramzi/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Svelte 5 UI for apps: primitives, effects and animations, plus tasks, charts, slides, an editor, onboarding and a canvas as subpaths.",
|
|
5
5
|
"homepage": "https://ui.heyramzi.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
xTicks?: number;
|
|
46
46
|
/** A target drawn across the plot, like the deal a cost runs up to. */
|
|
47
47
|
reference?: ChartReference;
|
|
48
|
+
/** The headline while no point is hovered, like a period total. Defaults to the last point. */
|
|
49
|
+
resting?: { value: number; label: string };
|
|
48
50
|
class?: string;
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
formatX = (v) => String(v),
|
|
66
68
|
xTicks = 6,
|
|
67
69
|
reference,
|
|
70
|
+
resting,
|
|
68
71
|
class: className,
|
|
69
72
|
}: Props = $props();
|
|
70
73
|
|
|
@@ -127,7 +130,14 @@
|
|
|
127
130
|
aria-label="Area chart"
|
|
128
131
|
onpointerleave={() => (active = null)}
|
|
129
132
|
>
|
|
130
|
-
{#if
|
|
133
|
+
{#if resting && !active && series[0]}
|
|
134
|
+
<Figure
|
|
135
|
+
series={[{ value: resting.value, label: series[0].label, color: series[0].color }]}
|
|
136
|
+
eyebrow={resting.label}
|
|
137
|
+
format={formatValue}
|
|
138
|
+
size="md"
|
|
139
|
+
/>
|
|
140
|
+
{:else if figureRows && display}
|
|
131
141
|
<Figure series={figureRows} eyebrow={formatX(display[xKey])} format={formatValue} size="md" />
|
|
132
142
|
{:else}
|
|
133
143
|
<Figure value="—" eyebrow="Latest" size="md" />
|
|
@@ -43,6 +43,8 @@
|
|
|
43
43
|
sort?: SortState<Row>;
|
|
44
44
|
/** Where a row goes. Draws a real link over the row, so middle-click and cmd-click work. */
|
|
45
45
|
rowHref?: (row: Row) => string | undefined;
|
|
46
|
+
/** Runs on a plain left click of the row link. Call `event.preventDefault()` to open in place; a modified click still follows `href`. */
|
|
47
|
+
onRowLink?: (row: Row, event: MouseEvent) => void;
|
|
46
48
|
/** The link's accessible name. Defaults to the row's first column text. */
|
|
47
49
|
rowLabel?: (row: Row) => string;
|
|
48
50
|
/** Adds a checkbox column with a select-all in the head. */
|
|
@@ -53,8 +55,8 @@
|
|
|
53
55
|
selectRowLabel?: (row: Row) => string;
|
|
54
56
|
/** The items of a right-click menu for one row: `ContextMenu.Item`s. */
|
|
55
57
|
rowMenu?: Snippet<[Row]>;
|
|
56
|
-
/** Rows that return the same key sit under one header row. Order `rows` so a group is contiguous. */
|
|
57
|
-
group?: (row: Row) => string;
|
|
58
|
+
/** Rows that return the same key sit under one header row, and `null` rows sit with no header. Order `rows` so a group is contiguous. */
|
|
59
|
+
group?: (row: Row) => string | null;
|
|
58
60
|
/** Draws a group's header. Prints the key when left out. */
|
|
59
61
|
groupHeader?: Snippet<[TableGroup<Row>]>;
|
|
60
62
|
/** Makes each group header a button that folds its rows. */
|
|
@@ -94,6 +96,7 @@
|
|
|
94
96
|
class: className,
|
|
95
97
|
sort = $bindable(null),
|
|
96
98
|
rowHref,
|
|
99
|
+
onRowLink,
|
|
97
100
|
rowLabel,
|
|
98
101
|
selectable = false,
|
|
99
102
|
selected = $bindable(new Set<string>()),
|
|
@@ -127,15 +130,16 @@
|
|
|
127
130
|
align: column.numeric ? ("right" as const) : ("left" as const),
|
|
128
131
|
})),
|
|
129
132
|
]);
|
|
130
|
-
|
|
133
|
+
type Run = { id: string; key: string | null; rows: Row[] };
|
|
134
|
+
let entries = $derived.by<Run[]>(() => {
|
|
131
135
|
if (!group) return [];
|
|
132
|
-
const out:
|
|
133
|
-
let current:
|
|
136
|
+
const out: Run[] = [];
|
|
137
|
+
let current: Run | undefined;
|
|
134
138
|
for (const row of rows) {
|
|
135
139
|
const groupKey = group(row);
|
|
136
140
|
if (!current || current.key !== groupKey) {
|
|
137
|
-
current = { key: groupKey, rows: [] };
|
|
138
|
-
out.push(
|
|
141
|
+
current = { id: groupKey ?? `\u0000${out.length}`, key: groupKey, rows: [] };
|
|
142
|
+
out.push(current);
|
|
139
143
|
}
|
|
140
144
|
current.rows.push(row);
|
|
141
145
|
}
|
|
@@ -204,6 +208,12 @@
|
|
|
204
208
|
onRowClick?.(row);
|
|
205
209
|
}
|
|
206
210
|
|
|
211
|
+
function linkClick(event: MouseEvent, row: Row) {
|
|
212
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
|
|
213
|
+
return;
|
|
214
|
+
onRowLink?.(row, event);
|
|
215
|
+
}
|
|
216
|
+
|
|
207
217
|
function linkName(row: Row): string {
|
|
208
218
|
return rowLabel?.(row) ?? (columns[0] ? textOf(columns[0], row) : label);
|
|
209
219
|
}
|
|
@@ -288,6 +298,7 @@
|
|
|
288
298
|
<a
|
|
289
299
|
{href}
|
|
290
300
|
aria-label={linkName(row)}
|
|
301
|
+
onclick={onRowLink ? (event: MouseEvent) => linkClick(event, row) : undefined}
|
|
291
302
|
class="absolute inset-0 z-0 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
|
|
292
303
|
></a>
|
|
293
304
|
</td>
|
|
@@ -370,40 +381,45 @@
|
|
|
370
381
|
<TableSkeleton rows={skeletonRows} columns={skeletonColumns} />
|
|
371
382
|
{:else}
|
|
372
383
|
{#if group}
|
|
373
|
-
{#each entries as entry (entry.
|
|
374
|
-
{@const folded = collapsible && collapsed.has(entry.
|
|
384
|
+
{#each entries as entry (entry.id)}
|
|
385
|
+
{@const folded = entry.key !== null && collapsible && collapsed.has(entry.key)}
|
|
386
|
+
{@const tableGroup = { key: entry.key ?? "", rows: entry.rows }}
|
|
375
387
|
<Table.Body>
|
|
376
|
-
|
|
377
|
-
<Table.
|
|
378
|
-
|
|
379
|
-
scope="colgroup"
|
|
380
|
-
class="text-foreground h-10 p-0"
|
|
388
|
+
{#if entry.key !== null}
|
|
389
|
+
<Table.Row
|
|
390
|
+
class="bg-muted/40 hover:bg-muted/40 hover:translate-y-0 hover:shadow-none"
|
|
381
391
|
>
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
392
|
+
<Table.Head
|
|
393
|
+
colspan={span + (rowHref ? 1 : 0)}
|
|
394
|
+
scope="colgroup"
|
|
395
|
+
class="text-foreground h-10 p-0"
|
|
396
|
+
>
|
|
397
|
+
{#if collapsible}
|
|
398
|
+
<button
|
|
399
|
+
type="button"
|
|
400
|
+
aria-expanded={!folded}
|
|
401
|
+
class="t-press focus-visible:ring-ring flex h-10 w-full items-center gap-2 px-2 text-start outline-none focus-visible:ring-2 focus-visible:ring-inset"
|
|
402
|
+
onclick={() => toggleGroup(tableGroup.key)}
|
|
403
|
+
>
|
|
404
|
+
<CaretDown
|
|
405
|
+
class={cn(
|
|
406
|
+
"size-3.5 shrink-0 transition-transform duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] motion-reduce:transition-none",
|
|
407
|
+
folded && "-rotate-90",
|
|
408
|
+
)}
|
|
409
|
+
aria-hidden="true"
|
|
410
|
+
/>
|
|
411
|
+
{#if groupHeader}{@render groupHeader(tableGroup)}{:else}{tableGroup.key}{/if}
|
|
412
|
+
</button>
|
|
413
|
+
{:else}
|
|
414
|
+
<span class="flex h-10 items-center px-2">
|
|
415
|
+
{#if groupHeader}{@render groupHeader(tableGroup)}{:else}{tableGroup.key}{/if}
|
|
416
|
+
</span>
|
|
417
|
+
{/if}
|
|
418
|
+
</Table.Head>
|
|
419
|
+
</Table.Row>
|
|
420
|
+
{/if}
|
|
405
421
|
{#if !folded}
|
|
406
|
-
{#each entry.
|
|
422
|
+
{#each entry.rows as row (rowKey(row))}
|
|
407
423
|
{@render rowOrMenu(row)}
|
|
408
424
|
{/each}
|
|
409
425
|
{/if}
|