@heyramzi/ui 2.20.0 → 2.21.1
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/alert/alert-variants.js +9 -0
- package/dist/shadcn/alert/index.js +1 -1
- package/dist/shadcn/badge/badge-variants.js +17 -0
- package/dist/shadcn/badge/index.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/shadcn/toggle/index.js +1 -1
- package/dist/shadcn/toggle/toggle-variants.js +20 -0
- package/dist/status/index.js +2 -0
- package/dist/status/pill-tint.js +13 -0
- package/dist/work/kanban-drag.js +13 -7
- package/package.json +23 -23
- 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-rail.svelte +5 -1
- 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/alert-variants.ts +17 -0
- package/src/shadcn/alert/alert.svelte +2 -16
- package/src/shadcn/alert/index.ts +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-variants.ts +23 -0
- package/src/shadcn/badge/badge.svelte +2 -20
- package/src/shadcn/badge/index.ts +1 -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/index.ts +1 -1
- package/src/shadcn/toggle/toggle-variants.ts +26 -0
- package/src/shadcn/toggle/toggle.svelte +7 -25
- 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 +2 -0
- package/src/status/pill-tint.ts +16 -0
- package/src/status/status-badge.svelte +21 -7
- package/src/status/status-dot.svelte +6 -11
- package/src/status/status-pill.svelte +40 -0
- package/src/svelte-reexports.test.ts +43 -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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import "../animations/digit-pop-in.css";
|
|
6
|
+
import { cn } from "../lib/utils.js";
|
|
7
|
+
|
|
8
|
+
// =========================
|
|
9
|
+
// PROPS
|
|
10
|
+
// =========================
|
|
11
|
+
interface Props {
|
|
12
|
+
/** The formatted value. Characters that change between renders pop in; the rest stay still. */
|
|
13
|
+
value: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
let { value, class: className }: Props = $props();
|
|
17
|
+
|
|
18
|
+
// =========================
|
|
19
|
+
// STATE
|
|
20
|
+
// =========================
|
|
21
|
+
let group = $state<HTMLElement | null>(null);
|
|
22
|
+
// Plain, not $state: it only remembers the last value painted, so writing it never re-renders.
|
|
23
|
+
let previous: string | null = null;
|
|
24
|
+
|
|
25
|
+
// =========================
|
|
26
|
+
// DERIVED
|
|
27
|
+
// =========================
|
|
28
|
+
// Only the characters that differ from the last value animate, so 1,204 to 1,205 moves one
|
|
29
|
+
// digit, not four. The stagger caps at 2 steps to keep the whole pop under the 300ms ceiling
|
|
30
|
+
// plus two stagger beats.
|
|
31
|
+
let chars = $derived.by(() => {
|
|
32
|
+
const before = previous;
|
|
33
|
+
let changed = 0;
|
|
34
|
+
return [...value].map((char, index) => {
|
|
35
|
+
const moved = before !== null && before[index] !== char;
|
|
36
|
+
return { char, moved, stagger: moved ? Math.min(changed++, 2) : 0 };
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// =========================
|
|
41
|
+
// EFFECTS
|
|
42
|
+
// =========================
|
|
43
|
+
// transitions-dev number pop-in: drop the class, force a reflow, add it back, or a second
|
|
44
|
+
// change never replays. The first paint doesn't animate: nothing changed yet.
|
|
45
|
+
$effect(() => {
|
|
46
|
+
const next = value;
|
|
47
|
+
if (!group) return;
|
|
48
|
+
if (previous !== null && previous !== next) {
|
|
49
|
+
group.classList.remove("is-animating");
|
|
50
|
+
void group.offsetWidth;
|
|
51
|
+
group.classList.add("is-animating");
|
|
52
|
+
}
|
|
53
|
+
previous = next;
|
|
54
|
+
});
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<span bind:this={group} class={cn("t-digit-group whitespace-pre tabular-nums", className)}>
|
|
58
|
+
<span class="sr-only">{value}</span>
|
|
59
|
+
<span aria-hidden="true" class="contents">
|
|
60
|
+
{#each chars as part, index (`${index}:${part.char}`)}
|
|
61
|
+
{#if part.moved}
|
|
62
|
+
<span class="t-digit" data-stagger={part.stagger}>{part.char}</span>
|
|
63
|
+
{:else}
|
|
64
|
+
<span>{part.char}</span>
|
|
65
|
+
{/if}
|
|
66
|
+
{/each}
|
|
67
|
+
</span>
|
|
68
|
+
</span>
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
<TooltipPrimitive.Root variant="none">
|
|
93
93
|
<div
|
|
94
94
|
class={cn(
|
|
95
|
-
"
|
|
95
|
+
"bg-card grid min-w-[9rem] items-start gap-1.5 rounded-lg px-2.5 py-1.5 text-xs shadow-edge-raised",
|
|
96
96
|
className,
|
|
97
97
|
)}
|
|
98
98
|
{...restProps}
|
package/src/charts/stat-card.css
CHANGED
|
@@ -6,18 +6,18 @@
|
|
|
6
6
|
flex-direction: column;
|
|
7
7
|
gap: 0.375rem;
|
|
8
8
|
padding: 1.25rem 1.5rem;
|
|
9
|
-
border:
|
|
10
|
-
border-radius:
|
|
9
|
+
border: 0;
|
|
10
|
+
border-radius: var(--radius-xl);
|
|
11
11
|
background: var(--card, white);
|
|
12
12
|
box-shadow: var(--chart-card-shadow);
|
|
13
13
|
min-width: 0;
|
|
14
14
|
/* WHY: clips the edge-to-edge chart to the card's rounded corners. */
|
|
15
15
|
overflow: hidden;
|
|
16
|
-
transition:
|
|
16
|
+
transition: box-shadow var(--chart-motion) var(--chart-ease-out);
|
|
17
17
|
}
|
|
18
18
|
@media (hover: hover) and (pointer: fine) {
|
|
19
19
|
.hr-stat-card:hover {
|
|
20
|
-
|
|
20
|
+
box-shadow: var(--chart-card-shadow-hover);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
/* WHY: a bare figure, not a card. Dropping the chrome avoids a card-in-card
|
|
@@ -105,35 +105,36 @@
|
|
|
105
105
|
title={label}
|
|
106
106
|
aria-label={announce === "name" && copied ? copiedLabel : label}
|
|
107
107
|
class={cn(
|
|
108
|
-
"group/copy t-press shrink-0 outline-none focus-visible:ring-
|
|
108
|
+
"group/copy t-press shrink-0 outline-none focus-visible:shadow-(--ring-focus)",
|
|
109
109
|
text === undefined
|
|
110
110
|
? size === "sm"
|
|
111
|
-
? "grid size-5 place-items-center rounded-
|
|
111
|
+
? "grid size-5 place-items-center rounded-sm transition-colors duration-quick ease-smooth-out hover:bg-foreground/6 motion-reduce:transition-none"
|
|
112
112
|
: buttonVariants({
|
|
113
113
|
variant: variant === "outline" ? "outline" : "ghost",
|
|
114
114
|
size: size === "md" ? "sm" : "default",
|
|
115
115
|
icon: true,
|
|
116
116
|
})
|
|
117
117
|
: inline
|
|
118
|
-
? "inline-flex cursor-copy items-baseline gap-1.5 transition-colors hover:text-foreground"
|
|
119
|
-
: "flex items-center gap-1 rounded px-1.5 py-0.5 font-mono text-xs tabular-nums text-muted-foreground
|
|
118
|
+
? "inline-flex cursor-copy items-baseline gap-1.5 transition-colors duration-quick ease-smooth-out hover:text-foreground motion-reduce:transition-none"
|
|
119
|
+
: "flex items-center gap-1 rounded-sm px-1.5 py-0.5 font-mono text-xs tabular-nums text-muted-foreground transition-colors duration-quick ease-smooth-out hover:bg-foreground/6 hover:text-foreground motion-reduce:transition-none",
|
|
120
120
|
className,
|
|
121
121
|
)}
|
|
122
122
|
>
|
|
123
123
|
<!-- One slot, two glyphs, per the transitions-dev icon-swap: the check
|
|
124
|
-
|
|
124
|
+
grows in from half size, the copy glyph only fades. Reduced motion keeps the fade. -->
|
|
125
125
|
<span
|
|
126
126
|
class={cn(
|
|
127
127
|
"relative grid place-items-center",
|
|
128
128
|
text === undefined && size !== "sm" ? "size-4" : "size-3",
|
|
129
|
-
inline &&
|
|
129
|
+
inline &&
|
|
130
|
+
"order-last self-center opacity-60 transition-opacity duration-quick ease-smooth-out group-hover/copy:opacity-100",
|
|
130
131
|
)}
|
|
131
132
|
>
|
|
132
133
|
<Check
|
|
133
134
|
class={cn(
|
|
134
|
-
"col-start-1 row-start-1 size-full transition-[opacity,transform] duration-
|
|
135
|
+
"col-start-1 row-start-1 size-full transition-[opacity,transform] duration-fast ease-smooth-out motion-reduce:transition-opacity",
|
|
135
136
|
inline ? "text-current" : "text-success-800 dark:text-success-400",
|
|
136
|
-
copied ? "scale-100 opacity-100" : "scale-50 opacity-0",
|
|
137
|
+
copied ? "scale-100 opacity-100" : "scale-50 opacity-0 motion-reduce:scale-100",
|
|
137
138
|
)}
|
|
138
139
|
weight="bold"
|
|
139
140
|
aria-hidden="true"
|
|
@@ -141,7 +142,7 @@
|
|
|
141
142
|
{#if agent}
|
|
142
143
|
<StarFour
|
|
143
144
|
class={cn(
|
|
144
|
-
"col-start-1 row-start-1 size-full text-brand/70 transition-[opacity,color] duration-
|
|
145
|
+
"col-start-1 row-start-1 size-full text-brand/70 transition-[opacity,color] duration-fast ease-smooth-out motion-reduce:transition-opacity group-hover/copy:text-brand",
|
|
145
146
|
copied ? "opacity-0" : "opacity-100",
|
|
146
147
|
)}
|
|
147
148
|
weight="bold"
|
|
@@ -150,7 +151,7 @@
|
|
|
150
151
|
{:else}
|
|
151
152
|
<Copy
|
|
152
153
|
class={cn(
|
|
153
|
-
"col-start-1 row-start-1 size-full transition-[opacity,color] duration-
|
|
154
|
+
"col-start-1 row-start-1 size-full transition-[opacity,color] duration-fast ease-smooth-out motion-reduce:transition-opacity",
|
|
154
155
|
inline ? "text-current" : "text-muted-foreground group-hover/copy:text-foreground",
|
|
155
156
|
copied ? "opacity-0" : "opacity-100",
|
|
156
157
|
)}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<!-- =========================== -->
|
|
24
24
|
<CaretRight
|
|
25
25
|
class={cn(
|
|
26
|
-
"size-3.5 text-muted-foreground opacity-0 transition-opacity group-hover/row:opacity-100",
|
|
26
|
+
"size-3.5 text-muted-foreground opacity-0 transition-opacity duration-quick ease-smooth-out group-hover/row:opacity-100",
|
|
27
27
|
className,
|
|
28
28
|
)}
|
|
29
29
|
weight="bold"
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
<HoldToConfirm
|
|
93
93
|
duration={800}
|
|
94
94
|
onConfirm={onDelete}
|
|
95
|
-
class="relative flex w-full cursor-default items-center justify-start gap-2 rounded-
|
|
95
|
+
class="relative flex w-full cursor-default items-center justify-start gap-2 rounded-lg px-1.5 py-1 text-sm text-destructive outline-hidden"
|
|
96
96
|
>
|
|
97
97
|
{#snippet idle()}
|
|
98
98
|
<Trash size={14} />
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
data-slot="table-head"
|
|
36
36
|
class={cn(
|
|
37
37
|
"text-muted-foreground h-8 px-2 text-left align-middle text-xs font-medium whitespace-nowrap select-none",
|
|
38
|
-
"cursor-pointer hover:bg-
|
|
38
|
+
"cursor-pointer transition-colors duration-quick ease-smooth-out hover:bg-foreground/4 motion-reduce:transition-none",
|
|
39
39
|
className,
|
|
40
40
|
)}
|
|
41
41
|
tabindex="0"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
{:else if direction === "desc"}
|
|
51
51
|
<CaretDown class="size-3.5 text-foreground" weight="bold" />
|
|
52
52
|
{:else}
|
|
53
|
-
<CaretUpDown class="size-3.5 text-muted-foreground
|
|
53
|
+
<CaretUpDown class="size-3.5 text-muted-foreground" />
|
|
54
54
|
{/if}
|
|
55
55
|
</span>
|
|
56
56
|
</th>
|
|
@@ -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",
|