@heyramzi/ui 2.20.0 → 2.21.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 +2 -0
- package/dist/charts/dashboard/data-table.js +1 -2
- package/dist/components/list-page/filter-section.js +1 -1
- package/dist/components/list-page/list-table.js +5 -6
- package/dist/components/list-page/queue-rail.js +2 -1
- package/dist/components/picker/trigger.js +3 -3
- package/dist/components/segmented/segmented.js +5 -5
- package/dist/components/work-parts/looks.js +1 -1
- package/dist/shadcn/dialog/index.js +3 -2
- package/dist/shadcn/dropdown-menu/menu-item.js +1 -1
- package/dist/shadcn/sheet/index.js +3 -2
- package/dist/shadcn/skeleton/index.js +3 -2
- package/dist/status/index.js +1 -0
- package/dist/work/kanban-drag.js +13 -7
- package/package.json +1 -1
- package/src/ai/confirmation/ConfirmationTitle.svelte +1 -4
- package/src/ai/loading-state/PixelLoader.svelte +1 -1
- package/src/animations/dropdown.css +19 -10
- package/src/animations/modal.css +7 -2
- package/src/charts/charts.css +3 -5
- package/src/charts/dashboard/dashboard-section.svelte +1 -1
- package/src/charts/dashboard/data-table.ts +1 -2
- package/src/charts/dashboard/stat-tile.svelte +1 -1
- package/src/charts/number-chart.css +8 -7
- package/src/charts/pop-value.svelte +68 -0
- package/src/charts/primitives/chart-tooltip.svelte +1 -1
- package/src/charts/reference-line.css +1 -1
- package/src/charts/stat-card.css +4 -4
- package/src/components/copy/copy-button.svelte +11 -10
- package/src/components/data-table/row-arrow.svelte +1 -1
- package/src/components/data-table/row-context-menu.svelte +1 -1
- package/src/components/data-table/sortable-head.svelte +2 -2
- package/src/components/data-table/table-skeleton.svelte +5 -5
- package/src/components/docs/doc-page.svelte +1 -3
- package/src/components/docs/on-this-page.svelte +4 -4
- package/src/components/field-row/field-row.svelte +1 -1
- package/src/components/list-page/detail-empty.svelte +3 -8
- package/src/components/list-page/detail-header.svelte +1 -1
- package/src/components/list-page/detail-row.svelte +2 -1
- package/src/components/list-page/empty-state.svelte +15 -7
- package/src/components/list-page/filter-item.svelte +2 -2
- package/src/components/list-page/filter-popover.svelte +1 -1
- package/src/components/list-page/filter-section.ts +1 -4
- package/src/components/list-page/grouped-list.svelte +37 -8
- package/src/components/list-page/list-page-shell.svelte +1 -1
- package/src/components/list-page/list-table.ts +6 -6
- package/src/components/list-page/master-detail.svelte +1 -1
- package/src/components/list-page/queue-rail.ts +2 -2
- package/src/components/list-page/rail-empty.svelte +1 -1
- package/src/components/list-page/rail-row.svelte +5 -5
- package/src/components/open-in/open-in.svelte +2 -1
- package/src/components/picker/trigger.ts +6 -5
- package/src/components/section-card/section-card.svelte +1 -1
- package/src/components/section-header/section-header.svelte +4 -2
- package/src/components/section-header/section-label.svelte +2 -2
- package/src/components/segmented/animated-tabs.svelte +1 -1
- package/src/components/segmented/section-runs.svelte +1 -1
- package/src/components/segmented/segment.svelte +4 -6
- package/src/components/segmented/segmented-track.svelte +18 -52
- package/src/components/segmented/segmented.ts +5 -5
- package/src/components/table-view/table-view.svelte +43 -24
- package/src/components/tags-input/tags-input.svelte +17 -31
- package/src/components/work-parts/looks.ts +1 -1
- package/src/editor/content.css +3 -3
- package/src/editor/editor.svelte +1 -1
- package/src/inbox/thread-inbox.svelte +2 -9
- package/src/panel/side-panel.svelte +9 -4
- package/src/record/info-list.svelte +7 -8
- package/src/record/project-list.svelte +1 -1
- package/src/record/record-demo.svelte +2 -4
- package/src/record/record-shell.svelte +1 -1
- package/src/record/record-stats.svelte +9 -3
- package/src/record/steps-rail.svelte +7 -11
- package/src/shadcn/accordion/accordion-trigger.svelte +1 -1
- package/src/shadcn/alert-dialog/alert-dialog-content.svelte +5 -1
- package/src/shadcn/alert-dialog/alert-dialog-footer.svelte +12 -2
- package/src/shadcn/alert-dialog/alert-dialog-header.svelte +1 -1
- package/src/shadcn/alert-dialog/alert-dialog-title.svelte +1 -1
- package/src/shadcn/badge/badge.svelte +3 -1
- package/src/shadcn/bubble/bubble-content.svelte +1 -1
- package/src/shadcn/bubble/bubble.svelte +1 -1
- package/src/shadcn/button/button.svelte +11 -5
- package/src/shadcn/calendar/calendar-month-select.svelte +1 -1
- package/src/shadcn/calendar/calendar-year-select.svelte +1 -1
- package/src/shadcn/calendar/calendar.svelte +1 -1
- package/src/shadcn/card/card.svelte +2 -1
- package/src/shadcn/checkbox/checkbox.svelte +1 -1
- package/src/shadcn/code-block/code-block-actions.svelte +1 -1
- package/src/shadcn/command/command-dialog.svelte +2 -2
- package/src/shadcn/command/command-group.svelte +1 -1
- package/src/shadcn/command/command-input.svelte +1 -1
- package/src/shadcn/command/command-item.svelte +1 -1
- package/src/shadcn/dialog/dialog-body.svelte +22 -0
- package/src/shadcn/dialog/dialog-content.svelte +19 -4
- package/src/shadcn/dialog/dialog-footer.svelte +19 -1
- package/src/shadcn/dialog/dialog-header.svelte +6 -1
- package/src/shadcn/dialog/dialog-title.svelte +2 -1
- package/src/shadcn/dialog/index.ts +3 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-content.svelte +5 -1
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
- package/src/shadcn/dropdown-menu/menu-item.ts +1 -1
- package/src/shadcn/field/field-separator.svelte +1 -1
- package/src/shadcn/hover-card/hover-card-content.svelte +1 -1
- package/src/shadcn/input/input.svelte +2 -2
- package/src/shadcn/input-otp/input-otp-slot.svelte +1 -1
- package/src/shadcn/message-scroller/message-scroller-button.svelte +1 -1
- package/src/shadcn/popover/popover-content.svelte +1 -1
- package/src/shadcn/radio-group/radio-group-item.svelte +1 -1
- package/src/shadcn/scroll-area/scroll-area.svelte +1 -1
- package/src/shadcn/select/select-content.svelte +6 -2
- package/src/shadcn/select/select-item.svelte +1 -1
- package/src/shadcn/select/select-trigger.svelte +1 -1
- package/src/shadcn/sheet/index.ts +3 -0
- package/src/shadcn/sheet/sheet-body.svelte +22 -0
- package/src/shadcn/sheet/sheet-content.svelte +27 -5
- package/src/shadcn/sheet/sheet-footer.svelte +14 -2
- package/src/shadcn/sheet/sheet-header.svelte +1 -1
- package/src/shadcn/sheet/sheet-title.svelte +1 -1
- package/src/shadcn/sheet/sheet.css +11 -5
- package/src/shadcn/sidebar/sidebar-group-action.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-group-label.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-inset.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu-action.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu-button.svelte +2 -2
- package/src/shadcn/sidebar/sidebar-menu-sub-button.svelte +1 -1
- package/src/shadcn/sidebar/sidebar-menu.svelte +1 -1
- package/src/shadcn/sidebar/sidebar.svelte +1 -1
- package/src/shadcn/skeleton/index.ts +3 -0
- package/src/shadcn/skeleton/skeleton-delay.svelte +63 -0
- package/src/shadcn/skeleton/skeleton-delay.svelte.test.ts +57 -0
- package/src/shadcn/skeleton/skeleton.svelte +5 -1
- package/src/shadcn/slider/slider.svelte +1 -1
- package/src/shadcn/switch/switch.svelte +2 -2
- package/src/shadcn/table/table-row.svelte +1 -1
- package/src/shadcn/tabs/tabs-list.svelte +1 -1
- package/src/shadcn/tabs/tabs-trigger.svelte +2 -1
- package/src/shadcn/textarea/textarea.svelte +1 -1
- package/src/shadcn/toggle/toggle.svelte +2 -2
- package/src/shadcn/toggle-group/toggle-group.svelte +1 -1
- package/src/shadcn/tooltip/tooltip-content.svelte +1 -1
- package/src/shadcn/trace/trace-collapsible-trigger.svelte +1 -1
- package/src/shadcn/trace/trace.svelte +1 -1
- package/src/status/README.md +1 -1
- package/src/status/index.ts +1 -0
- package/src/status/status-badge.svelte +21 -7
- package/src/status/status-dot.svelte +6 -11
- package/src/status/status-pill.svelte +50 -0
- package/src/thread/thread.svelte +54 -136
- package/src/tokens/craft.css +55 -36
- package/src/tokens/palette.css +16 -9
- package/src/work/demo.svelte +48 -42
- package/src/work/detail/detail-demo.svelte +33 -24
- package/src/work/detail/task-chip.svelte +4 -4
- package/src/work/detail/task-detail.svelte +41 -108
- package/src/work/detail/task-property.svelte +7 -13
- package/src/work/detail/task-thread.svelte +48 -119
- package/src/work/docs/doc-editor-page.svelte +5 -4
- package/src/work/docs/doc-menu.svelte +9 -4
- package/src/work/docs/docs-demo.svelte +4 -4
- package/src/work/docs/edited-chip.svelte +11 -4
- package/src/work/gantt/gantt-bar.svelte +18 -14
- package/src/work/gantt/gantt-grid.svelte +7 -7
- package/src/work/gantt/gantt-group-row.svelte +7 -10
- package/src/work/gantt/gantt-header.svelte +3 -1
- package/src/work/gantt/gantt-task-row.svelte +19 -9
- package/src/work/gantt/gantt.css +4 -5
- package/src/work/inbox/inbox-card.svelte +32 -10
- package/src/work/inbox/inbox-draft.svelte +2 -2
- package/src/work/kanban-column.svelte +7 -15
- package/src/work/kanban-drag.ts +13 -8
- package/src/work/task-gantt.svelte +2 -1
- package/src/work/task-list.svelte +54 -53
- package/src/work/time/time-chip.svelte +16 -19
- package/src/work/time/time-dot.svelte +2 -1
- package/src/work/time/time-ring.svelte +2 -3
- package/src/work/time/time-tracker.svelte +72 -166
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
{#each { length: rows } as _, rowIdx (rowIdx)}
|
|
43
43
|
<tr
|
|
44
44
|
data-slot="table-row"
|
|
45
|
-
class="border-b border-border/
|
|
46
|
-
style="animation: skeleton-fade-in var(--duration-slow) var(--ease-out) calc(var(--duration-stagger) * {rowIdx}) both"
|
|
45
|
+
class="border-b border-border/70 transition-opacity"
|
|
46
|
+
style="animation: skeleton-fade-in var(--duration-slow) var(--ease-smooth-out) calc(var(--duration-stagger) * {rowIdx}) both"
|
|
47
47
|
>
|
|
48
48
|
{#each cols as col, colIdx (colIdx)}
|
|
49
49
|
<td
|
|
50
50
|
data-slot="table-cell"
|
|
51
|
-
class="h-
|
|
51
|
+
class="h-8 px-2 any-pointer-coarse:h-11 {col.align === 'center'
|
|
52
52
|
? 'text-center'
|
|
53
53
|
: col.align === 'right'
|
|
54
54
|
? 'text-right'
|
|
@@ -62,10 +62,10 @@
|
|
|
62
62
|
: ''}"
|
|
63
63
|
>
|
|
64
64
|
{#if col.circle}
|
|
65
|
-
<Skeleton class="size-
|
|
65
|
+
<Skeleton class="size-5 shrink-0 rounded-full" />
|
|
66
66
|
{/if}
|
|
67
67
|
<Skeleton
|
|
68
|
-
class="h-3
|
|
68
|
+
class="h-3 rounded-sm {col.width ?? WIDTHS[(rowIdx + colIdx) % WIDTHS.length]}"
|
|
69
69
|
/>
|
|
70
70
|
</div>
|
|
71
71
|
</td>
|
|
@@ -89,9 +89,7 @@
|
|
|
89
89
|
>
|
|
90
90
|
{#if hasRail}
|
|
91
91
|
<!-- Below the rail's width the contents sit on a strip that stays put. -->
|
|
92
|
-
<div
|
|
93
|
-
class="sticky top-0 z-20 border-b border-border bg-background/95 backdrop-blur @3xl/page:hidden"
|
|
94
|
-
>
|
|
92
|
+
<div class="sticky top-0 z-20 border-b border-border bg-card/95 backdrop-blur @3xl/page:hidden">
|
|
95
93
|
<OnThisPage
|
|
96
94
|
variant="bar"
|
|
97
95
|
items={headings}
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
type="button"
|
|
150
150
|
tabindex="-1"
|
|
151
151
|
onclick={() => pick(item.id)}
|
|
152
|
-
class="block w-full truncate rounded-md py-1 pr-3 text-left text-
|
|
152
|
+
class="block w-full truncate rounded-md py-1 pr-3 text-left text-(length:--text-control) leading-5 transition-colors duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] hover:bg-foreground/6 {item.level ===
|
|
153
153
|
3
|
|
154
154
|
? 'pl-5'
|
|
155
155
|
: 'pl-2'} {current
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
{:else if variant === "rail"}
|
|
165
165
|
<nav aria-label={label} class="flex max-h-[calc(100svh-8rem)] flex-col gap-3">
|
|
166
166
|
<!-- Set like the map's own headings on the left, so the two rails read as one. -->
|
|
167
|
-
<p class="font-heading text-xs font-semibold
|
|
167
|
+
<p class="font-heading text-xs font-semibold text-muted-foreground">
|
|
168
168
|
{label}
|
|
169
169
|
</p>
|
|
170
170
|
<div
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
data-current={current}
|
|
191
191
|
aria-current={current ? "location" : undefined}
|
|
192
192
|
onclick={() => pick(item.id)}
|
|
193
|
-
class="otp-row block w-full py-1.5 pl-3 pr-1 text-left text-
|
|
193
|
+
class="otp-row block w-full py-1.5 pl-3 pr-1 text-left text-(length:--text-control) leading-snug transition-colors duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] any-pointer-coarse:py-2.5 {current
|
|
194
194
|
? 'font-medium text-foreground'
|
|
195
195
|
: 'text-muted-foreground hover:text-foreground'}"
|
|
196
196
|
>
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
{#if open}
|
|
233
233
|
<nav
|
|
234
234
|
aria-label={label}
|
|
235
|
-
class="thin-scrollbar absolute inset-x-2 top-full z-20 mt-1 max-h-[60svh] overflow-y-auto rounded-
|
|
235
|
+
class="thin-scrollbar absolute inset-x-2 top-full z-20 mt-1 max-h-[60svh] overflow-y-auto rounded-2xl bg-popover p-1.5 shadow-edge-raised"
|
|
236
236
|
>
|
|
237
237
|
<ul class="flex flex-col">
|
|
238
238
|
{#each items as item (item.id)}
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
{#if action}
|
|
53
53
|
<!-- A disabled action reads as a muted button, not a half-faded one. -->
|
|
54
54
|
<div
|
|
55
|
-
class="shrink-0 [&_button:disabled]:bg-
|
|
55
|
+
class="shrink-0 [&_button:disabled]:bg-foreground/6 [&_button:disabled]:text-muted-foreground [&_button:disabled]:opacity-100 [&_button:disabled]:shadow-none"
|
|
56
56
|
>
|
|
57
57
|
{@render action()}
|
|
58
58
|
</div>
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
// TYPES
|
|
11
11
|
// =========================
|
|
12
12
|
interface Props {
|
|
13
|
-
// Shown when nothing is picked or the board is empty.
|
|
14
|
-
//
|
|
13
|
+
// Shown when nothing is picked or the board is empty. It sits on the white
|
|
14
|
+
// panel with no frame of its own: a dashed edge drew a box round nothing.
|
|
15
15
|
icon?: PhosphorIcon;
|
|
16
16
|
iconWeight?: IconWeight;
|
|
17
17
|
title: string;
|
|
@@ -40,11 +40,6 @@
|
|
|
40
40
|
<!-- =========================== -->
|
|
41
41
|
<!-- MARKUP -->
|
|
42
42
|
<!-- =========================== -->
|
|
43
|
-
<div
|
|
44
|
-
class={cn(
|
|
45
|
-
"flex h-full items-center justify-center rounded-xl border-2 border-dashed border-border",
|
|
46
|
-
className,
|
|
47
|
-
)}
|
|
48
|
-
>
|
|
43
|
+
<div class={cn("flex h-full items-center justify-center rounded-xl bg-card", className)}>
|
|
49
44
|
<EmptyState {icon} {iconWeight} {title} {description} {actionLabel} {actionHref} {onAction} />
|
|
50
45
|
</div>
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<!-- =========================== -->
|
|
62
62
|
<!-- MARKUP -->
|
|
63
63
|
<!-- =========================== -->
|
|
64
|
-
<section class={cn("overflow-hidden rounded-xl
|
|
64
|
+
<section class={cn("overflow-hidden rounded-xl bg-card shadow-card", className)}>
|
|
65
65
|
<div class="flex flex-wrap items-start justify-between gap-x-4 gap-y-3 p-4">
|
|
66
66
|
{#if leading}
|
|
67
67
|
<div class="shrink-0">{@render leading()}</div>
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
<!-- =========================== -->
|
|
28
28
|
<div
|
|
29
29
|
class={cn(
|
|
30
|
-
|
|
30
|
+
// WHY first:border-t-0: the panel header already draws the line above the first row.
|
|
31
|
+
"border-t border-border px-4 py-3 text-sm leading-relaxed first:border-t-0",
|
|
31
32
|
muted && "text-xs text-muted-foreground",
|
|
32
33
|
className,
|
|
33
34
|
)}
|
|
@@ -36,16 +36,18 @@
|
|
|
36
36
|
<!-- =========================== -->
|
|
37
37
|
<!-- MARKUP -->
|
|
38
38
|
<!-- =========================== -->
|
|
39
|
-
<!--
|
|
40
|
-
|
|
39
|
+
<!-- transitions-dev texts reveal: the parts arrive in reading order, so the page reads as
|
|
40
|
+
loaded and empty. Reduced motion keeps the fade. -->
|
|
41
41
|
<Empty.Root class="t-empty py-16">
|
|
42
42
|
<Empty.Header class="max-w-md">
|
|
43
43
|
{#if Icon}
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
<!-- A white tile, not a brand tint: the accent marks the one action (DESIGN.md
|
|
45
|
+
rule 1), and the button below is it. -->
|
|
46
|
+
<Empty.Media class="mb-3 size-12 rounded-xl bg-card text-foreground shadow-card">
|
|
47
|
+
<Icon weight={iconWeight} class="size-6" />
|
|
46
48
|
</Empty.Media>
|
|
47
49
|
{/if}
|
|
48
|
-
<Empty.Title class="font-heading text-
|
|
50
|
+
<Empty.Title class="font-heading text-base font-semibold text-balance [overflow-wrap:anywhere]"
|
|
49
51
|
>{title}</Empty.Title
|
|
50
52
|
>
|
|
51
53
|
{#if description}
|
|
@@ -84,10 +86,16 @@
|
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
@keyframes -global-t-empty-fade {
|
|
90
|
+
from {
|
|
91
|
+
opacity: 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
87
95
|
@media (prefers-reduced-motion: reduce) {
|
|
88
96
|
:global(.t-empty) > :global(*) {
|
|
89
|
-
animation-
|
|
90
|
-
animation-delay:
|
|
97
|
+
animation-name: t-empty-fade;
|
|
98
|
+
animation-delay: 0s;
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
</style>
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
<!-- =========================== -->
|
|
29
29
|
<button
|
|
30
30
|
type="button"
|
|
31
|
-
class="flex items-center gap-2 rounded-lg px-2
|
|
31
|
+
class="flex min-h-8 items-center gap-2 rounded-lg px-2 text-sm transition-colors duration-quick ease-smooth-out hover:bg-foreground/6 motion-reduce:transition-none any-pointer-coarse:min-h-11"
|
|
32
32
|
{onclick}
|
|
33
33
|
>
|
|
34
34
|
{#if variant === "radio"}
|
|
35
35
|
<span
|
|
36
36
|
class={cn(
|
|
37
|
-
"grid size-3.5 shrink-0 place-items-center rounded-full border transition-colors",
|
|
37
|
+
"grid size-3.5 shrink-0 place-items-center rounded-full border transition-colors duration-quick ease-smooth-out motion-reduce:transition-none",
|
|
38
38
|
checked ? "border-primary bg-primary text-primary-foreground" : "border-border",
|
|
39
39
|
)}
|
|
40
40
|
>
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
{/if}
|
|
72
72
|
{#if hasCount}
|
|
73
73
|
<span
|
|
74
|
-
class="ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-brand/
|
|
74
|
+
class="ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-brand/12 px-1 text-xs leading-none font-semibold text-brand tabular-nums dark:text-brand-light"
|
|
75
75
|
>
|
|
76
76
|
{count.toLocaleString()}
|
|
77
77
|
</span>
|
|
@@ -5,8 +5,5 @@ import { cn } from "../../lib/utils.js";
|
|
|
5
5
|
|
|
6
6
|
// `first:` drops the leading header's top padding, since the popover pads itself.
|
|
7
7
|
export function filterSectionClass(className?: string): string {
|
|
8
|
-
return cn(
|
|
9
|
-
"px-2 pt-2 pb-1 text-[11px] font-medium uppercase tracking-wide text-muted-foreground first:pt-1",
|
|
10
|
-
className,
|
|
11
|
-
);
|
|
8
|
+
return cn("px-2 pt-2 pb-1 text-2xs font-medium text-muted-foreground first:pt-1", className);
|
|
12
9
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// =========================
|
|
5
5
|
import { untrack, type Snippet } from "svelte";
|
|
6
6
|
import { SvelteSet } from "svelte/reactivity";
|
|
7
|
-
import { CaretDown } from "phosphor-svelte";
|
|
7
|
+
import { CaretDown, Plus } from "phosphor-svelte";
|
|
8
8
|
import { cn } from "../../lib/utils.js";
|
|
9
9
|
|
|
10
10
|
// =========================
|
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
one doesn't collapse the group. Shown on hover of the header row. */
|
|
23
23
|
actions?: Snippet<[TGroup]>;
|
|
24
24
|
rows: Snippet<[TGroup]>;
|
|
25
|
+
/** A count beside the header, in a small pill. Leave it out when the header draws its own. */
|
|
26
|
+
getCount?: (group: TGroup) => number;
|
|
27
|
+
/** Shows an add button at the end of the header row. */
|
|
28
|
+
onAdd?: (group: TGroup) => void;
|
|
29
|
+
/** The add button's accessible name. */
|
|
30
|
+
addLabel?: string;
|
|
25
31
|
class?: string;
|
|
26
32
|
onGroupDragOver?: (e: DragEvent, group: TGroup) => void;
|
|
27
33
|
onGroupDragLeave?: (e: DragEvent) => void;
|
|
@@ -39,6 +45,9 @@
|
|
|
39
45
|
header,
|
|
40
46
|
actions,
|
|
41
47
|
rows,
|
|
48
|
+
getCount,
|
|
49
|
+
onAdd,
|
|
50
|
+
addLabel = "Add",
|
|
42
51
|
class: className,
|
|
43
52
|
onGroupDragOver,
|
|
44
53
|
onGroupDragLeave,
|
|
@@ -70,38 +79,58 @@
|
|
|
70
79
|
{#each groups as group, index (getKey(group))}
|
|
71
80
|
{@const key = getKey(group)}
|
|
72
81
|
{@const isCollapsed = collapsed.has(key)}
|
|
82
|
+
<!-- Groups part by space, not a rule: the header band already marks where one starts
|
|
83
|
+
(DESIGN.md rule 19). -->
|
|
73
84
|
<div
|
|
74
|
-
class={cn(index > 0 && "mt-
|
|
85
|
+
class={cn(index > 0 && "mt-4")}
|
|
75
86
|
role={onGroupDragOver || onGroupDrop ? "group" : undefined}
|
|
76
87
|
ondragover={onGroupDragOver ? (e) => onGroupDragOver(e, group) : undefined}
|
|
77
88
|
ondragleave={onGroupDragLeave}
|
|
78
89
|
ondrop={onGroupDrop ? (e) => onGroupDrop(e, group) : undefined}
|
|
79
90
|
>
|
|
91
|
+
<!-- A quiet band, 32px like a row, so the header reads as the group's lid
|
|
92
|
+
without a border. The tint is on the foreground: bg-muted vanishes on the canvas. -->
|
|
80
93
|
<div
|
|
81
|
-
class="group/header flex items-center gap-1 rounded-lg transition-colors hover:bg-
|
|
94
|
+
class="group/header hover-row flex min-h-8 items-center gap-1 rounded-lg bg-foreground/4 transition-colors duration-quick ease-smooth-out hover:bg-foreground/6 motion-reduce:transition-none any-pointer-coarse:min-h-11"
|
|
82
95
|
>
|
|
83
96
|
<button
|
|
84
97
|
type="button"
|
|
85
|
-
class="flex min-w-0 flex-1 cursor-pointer items-center gap-2
|
|
98
|
+
class="flex min-w-0 flex-1 cursor-pointer items-center gap-2 self-stretch rounded-lg px-2.5 text-left outline-none focus-visible:shadow-[var(--ring-focus)]"
|
|
86
99
|
onclick={() => toggle(key)}
|
|
87
100
|
aria-expanded={!isCollapsed}
|
|
88
101
|
>
|
|
102
|
+
<!-- The accordion chevron: it turns on the fast step and holds still under
|
|
103
|
+
reduced motion, where the open state is aria-expanded and the rows. -->
|
|
89
104
|
<CaretDown
|
|
90
105
|
class={cn(
|
|
91
|
-
"size-3 shrink-0 text-muted-foreground
|
|
106
|
+
"size-3 shrink-0 text-muted-foreground transition-transform duration-fast ease-smooth-out motion-reduce:transition-none",
|
|
92
107
|
isCollapsed && "-rotate-90",
|
|
93
108
|
)}
|
|
94
109
|
weight="bold"
|
|
95
110
|
/>
|
|
96
111
|
{@render header(group, { collapsed: isCollapsed })}
|
|
112
|
+
{#if getCount}
|
|
113
|
+
<span
|
|
114
|
+
class="rounded-full bg-foreground/6 px-1.5 text-xs leading-5 font-medium text-muted-foreground tabular-nums"
|
|
115
|
+
>{getCount(group)}</span
|
|
116
|
+
>
|
|
117
|
+
{/if}
|
|
97
118
|
</button>
|
|
98
119
|
{#if actions}
|
|
99
|
-
<div
|
|
100
|
-
class="flex shrink-0 items-center gap-1 pe-2 opacity-0 transition-opacity group-hover/header:opacity-100 focus-within:opacity-100 any-pointer-coarse:opacity-100"
|
|
101
|
-
>
|
|
120
|
+
<div class="row-action flex shrink-0 items-center gap-1 pe-1">
|
|
102
121
|
{@render actions(group)}
|
|
103
122
|
</div>
|
|
104
123
|
{/if}
|
|
124
|
+
{#if onAdd}
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
class="t-press me-1 grid size-6 shrink-0 place-items-center rounded-sm text-muted-foreground transition-colors duration-quick ease-smooth-out outline-none hover:bg-foreground/6 hover:text-foreground focus-visible:shadow-[var(--ring-focus)] motion-reduce:transition-none any-pointer-coarse:size-11"
|
|
128
|
+
aria-label={addLabel}
|
|
129
|
+
onclick={() => onAdd(group)}
|
|
130
|
+
>
|
|
131
|
+
<Plus class="size-3.5" weight="bold" aria-hidden="true" />
|
|
132
|
+
</button>
|
|
133
|
+
{/if}
|
|
105
134
|
</div>
|
|
106
135
|
|
|
107
136
|
{#if !isCollapsed}
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
<!-- Sticky, because the section tabs live here. `-mx-4 px-4` covers the
|
|
126
126
|
gutter, `-mt-4 py-4` pulls the top padding inside the sticky box. -->
|
|
127
127
|
<div
|
|
128
|
-
class="@container/toolbar sticky top-0 z-20 -mx-4 -mt-4 bg-
|
|
128
|
+
class="@container/toolbar sticky top-0 z-20 -mx-4 -mt-4 bg-card/95 px-4 py-4 backdrop-blur supports-[backdrop-filter]:bg-card/70"
|
|
129
129
|
>
|
|
130
130
|
<div class={cn("flex flex-wrap items-center gap-2", toolbarClass)}>
|
|
131
131
|
<!-- The section tabs lead: which page comes before which slice of it.
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { cn } from "../../lib/utils.js";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/** The list-page table rhythm: 32px rows (DESIGN.md rule 19), hairlines, a flat hover. */
|
|
4
|
+
|
|
5
|
+
// The header is a muted band tinted off the foreground, so it shows on card and canvas.
|
|
6
6
|
export const listTable = {
|
|
7
|
-
/** The header row:
|
|
7
|
+
/** The header row: 32px, muted, a hairline under it. On the cells: `thead` zeroes its row's border. */
|
|
8
8
|
headRow: (className?: string) =>
|
|
9
9
|
cn(
|
|
10
|
-
"hover:translate-y-0 hover:bg-transparent hover:shadow-none [&>th]:h-
|
|
10
|
+
"hover:translate-y-0 hover:bg-transparent hover:shadow-none [&>th]:h-8 [&>th]:border-b [&>th]:border-border [&>th]:bg-foreground/3 [&>th]:text-xs [&>th]:font-medium [&>th]:text-muted-foreground",
|
|
11
11
|
className,
|
|
12
12
|
),
|
|
13
13
|
/** A body row. The shared row lifts on hover; a flush list row does not. */
|
|
14
14
|
row: (className?: string) =>
|
|
15
15
|
cn(
|
|
16
|
-
"border-b border-border/50 last:border-b-0 hover:translate-y-0 hover:bg-
|
|
16
|
+
"border-b border-border/50 last:border-b-0 hover:translate-y-0 hover:bg-foreground/3 hover:shadow-none [&>td]:h-8 [&>td]:py-0 [&>td]:tabular-nums any-pointer-coarse:[&>td]:h-11",
|
|
17
17
|
className,
|
|
18
18
|
),
|
|
19
19
|
};
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* A class, not a wrapper: the shell's `<aside>` already owns the scroll height.
|
|
4
4
|
*/
|
|
5
5
|
// Copies of this string drifted to `rounded-lg`, the control radius, not the container one.
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// No `border`: the card shadow draws its own hairline, and both drew a 2px edge.
|
|
7
|
+
export const queueRailClass = "flex flex-col overflow-hidden rounded-xl bg-card shadow-card";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The rail column track, passed to `MasterDetail`'s `railWidth`. One page ran
|
|
@@ -91,10 +91,10 @@
|
|
|
91
91
|
<div
|
|
92
92
|
bind:this={row}
|
|
93
93
|
class={cn(
|
|
94
|
-
// `
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
selected ? "bg-accent" : "hover:bg-
|
|
94
|
+
// `hover-row`: a caller's `row-action` (CopyHandoff) reveals on it. DESIGN.md Patterns.
|
|
95
|
+
"group group/row hover-row relative border-t border-border first:border-t-0",
|
|
96
|
+
"transition-colors duration-quick ease-smooth-out motion-reduce:transition-none",
|
|
97
|
+
selected ? "bg-accent" : "hover:bg-foreground/4",
|
|
98
98
|
className,
|
|
99
99
|
)}
|
|
100
100
|
>
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
type="button"
|
|
103
103
|
class={cn(
|
|
104
104
|
"t-press flex w-full items-start gap-3 px-4 py-3 text-left any-pointer-coarse:py-3.5",
|
|
105
|
-
"focus-visible:outline-none focus-visible:
|
|
105
|
+
"focus-visible:outline-none focus-visible:shadow-(--ring-focus-inset)",
|
|
106
106
|
// Room for the actions only where they always show; a hover-only reserve
|
|
107
107
|
// cost every title 96px. The overlay below sits on its own surface instead.
|
|
108
108
|
actions && "any-pointer-coarse:pr-24",
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
call site that forgot would open the row as well. -->
|
|
73
73
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
74
74
|
<div
|
|
75
|
+
role="presentation"
|
|
75
76
|
class="inline-flex shrink-0"
|
|
76
77
|
onclick={(e) => e.stopPropagation()}
|
|
77
78
|
onkeydown={(e) => e.stopPropagation()}
|
|
@@ -89,7 +90,7 @@
|
|
|
89
90
|
? "inline-flex shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
90
91
|
: cn(
|
|
91
92
|
"inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
92
|
-
size === "sm" ? "px-1.5 py-0.5 text-
|
|
93
|
+
size === "sm" ? "px-1.5 py-0.5 text-2xs" : "px-2 py-1 text-xs",
|
|
93
94
|
),
|
|
94
95
|
variant === "icon" && (size === "sm" ? "size-5" : "size-6"),
|
|
95
96
|
className,
|
|
@@ -14,10 +14,11 @@ import { tv } from "tailwind-variants";
|
|
|
14
14
|
// box shows only under the pointer and on focus.
|
|
15
15
|
export const pickerTriggerClass = tv({
|
|
16
16
|
base: [
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
// `length:` stops tailwind-merge reading `text-control` as a colour (craft.css, Type).
|
|
18
|
+
"t-press inline-flex min-w-0 items-center gap-1.5 rounded-lg text-(length:--text-control) font-medium whitespace-nowrap select-none",
|
|
19
|
+
// The hover box is a foreground tint: property rows sit on the canvas too, where bg-muted
|
|
20
|
+
// is 0.005 L away and vanished. Radius is the control step, 10px.
|
|
21
|
+
"transition-colors duration-quick ease-smooth-out hover:bg-foreground/6 data-[state=open]:bg-foreground/6 motion-reduce:transition-none",
|
|
21
22
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
22
23
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
23
24
|
],
|
|
@@ -34,7 +35,7 @@ export const pickerTriggerClass = tv({
|
|
|
34
35
|
*/
|
|
35
36
|
block: { true: "flex w-full", false: "w-fit" },
|
|
36
37
|
/** No value yet: the label is a prompt, so it recedes until hovered. */
|
|
37
|
-
empty: { true: "text-muted-foreground
|
|
38
|
+
empty: { true: "text-muted-foreground hover:text-foreground", false: "" },
|
|
38
39
|
/** A row that carries no action still needs the column's box. */
|
|
39
40
|
inert: { true: "hover:bg-transparent cursor-default", false: "" },
|
|
40
41
|
},
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<!-- =========================== -->
|
|
33
33
|
<Card.Root class={cn("gap-0 py-0", className)}>
|
|
34
34
|
<header class="flex min-h-14 items-center justify-between gap-3 border-b px-4 py-3 sm:px-6">
|
|
35
|
-
<Card.Title as="h3" class="min-w-0 text-base font-medium [overflow-wrap:anywhere]"
|
|
35
|
+
<Card.Title as="h3" class="min-w-0 text-base font-medium text-balance [overflow-wrap:anywhere]"
|
|
36
36
|
>{title}</Card.Title
|
|
37
37
|
>
|
|
38
38
|
{#if actions}
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<SectionLabel class="min-w-0 [overflow-wrap:anywhere]">{title}</SectionLabel>
|
|
38
38
|
{#if count !== undefined}
|
|
39
39
|
<span
|
|
40
|
-
class="shrink-0 rounded-
|
|
40
|
+
class="shrink-0 rounded-full bg-foreground/6 px-1.5 py-0.5 text-xs leading-none font-medium tabular-nums text-muted-foreground"
|
|
41
41
|
aria-label={`${count.toLocaleString()} ${countLabel}`}
|
|
42
42
|
>
|
|
43
43
|
{count.toLocaleString()}
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
{/if}
|
|
46
46
|
</svelte:element>
|
|
47
47
|
{#if description}
|
|
48
|
-
<p class="text-sm text-muted-foreground [overflow-wrap:anywhere]">
|
|
48
|
+
<p class="text-sm text-pretty text-muted-foreground [overflow-wrap:anywhere]">
|
|
49
|
+
{description}
|
|
50
|
+
</p>
|
|
49
51
|
{/if}
|
|
50
52
|
</div>
|
|
51
53
|
{#if actions}
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
<!-- =========================== -->
|
|
29
29
|
<span
|
|
30
30
|
class={cn(
|
|
31
|
-
"flex items-center gap-2 text-xs font-semibold
|
|
31
|
+
"flex items-center gap-2 text-xs font-semibold text-balance text-muted-foreground",
|
|
32
32
|
className,
|
|
33
33
|
)}
|
|
34
34
|
>
|
|
35
35
|
{#if Icon}
|
|
36
|
-
<Icon class="size-4
|
|
36
|
+
<Icon class="size-4" weight="bold" aria-hidden="true" />
|
|
37
37
|
{/if}
|
|
38
38
|
{@render children()}
|
|
39
39
|
</span>
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
<span
|
|
158
158
|
role="presentation"
|
|
159
159
|
aria-hidden="true"
|
|
160
|
-
class="flex shrink-0 items-center px-2 text-xs font-medium
|
|
160
|
+
class="flex shrink-0 items-center px-2 text-xs font-medium text-muted-foreground"
|
|
161
161
|
>{tab.group.label}</span
|
|
162
162
|
>
|
|
163
163
|
{/if}
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
{#each strip.runs as run, index (index)}
|
|
66
66
|
<div class={cn("flex shrink-0 items-center gap-2", index === firstTitled && "ml-auto")}>
|
|
67
67
|
{#if run.label}
|
|
68
|
-
<span class="text-
|
|
68
|
+
<span class="text-xs font-medium text-muted-foreground">
|
|
69
69
|
{run.label}
|
|
70
70
|
</span>
|
|
71
71
|
{/if}
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
src={image}
|
|
65
65
|
{label}
|
|
66
66
|
class={cn(
|
|
67
|
-
"opacity-75 transition-opacity duration-
|
|
67
|
+
"opacity-75 transition-opacity duration-fast ease-smooth-out motion-reduce:transition-none group-hover/segment:opacity-100",
|
|
68
68
|
on && "opacity-100",
|
|
69
69
|
auto && "group-data-[state=active]/segment:opacity-100",
|
|
70
70
|
)}
|
|
@@ -76,9 +76,7 @@
|
|
|
76
76
|
{label}
|
|
77
77
|
|
|
78
78
|
{#if badge}
|
|
79
|
-
<span class="text-primary text-
|
|
80
|
-
>{badge}</span
|
|
81
|
-
>
|
|
79
|
+
<span class="text-primary text-xs font-semibold whitespace-nowrap">{badge}</span>
|
|
82
80
|
{/if}
|
|
83
81
|
|
|
84
82
|
{#if count != null}
|
|
@@ -88,8 +86,8 @@
|
|
|
88
86
|
|
|
89
87
|
// WHY min-width equals height with 4px padding: `min-w-5 px-1.5` made a digit
|
|
90
88
|
// 20 by 18, so `rounded-full` drew an ellipse.
|
|
91
|
-
"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-
|
|
92
|
-
"bg-
|
|
89
|
+
"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-xs leading-none font-medium tabular-nums",
|
|
90
|
+
"bg-foreground/8 text-muted-foreground",
|
|
93
91
|
on && "bg-brand/14 text-brand",
|
|
94
92
|
auto &&
|
|
95
93
|
"group-data-[state=active]/segment:bg-brand/14 group-data-[state=active]/segment:text-brand",
|