@seekalfred/primitives 0.5.1 → 0.6.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.
Files changed (215) hide show
  1. package/README.md +664 -9
  2. package/dist/accordion/accordion.js +10 -4
  3. package/dist/accordion/accordion.js.map +1 -1
  4. package/dist/action-button/action-button.d.ts +50 -0
  5. package/dist/action-button/action-button.js +159 -0
  6. package/dist/action-button/action-button.js.map +1 -0
  7. package/dist/action-button/index.d.ts +2 -0
  8. package/dist/action-button/index.js +2 -0
  9. package/dist/alert/alert.d.ts +15 -1
  10. package/dist/alert/alert.js +116 -20
  11. package/dist/alert/alert.js.map +1 -1
  12. package/dist/avatar/avatar.d.ts +6 -0
  13. package/dist/avatar/avatar.js +47 -16
  14. package/dist/avatar/avatar.js.map +1 -1
  15. package/dist/badge/badge.d.ts +11 -4
  16. package/dist/badge/badge.js +35 -5
  17. package/dist/badge/badge.js.map +1 -1
  18. package/dist/breadcrumb/breadcrumb.d.ts +0 -27
  19. package/dist/breadcrumb/breadcrumb.js +20 -6
  20. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  21. package/dist/button/button.d.ts +35 -22
  22. package/dist/button/button.js +177 -34
  23. package/dist/button/button.js.map +1 -1
  24. package/dist/button-group/button-group.d.ts +76 -0
  25. package/dist/button-group/button-group.js +403 -0
  26. package/dist/button-group/button-group.js.map +1 -0
  27. package/dist/button-group/index.d.ts +2 -0
  28. package/dist/button-group/index.js +2 -0
  29. package/dist/calendar/calendar.d.ts +1 -1
  30. package/dist/calendar/calendar.js +257 -30
  31. package/dist/calendar/calendar.js.map +1 -1
  32. package/dist/card/card.d.ts +9 -2
  33. package/dist/card/card.js +15 -4
  34. package/dist/card/card.js.map +1 -1
  35. package/dist/checkbox/checkbox.d.ts +0 -7
  36. package/dist/checkbox/checkbox.js +24 -27
  37. package/dist/checkbox/checkbox.js.map +1 -1
  38. package/dist/confirm-morph/confirm-morph.d.ts +85 -0
  39. package/dist/confirm-morph/confirm-morph.js +652 -0
  40. package/dist/confirm-morph/confirm-morph.js.map +1 -0
  41. package/dist/confirm-morph/index.d.ts +2 -0
  42. package/dist/confirm-morph/index.js +2 -0
  43. package/dist/copy-button/copy-button.d.ts +32 -0
  44. package/dist/copy-button/copy-button.js +134 -0
  45. package/dist/copy-button/copy-button.js.map +1 -0
  46. package/dist/copy-button/index.d.ts +2 -0
  47. package/dist/copy-button/index.js +2 -0
  48. package/dist/date-picker/date-picker.d.ts +51 -0
  49. package/dist/date-picker/date-picker.js +241 -0
  50. package/dist/date-picker/date-picker.js.map +1 -0
  51. package/dist/date-picker/date-range-picker.d.ts +78 -0
  52. package/dist/date-picker/date-range-picker.js +1015 -0
  53. package/dist/date-picker/date-range-picker.js.map +1 -0
  54. package/dist/date-picker/index.d.ts +4 -0
  55. package/dist/date-picker/index.js +4 -0
  56. package/dist/date-picker/picker-parts.d.ts +46 -0
  57. package/dist/date-picker/picker-parts.js +135 -0
  58. package/dist/date-picker/picker-parts.js.map +1 -0
  59. package/dist/dialog/dialog.d.ts +10 -14
  60. package/dist/dialog/dialog.js +39 -11
  61. package/dist/dialog/dialog.js.map +1 -1
  62. package/dist/empty-state/empty-state.d.ts +44 -0
  63. package/dist/empty-state/empty-state.js +83 -0
  64. package/dist/empty-state/empty-state.js.map +1 -0
  65. package/dist/empty-state/index.d.ts +2 -0
  66. package/dist/empty-state/index.js +2 -0
  67. package/dist/expanding-button-group/expanding-button-group.d.ts +54 -0
  68. package/dist/expanding-button-group/expanding-button-group.js +350 -0
  69. package/dist/expanding-button-group/expanding-button-group.js.map +1 -0
  70. package/dist/expanding-button-group/index.d.ts +2 -0
  71. package/dist/expanding-button-group/index.js +2 -0
  72. package/dist/file-drop-zone/file-drop-zone.d.ts +71 -0
  73. package/dist/file-drop-zone/file-drop-zone.js +116 -0
  74. package/dist/file-drop-zone/file-drop-zone.js.map +1 -0
  75. package/dist/file-drop-zone/index.d.ts +2 -0
  76. package/dist/file-drop-zone/index.js +2 -0
  77. package/dist/filter-bar/filter-bar.d.ts +121 -0
  78. package/dist/filter-bar/filter-bar.js +300 -0
  79. package/dist/filter-bar/filter-bar.js.map +1 -0
  80. package/dist/filter-bar/index.d.ts +2 -0
  81. package/dist/filter-bar/index.js +2 -0
  82. package/dist/floating-button-group/floating-button-group.d.ts +61 -0
  83. package/dist/floating-button-group/floating-button-group.js +340 -0
  84. package/dist/floating-button-group/floating-button-group.js.map +1 -0
  85. package/dist/floating-button-group/index.d.ts +2 -0
  86. package/dist/floating-button-group/index.js +2 -0
  87. package/dist/icon-morph/icon-morph.d.ts +55 -0
  88. package/dist/icon-morph/icon-morph.js +331 -0
  89. package/dist/icon-morph/icon-morph.js.map +1 -0
  90. package/dist/icon-morph/index.d.ts +2 -0
  91. package/dist/icon-morph/index.js +2 -0
  92. package/dist/index.d.ts +24 -2
  93. package/dist/index.js +29 -7
  94. package/dist/input/input.d.ts +22 -0
  95. package/dist/input/input.js +36 -12
  96. package/dist/input/input.js.map +1 -1
  97. package/dist/lib/count-to.js +113 -0
  98. package/dist/lib/count-to.js.map +1 -0
  99. package/dist/lib/eye-morph.js +62 -0
  100. package/dist/lib/eye-morph.js.map +1 -0
  101. package/dist/lib/field-message.js +336 -0
  102. package/dist/lib/field-message.js.map +1 -0
  103. package/dist/lib/field-shell.js +81 -0
  104. package/dist/lib/field-shell.js.map +1 -0
  105. package/dist/lib/field-surface.js +4 -2
  106. package/dist/lib/field-surface.js.map +1 -1
  107. package/dist/lib/flip.js +80 -0
  108. package/dist/lib/flip.js.map +1 -0
  109. package/dist/lib/height-follow.js +59 -0
  110. package/dist/lib/height-follow.js.map +1 -0
  111. package/dist/lib/icon-swap.js +47 -0
  112. package/dist/lib/icon-swap.js.map +1 -0
  113. package/dist/lib/leaving-list.js +57 -0
  114. package/dist/lib/leaving-list.js.map +1 -0
  115. package/dist/lib/merge-refs.js +3 -3
  116. package/dist/lib/merge-refs.js.map +1 -1
  117. package/dist/lib/morph-text.js +128 -0
  118. package/dist/lib/morph-text.js.map +1 -0
  119. package/dist/lib/motion.js +162 -0
  120. package/dist/lib/motion.js.map +1 -0
  121. package/dist/lib/pausable-timeout.js +67 -0
  122. package/dist/lib/pausable-timeout.js.map +1 -0
  123. package/dist/lib/rolling-value.js +31 -0
  124. package/dist/lib/rolling-value.js.map +1 -0
  125. package/dist/lib/spring-progress.js +114 -0
  126. package/dist/lib/spring-progress.js.map +1 -0
  127. package/dist/lib/svg-id.js +10 -0
  128. package/dist/lib/svg-id.js.map +1 -0
  129. package/dist/lib/swap-text.js +77 -0
  130. package/dist/lib/swap-text.js.map +1 -0
  131. package/dist/lib/use-copy-feedback.js +36 -0
  132. package/dist/lib/use-copy-feedback.js.map +1 -0
  133. package/dist/page-header/index.d.ts +2 -0
  134. package/dist/page-header/index.js +2 -0
  135. package/dist/page-header/page-header.d.ts +75 -0
  136. package/dist/page-header/page-header.js +83 -0
  137. package/dist/page-header/page-header.js.map +1 -0
  138. package/dist/password-field/index.d.ts +2 -0
  139. package/dist/password-field/index.js +2 -0
  140. package/dist/password-field/password-field.d.ts +37 -0
  141. package/dist/password-field/password-field.js +101 -0
  142. package/dist/password-field/password-field.js.map +1 -0
  143. package/dist/password-strength/index.d.ts +2 -0
  144. package/dist/password-strength/index.js +2 -0
  145. package/dist/password-strength/password-strength.d.ts +127 -0
  146. package/dist/password-strength/password-strength.js +455 -0
  147. package/dist/password-strength/password-strength.js.map +1 -0
  148. package/dist/popover/popover.d.ts +8 -0
  149. package/dist/popover/popover.js +3 -3
  150. package/dist/popover/popover.js.map +1 -1
  151. package/dist/progress/progress.js +46 -5
  152. package/dist/progress/progress.js.map +1 -1
  153. package/dist/radio-group/radio-group.js +7 -6
  154. package/dist/radio-group/radio-group.js.map +1 -1
  155. package/dist/scroll-area/scroll-area.d.ts +10 -0
  156. package/dist/scroll-area/scroll-area.js +25 -3
  157. package/dist/scroll-area/scroll-area.js.map +1 -1
  158. package/dist/search-field/index.d.ts +2 -0
  159. package/dist/search-field/index.js +2 -0
  160. package/dist/search-field/search-field.d.ts +26 -0
  161. package/dist/search-field/search-field.js +155 -0
  162. package/dist/search-field/search-field.js.map +1 -0
  163. package/dist/select/select.js +79 -21
  164. package/dist/select/select.js.map +1 -1
  165. package/dist/sheet/sheet.js +2 -1
  166. package/dist/sheet/sheet.js.map +1 -1
  167. package/dist/side-nav/index.d.ts +2 -0
  168. package/dist/side-nav/index.js +2 -0
  169. package/dist/side-nav/side-nav.d.ts +72 -0
  170. package/dist/side-nav/side-nav.js +142 -0
  171. package/dist/side-nav/side-nav.js.map +1 -0
  172. package/dist/skeleton/skeleton.d.ts +8 -2
  173. package/dist/skeleton/skeleton.js +6 -2
  174. package/dist/skeleton/skeleton.js.map +1 -1
  175. package/dist/slider/slider.js +61 -7
  176. package/dist/slider/slider.js.map +1 -1
  177. package/dist/split-button/index.d.ts +2 -0
  178. package/dist/split-button/index.js +2 -0
  179. package/dist/split-button/split-button.d.ts +46 -0
  180. package/dist/split-button/split-button.js +122 -0
  181. package/dist/split-button/split-button.js.map +1 -0
  182. package/dist/stat-card/index.d.ts +2 -0
  183. package/dist/stat-card/index.js +2 -0
  184. package/dist/stat-card/stat-card.d.ts +81 -0
  185. package/dist/stat-card/stat-card.js +152 -0
  186. package/dist/stat-card/stat-card.js.map +1 -0
  187. package/dist/styles.css +1 -1
  188. package/dist/switch/switch.js +9 -8
  189. package/dist/switch/switch.js.map +1 -1
  190. package/dist/table/index.d.ts +2 -2
  191. package/dist/table/index.js +2 -2
  192. package/dist/table/table.d.ts +36 -5
  193. package/dist/table/table.js +112 -11
  194. package/dist/table/table.js.map +1 -1
  195. package/dist/tabs/tabs.js +142 -6
  196. package/dist/tabs/tabs.js.map +1 -1
  197. package/dist/textarea/textarea.d.ts +23 -0
  198. package/dist/textarea/textarea.js +24 -2
  199. package/dist/textarea/textarea.js.map +1 -1
  200. package/dist/theme-switch/index.d.ts +2 -0
  201. package/dist/theme-switch/index.js +2 -0
  202. package/dist/theme-switch/theme-switch.d.ts +37 -0
  203. package/dist/theme-switch/theme-switch.js +123 -0
  204. package/dist/theme-switch/theme-switch.js.map +1 -0
  205. package/dist/time-picker/index.d.ts +2 -0
  206. package/dist/time-picker/index.js +2 -0
  207. package/dist/time-picker/time-picker.d.ts +54 -0
  208. package/dist/time-picker/time-picker.js +201 -0
  209. package/dist/time-picker/time-picker.js.map +1 -0
  210. package/dist/toast/toast.js +58 -38
  211. package/dist/toast/toast.js.map +1 -1
  212. package/dist/tooltip/tooltip.d.ts +1 -1
  213. package/dist/tooltip/tooltip.js +63 -14
  214. package/dist/tooltip/tooltip.js.map +1 -1
  215. package/package.json +83 -3
@@ -0,0 +1,81 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/stat-card/stat-card.d.ts
3
+ /**
4
+ * StatCard: one headline figure with its label and a line of context. A row
5
+ * of them is a dashboard's summary strip.
6
+ *
7
+ * It exists because a summary strip is on almost every overview screen, and
8
+ * without a shared card each screen picks its own label case, number size,
9
+ * colour and height, so the same figure looks different on two pages.
10
+ *
11
+ * Three rules it holds:
12
+ *
13
+ * - The label is ONE line. A long label is cut with an ellipsis and the full
14
+ * text is on hover, so a row of cards keeps its numbers on one baseline
15
+ * instead of one card's label wrapping and pushing its number down.
16
+ * - The number's colour comes from `tone`, a fixed set of meanings, never a
17
+ * colour. `neutral` is the default and is right for any figure without a
18
+ * verdict in it, including every zero.
19
+ * - Cards in a row are the same height (`h-full` in a grid or flex row).
20
+ *
21
+ * A card can be a link (`href`, React Aria Link, so a router provider routes
22
+ * it), a button (`onPress`), or a filter toggle (`isSelected` + `onPress`, a
23
+ * React Aria ToggleButton with `aria-pressed` and one shared selected look).
24
+ *
25
+ * Every string in the cva blocks below is a STATIC literal. Tailwind scans
26
+ * source text, so a class assembled at runtime is invisible to it and would
27
+ * simply never be emitted.
28
+ */
29
+ export declare const statCardVariants: (props?: ({
30
+ selected?: boolean | null | undefined;
31
+ interactive?: boolean | null | undefined;
32
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
33
+ export declare const statCardLabelVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
34
+ export declare const statCardValueVariants: (props?: ({
35
+ tone?: "neutral" | "warning" | "positive" | "negative" | "attention" | null | undefined;
36
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
37
+ export declare const statCardSubVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
38
+ export type StatCardTone = "neutral" | "positive" | "negative" | "warning" | "attention";
39
+ export interface StatCardProps {
40
+ /** What the figure is. One line; cut with an ellipsis, full text on hover. */
41
+ label: string;
42
+ /** The figure itself, already formatted. */
43
+ value: ReactNode;
44
+ /** One line of context under the figure. */
45
+ sub?: ReactNode;
46
+ /**
47
+ * What the figure means: `neutral` (default, and right for every zero),
48
+ * `positive`, `negative`, `warning` (part-way or waiting) or `attention`
49
+ * (a person has to look).
50
+ */
51
+ tone?: StatCardTone;
52
+ /**
53
+ * The card is a filter, and this says whether it is the one applied.
54
+ * Renders a toggle button with `aria-pressed`. Pair with `onPress`.
55
+ */
56
+ isSelected?: boolean;
57
+ /** Renders the card as a link to this URL. */
58
+ href?: string;
59
+ /** Renders the card as a button (or, with `isSelected`, a toggle). */
60
+ onPress?: () => void;
61
+ /** Hover text for the whole card. */
62
+ description?: string;
63
+ /** Anything under the context line. */
64
+ children?: ReactNode;
65
+ /** Layout classes for the card (margins, grid placement). */
66
+ className?: string;
67
+ "aria-label"?: string;
68
+ }
69
+ /**
70
+ * One headline figure.
71
+ *
72
+ * @example
73
+ * <div className="grid gap-4 sm:grid-cols-3">
74
+ * <StatCard label="Open" value="$12,400" sub="18 invoices" />
75
+ * <StatCard label="Overdue" value="$3,100" sub="4 invoices" tone="negative" />
76
+ * <StatCard label="Collected" value="$9,800" tone="positive" href="/payments" />
77
+ * </div>
78
+ */
79
+ export declare const StatCard: import("react").ForwardRefExoticComponent<StatCardProps & import("react").RefAttributes<HTMLElement>>;
80
+ //#endregion
81
+ //# sourceMappingURL=stat-card.d.ts.map
@@ -0,0 +1,152 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { cva } from "class-variance-authority";
4
+ import { forwardRef } from "react";
5
+ import { Button, Link, ToggleButton } from "react-aria-components";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/stat-card/stat-card.tsx
8
+ /**
9
+ * StatCard: one headline figure with its label and a line of context. A row
10
+ * of them is a dashboard's summary strip.
11
+ *
12
+ * It exists because a summary strip is on almost every overview screen, and
13
+ * without a shared card each screen picks its own label case, number size,
14
+ * colour and height, so the same figure looks different on two pages.
15
+ *
16
+ * Three rules it holds:
17
+ *
18
+ * - The label is ONE line. A long label is cut with an ellipsis and the full
19
+ * text is on hover, so a row of cards keeps its numbers on one baseline
20
+ * instead of one card's label wrapping and pushing its number down.
21
+ * - The number's colour comes from `tone`, a fixed set of meanings, never a
22
+ * colour. `neutral` is the default and is right for any figure without a
23
+ * verdict in it, including every zero.
24
+ * - Cards in a row are the same height (`h-full` in a grid or flex row).
25
+ *
26
+ * A card can be a link (`href`, React Aria Link, so a router provider routes
27
+ * it), a button (`onPress`), or a filter toggle (`isSelected` + `onPress`, a
28
+ * React Aria ToggleButton with `aria-pressed` and one shared selected look).
29
+ *
30
+ * Every string in the cva blocks below is a STATIC literal. Tailwind scans
31
+ * source text, so a class assembled at runtime is invisible to it and would
32
+ * simply never be emitted.
33
+ */
34
+ const statCardVariants = cva([
35
+ "flex h-full min-w-0 flex-col p-4 text-start",
36
+ "rounded-lg border-(length:--border-width) bg-card text-card-foreground",
37
+ "box-border font-sans"
38
+ ], {
39
+ variants: {
40
+ /** A filter card that is the one applied. */
41
+ selected: {
42
+ true: "border-primary-strong",
43
+ false: "border-border"
44
+ },
45
+ /** Whether the card is a link or a button (hover and focus states). */
46
+ interactive: {
47
+ true: [
48
+ "cursor-pointer outline-none",
49
+ "data-[hovered]:not-data-[selected]:border-muted-foreground",
50
+ "data-[focus-visible]:outline-solid",
51
+ "data-[focus-visible]:outline-(length:--focus-ring-width)",
52
+ "data-[focus-visible]:outline-offset-(--focus-ring-offset)",
53
+ "data-[focus-visible]:outline-(--color-ring)"
54
+ ],
55
+ false: ""
56
+ }
57
+ },
58
+ defaultVariants: {
59
+ selected: false,
60
+ interactive: false
61
+ }
62
+ });
63
+ const statCardLabelVariants = cva(["m-0 truncate text-2xs leading-(--leading-normal) font-medium tracking-wide uppercase", "text-muted-foreground"]);
64
+ const statCardValueVariants = cva("m-0 mt-1 text-h4 leading-(--leading-snug) font-semibold tabular-nums break-words", {
65
+ variants: {
66
+ /** What the number MEANS. Colour follows from it; it is never picked directly. */
67
+ tone: {
68
+ neutral: "text-foreground",
69
+ positive: "text-success-strong",
70
+ negative: "text-destructive-strong",
71
+ warning: "text-warning-strong",
72
+ attention: "text-primary-strong"
73
+ } },
74
+ defaultVariants: { tone: "neutral" }
75
+ });
76
+ const statCardSubVariants = cva("m-0 mt-1 text-xs leading-(--leading-normal) text-muted-foreground");
77
+ /**
78
+ * One headline figure.
79
+ *
80
+ * @example
81
+ * <div className="grid gap-4 sm:grid-cols-3">
82
+ * <StatCard label="Open" value="$12,400" sub="18 invoices" />
83
+ * <StatCard label="Overdue" value="$3,100" sub="4 invoices" tone="negative" />
84
+ * <StatCard label="Collected" value="$9,800" tone="positive" href="/payments" />
85
+ * </div>
86
+ */
87
+ const StatCard = forwardRef(function StatCard({ label, value, sub, tone, isSelected, href, onPress, description, children, className, ...rest }, ref) {
88
+ const body = /* @__PURE__ */ jsxs("span", {
89
+ className: "flex min-w-0 flex-1 flex-col",
90
+ title: description,
91
+ children: [
92
+ /* @__PURE__ */ jsx("span", {
93
+ "data-slot": "stat-card-label",
94
+ className: cn(statCardLabelVariants(), "block"),
95
+ title: label,
96
+ children: label
97
+ }),
98
+ /* @__PURE__ */ jsx("span", {
99
+ "data-slot": "stat-card-value",
100
+ "data-tone": tone ?? "neutral",
101
+ className: cn(statCardValueVariants({ tone }), "block"),
102
+ children: value
103
+ }),
104
+ sub === void 0 ? null : /* @__PURE__ */ jsx("span", {
105
+ "data-slot": "stat-card-sub",
106
+ className: cn(statCardSubVariants(), "block"),
107
+ children: sub
108
+ }),
109
+ children
110
+ ]
111
+ });
112
+ const classes = cn(statCardVariants({
113
+ selected: isSelected === true,
114
+ interactive: isSelected !== void 0 || onPress !== void 0 || href !== void 0
115
+ }), className);
116
+ const common = {
117
+ "data-slot": "stat-card",
118
+ "aria-label": rest["aria-label"]
119
+ };
120
+ if (isSelected !== void 0) return /* @__PURE__ */ jsx(ToggleButton, {
121
+ ref,
122
+ ...common,
123
+ isSelected,
124
+ onChange: () => onPress?.(),
125
+ className: classes,
126
+ children: body
127
+ });
128
+ if (onPress) return /* @__PURE__ */ jsx(Button, {
129
+ ref,
130
+ ...common,
131
+ onPress,
132
+ className: classes,
133
+ children: body
134
+ });
135
+ if (href !== void 0) return /* @__PURE__ */ jsx(Link, {
136
+ ref,
137
+ ...common,
138
+ href,
139
+ className: classes,
140
+ children: body
141
+ });
142
+ return /* @__PURE__ */ jsx("div", {
143
+ ref,
144
+ ...common,
145
+ className: classes,
146
+ children: body
147
+ });
148
+ });
149
+ //#endregion
150
+ export { StatCard, statCardLabelVariants, statCardSubVariants, statCardValueVariants, statCardVariants };
151
+
152
+ //# sourceMappingURL=stat-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stat-card.js","names":["AriaToggleButton","AriaButton","AriaLink"],"sources":["../../src/stat-card/stat-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode, type Ref } from \"react\";\nimport {\n Button as AriaButton,\n Link as AriaLink,\n ToggleButton as AriaToggleButton,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * StatCard: one headline figure with its label and a line of context. A row\n * of them is a dashboard's summary strip.\n *\n * It exists because a summary strip is on almost every overview screen, and\n * without a shared card each screen picks its own label case, number size,\n * colour and height, so the same figure looks different on two pages.\n *\n * Three rules it holds:\n *\n * - The label is ONE line. A long label is cut with an ellipsis and the full\n * text is on hover, so a row of cards keeps its numbers on one baseline\n * instead of one card's label wrapping and pushing its number down.\n * - The number's colour comes from `tone`, a fixed set of meanings, never a\n * colour. `neutral` is the default and is right for any figure without a\n * verdict in it, including every zero.\n * - Cards in a row are the same height (`h-full` in a grid or flex row).\n *\n * A card can be a link (`href`, React Aria Link, so a router provider routes\n * it), a button (`onPress`), or a filter toggle (`isSelected` + `onPress`, a\n * React Aria ToggleButton with `aria-pressed` and one shared selected look).\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const statCardVariants = cva(\n [\n \"flex h-full min-w-0 flex-col p-4 text-start\",\n \"rounded-lg border-(length:--border-width) bg-card text-card-foreground\",\n // box-border and font-sans are not boilerplate: the reset in\n // @seekalfred/styles is opt-in, so a consumer may load none.\n \"box-border font-sans\",\n ],\n {\n variants: {\n /** A filter card that is the one applied. */\n selected: {\n true: \"border-primary-strong\",\n false: \"border-border\",\n },\n /** Whether the card is a link or a button (hover and focus states). */\n interactive: {\n true: [\n \"cursor-pointer outline-none\",\n // A border, not a fill: a muted fill under the muted label fails contrast.\n \"data-[hovered]:not-data-[selected]:border-muted-foreground\",\n // outline-solid on the focus variant is load-bearing: outline-none\n // sets the outline style the width utility resolves from.\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n ],\n false: \"\",\n },\n },\n defaultVariants: { selected: false, interactive: false },\n },\n);\n\nexport const statCardLabelVariants = cva([\n \"m-0 truncate text-2xs leading-(--leading-normal) font-medium tracking-wide uppercase\",\n \"text-muted-foreground\",\n]);\n\nexport const statCardValueVariants = cva(\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"m-0 mt-1 text-h4 leading-(--leading-snug) font-semibold tabular-nums break-words\",\n {\n variants: {\n /** What the number MEANS. Colour follows from it; it is never picked directly. */\n tone: {\n neutral: \"text-foreground\",\n positive: \"text-success-strong\",\n negative: \"text-destructive-strong\",\n warning: \"text-warning-strong\",\n attention: \"text-primary-strong\",\n },\n },\n defaultVariants: { tone: \"neutral\" },\n },\n);\n\nexport const statCardSubVariants = cva(\n \"m-0 mt-1 text-xs leading-(--leading-normal) text-muted-foreground\",\n);\n\nexport type StatCardTone = \"neutral\" | \"positive\" | \"negative\" | \"warning\" | \"attention\";\n\nexport interface StatCardProps {\n /** What the figure is. One line; cut with an ellipsis, full text on hover. */\n label: string;\n /** The figure itself, already formatted. */\n value: ReactNode;\n /** One line of context under the figure. */\n sub?: ReactNode;\n /**\n * What the figure means: `neutral` (default, and right for every zero),\n * `positive`, `negative`, `warning` (part-way or waiting) or `attention`\n * (a person has to look).\n */\n tone?: StatCardTone;\n /**\n * The card is a filter, and this says whether it is the one applied.\n * Renders a toggle button with `aria-pressed`. Pair with `onPress`.\n */\n isSelected?: boolean;\n /** Renders the card as a link to this URL. */\n href?: string;\n /** Renders the card as a button (or, with `isSelected`, a toggle). */\n onPress?: () => void;\n /** Hover text for the whole card. */\n description?: string;\n /** Anything under the context line. */\n children?: ReactNode;\n /** Layout classes for the card (margins, grid placement). */\n className?: string;\n \"aria-label\"?: string;\n}\n\n/**\n * One headline figure.\n *\n * @example\n * <div className=\"grid gap-4 sm:grid-cols-3\">\n * <StatCard label=\"Open\" value=\"$12,400\" sub=\"18 invoices\" />\n * <StatCard label=\"Overdue\" value=\"$3,100\" sub=\"4 invoices\" tone=\"negative\" />\n * <StatCard label=\"Collected\" value=\"$9,800\" tone=\"positive\" href=\"/payments\" />\n * </div>\n */\nexport const StatCard = forwardRef<HTMLElement, StatCardProps>(function StatCard(\n { label, value, sub, tone, isSelected, href, onPress, description, children, className, ...rest },\n ref,\n) {\n const body = (\n // The hover text sits on a box that fills the card: React Aria's Button\n // does not pass a `title` through to the element it renders.\n <span className=\"flex min-w-0 flex-1 flex-col\" title={description}>\n <span\n data-slot=\"stat-card-label\"\n className={cn(statCardLabelVariants(), \"block\")}\n title={label}\n >\n {label}\n </span>\n <span\n data-slot=\"stat-card-value\"\n data-tone={tone ?? \"neutral\"}\n className={cn(statCardValueVariants({ tone }), \"block\")}\n >\n {value}\n </span>\n {sub === undefined ? null : (\n <span data-slot=\"stat-card-sub\" className={cn(statCardSubVariants(), \"block\")}>\n {sub}\n </span>\n )}\n {children}\n </span>\n );\n const interactive = isSelected !== undefined || onPress !== undefined || href !== undefined;\n const classes = cn(statCardVariants({ selected: isSelected === true, interactive }), className);\n const common = { \"data-slot\": \"stat-card\", \"aria-label\": rest[\"aria-label\"] };\n\n if (isSelected !== undefined) {\n return (\n <AriaToggleButton\n ref={ref as Ref<HTMLButtonElement>}\n {...common}\n isSelected={isSelected}\n onChange={() => onPress?.()}\n className={classes}\n >\n {body}\n </AriaToggleButton>\n );\n }\n if (onPress) {\n return (\n <AriaButton\n ref={ref as Ref<HTMLButtonElement>}\n {...common}\n onPress={onPress}\n className={classes}\n >\n {body}\n </AriaButton>\n );\n }\n if (href !== undefined) {\n return (\n <AriaLink ref={ref as Ref<HTMLAnchorElement>} {...common} href={href} className={classes}>\n {body}\n </AriaLink>\n );\n }\n return (\n <div ref={ref as Ref<HTMLDivElement>} {...common} className={classes}>\n {body}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,MAAa,mBAAmB,IAC9B;CACE;CACA;CAGA;AACF,GACA;CACE,UAAU;;EAER,UAAU;GACR,MAAM;GACN,OAAO;EACT;;EAEA,aAAa;GACX,MAAM;IACJ;IAEA;IAGA;IACA;IACA;IACA;GACF;GACA,OAAO;EACT;CACF;CACA,iBAAiB;EAAE,UAAU;EAAO,aAAa;CAAM;AACzD,CACF;AAEA,MAAa,wBAAwB,IAAI,CACvC,wFACA,uBACF,CAAC;AAED,MAAa,wBAAwB,IAGnC,oFACA;CACE,UAAU;;AAER,MAAM;EACJ,SAAS;EACT,UAAU;EACV,UAAU;EACV,SAAS;EACT,WAAW;CACb,EACF;CACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CACF;AAEA,MAAa,sBAAsB,IACjC,mEACF;;;;;;;;;;;AA6CA,MAAa,WAAW,WAAuC,SAAS,SACtE,EAAE,OAAO,OAAO,KAAK,MAAM,YAAY,MAAM,SAAS,aAAa,UAAU,WAAW,GAAG,QAC3F,KACA;CACA,MAAM,OAGJ,qBAAC,QAAD;EAAM,WAAU;EAA+B,OAAO;EAAtD,UAAA;GACE,oBAAC,QAAD;IACE,aAAU;IACV,WAAW,GAAG,sBAAsB,GAAG,OAAO;IAC9C,OAAO;IAEN,UAAA;GACG,CAAA;GACN,oBAAC,QAAD;IACE,aAAU;IACV,aAAW,QAAQ;IACnB,WAAW,GAAG,sBAAsB,EAAE,KAAK,CAAC,GAAG,OAAO;IAErD,UAAA;GACG,CAAA;GACL,QAAQ,KAAA,IAAY,OACnB,oBAAC,QAAD;IAAM,aAAU;IAAgB,WAAW,GAAG,oBAAoB,GAAG,OAAO;IACzE,UAAA;GACG,CAAA;GAEP;EACG;;CAGR,MAAM,UAAU,GAAG,iBAAiB;EAAE,UAAU,eAAe;EAAM,aADjD,eAAe,KAAA,KAAa,YAAY,KAAA,KAAa,SAAS,KAAA;CACD,CAAC,GAAG,SAAS;CAC9F,MAAM,SAAS;EAAE,aAAa;EAAa,cAAc,KAAK;CAAc;CAE5E,IAAI,eAAe,KAAA,GACjB,OACE,oBAACA,cAAD;EACO;EACL,GAAI;EACQ;EACZ,gBAAgB,UAAU;EAC1B,WAAW;EAEV,UAAA;CACe,CAAA;CAGtB,IAAI,SACF,OACE,oBAACC,QAAD;EACO;EACL,GAAI;EACK;EACT,WAAW;EAEV,UAAA;CACS,CAAA;CAGhB,IAAI,SAAS,KAAA,GACX,OACE,oBAACC,MAAD;EAAe;EAA+B,GAAI;EAAc;EAAM,WAAW;EAC9E,UAAA;CACO,CAAA;CAGd,OACE,oBAAC,OAAD;EAAU;EAA4B,GAAI;EAAQ,WAAW;EAC1D,UAAA;CACE,CAAA;AAET,CAAC"}