@heyramzi/ui 2.19.2 → 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 +88 -30
- 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
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
interface Props {
|
|
17
17
|
/** Fill the row rather than sit at its natural width. */
|
|
18
18
|
fill?: boolean;
|
|
19
|
-
/** `pill`, a
|
|
19
|
+
/** `pill`, a grey track with a white thumb that slides, or `underline`, a travelling rule. */
|
|
20
20
|
variant?: SegmentedVariant;
|
|
21
21
|
class?: string;
|
|
22
22
|
children: Snippet;
|
|
@@ -64,45 +64,6 @@
|
|
|
64
64
|
<!-- STYLES -->
|
|
65
65
|
<!-- =========================== -->
|
|
66
66
|
<style>
|
|
67
|
-
/* Inset separators keep the strip one bordered object. */
|
|
68
|
-
.track :global(.ui-segment)::before {
|
|
69
|
-
content: "";
|
|
70
|
-
position: absolute;
|
|
71
|
-
top: 22%;
|
|
72
|
-
bottom: 22%;
|
|
73
|
-
left: 0;
|
|
74
|
-
width: 1px;
|
|
75
|
-
background-color: var(--color-border);
|
|
76
|
-
transition: opacity var(--duration-fast) var(--ease-smooth-out);
|
|
77
|
-
pointer-events: none;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.track :global(.pill + .ui-segment)::before,
|
|
81
|
-
.track :global([data-slot="tabs-list"] > .ui-segment:first-child)::before {
|
|
82
|
-
display: none;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/* The highlight and hover wash hide the seams on either side. */
|
|
86
|
-
.track :global(.ui-segment[data-state="active"])::before,
|
|
87
|
-
.track :global(.ui-segment[aria-current="page"])::before,
|
|
88
|
-
.track :global(.ui-segment[aria-pressed="true"])::before,
|
|
89
|
-
.track :global(.ui-segment[data-state="active"] + .ui-segment)::before,
|
|
90
|
-
.track :global(.ui-segment[aria-current="page"] + .ui-segment)::before,
|
|
91
|
-
.track :global(.ui-segment[aria-pressed="true"] + .ui-segment)::before,
|
|
92
|
-
.track
|
|
93
|
-
:global(
|
|
94
|
-
[data-slot="tabs-list"]:has(> .ui-segment:last-child[data-state="active"]) + .ui-segment
|
|
95
|
-
)::before {
|
|
96
|
-
opacity: 0;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
@media (hover: hover) and (pointer: fine) {
|
|
100
|
-
.track :global(.ui-segment:hover)::before,
|
|
101
|
-
.track :global(.ui-segment:hover + .ui-segment)::before {
|
|
102
|
-
opacity: 0;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
67
|
/* The label's colour lands with the pill, on the same fast step. */
|
|
107
68
|
.track :global(.ui-segment) {
|
|
108
69
|
transition:
|
|
@@ -122,7 +83,7 @@
|
|
|
122
83
|
[data-state="open"]
|
|
123
84
|
)
|
|
124
85
|
) {
|
|
125
|
-
background-color: color-mix(in oklch, var(--color-
|
|
86
|
+
background-color: color-mix(in oklch, var(--color-foreground) 5%, transparent);
|
|
126
87
|
}
|
|
127
88
|
}
|
|
128
89
|
|
|
@@ -134,17 +95,18 @@
|
|
|
134
95
|
z-index: 0;
|
|
135
96
|
width: 0;
|
|
136
97
|
opacity: 0;
|
|
137
|
-
/*
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
98
|
+
/* The white thumb on the grey track. Concentric: the track's control radius less its
|
|
99
|
+
2px inset. The resting button shadow lifts it; no border, so no seam to fight. */
|
|
100
|
+
border-radius: calc(var(--radius-lg) - 2px);
|
|
101
|
+
background-color: var(--color-card);
|
|
102
|
+
box-shadow: var(--elev-btn), var(--elev-hairline);
|
|
141
103
|
transform: translateX(0);
|
|
142
104
|
/* transitions-dev `16-tabs-sliding`: the fast step, 250ms. */
|
|
143
105
|
transition:
|
|
144
106
|
transform var(--duration-fast) var(--ease-smooth-out),
|
|
145
107
|
width var(--duration-fast) var(--ease-smooth-out),
|
|
146
108
|
opacity var(--duration-quick) var(--ease-smooth-out);
|
|
147
|
-
will-change: transform
|
|
109
|
+
will-change: transform;
|
|
148
110
|
pointer-events: none;
|
|
149
111
|
}
|
|
150
112
|
|
|
@@ -152,16 +114,20 @@
|
|
|
152
114
|
so a server-rendered strip is right before any JS arrives. */
|
|
153
115
|
.track:not([data-pill="on"])
|
|
154
116
|
:global(:is([data-state="active"], [aria-current="page"], [aria-pressed="true"])) {
|
|
155
|
-
background-color: var(--color-
|
|
156
|
-
box-shadow:
|
|
117
|
+
background-color: var(--color-card);
|
|
118
|
+
box-shadow: var(--elev-btn), var(--elev-hairline);
|
|
157
119
|
}
|
|
158
120
|
|
|
159
|
-
/*
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
121
|
+
/* On dark the card is the darker surface, so the thumb lifts toward the text instead. */
|
|
122
|
+
:global(.dark) .pill,
|
|
123
|
+
:global(.dark)
|
|
124
|
+
.track:not([data-pill="on"])
|
|
125
|
+
:global(:is([data-state="active"], [aria-current="page"], [aria-pressed="true"])) {
|
|
126
|
+
background-color: color-mix(in oklch, var(--color-foreground) 14%, var(--color-card));
|
|
163
127
|
}
|
|
164
128
|
|
|
129
|
+
/* The underline: no wash, and the pill becomes a 2px rule on the
|
|
130
|
+
hairline. `--segmented-rule` lets a brand colour it from a class. */
|
|
165
131
|
@media (hover: hover) {
|
|
166
132
|
.track[data-variant="underline"]
|
|
167
133
|
:global(
|
|
@@ -17,10 +17,10 @@ export const segmentedTrackClass = tv({
|
|
|
17
17
|
// WHY no size variant: `sm` drew 24px, and every tab control must share one height.
|
|
18
18
|
base: "h-7 any-pointer-coarse:h-11 shrink-0 items-stretch",
|
|
19
19
|
variants: {
|
|
20
|
-
// `pill`:
|
|
20
|
+
// `pill`: grey track, sliding white thumb. `underline`: bare
|
|
21
21
|
// labels over one hairline, the active one marked by a rule that travels.
|
|
22
22
|
variant: {
|
|
23
|
-
pill: "overflow-hidden rounded-lg
|
|
23
|
+
pill: "overflow-hidden rounded-lg bg-foreground/6 p-0.5",
|
|
24
24
|
underline: "gap-5 border-b border-border",
|
|
25
25
|
},
|
|
26
26
|
// Fill the row rather than sit at its natural width. For a narrow rail.
|
|
@@ -46,16 +46,16 @@ export const segmentedTriggerClass = tv({
|
|
|
46
46
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
47
47
|
// WHY an inset focus ring: the track clips to its rounded corners, so an
|
|
48
48
|
// outside ring was cut in half on the end segments.
|
|
49
|
-
"focus-visible:
|
|
49
|
+
"focus-visible:shadow-(--ring-focus-inset) focus-visible:outline-none",
|
|
50
50
|
// WHY no height on the trigger: an icon makes a segment taller than a word.
|
|
51
51
|
// `h-full` against the track's `items-stretch` keeps every segment the same box.
|
|
52
52
|
"h-full text-xs",
|
|
53
53
|
],
|
|
54
54
|
variants: {
|
|
55
|
-
// The pill sits concentric in the track's 2px inset; the underline has no well, so
|
|
55
|
+
// The pill sits concentric in the track's 2px inset , so 8px; the underline has no well, so
|
|
56
56
|
// no padding, and its rule is as wide as the label.
|
|
57
57
|
variant: {
|
|
58
|
-
pill: "rounded-[calc(var(--radius-lg)-
|
|
58
|
+
pill: "rounded-[calc(var(--radius-lg)-2px)] px-2.5 any-pointer-coarse:px-3.5",
|
|
59
59
|
underline: "rounded-sm px-0",
|
|
60
60
|
},
|
|
61
61
|
// WHY `min-w-fit flex-auto`, not equal fractions: fractions cut "Approved" to
|
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
/** Draws a cell its own way. Print `text` for the plain case. */
|
|
35
35
|
cell?: Snippet<[TableCell<Row>]>;
|
|
36
36
|
rowClass?: (row: Row) => string | undefined;
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Below this width the table scrolls sideways inside its frame. Defaults to the columns'
|
|
39
|
+
* own widths (8rem a text column, 5rem a number), so a phone scrolls rather than splits words.
|
|
40
|
+
* Pass "0" for a table that should squeeze.
|
|
41
|
+
*/
|
|
38
42
|
minWidth?: string;
|
|
39
|
-
/** Ignored: the panel or card under a table is its frame, so it draws none of its own. */
|
|
40
|
-
bare?: boolean;
|
|
41
43
|
class?: string;
|
|
42
44
|
/** The sorted column and its direction. Bind it; columns with `sortable` set it. */
|
|
43
45
|
sort?: SortState<Row>;
|
|
@@ -91,7 +93,7 @@
|
|
|
91
93
|
footer,
|
|
92
94
|
cell,
|
|
93
95
|
rowClass,
|
|
94
|
-
minWidth
|
|
96
|
+
minWidth,
|
|
95
97
|
class: className,
|
|
96
98
|
sort = $bindable(null),
|
|
97
99
|
rowHref,
|
|
@@ -120,6 +122,17 @@
|
|
|
120
122
|
// DERIVED
|
|
121
123
|
// =========================
|
|
122
124
|
let span = $derived(columns.length + (selectable ? 1 : 0));
|
|
125
|
+
// WHY a floor from the columns: with none, a 390px panel squeezed a text column until it broke
|
|
126
|
+
// "Handover" mid-word. A column hidden on small screens doesn't count, since it's gone there.
|
|
127
|
+
let floor = $derived.by(() => {
|
|
128
|
+
const parts = columns
|
|
129
|
+
.filter((c) => !c.hideBelow)
|
|
130
|
+
.map((c) =>
|
|
131
|
+
c.width && /^[\d.]+(rem|px)$/.test(c.width) ? c.width : c.numeric ? "5rem" : "8rem",
|
|
132
|
+
);
|
|
133
|
+
if (selectable) parts.push("2.5rem");
|
|
134
|
+
return parts.length ? `calc(${parts.join(" + ")})` : "0";
|
|
135
|
+
});
|
|
123
136
|
let selectedCount = $derived(rows.filter((row) => selected.has(rowKey(row))).length);
|
|
124
137
|
let allSelected = $derived(rows.length > 0 && selectedCount === rows.length);
|
|
125
138
|
let someSelected = $derived(selectedCount > 0 && !allSelected);
|
|
@@ -230,12 +243,15 @@
|
|
|
230
243
|
{#each columns as column (column.key)}
|
|
231
244
|
<Table.Cell
|
|
232
245
|
class={cn(
|
|
233
|
-
"align-top",
|
|
234
|
-
|
|
246
|
+
"h-8 py-1.5 align-top any-pointer-coarse:h-11",
|
|
247
|
+
// WHY break-words, not overflow-wrap anywhere: anywhere shrinks a cell's min-content to one
|
|
248
|
+
// letter, so the table split words before it scrolled. break-words only splits a word
|
|
249
|
+
// longer than the whole column, a URL.
|
|
250
|
+
column.numeric ? "text-end tabular-nums" : "whitespace-normal break-words",
|
|
235
251
|
column.muted && "text-muted-foreground",
|
|
236
252
|
column.interactive && "relative z-10",
|
|
237
253
|
column.hideBelow && HIDE[column.hideBelow],
|
|
238
|
-
column.sticky && "bg-
|
|
254
|
+
column.sticky && "bg-card sticky left-0 z-10",
|
|
239
255
|
column.class,
|
|
240
256
|
)}
|
|
241
257
|
>
|
|
@@ -280,9 +296,12 @@
|
|
|
280
296
|
onclick={onRowClick ? (event: MouseEvent) => rowClick(event, row) : undefined}
|
|
281
297
|
onkeydown={onRowClick ? (event: KeyboardEvent) => rowKeydown(event, row) : undefined}
|
|
282
298
|
class={cn(
|
|
283
|
-
|
|
299
|
+
// Rows, not a card per row (DESIGN.md rule 19): a hairline under each and a flat
|
|
300
|
+
// hover tint. The atom's row lifts; this one does not.
|
|
301
|
+
"border-b border-border/70 last:border-b-0 hover:translate-y-0 hover:bg-foreground/3 hover:shadow-none",
|
|
302
|
+
href && "relative t-press active:bg-foreground/6",
|
|
284
303
|
onRowClick &&
|
|
285
|
-
"t-press active:bg-
|
|
304
|
+
"t-press active:bg-foreground/6 cursor-pointer outline-none focus-visible:shadow-(--ring-focus-inset)",
|
|
286
305
|
rowClass?.(row),
|
|
287
306
|
)}
|
|
288
307
|
>
|
|
@@ -303,13 +322,13 @@
|
|
|
303
322
|
{href}
|
|
304
323
|
aria-label={linkName(row)}
|
|
305
324
|
onclick={onRowLink ? (event: MouseEvent) => linkClick(event, row) : undefined}
|
|
306
|
-
class="absolute inset-0 z-0 outline-none focus-visible:
|
|
325
|
+
class="absolute inset-0 z-0 outline-none focus-visible:shadow-(--ring-focus-inset)"
|
|
307
326
|
></a>
|
|
308
327
|
</td>
|
|
309
328
|
{/if}
|
|
310
329
|
</Table.Row>
|
|
311
330
|
{#if subRow && expanded?.(row)}
|
|
312
|
-
<Table.Row class="bg-
|
|
331
|
+
<Table.Row class="bg-foreground/2 hover:bg-foreground/2 hover:translate-y-0 hover:shadow-none">
|
|
313
332
|
<Table.Cell colspan={span + (rowHref ? 1 : 0)} class="whitespace-normal">
|
|
314
333
|
{@render subRow(row)}
|
|
315
334
|
</Table.Cell>
|
|
@@ -317,16 +336,16 @@
|
|
|
317
336
|
{/if}
|
|
318
337
|
{/snippet}
|
|
319
338
|
|
|
320
|
-
<!-- WHY no frame: the surface under a table is its frame, the white panel or a section card. -->
|
|
339
|
+
<!-- WHY no frame: the surface under a table is its frame, the white panel or a section card (DESIGN.md, Patterns: tables). -->
|
|
321
340
|
<div class={cn("min-w-0", className)}>
|
|
322
341
|
<Table.Root
|
|
323
342
|
aria-label={label}
|
|
324
343
|
aria-busy={loading || undefined}
|
|
325
|
-
style={`min-width: ${minWidth}`}
|
|
344
|
+
style={`min-width: ${minWidth ?? floor}`}
|
|
326
345
|
class="min-w-0"
|
|
327
346
|
>
|
|
328
|
-
<Table.Header>
|
|
329
|
-
<Table.Row>
|
|
347
|
+
<Table.Header class="bg-foreground/3">
|
|
348
|
+
<Table.Row class="hover:translate-y-0 hover:bg-transparent hover:shadow-none">
|
|
330
349
|
{#if selectable}
|
|
331
350
|
<Table.Head class="w-10">
|
|
332
351
|
<Checkbox
|
|
@@ -343,9 +362,9 @@
|
|
|
343
362
|
class={cn(
|
|
344
363
|
"whitespace-normal",
|
|
345
364
|
column.numeric && "text-end",
|
|
346
|
-
column.sortable && "h-
|
|
365
|
+
column.sortable && "h-8 p-0",
|
|
347
366
|
column.hideBelow && HIDE[column.hideBelow],
|
|
348
|
-
column.sticky && "bg-
|
|
367
|
+
column.sticky && "bg-card sticky left-0 z-20",
|
|
349
368
|
column.headClass ?? column.class,
|
|
350
369
|
)}
|
|
351
370
|
style={column.width ? `width: ${column.width}` : undefined}
|
|
@@ -356,7 +375,7 @@
|
|
|
356
375
|
<button
|
|
357
376
|
type="button"
|
|
358
377
|
class={cn(
|
|
359
|
-
"t-press hover:bg-
|
|
378
|
+
"t-press transition-colors duration-quick ease-smooth-out hover:bg-foreground/4 flex h-8 w-full items-center gap-1 px-2 text-xs font-medium outline-none focus-visible:shadow-(--ring-focus-inset)",
|
|
360
379
|
column.numeric ? "justify-end" : "justify-start",
|
|
361
380
|
)}
|
|
362
381
|
onclick={() => (sort = toggleSort(sort, column.key as keyof Row & string))}
|
|
@@ -367,7 +386,7 @@
|
|
|
367
386
|
{:else if dir === "desc"}
|
|
368
387
|
<CaretDown class="text-foreground size-3.5" weight="bold" aria-hidden="true" />
|
|
369
388
|
{:else}
|
|
370
|
-
<CaretUpDown class="text-muted-foreground
|
|
389
|
+
<CaretUpDown class="text-muted-foreground size-3.5" aria-hidden="true" />
|
|
371
390
|
{/if}
|
|
372
391
|
</button>
|
|
373
392
|
{:else if head}
|
|
@@ -392,23 +411,23 @@
|
|
|
392
411
|
<Table.Body>
|
|
393
412
|
{#if entry.key !== null}
|
|
394
413
|
<Table.Row
|
|
395
|
-
class="bg-
|
|
414
|
+
class="bg-foreground/4 hover:translate-y-0 hover:bg-foreground/4 hover:shadow-none"
|
|
396
415
|
>
|
|
397
416
|
<Table.Head
|
|
398
417
|
colspan={span + (rowHref ? 1 : 0)}
|
|
399
418
|
scope="colgroup"
|
|
400
|
-
class="text-foreground h-
|
|
419
|
+
class="text-foreground h-8 p-0"
|
|
401
420
|
>
|
|
402
421
|
{#if collapsible}
|
|
403
422
|
<button
|
|
404
423
|
type="button"
|
|
405
424
|
aria-expanded={!folded}
|
|
406
|
-
class="t-press
|
|
425
|
+
class="t-press flex h-8 w-full items-center gap-2 px-2 text-start outline-none focus-visible:shadow-(--ring-focus-inset)"
|
|
407
426
|
onclick={() => toggleGroup(tableGroup.key)}
|
|
408
427
|
>
|
|
409
428
|
<CaretDown
|
|
410
429
|
class={cn(
|
|
411
|
-
"size-3.5 shrink-0 transition-transform duration-
|
|
430
|
+
"size-3.5 shrink-0 transition-transform duration-fast ease-smooth-out motion-reduce:transition-none",
|
|
412
431
|
folded && "-rotate-90",
|
|
413
432
|
)}
|
|
414
433
|
aria-hidden="true"
|
|
@@ -416,7 +435,7 @@
|
|
|
416
435
|
{#if groupHeader}{@render groupHeader(tableGroup)}{:else}{tableGroup.key}{/if}
|
|
417
436
|
</button>
|
|
418
437
|
{:else}
|
|
419
|
-
<span class="flex h-
|
|
438
|
+
<span class="flex h-8 items-center px-2">
|
|
420
439
|
{#if groupHeader}{@render groupHeader(tableGroup)}{:else}{tableGroup.key}{/if}
|
|
421
440
|
</span>
|
|
422
441
|
{/if}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// =========================
|
|
3
3
|
// IMPORTS
|
|
4
4
|
// =========================
|
|
5
|
-
import { Plus
|
|
5
|
+
import { Plus } from "phosphor-svelte";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
|
+
import TagChip from "../chip/tag-chip.svelte";
|
|
7
8
|
import {
|
|
8
9
|
addTag,
|
|
9
10
|
canCreate,
|
|
@@ -105,9 +106,7 @@
|
|
|
105
106
|
...(creatable ? [{ kind: "create" as const, name: query.trim() }] : []),
|
|
106
107
|
],
|
|
107
108
|
);
|
|
108
|
-
const nothing = $derived(
|
|
109
|
-
!free && editing && !full && query.trim() !== "" && rows.length === 0,
|
|
110
|
-
);
|
|
109
|
+
const nothing = $derived(!free && editing && !full && query.trim() !== "" && rows.length === 0);
|
|
111
110
|
const open = $derived(editing && !full && (rows.length > 0 || nothing));
|
|
112
111
|
|
|
113
112
|
// =========================
|
|
@@ -129,8 +128,7 @@
|
|
|
129
128
|
queueMicrotask(() => input?.focus());
|
|
130
129
|
}
|
|
131
130
|
|
|
132
|
-
//
|
|
133
|
-
// Anything else is dropped unless the field is free: free text is never saved by walking away.
|
|
131
|
+
// Blur keeps typed text only when it's an existing label spelt another way, or the field is free.
|
|
134
132
|
function stop() {
|
|
135
133
|
const typed = query;
|
|
136
134
|
query = "";
|
|
@@ -219,31 +217,19 @@
|
|
|
219
217
|
|
|
220
218
|
<!-- =========================== -->
|
|
221
219
|
<!-- MARKUP -->
|
|
222
|
-
<!-- WHY: the
|
|
223
|
-
|
|
220
|
+
<!-- WHY: the chips are TagChip at --radius-sm (6px); the edit frame is --radius-lg (10px), which
|
|
221
|
+
is 6 plus its 4px of padding, so the curves stay concentric. -->
|
|
224
222
|
<!-- WHY text-base on the input: below 16px iOS zooms the page when it gets focus. -->
|
|
225
223
|
<!-- =========================== -->
|
|
226
224
|
{#snippet chip(tag: string, removable: boolean)}
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)}
|
|
225
|
+
<TagChip
|
|
226
|
+
title={tag}
|
|
227
|
+
removeLabel={text.remove(tag)}
|
|
228
|
+
removeFocusable={false}
|
|
229
|
+
onremove={removable ? () => commit(removeTag(tags, tag)) : undefined}
|
|
232
230
|
>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
<button
|
|
236
|
-
type="button"
|
|
237
|
-
tabindex="-1"
|
|
238
|
-
class="relative grid size-4 shrink-0 place-items-center rounded-(--radius-xs) text-muted-foreground transition-colors hover:bg-foreground/10 hover:text-foreground before:absolute before:-inset-1.5 before:content-['']"
|
|
239
|
-
aria-label={text.remove(tag)}
|
|
240
|
-
onmousedown={(e) => e.preventDefault()}
|
|
241
|
-
onclick={() => commit(removeTag(tags, tag))}
|
|
242
|
-
>
|
|
243
|
-
<X class="size-3" weight="bold" aria-hidden="true" />
|
|
244
|
-
</button>
|
|
245
|
-
{/if}
|
|
246
|
-
</span>
|
|
231
|
+
{tag}
|
|
232
|
+
</TagChip>
|
|
247
233
|
{/snippet}
|
|
248
234
|
|
|
249
235
|
<div
|
|
@@ -254,7 +240,7 @@
|
|
|
254
240
|
>
|
|
255
241
|
{#if editing}
|
|
256
242
|
<div
|
|
257
|
-
class="-ml-1.5 flex min-h-7 flex-wrap items-center gap-1 rounded-lg bg-
|
|
243
|
+
class="-ml-1.5 flex min-h-7 flex-wrap items-center gap-1 rounded-lg bg-card p-1 ring-1 ring-ring/60 focus-within:ring-2 focus-within:ring-ring"
|
|
258
244
|
>
|
|
259
245
|
{#each tags as tag (tag.toLowerCase())}
|
|
260
246
|
{@render chip(tag, true)}
|
|
@@ -288,7 +274,7 @@
|
|
|
288
274
|
id={listId}
|
|
289
275
|
role="listbox"
|
|
290
276
|
aria-label={label}
|
|
291
|
-
class="absolute top-full left-0 z-50 mt-1 max-h-60 w-full min-w-48 overflow-y-auto rounded-xl
|
|
277
|
+
class="absolute top-full left-0 z-50 mt-1 max-h-60 w-full min-w-48 overflow-y-auto rounded-xl bg-popover p-1 text-popover-foreground shadow-edge-raised"
|
|
292
278
|
>
|
|
293
279
|
{#each rows as row, index (row.kind + row.name)}
|
|
294
280
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
@@ -298,7 +284,7 @@
|
|
|
298
284
|
aria-selected={index === active}
|
|
299
285
|
class={cn(
|
|
300
286
|
"flex min-h-8 cursor-default items-center gap-2 rounded-lg px-2 text-sm any-pointer-coarse:min-h-11",
|
|
301
|
-
index === active && "bg-
|
|
287
|
+
index === active && "bg-foreground/6 text-foreground",
|
|
302
288
|
)}
|
|
303
289
|
onmousedown={(e) => e.preventDefault()}
|
|
304
290
|
onmousemove={() => (active = index)}
|
|
@@ -321,7 +307,7 @@
|
|
|
321
307
|
<button
|
|
322
308
|
type="button"
|
|
323
309
|
class={cn(
|
|
324
|
-
"-ml-1.5 flex min-h-7 w-full flex-wrap items-center gap-1 rounded-lg px-1.5 py-1 text-left transition-colors outline-none hover:bg-
|
|
310
|
+
"-ml-1.5 flex min-h-7 w-full flex-wrap items-center gap-1 rounded-lg px-1.5 py-1 text-left transition-colors outline-none duration-quick ease-smooth-out hover:bg-foreground/4 focus-visible:shadow-(--ring-focus) disabled:pointer-events-none motion-reduce:transition-none",
|
|
325
311
|
)}
|
|
326
312
|
aria-label={label}
|
|
327
313
|
{disabled}
|
|
@@ -31,7 +31,7 @@ export const ganttLooks = {
|
|
|
31
31
|
bar: "rounded-lg",
|
|
32
32
|
mark: "rounded-sm",
|
|
33
33
|
/** The readout that follows a dragged bar. */
|
|
34
|
-
tip: "rounded-sm bg-foreground px-2 py-1 text-
|
|
34
|
+
tip: "rounded-sm bg-foreground px-2 py-1 text-2xs font-medium whitespace-nowrap text-background tabular-nums shadow-raised",
|
|
35
35
|
/** A title that didn't fit its bar, set beside it. */
|
|
36
36
|
outsideLabel: "rounded-sm bg-card/90 px-1 text-xs text-muted-foreground",
|
|
37
37
|
track: "rounded-full",
|
package/src/editor/content.css
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
consuming Svelte components. */
|
|
4
4
|
|
|
5
5
|
.tiptap-content {
|
|
6
|
-
line-height: 1.75;
|
|
7
|
-
font-size: 1rem;
|
|
6
|
+
line-height: var(--content-leading, 1.75);
|
|
7
|
+
font-size: var(--content-size, 1rem);
|
|
8
8
|
color: var(--color-foreground);
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
.tiptap-content p {
|
|
66
|
-
margin-bottom: 1rem;
|
|
66
|
+
margin-bottom: var(--content-gap, 1rem);
|
|
67
67
|
text-wrap: pretty;
|
|
68
68
|
overflow-wrap: break-word;
|
|
69
69
|
}
|
package/src/editor/editor.svelte
CHANGED
|
@@ -379,7 +379,7 @@
|
|
|
379
379
|
.editor-wrapper {
|
|
380
380
|
position: relative;
|
|
381
381
|
border-radius: var(--radius-xl);
|
|
382
|
-
background: var(--color-
|
|
382
|
+
background: var(--color-card);
|
|
383
383
|
border: 1px solid var(--color-border);
|
|
384
384
|
transition:
|
|
385
385
|
border-color var(--duration-quick) var(--ease-smooth-out),
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
import { Button } from "../shadcn/button/index.js";
|
|
30
30
|
import { Kbd, KbdGroup } from "../shadcn/kbd/index.js";
|
|
31
31
|
import { cn } from "../lib/utils.js";
|
|
32
|
-
import
|
|
32
|
+
import StatusPill from "../status/status-pill.svelte";
|
|
33
33
|
import {
|
|
34
34
|
Thread,
|
|
35
35
|
type ThreadComment,
|
|
@@ -414,14 +414,7 @@
|
|
|
414
414
|
{#if thread.subject.snoozedUntil}
|
|
415
415
|
<BellSlash class="size-3.5 text-muted-foreground" aria-label={labels.snoozed} />
|
|
416
416
|
{:else if thread.pending.length > 0}
|
|
417
|
-
<
|
|
418
|
-
class={cn(
|
|
419
|
-
"inline-flex h-5 items-center rounded-sm border px-1.5 font-medium",
|
|
420
|
-
TONE_CLASS.warning,
|
|
421
|
-
)}
|
|
422
|
-
>
|
|
423
|
-
{labels.needsYou}
|
|
424
|
-
</span>
|
|
417
|
+
<StatusPill tone="warning">{labels.needsYou}</StatusPill>
|
|
425
418
|
{:else if thread.unread > 0}
|
|
426
419
|
<Circle
|
|
427
420
|
weight="fill"
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import type { Tone } from "../lib/tone.js";
|
|
15
15
|
import { cn } from "../lib/utils.js";
|
|
16
16
|
import StatusBadge from "../status/status-badge.svelte";
|
|
17
|
+
import PopValue from "../charts/pop-value.svelte";
|
|
17
18
|
|
|
18
19
|
// =========================
|
|
19
20
|
// PROPS
|
|
@@ -113,7 +114,8 @@
|
|
|
113
114
|
side="right"
|
|
114
115
|
bind:ref={panel}
|
|
115
116
|
tabindex={-1}
|
|
116
|
-
|
|
117
|
+
floating
|
|
118
|
+
class={cn("flex w-full flex-col gap-0 overflow-hidden p-0", WIDTH[width])}
|
|
117
119
|
{onOpenAutoFocus}
|
|
118
120
|
>
|
|
119
121
|
{#if bare}
|
|
@@ -165,11 +167,14 @@
|
|
|
165
167
|
<div class="min-w-0">
|
|
166
168
|
<dt class="text-xs text-muted-foreground">{stat.label}</dt>
|
|
167
169
|
<dd
|
|
168
|
-
class={cn(
|
|
170
|
+
class={cn(
|
|
171
|
+
"text-lg leading-tight font-semibold tabular-nums",
|
|
172
|
+
STAT_TONE[stat.tone ?? "default"],
|
|
173
|
+
)}
|
|
169
174
|
>
|
|
170
|
-
{stat.value}
|
|
175
|
+
<PopValue value={stat.value} />
|
|
171
176
|
{#if stat.sub}
|
|
172
|
-
<span class="text-xs font-normal text-muted-foreground">{stat.sub}</span>
|
|
177
|
+
<span class="ms-1 text-xs font-normal text-muted-foreground">{stat.sub}</span>
|
|
173
178
|
{/if}
|
|
174
179
|
</dd>
|
|
175
180
|
</div>
|
|
@@ -171,15 +171,16 @@
|
|
|
171
171
|
{/each}
|
|
172
172
|
</span>
|
|
173
173
|
{:else if row.tone && row.value}
|
|
174
|
-
<StatusBadge tone={row.tone} capitalize={false}
|
|
174
|
+
<StatusBadge tone={row.tone} capitalize={false} class="w-fit max-w-full"
|
|
175
|
+
>{row.value}</StatusBadge
|
|
176
|
+
>
|
|
175
177
|
{:else if row.href && row.value}
|
|
176
178
|
<a
|
|
177
179
|
href={row.href}
|
|
178
180
|
title={row.value}
|
|
179
181
|
class={cn(
|
|
180
182
|
"min-w-0 truncate text-sm font-medium underline-offset-2 hover:underline",
|
|
181
|
-
clamp &&
|
|
182
|
-
"rounded-xs focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
|
|
183
|
+
clamp && "rounded-xs focus-visible:shadow-(--ring-focus) focus-visible:outline-none",
|
|
183
184
|
)}>{row.value}</a
|
|
184
185
|
>
|
|
185
186
|
{:else if row.value}
|
|
@@ -299,12 +300,10 @@
|
|
|
299
300
|
{:else if shown.length > 0}
|
|
300
301
|
<div class={cn("flex flex-col gap-2", className)}>
|
|
301
302
|
{#if title}
|
|
302
|
-
<h2 class="text-xs font-medium
|
|
303
|
+
<h2 class="text-xs font-medium text-muted-foreground">{title}</h2>
|
|
303
304
|
{/if}
|
|
304
305
|
<!-- Labels size to their text, capped at 45%; the dt's min-w-20 is the 5rem floor. min() can't take max-content, so a min() template is dropped whole. -->
|
|
305
|
-
<dl
|
|
306
|
-
class="grid grid-cols-[fit-content(45%)_minmax(0,1fr)] items-start gap-x-4 gap-y-2.5"
|
|
307
|
-
>
|
|
306
|
+
<dl class="grid grid-cols-[fit-content(45%)_minmax(0,1fr)] items-start gap-x-4 gap-y-2.5">
|
|
308
307
|
{#each shown as row, index (row.divider ? `divider-${index}` : keyOf(row))}
|
|
309
308
|
{#if row.divider}
|
|
310
309
|
<div class="col-span-2 -my-0.5 border-t border-border/50"></div>
|
|
@@ -355,7 +354,7 @@
|
|
|
355
354
|
<Select.Trigger
|
|
356
355
|
size="sm"
|
|
357
356
|
aria-label={t.edit(row.label)}
|
|
358
|
-
class="-ml-1.5 min-h-7 h-auto w-fit max-w-full gap-1.5 border-transparent bg-transparent px-1.5 shadow-none transition-colors hover:bg-muted/60 focus-visible:ring-
|
|
357
|
+
class="-ml-1.5 min-h-7 h-auto w-fit max-w-full gap-1.5 border-transparent bg-transparent px-1.5 shadow-none transition-colors hover:bg-muted/60 focus-visible:shadow-(--ring-focus) data-[state=open]:bg-muted/60"
|
|
359
358
|
>
|
|
360
359
|
<span data-slot="select-value" class="flex min-w-0 items-center gap-1.5">
|
|
361
360
|
{@render display(row)}
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
aria-current={active ? "page" : undefined}
|
|
123
123
|
class={cn(
|
|
124
124
|
"flex flex-col gap-1.5 rounded-lg px-2 py-2 transition-colors any-pointer-coarse:py-3",
|
|
125
|
-
"outline-none focus-visible:ring-
|
|
125
|
+
"outline-none focus-visible:shadow-(--ring-focus)",
|
|
126
126
|
active ? "bg-muted" : "hover:bg-muted/60",
|
|
127
127
|
)}
|
|
128
128
|
>
|
|
@@ -183,7 +183,7 @@
|
|
|
183
183
|
</div>
|
|
184
184
|
|
|
185
185
|
<div class={narrow ? "mx-auto w-full max-w-[390px]" : "w-full"}>
|
|
186
|
-
<div class="overflow-hidden rounded-xl
|
|
186
|
+
<div class="overflow-hidden rounded-xl shadow-card">
|
|
187
187
|
{#if fixture === "project"}
|
|
188
188
|
<RecordShell
|
|
189
189
|
bind:tab
|
|
@@ -320,9 +320,7 @@
|
|
|
320
320
|
{#snippet rail()}
|
|
321
321
|
<div class="flex flex-col gap-6">
|
|
322
322
|
<section class="flex flex-col gap-2">
|
|
323
|
-
<h2 class="px-2 text-xs font-medium
|
|
324
|
-
Projects
|
|
325
|
-
</h2>
|
|
323
|
+
<h2 class="px-2 text-xs font-medium text-muted-foreground">Projects</h2>
|
|
326
324
|
<ProjectList projects={summaries} {stages} today={DEMO_TODAY} {locale} />
|
|
327
325
|
</section>
|
|
328
326
|
<FieldSection
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
<!-- =========================== -->
|
|
63
63
|
<!-- WHY a container query: the record sits in a page with its own sidebar, so the
|
|
64
64
|
viewport says nothing about how much room it has. -->
|
|
65
|
-
<div class={cn("@container flex min-w-0 flex-col bg-
|
|
65
|
+
<div class={cn("@container flex min-w-0 flex-col bg-card", className)}>
|
|
66
66
|
{@render header()}
|
|
67
67
|
{@render banner?.()}
|
|
68
68
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// IMPORTS
|
|
4
4
|
// =========================
|
|
5
5
|
import { cn } from "../lib/utils.js";
|
|
6
|
+
import PopValue from "../charts/pop-value.svelte";
|
|
6
7
|
import type { PanelStat } from "../panel/side-panel-types.js";
|
|
7
8
|
import { gutterClass } from "./gutter.js";
|
|
8
9
|
|
|
@@ -36,10 +37,15 @@
|
|
|
36
37
|
{#each stats as stat (stat.label)}
|
|
37
38
|
<div class="min-w-0">
|
|
38
39
|
<dt class="text-xs text-muted-foreground">{stat.label}</dt>
|
|
39
|
-
<dd
|
|
40
|
-
{
|
|
40
|
+
<dd
|
|
41
|
+
class={cn(
|
|
42
|
+
"text-lg leading-tight font-semibold tabular-nums",
|
|
43
|
+
TONE[stat.tone ?? "default"],
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
<PopValue value={stat.value} />
|
|
41
47
|
{#if stat.sub}
|
|
42
|
-
<span class="text-xs font-normal text-muted-foreground">{stat.sub}</span>
|
|
48
|
+
<span class="ms-1 text-xs font-normal text-muted-foreground">{stat.sub}</span>
|
|
43
49
|
{/if}
|
|
44
50
|
</dd>
|
|
45
51
|
</div>
|