@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.
- package/README.md +664 -9
- package/dist/accordion/accordion.js +10 -4
- package/dist/accordion/accordion.js.map +1 -1
- package/dist/action-button/action-button.d.ts +50 -0
- package/dist/action-button/action-button.js +159 -0
- package/dist/action-button/action-button.js.map +1 -0
- package/dist/action-button/index.d.ts +2 -0
- package/dist/action-button/index.js +2 -0
- package/dist/alert/alert.d.ts +15 -1
- package/dist/alert/alert.js +116 -20
- package/dist/alert/alert.js.map +1 -1
- package/dist/avatar/avatar.d.ts +6 -0
- package/dist/avatar/avatar.js +47 -16
- package/dist/avatar/avatar.js.map +1 -1
- package/dist/badge/badge.d.ts +11 -4
- package/dist/badge/badge.js +35 -5
- package/dist/badge/badge.js.map +1 -1
- package/dist/breadcrumb/breadcrumb.d.ts +0 -27
- package/dist/breadcrumb/breadcrumb.js +20 -6
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/button/button.d.ts +35 -22
- package/dist/button/button.js +177 -34
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.d.ts +76 -0
- package/dist/button-group/button-group.js +403 -0
- package/dist/button-group/button-group.js.map +1 -0
- package/dist/button-group/index.d.ts +2 -0
- package/dist/button-group/index.js +2 -0
- package/dist/calendar/calendar.d.ts +1 -1
- package/dist/calendar/calendar.js +257 -30
- package/dist/calendar/calendar.js.map +1 -1
- package/dist/card/card.d.ts +9 -2
- package/dist/card/card.js +15 -4
- package/dist/card/card.js.map +1 -1
- package/dist/checkbox/checkbox.d.ts +0 -7
- package/dist/checkbox/checkbox.js +24 -27
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/confirm-morph/confirm-morph.d.ts +85 -0
- package/dist/confirm-morph/confirm-morph.js +652 -0
- package/dist/confirm-morph/confirm-morph.js.map +1 -0
- package/dist/confirm-morph/index.d.ts +2 -0
- package/dist/confirm-morph/index.js +2 -0
- package/dist/copy-button/copy-button.d.ts +32 -0
- package/dist/copy-button/copy-button.js +134 -0
- package/dist/copy-button/copy-button.js.map +1 -0
- package/dist/copy-button/index.d.ts +2 -0
- package/dist/copy-button/index.js +2 -0
- package/dist/date-picker/date-picker.d.ts +51 -0
- package/dist/date-picker/date-picker.js +241 -0
- package/dist/date-picker/date-picker.js.map +1 -0
- package/dist/date-picker/date-range-picker.d.ts +78 -0
- package/dist/date-picker/date-range-picker.js +1015 -0
- package/dist/date-picker/date-range-picker.js.map +1 -0
- package/dist/date-picker/index.d.ts +4 -0
- package/dist/date-picker/index.js +4 -0
- package/dist/date-picker/picker-parts.d.ts +46 -0
- package/dist/date-picker/picker-parts.js +135 -0
- package/dist/date-picker/picker-parts.js.map +1 -0
- package/dist/dialog/dialog.d.ts +10 -14
- package/dist/dialog/dialog.js +39 -11
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/empty-state/empty-state.d.ts +44 -0
- package/dist/empty-state/empty-state.js +83 -0
- package/dist/empty-state/empty-state.js.map +1 -0
- package/dist/empty-state/index.d.ts +2 -0
- package/dist/empty-state/index.js +2 -0
- package/dist/expanding-button-group/expanding-button-group.d.ts +54 -0
- package/dist/expanding-button-group/expanding-button-group.js +350 -0
- package/dist/expanding-button-group/expanding-button-group.js.map +1 -0
- package/dist/expanding-button-group/index.d.ts +2 -0
- package/dist/expanding-button-group/index.js +2 -0
- package/dist/file-drop-zone/file-drop-zone.d.ts +71 -0
- package/dist/file-drop-zone/file-drop-zone.js +116 -0
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -0
- package/dist/file-drop-zone/index.d.ts +2 -0
- package/dist/file-drop-zone/index.js +2 -0
- package/dist/filter-bar/filter-bar.d.ts +121 -0
- package/dist/filter-bar/filter-bar.js +300 -0
- package/dist/filter-bar/filter-bar.js.map +1 -0
- package/dist/filter-bar/index.d.ts +2 -0
- package/dist/filter-bar/index.js +2 -0
- package/dist/floating-button-group/floating-button-group.d.ts +61 -0
- package/dist/floating-button-group/floating-button-group.js +340 -0
- package/dist/floating-button-group/floating-button-group.js.map +1 -0
- package/dist/floating-button-group/index.d.ts +2 -0
- package/dist/floating-button-group/index.js +2 -0
- package/dist/icon-morph/icon-morph.d.ts +55 -0
- package/dist/icon-morph/icon-morph.js +331 -0
- package/dist/icon-morph/icon-morph.js.map +1 -0
- package/dist/icon-morph/index.d.ts +2 -0
- package/dist/icon-morph/index.js +2 -0
- package/dist/index.d.ts +24 -2
- package/dist/index.js +29 -7
- package/dist/input/input.d.ts +22 -0
- package/dist/input/input.js +36 -12
- package/dist/input/input.js.map +1 -1
- package/dist/lib/count-to.js +113 -0
- package/dist/lib/count-to.js.map +1 -0
- package/dist/lib/eye-morph.js +62 -0
- package/dist/lib/eye-morph.js.map +1 -0
- package/dist/lib/field-message.js +336 -0
- package/dist/lib/field-message.js.map +1 -0
- package/dist/lib/field-shell.js +81 -0
- package/dist/lib/field-shell.js.map +1 -0
- package/dist/lib/field-surface.js +4 -2
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flip.js +80 -0
- package/dist/lib/flip.js.map +1 -0
- package/dist/lib/height-follow.js +59 -0
- package/dist/lib/height-follow.js.map +1 -0
- package/dist/lib/icon-swap.js +47 -0
- package/dist/lib/icon-swap.js.map +1 -0
- package/dist/lib/leaving-list.js +57 -0
- package/dist/lib/leaving-list.js.map +1 -0
- package/dist/lib/merge-refs.js +3 -3
- package/dist/lib/merge-refs.js.map +1 -1
- package/dist/lib/morph-text.js +128 -0
- package/dist/lib/morph-text.js.map +1 -0
- package/dist/lib/motion.js +162 -0
- package/dist/lib/motion.js.map +1 -0
- package/dist/lib/pausable-timeout.js +67 -0
- package/dist/lib/pausable-timeout.js.map +1 -0
- package/dist/lib/rolling-value.js +31 -0
- package/dist/lib/rolling-value.js.map +1 -0
- package/dist/lib/spring-progress.js +114 -0
- package/dist/lib/spring-progress.js.map +1 -0
- package/dist/lib/svg-id.js +10 -0
- package/dist/lib/svg-id.js.map +1 -0
- package/dist/lib/swap-text.js +77 -0
- package/dist/lib/swap-text.js.map +1 -0
- package/dist/lib/use-copy-feedback.js +36 -0
- package/dist/lib/use-copy-feedback.js.map +1 -0
- package/dist/page-header/index.d.ts +2 -0
- package/dist/page-header/index.js +2 -0
- package/dist/page-header/page-header.d.ts +75 -0
- package/dist/page-header/page-header.js +83 -0
- package/dist/page-header/page-header.js.map +1 -0
- package/dist/password-field/index.d.ts +2 -0
- package/dist/password-field/index.js +2 -0
- package/dist/password-field/password-field.d.ts +37 -0
- package/dist/password-field/password-field.js +101 -0
- package/dist/password-field/password-field.js.map +1 -0
- package/dist/password-strength/index.d.ts +2 -0
- package/dist/password-strength/index.js +2 -0
- package/dist/password-strength/password-strength.d.ts +127 -0
- package/dist/password-strength/password-strength.js +455 -0
- package/dist/password-strength/password-strength.js.map +1 -0
- package/dist/popover/popover.d.ts +8 -0
- package/dist/popover/popover.js +3 -3
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +46 -5
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/radio-group.js +7 -6
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/scroll-area/scroll-area.d.ts +10 -0
- package/dist/scroll-area/scroll-area.js +25 -3
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/search-field/index.d.ts +2 -0
- package/dist/search-field/index.js +2 -0
- package/dist/search-field/search-field.d.ts +26 -0
- package/dist/search-field/search-field.js +155 -0
- package/dist/search-field/search-field.js.map +1 -0
- package/dist/select/select.js +79 -21
- package/dist/select/select.js.map +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/side-nav/index.d.ts +2 -0
- package/dist/side-nav/index.js +2 -0
- package/dist/side-nav/side-nav.d.ts +72 -0
- package/dist/side-nav/side-nav.js +142 -0
- package/dist/side-nav/side-nav.js.map +1 -0
- package/dist/skeleton/skeleton.d.ts +8 -2
- package/dist/skeleton/skeleton.js +6 -2
- package/dist/skeleton/skeleton.js.map +1 -1
- package/dist/slider/slider.js +61 -7
- package/dist/slider/slider.js.map +1 -1
- package/dist/split-button/index.d.ts +2 -0
- package/dist/split-button/index.js +2 -0
- package/dist/split-button/split-button.d.ts +46 -0
- package/dist/split-button/split-button.js +122 -0
- package/dist/split-button/split-button.js.map +1 -0
- package/dist/stat-card/index.d.ts +2 -0
- package/dist/stat-card/index.js +2 -0
- package/dist/stat-card/stat-card.d.ts +81 -0
- package/dist/stat-card/stat-card.js +152 -0
- package/dist/stat-card/stat-card.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/switch/switch.js +9 -8
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +36 -5
- package/dist/table/table.js +112 -11
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +142 -6
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/textarea/textarea.d.ts +23 -0
- package/dist/textarea/textarea.js +24 -2
- package/dist/textarea/textarea.js.map +1 -1
- package/dist/theme-switch/index.d.ts +2 -0
- package/dist/theme-switch/index.js +2 -0
- package/dist/theme-switch/theme-switch.d.ts +37 -0
- package/dist/theme-switch/theme-switch.js +123 -0
- package/dist/theme-switch/theme-switch.js.map +1 -0
- package/dist/time-picker/index.d.ts +2 -0
- package/dist/time-picker/index.js +2 -0
- package/dist/time-picker/time-picker.d.ts +54 -0
- package/dist/time-picker/time-picker.js +201 -0
- package/dist/time-picker/time-picker.js.map +1 -0
- package/dist/toast/toast.js +58 -38
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +1 -1
- package/dist/tooltip/tooltip.js +63 -14
- package/dist/tooltip/tooltip.js.map +1 -1
- 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"}
|