@seekalfred/primitives 0.7.1 → 0.7.4
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 +255 -5
- package/dist/alert/alert.d.ts +49 -4
- package/dist/alert/alert.js +66 -38
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert/index.d.ts +2 -2
- package/dist/alert/index.js +2 -2
- package/dist/alert-dialog/alert-dialog.d.ts +31 -4
- package/dist/alert-dialog/alert-dialog.js +75 -26
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/announcement-bar/announcement-bar.d.ts +15 -4
- package/dist/announcement-bar/announcement-bar.js +31 -6
- package/dist/announcement-bar/announcement-bar.js.map +1 -1
- package/dist/avatar/avatar.d.ts +7 -0
- package/dist/avatar/avatar.js +33 -12
- package/dist/avatar/avatar.js.map +1 -1
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart-series.d.ts +75 -0
- package/dist/bar-chart/bar-chart-series.js +401 -0
- package/dist/bar-chart/bar-chart-series.js.map +1 -0
- package/dist/bar-chart/bar-chart.d.ts +25 -1
- package/dist/bar-chart/bar-chart.js +26 -1
- package/dist/bar-chart/bar-chart.js.map +1 -1
- package/dist/bar-chart/index.d.ts +2 -1
- package/dist/button/button.d.ts +1 -1
- package/dist/calendar/calendar.d.ts +1 -1
- package/dist/card/card.d.ts +22 -1
- package/dist/card/card.js +60 -7
- package/dist/card/card.js.map +1 -1
- package/dist/carousel/carousel.d.ts +21 -0
- package/dist/carousel/carousel.js +34 -8
- package/dist/carousel/carousel.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +11 -0
- package/dist/chip-group/chip-group.js +50 -34
- package/dist/chip-group/chip-group.js.map +1 -1
- package/dist/chip-group/index.d.ts +2 -2
- package/dist/copy-button/copy-button.d.ts +37 -5
- package/dist/copy-button/copy-button.js +35 -11
- package/dist/copy-button/copy-button.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +22 -0
- package/dist/donut-chart/donut-chart.js +35 -18
- package/dist/donut-chart/donut-chart.js.map +1 -1
- package/dist/donut-chart/index.d.ts +2 -2
- package/dist/empty-state/empty-state.d.ts +21 -0
- package/dist/empty-state/empty-state.js +16 -4
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.d.ts +29 -3
- package/dist/file-drop-zone/file-drop-zone.js +26 -8
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +4 -1
- package/dist/filter-bar/filter-bar.js +53 -9
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +18 -1
- package/dist/gauge/gauge.js +35 -21
- package/dist/gauge/gauge.js.map +1 -1
- package/dist/gauge/index.d.ts +2 -2
- package/dist/index.d.ts +15 -14
- package/dist/index.js +9 -9
- package/dist/input/input.d.ts +7 -0
- package/dist/input/input.js +7 -5
- package/dist/input/input.js.map +1 -1
- package/dist/lib/field-shell.js +6 -3
- package/dist/lib/field-shell.js.map +1 -1
- package/dist/lib/field-surface.js +22 -1
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/required-marker.js +20 -0
- package/dist/lib/required-marker.js.map +1 -0
- package/dist/lib/use-copy-feedback.js +17 -3
- package/dist/lib/use-copy-feedback.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -2
- package/dist/line-chart/line-chart.d.ts +144 -2
- package/dist/line-chart/line-chart.js +314 -46
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -2
- package/dist/money-input/money-input.d.ts +36 -1
- package/dist/money-input/money-input.js +30 -14
- package/dist/money-input/money-input.js.map +1 -1
- package/dist/morph-select/morph-select.d.ts +26 -1
- package/dist/morph-select/morph-select.js +28 -6
- package/dist/morph-select/morph-select.js.map +1 -1
- package/dist/multi-select/index.d.ts +2 -2
- package/dist/multi-select/index.js +2 -2
- package/dist/multi-select/multi-select.d.ts +74 -2
- package/dist/multi-select/multi-select.js +213 -39
- package/dist/multi-select/multi-select.js.map +1 -1
- package/dist/password-field/password-field.d.ts +16 -0
- package/dist/password-field/password-field.js +37 -8
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/popover/index.d.ts +2 -2
- package/dist/popover/index.js +2 -2
- package/dist/popover/popover.d.ts +39 -2
- package/dist/popover/popover.js +29 -2
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/index.d.ts +2 -2
- package/dist/progress/index.js +2 -2
- package/dist/progress/progress.d.ts +68 -3
- package/dist/progress/progress.js +156 -39
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/index.d.ts +2 -2
- package/dist/radio-group/index.js +2 -2
- package/dist/radio-group/radio-group.d.ts +33 -3
- package/dist/radio-group/radio-group.js +109 -9
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/scroll-area/scroll-area.js +4 -3
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/scrub-input.d.ts +11 -1
- package/dist/scrub-input/scrub-input.js +22 -10
- package/dist/scrub-input/scrub-input.js.map +1 -1
- package/dist/search-field/search-field.d.ts +22 -1
- package/dist/search-field/search-field.js +22 -7
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +10 -1
- package/dist/select/select.js +11 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +21 -9
- package/dist/separator/separator.js +33 -11
- package/dist/separator/separator.js.map +1 -1
- package/dist/sparkline/sparkline.d.ts +27 -2
- package/dist/sparkline/sparkline.js +115 -18
- package/dist/sparkline/sparkline.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +65 -1
- package/dist/stat-card/stat-card.js +98 -25
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +115 -5
- package/dist/table/table.js +374 -94
- package/dist/table/table.js.map +1 -1
- package/dist/text-shimmer/text-shimmer.d.ts +18 -2
- package/dist/text-shimmer/text-shimmer.js +5 -2
- package/dist/text-shimmer/text-shimmer.js.map +1 -1
- package/dist/textarea/textarea.d.ts +7 -0
- package/dist/textarea/textarea.js +3 -1
- package/dist/textarea/textarea.js.map +1 -1
- package/dist/time-picker/time-picker.d.ts +36 -1
- package/dist/time-picker/time-picker.js +33 -5
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/toast.d.ts +2 -2
- package/dist/usage-meter/index.d.ts +2 -2
- package/dist/usage-meter/usage-meter.d.ts +30 -0
- package/dist/usage-meter/usage-meter.js +56 -38
- package/dist/usage-meter/usage-meter.js.map +1 -1
- package/package.json +3 -3
package/dist/popover/popover.js
CHANGED
|
@@ -3,7 +3,7 @@ import { cnAll } from "../lib/cn.js";
|
|
|
3
3
|
import { Button as Button$1 } from "../button/button.js";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { forwardRef, useContext } from "react";
|
|
6
|
-
import { DialogTrigger as PopoverTrigger, OverlayTriggerStateContext, Popover } from "react-aria-components";
|
|
6
|
+
import { DialogTrigger as PopoverTrigger, OverlayTriggerStateContext, Popover, PreviewTrigger } from "react-aria-components";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/popover/popover.tsx
|
|
9
9
|
/**
|
|
@@ -66,7 +66,34 @@ const Popover$1 = forwardRef(function Popover$2({ children, className, placement
|
|
|
66
66
|
]
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
|
+
/**
|
|
70
|
+
* Opens a popover on hover instead of press: a hover card.
|
|
71
|
+
*
|
|
72
|
+
* React Aria's `PreviewTrigger`. The card opens after `delay` when the pointer
|
|
73
|
+
* rests on the trigger or keyboard focus reaches it, and on a long press on
|
|
74
|
+
* touch. It stays open while the pointer travels onto it (a safe area covers
|
|
75
|
+
* the gap) and closes `closeDelay` after the pointer or focus leaves both.
|
|
76
|
+
* Escape closes it; Tab from the trigger moves into it. It is non-modal: no
|
|
77
|
+
* focus trap and no underlay, and the page behind stays usable.
|
|
78
|
+
*
|
|
79
|
+
* The trigger keeps its own press action, so a Link still navigates and a
|
|
80
|
+
* Button still runs `onPress`. Content anyone needs must not live only in the
|
|
81
|
+
* card: touch without a long press never sees it.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* <PopoverHoverTrigger delay={300}>
|
|
85
|
+
* <Button variant="ghost" aria-label="3 more sources">+3</Button>
|
|
86
|
+
* <Popover placement="bottom start">...</Popover>
|
|
87
|
+
* </PopoverHoverTrigger>
|
|
88
|
+
*/
|
|
89
|
+
function PopoverHoverTrigger({ delay = 600, closeDelay = 200, ...props }) {
|
|
90
|
+
return /* @__PURE__ */ jsx(PreviewTrigger, {
|
|
91
|
+
...props,
|
|
92
|
+
delay,
|
|
93
|
+
closeDelay
|
|
94
|
+
});
|
|
95
|
+
}
|
|
69
96
|
//#endregion
|
|
70
|
-
export { Popover$1 as Popover, PopoverTrigger, popoverVariants };
|
|
97
|
+
export { Popover$1 as Popover, PopoverHoverTrigger, PopoverTrigger, popoverVariants };
|
|
71
98
|
|
|
72
99
|
//# sourceMappingURL=popover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.js","names":["Popover","AriaPopover","Button"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useContext, type ReactNode } from \"react\";\nimport {\n OverlayTriggerStateContext,\n Popover as AriaPopover,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { Button } from \"../button/button.js\";\nimport { cnAll } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-panel border-(length:--border-width) border-border p-4\",\n \"bg-card text-card-foreground shadow-floating\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load\n // none: an unstyled document renders text in a serif, and content-box\n // would add the 16px padding on top of the width.\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // React Aria gives the popover tabindex=-1 and focuses it on open, so it\n // can take a focus ring it does not want.\n \"outline-none\",\n]);\n\n/**\n * Content anchored to a trigger.\n *\n * @example\n * <PopoverTrigger>\n * <Button>Pick a date</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverTrigger>\n */\nexport interface PopoverProps\n extends Omit<AriaPopoverProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Where it opens, as React Aria's single `placement` string. The legacy\n * split this across `side` and `align`; its one non-default call site is\n * `align=\"start\"`, which is `\"bottom start\"` here.\n *\n * @default \"bottom\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /**\n * How it comes in: `rise` grows out of the trigger (offset toward it, a little\n * small, then settles on a spring) and leaves quicker; `none` leaves the entrance\n * to the caller, as a picker that animates its own popover does.\n *\n * @default \"rise\"\n */\n entrance?: \"rise\" | \"none\";\n /**\n * A heading at the top of the panel. Together with `description` and\n * `actionLabel` it makes the message popover: a dark title, grey body text\n * and a full-width button, with `children` rendered below them.\n */\n title?: ReactNode;\n /** Grey body text under the title. */\n description?: ReactNode;\n /** The label of the full-width button at the bottom. Pressing it runs `onAction` and closes the popover. */\n actionLabel?: ReactNode;\n /** Runs when the action button is pressed, before the popover closes. */\n onAction?: () => void;\n}\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(function Popover(\n {\n children,\n className,\n placement = \"bottom\",\n offset = 4,\n entrance = \"rise\",\n title,\n description,\n actionLabel,\n onAction,\n ...props\n },\n ref,\n) {\n // The open state the enclosing PopoverTrigger owns, so the action button can close it.\n const state = useContext(OverlayTriggerStateContext);\n return (\n <AriaPopover\n ref={ref}\n {...props}\n placement={placement}\n offset={offset}\n data-slot=\"popover\"\n className={cnAll(popoverVariants(), entrance === \"rise\" && \"popover-motion\", className)}\n >\n {/*\n No inner Dialog. Wrapping the content in one is the common React\n Aria idiom and it was in the first draft here -- but deleting it\n broke no assertion, so it was doing nothing: a Popover under a\n DialogTrigger already gets role=\"dialog\", a focus scope, and\n aria-labelledby pointing at the trigger. The wrapper was one more\n element and one more className to explain.\n */}\n {title == null ? null : (\n <h3\n data-slot=\"popover-title\"\n className=\"m-0 text-sm leading-(--leading-tight) font-semibold text-foreground\"\n >\n {title}\n </h3>\n )}\n {description == null ? null : (\n <p\n data-slot=\"popover-description\"\n className=\"m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary\"\n >\n {description}\n </p>\n )}\n {actionLabel == null ? null : (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className=\"mt-4 w-full\"\n onPress={() => {\n onAction?.();\n state?.close();\n }}\n >\n {actionLabel}\n </Button>\n )}\n {children}\n </AriaPopover>\n );\n});\n\n/**\n * Pairs a trigger with a popover and owns the open state.\n *\n * React Aria's `DialogTrigger`, renamed for the migration: it takes the\n * trigger element directly as its first child, which is what `asChild` was\n * doing on both legacy call sites.\n */\nexport { DialogTrigger as PopoverTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"popover.js","names":["Popover","AriaPopover","Button"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useContext, type JSX, type ReactNode } from \"react\";\nimport {\n OverlayTriggerStateContext,\n Popover as AriaPopover,\n PreviewTrigger,\n type PopoverProps as AriaPopoverProps,\n type PreviewTriggerProps,\n} from \"react-aria-components\";\n\nimport { Button } from \"../button/button.js\";\nimport { cnAll } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-panel border-(length:--border-width) border-border p-4\",\n \"bg-card text-card-foreground shadow-floating\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load\n // none: an unstyled document renders text in a serif, and content-box\n // would add the 16px padding on top of the width.\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // React Aria gives the popover tabindex=-1 and focuses it on open, so it\n // can take a focus ring it does not want.\n \"outline-none\",\n]);\n\n/**\n * Content anchored to a trigger.\n *\n * @example\n * <PopoverTrigger>\n * <Button>Pick a date</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverTrigger>\n */\nexport interface PopoverProps\n extends Omit<AriaPopoverProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Where it opens, as React Aria's single `placement` string. The legacy\n * split this across `side` and `align`; its one non-default call site is\n * `align=\"start\"`, which is `\"bottom start\"` here.\n *\n * @default \"bottom\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /**\n * How it comes in: `rise` grows out of the trigger (offset toward it, a little\n * small, then settles on a spring) and leaves quicker; `none` leaves the entrance\n * to the caller, as a picker that animates its own popover does.\n *\n * @default \"rise\"\n */\n entrance?: \"rise\" | \"none\";\n /**\n * A heading at the top of the panel. Together with `description` and\n * `actionLabel` it makes the message popover: a dark title, grey body text\n * and a full-width button, with `children` rendered below them.\n */\n title?: ReactNode;\n /** Grey body text under the title. */\n description?: ReactNode;\n /** The label of the full-width button at the bottom. Pressing it runs `onAction` and closes the popover. */\n actionLabel?: ReactNode;\n /** Runs when the action button is pressed, before the popover closes. */\n onAction?: () => void;\n}\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(function Popover(\n {\n children,\n className,\n placement = \"bottom\",\n offset = 4,\n entrance = \"rise\",\n title,\n description,\n actionLabel,\n onAction,\n ...props\n },\n ref,\n) {\n // The open state the enclosing PopoverTrigger owns, so the action button can close it.\n const state = useContext(OverlayTriggerStateContext);\n return (\n <AriaPopover\n ref={ref}\n {...props}\n placement={placement}\n offset={offset}\n data-slot=\"popover\"\n className={cnAll(popoverVariants(), entrance === \"rise\" && \"popover-motion\", className)}\n >\n {/*\n No inner Dialog. Wrapping the content in one is the common React\n Aria idiom and it was in the first draft here -- but deleting it\n broke no assertion, so it was doing nothing: a Popover under a\n DialogTrigger already gets role=\"dialog\", a focus scope, and\n aria-labelledby pointing at the trigger. The wrapper was one more\n element and one more className to explain.\n */}\n {title == null ? null : (\n <h3\n data-slot=\"popover-title\"\n className=\"m-0 text-sm leading-(--leading-tight) font-semibold text-foreground\"\n >\n {title}\n </h3>\n )}\n {description == null ? null : (\n <p\n data-slot=\"popover-description\"\n className=\"m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary\"\n >\n {description}\n </p>\n )}\n {actionLabel == null ? null : (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className=\"mt-4 w-full\"\n onPress={() => {\n onAction?.();\n state?.close();\n }}\n >\n {actionLabel}\n </Button>\n )}\n {children}\n </AriaPopover>\n );\n});\n\n/**\n * Pairs a trigger with a popover and owns the open state.\n *\n * React Aria's `DialogTrigger`, renamed for the migration: it takes the\n * trigger element directly as its first child, which is what `asChild` was\n * doing on both legacy call sites.\n */\nexport { DialogTrigger as PopoverTrigger } from \"react-aria-components\";\n\nexport interface PopoverHoverTriggerProps extends Omit<PreviewTriggerProps, \"delay\" | \"closeDelay\"> {\n /** The trigger, then the `Popover`. The trigger must be focusable (a `Button` or `Link`). */\n children: ReactNode;\n /**\n * Milliseconds the pointer rests on the trigger, or keyboard focus sits on\n * it, before the card opens.\n * @default 600\n */\n delay?: number;\n /**\n * Milliseconds after the pointer leaves the trigger and the card, or focus\n * leaves both, before it closes. Moving onto the card keeps it open.\n * @default 200\n */\n closeDelay?: number;\n}\n\n/**\n * Opens a popover on hover instead of press: a hover card.\n *\n * React Aria's `PreviewTrigger`. The card opens after `delay` when the pointer\n * rests on the trigger or keyboard focus reaches it, and on a long press on\n * touch. It stays open while the pointer travels onto it (a safe area covers\n * the gap) and closes `closeDelay` after the pointer or focus leaves both.\n * Escape closes it; Tab from the trigger moves into it. It is non-modal: no\n * focus trap and no underlay, and the page behind stays usable.\n *\n * The trigger keeps its own press action, so a Link still navigates and a\n * Button still runs `onPress`. Content anyone needs must not live only in the\n * card: touch without a long press never sees it.\n *\n * @example\n * <PopoverHoverTrigger delay={300}>\n * <Button variant=\"ghost\" aria-label=\"3 more sources\">+3</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverHoverTrigger>\n */\nexport function PopoverHoverTrigger({\n delay = 600,\n closeDelay = 200,\n ...props\n}: PopoverHoverTriggerProps): JSX.Element {\n return <PreviewTrigger {...props} delay={delay} closeDelay={closeDelay} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,kBAAkB,IAAI;CAEjC;CACA;CAIA;CAGA;AACF,CAAC;AA8CD,MAAaA,YAAU,WAAsC,SAASA,UACpE,EACE,UACA,WACA,YAAY,UACZ,SAAS,GACT,WAAW,QACX,OACA,aACA,aACA,UACA,GAAG,SAEL,KACA;CAEA,MAAM,QAAQ,WAAW,0BAA0B;CACnD,OACE,qBAACC,SAAD;EACO;EACL,GAAI;EACO;EACH;EACR,aAAU;EACV,WAAW,MAAM,gBAAgB,GAAG,aAAa,UAAU,kBAAkB,SAAS;EANxF,UAAA;GAgBG,SAAS,OAAO,OACf,oBAAC,MAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACC,CAAA;GAEL,eAAe,OAAO,OACrB,oBAAC,KAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACA,CAAA;GAEJ,eAAe,OAAO,OACrB,oBAACC,UAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,eAAe;KACb,WAAW;KACX,OAAO,MAAM;IACf;IAEC,UAAA;GACK,CAAA;GAET;EACU;;AAEjB,CAAC;;;;;;;;;;;;;;;;;;;;;AAgDD,SAAgB,oBAAoB,EAClC,QAAQ,KACR,aAAa,KACb,GAAG,SACqC;CACxC,OAAO,oBAAC,gBAAD;EAAgB,GAAI;EAAc;EAAmB;CAAa,CAAA;AAC3E"}
|
package/dist/progress/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Progress, ProgressProps, progressFillVariants, progressTrackVariants, progressVariants } from "./progress.js";
|
|
2
|
-
export { Progress, type ProgressProps, progressFillVariants, progressTrackVariants, progressVariants };
|
|
1
|
+
import { Progress, ProgressProps, progressFillVariants, progressTargetVariants, progressTrackVariants, progressVariants } from "./progress.js";
|
|
2
|
+
export { Progress, type ProgressProps, progressFillVariants, progressTargetVariants, progressTrackVariants, progressVariants };
|
package/dist/progress/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Progress, progressFillVariants, progressTrackVariants, progressVariants } from "./progress.js";
|
|
2
|
-
export { Progress, progressFillVariants, progressTrackVariants, progressVariants };
|
|
1
|
+
import { Progress, progressFillVariants, progressTargetVariants, progressTrackVariants, progressVariants } from "./progress.js";
|
|
2
|
+
export { Progress, progressFillVariants, progressTargetVariants, progressTrackVariants, progressVariants };
|
|
@@ -31,11 +31,35 @@ import { ProgressBarProps } from "react-aria-components";
|
|
|
31
31
|
* the track against the page at 5.25:1.
|
|
32
32
|
*
|
|
33
33
|
* Both are in the build-failing contrast contract.
|
|
34
|
+
*
|
|
35
|
+
* A TARGET MARKER is opt-in (`target`): a short upright tick that stands
|
|
36
|
+
* proud of the track at the value the bar should have reached -- "where the
|
|
37
|
+
* goal should be by now". The tick is decorative; its meaning is read to
|
|
38
|
+
* assistive technology as the bar's description (`targetLabel` plus the
|
|
39
|
+
* formatted value), and `showTargetLabel` also prints it under the tick.
|
|
40
|
+
*
|
|
41
|
+
* `surface="inverse"` is for a bar on the inverted (foreground-coloured)
|
|
42
|
+
* surface, such as a dark summary strip: the track is the inverse text colour
|
|
43
|
+
* mixed into the surface, the default fill and the copy take the inverse text
|
|
44
|
+
* colour, so the bar keeps its contrast where the default greys would vanish.
|
|
45
|
+
*/
|
|
46
|
+
export declare const progressVariants: (props?: ({
|
|
47
|
+
headerEmpty?: boolean | null | undefined;
|
|
48
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
49
|
+
export declare const progressTrackVariants: (props?: ({
|
|
50
|
+
surface?: "inverse" | "default" | null | undefined;
|
|
51
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
52
|
+
/**
|
|
53
|
+
* The target tick: 2px wide, standing 4px proud of the track on both sides,
|
|
54
|
+
* with a 1px halo of the surface colour so it reads over the fill as well as
|
|
55
|
+
* over the track. Placed by an inline `inset-inline-start`, so it mirrors in
|
|
56
|
+
* right-to-left text.
|
|
34
57
|
*/
|
|
35
|
-
export declare const
|
|
36
|
-
|
|
58
|
+
export declare const progressTargetVariants: (props?: ({
|
|
59
|
+
surface?: "inverse" | "default" | null | undefined;
|
|
60
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
37
61
|
export declare const progressFillVariants: (props?: ({
|
|
38
|
-
tone?: "neutral" | "
|
|
62
|
+
tone?: "neutral" | "warning" | "success" | "info" | "danger" | null | undefined;
|
|
39
63
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
40
64
|
/**
|
|
41
65
|
* A determinate or indeterminate progress bar.
|
|
@@ -70,6 +94,47 @@ export interface ProgressProps extends Omit<ProgressBarProps, "className" | "sty
|
|
|
70
94
|
* announced its value to assistive tech and showed a sighted reader nothing.
|
|
71
95
|
*/
|
|
72
96
|
showValue?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Mark a full bar as done: a default-toned fill eases to the success
|
|
99
|
+
* colour at 100% and, with `showValue`, a check lands beside the count.
|
|
100
|
+
*
|
|
101
|
+
* Pass `false` for a bar that can reach 100% before the work is finished
|
|
102
|
+
* -- a timer-driven estimate -- so it does not say "done" early. The fill
|
|
103
|
+
* keeps its tone and no check is shown. An explicit `tone` is never
|
|
104
|
+
* overridden either way.
|
|
105
|
+
*
|
|
106
|
+
* @default true
|
|
107
|
+
*/
|
|
108
|
+
showCompletion?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Where the bar should be by now, on the same scale as `value`
|
|
111
|
+
* (`minValue`..`maxValue`, clamped). Draws an upright tick across the track
|
|
112
|
+
* at that point. The tick is decorative; `targetLabel` and the formatted
|
|
113
|
+
* target are read as the bar's description. Leave it out (the default) for
|
|
114
|
+
* no marker and the bar exactly as before. Ignored while indeterminate.
|
|
115
|
+
*/
|
|
116
|
+
target?: number;
|
|
117
|
+
/**
|
|
118
|
+
* What the target means, such as "Expected by now". Read with the formatted
|
|
119
|
+
* target ("Expected by now: 40%") as the bar's description, and printed
|
|
120
|
+
* under the tick with `showTargetLabel`.
|
|
121
|
+
* @default "Target"
|
|
122
|
+
*/
|
|
123
|
+
targetLabel?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Print `targetLabel` and the target under the tick. It follows the tick and
|
|
126
|
+
* stays inside the bar near either end.
|
|
127
|
+
* @default false
|
|
128
|
+
*/
|
|
129
|
+
showTargetLabel?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* The surface the bar sits on. `inverse` is the inverted, foreground-coloured
|
|
132
|
+
* surface (a dark strip in the light theme, a light one in the dark theme):
|
|
133
|
+
* the track, the default-toned fill, the target tick and the copy take
|
|
134
|
+
* colours that hold their contrast there. Explicit tones keep their colours.
|
|
135
|
+
* @default "default"
|
|
136
|
+
*/
|
|
137
|
+
surface?: "default" | "inverse";
|
|
73
138
|
/** Extra classes for the track. */
|
|
74
139
|
trackClassName?: string;
|
|
75
140
|
/** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */
|
|
@@ -3,8 +3,8 @@ import { cn, cnAll } from "../lib/cn.js";
|
|
|
3
3
|
import { useAfter, useCountTo } from "../lib/count-to.js";
|
|
4
4
|
import { SwapText } from "../lib/swap-text.js";
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
6
|
-
import { forwardRef } from "react";
|
|
7
|
-
import { Label, ProgressBar } from "react-aria-components";
|
|
6
|
+
import { forwardRef, useId, useMemo } from "react";
|
|
7
|
+
import { Label, ProgressBar, useLocale } from "react-aria-components";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region src/progress/progress.tsx
|
|
10
10
|
/**
|
|
@@ -36,9 +36,47 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
36
36
|
* the track against the page at 5.25:1.
|
|
37
37
|
*
|
|
38
38
|
* Both are in the build-failing contrast contract.
|
|
39
|
+
*
|
|
40
|
+
* A TARGET MARKER is opt-in (`target`): a short upright tick that stands
|
|
41
|
+
* proud of the track at the value the bar should have reached -- "where the
|
|
42
|
+
* goal should be by now". The tick is decorative; its meaning is read to
|
|
43
|
+
* assistive technology as the bar's description (`targetLabel` plus the
|
|
44
|
+
* formatted value), and `showTargetLabel` also prints it under the tick.
|
|
45
|
+
*
|
|
46
|
+
* `surface="inverse"` is for a bar on the inverted (foreground-coloured)
|
|
47
|
+
* surface, such as a dark summary strip: the track is the inverse text colour
|
|
48
|
+
* mixed into the surface, the default fill and the copy take the inverse text
|
|
49
|
+
* colour, so the bar keeps its contrast where the default greys would vanish.
|
|
39
50
|
*/
|
|
40
|
-
const progressVariants = cva(["flex w-full flex-col
|
|
41
|
-
|
|
51
|
+
const progressVariants = cva(["flex w-full flex-col box-border font-sans"], {
|
|
52
|
+
variants: { headerEmpty: {
|
|
53
|
+
true: "gap-0",
|
|
54
|
+
false: "gap-1.5"
|
|
55
|
+
} },
|
|
56
|
+
defaultVariants: { headerEmpty: false }
|
|
57
|
+
});
|
|
58
|
+
const progressTrackVariants = cva(["relative h-2 w-full overflow-hidden rounded-pill box-border", "border-(length:--border-width)"], {
|
|
59
|
+
variants: {
|
|
60
|
+
/** The surface the bar sits on. */
|
|
61
|
+
surface: {
|
|
62
|
+
default: "bg-muted border-border",
|
|
63
|
+
inverse: "bg-[color-mix(in_oklab,var(--color-text-inverse)_28%,var(--color-foreground))] border-transparent"
|
|
64
|
+
} },
|
|
65
|
+
defaultVariants: { surface: "default" }
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* The target tick: 2px wide, standing 4px proud of the track on both sides,
|
|
69
|
+
* with a 1px halo of the surface colour so it reads over the fill as well as
|
|
70
|
+
* over the track. Placed by an inline `inset-inline-start`, so it mirrors in
|
|
71
|
+
* right-to-left text.
|
|
72
|
+
*/
|
|
73
|
+
const progressTargetVariants = cva("pointer-events-none absolute -top-1 -bottom-1 w-0.5 -ms-px rounded-pill", {
|
|
74
|
+
variants: { surface: {
|
|
75
|
+
default: "bg-foreground shadow-[0_0_0_1px_var(--color-surface)]",
|
|
76
|
+
inverse: "bg-text-inverse shadow-[0_0_0_1px_var(--color-foreground)]"
|
|
77
|
+
} },
|
|
78
|
+
defaultVariants: { surface: "default" }
|
|
79
|
+
});
|
|
42
80
|
const progressFillVariants = cva(["h-full rounded-pill", "progress-fill"], {
|
|
43
81
|
variants: { tone: {
|
|
44
82
|
neutral: "bg-primary-strong",
|
|
@@ -54,10 +92,10 @@ const progressFillVariants = cva(["h-full rounded-pill", "progress-fill"], {
|
|
|
54
92
|
* fill follows, and a check lands beside the final count once the bar is full.
|
|
55
93
|
* Any other text (a price, a step count) is shown as it is.
|
|
56
94
|
*/
|
|
57
|
-
function ProgressValue({ percentage, valueText }) {
|
|
95
|
+
function ProgressValue({ percentage, valueText, showCompletion }) {
|
|
58
96
|
const percent = Math.round(percentage);
|
|
59
97
|
const counted = useCountTo(percent);
|
|
60
|
-
const done = useAfter(percent >= 100, 240);
|
|
98
|
+
const done = useAfter(showCompletion && percent >= 100, 240);
|
|
61
99
|
const isPercent = valueText === `${percent}%`;
|
|
62
100
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [isPercent ? `${counted}%` : valueText, isPercent ? /* @__PURE__ */ jsx("span", {
|
|
63
101
|
"data-slot": "progress-done",
|
|
@@ -80,46 +118,125 @@ function ProgressValue({ percentage, valueText }) {
|
|
|
80
118
|
})
|
|
81
119
|
}) : null] });
|
|
82
120
|
}
|
|
83
|
-
|
|
121
|
+
/** The track's frame and the tick. With no marker it renders the track alone, as before. */
|
|
122
|
+
function TargetFrame({ marker, surface, children }) {
|
|
123
|
+
if (!marker) return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
124
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
125
|
+
"data-slot": "progress-target-frame",
|
|
126
|
+
className: "relative",
|
|
127
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
128
|
+
"data-slot": "progress-target",
|
|
129
|
+
"aria-hidden": "true",
|
|
130
|
+
className: progressTargetVariants({ surface }),
|
|
131
|
+
style: { insetInlineStart: `${marker.share * 100}%` }
|
|
132
|
+
})]
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The target's meaning, the bar's description. Printed, it hangs from a
|
|
137
|
+
* zero-width anchor at the tick: centred on it, or running inward near either
|
|
138
|
+
* end, in either text direction. Otherwise it is for screen readers only.
|
|
139
|
+
*/
|
|
140
|
+
function TargetText({ id, marker, shown, inverse }) {
|
|
141
|
+
if (!shown) return /* @__PURE__ */ jsx("span", {
|
|
142
|
+
id,
|
|
143
|
+
className: "sr-only",
|
|
144
|
+
children: marker.text
|
|
145
|
+
});
|
|
146
|
+
return /* @__PURE__ */ jsx("div", {
|
|
147
|
+
"data-slot": "progress-target-label",
|
|
148
|
+
"aria-hidden": "true",
|
|
149
|
+
className: "relative h-[1lh] text-xs leading-(--leading-normal)",
|
|
150
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
151
|
+
className: cnAll("absolute top-0 flex w-0 whitespace-nowrap", marker.share < .2 ? "justify-start" : marker.share > .8 ? "justify-end" : "justify-center", inverse ? "text-text-inverse" : "text-muted-foreground"),
|
|
152
|
+
style: { insetInlineStart: `${marker.share * 100}%` },
|
|
153
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
154
|
+
id,
|
|
155
|
+
children: marker.text
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const Progress = forwardRef(function Progress({ label, labelHidden, showValue, showCompletion = true, tone, target, targetLabel = "Target", showTargetLabel = false, surface = "default", trackClassName, fillClassName, className, ...props }, ref) {
|
|
161
|
+
const { locale } = useLocale();
|
|
162
|
+
const targetId = useId();
|
|
163
|
+
const { minValue = 0, maxValue = 100, formatOptions, isIndeterminate: indeterminate } = props;
|
|
164
|
+
const marker = useMemo(() => {
|
|
165
|
+
if (target === void 0 || !Number.isFinite(target) || indeterminate) return null;
|
|
166
|
+
const span = maxValue - minValue;
|
|
167
|
+
const share = span > 0 ? Math.min(1, Math.max(0, (target - minValue) / span)) : 0;
|
|
168
|
+
const options = formatOptions ?? { style: "percent" };
|
|
169
|
+
const shown = options.style === "percent" ? share : Math.min(maxValue, Math.max(minValue, target));
|
|
170
|
+
return {
|
|
171
|
+
share,
|
|
172
|
+
text: `${targetLabel}: ${new Intl.NumberFormat(locale, options).format(shown)}`
|
|
173
|
+
};
|
|
174
|
+
}, [
|
|
175
|
+
target,
|
|
176
|
+
indeterminate,
|
|
177
|
+
minValue,
|
|
178
|
+
maxValue,
|
|
179
|
+
formatOptions,
|
|
180
|
+
locale,
|
|
181
|
+
targetLabel
|
|
182
|
+
]);
|
|
183
|
+
const inverse = surface === "inverse";
|
|
184
|
+
const describedBy = marker ? [props["aria-describedby"], targetId].filter(Boolean).join(" ") : props["aria-describedby"];
|
|
84
185
|
return /* @__PURE__ */ jsx(ProgressBar, {
|
|
85
186
|
ref,
|
|
86
187
|
...props,
|
|
188
|
+
"aria-describedby": describedBy,
|
|
87
189
|
"data-slot": "progress",
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
190
|
+
"data-surface": inverse ? "inverse" : void 0,
|
|
191
|
+
className: cn(progressVariants({ headerEmpty: Boolean(labelHidden && !showValue) }), className),
|
|
192
|
+
children: ({ percentage, isIndeterminate, valueText }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
193
|
+
/* @__PURE__ */ jsxs("div", {
|
|
194
|
+
"data-slot": "progress-header",
|
|
195
|
+
className: cnAll(showValue && "flex items-baseline justify-between gap-2"),
|
|
196
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
197
|
+
"data-slot": "progress-label",
|
|
198
|
+
className: cnAll("text-sm leading-(--leading-normal)", inverse ? "text-text-inverse" : "text-foreground", labelHidden && "sr-only"),
|
|
199
|
+
children: typeof label === "string" ? /* @__PURE__ */ jsx(SwapText, {
|
|
200
|
+
swapKey: label,
|
|
201
|
+
className: "block",
|
|
202
|
+
children: label
|
|
203
|
+
}) : label
|
|
204
|
+
}), showValue ? /* @__PURE__ */ jsx("span", {
|
|
205
|
+
"data-slot": "progress-value",
|
|
206
|
+
"aria-hidden": "true",
|
|
207
|
+
className: cnAll("text-sm leading-(--leading-normal) tabular-nums", inverse ? "text-text-inverse" : "text-muted-foreground"),
|
|
208
|
+
children: /* @__PURE__ */ jsx(ProgressValue, {
|
|
209
|
+
percentage: percentage ?? 0,
|
|
210
|
+
valueText,
|
|
211
|
+
showCompletion
|
|
212
|
+
})
|
|
213
|
+
}) : null]
|
|
214
|
+
}),
|
|
215
|
+
/* @__PURE__ */ jsx(TargetFrame, {
|
|
216
|
+
marker,
|
|
217
|
+
surface,
|
|
218
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
219
|
+
"data-slot": "progress-track",
|
|
220
|
+
className: cn(progressTrackVariants({ surface }), trackClassName),
|
|
221
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
222
|
+
"data-slot": "progress-fill",
|
|
223
|
+
"data-indeterminate": isIndeterminate || void 0,
|
|
224
|
+
"data-complete": showCompletion && (!tone || tone === "neutral") && !isIndeterminate && (percentage ?? 0) >= 100 ? "" : void 0,
|
|
225
|
+
className: cn([progressFillVariants({ tone }), inverse && (!tone || tone === "neutral") && "bg-text-inverse"], fillClassName),
|
|
226
|
+
style: isIndeterminate ? void 0 : { width: `${percentage ?? 0}%` }
|
|
227
|
+
})
|
|
107
228
|
})
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
className: cn(progressFillVariants({ tone }), fillClassName),
|
|
117
|
-
style: { width: isIndeterminate ? "0%" : `${percentage ?? 0}%` }
|
|
118
|
-
})
|
|
119
|
-
})] })
|
|
229
|
+
}),
|
|
230
|
+
marker ? /* @__PURE__ */ jsx(TargetText, {
|
|
231
|
+
id: targetId,
|
|
232
|
+
marker,
|
|
233
|
+
shown: showTargetLabel,
|
|
234
|
+
inverse
|
|
235
|
+
}) : null
|
|
236
|
+
] })
|
|
120
237
|
});
|
|
121
238
|
});
|
|
122
239
|
//#endregion
|
|
123
|
-
export { Progress, progressFillVariants, progressTrackVariants, progressVariants };
|
|
240
|
+
export { Progress, progressFillVariants, progressTargetVariants, progressTrackVariants, progressVariants };
|
|
124
241
|
|
|
125
242
|
//# sourceMappingURL=progress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","names":["AriaProgressBar","AriaLabel"],"sources":["../../src/progress/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Label as AriaLabel,\n ProgressBar as AriaProgressBar,\n type ProgressBarProps as AriaProgressBarProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useAfter, useCountTo } from \"../lib/count-to.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Progress, ported from the legacy shadcn/Radix component.\n *\n * Census: seven files import it and only TWO render anything --\n * `ResponseOption.tsx` and `LiveQA.tsx`. Both pass `value` and\n * `className` and nothing else.\n *\n * `label` IS REQUIRED, and that is the point. Neither live call site\n * passes `aria-label`, `aria-labelledby` or an `id` a label targets, and\n * `role=\"progressbar\"` does not take its name from content -- so both\n * rendered progress bars are announced as an unnamed progressbar. React\n * Aria supplies the plumbing for three naming routes but no name of its\n * own: `<ProgressBar value={60}/>` is just as nameless there. Making the\n * prop required is what turns it into something you cannot forget.\n *\n * The migration plan said this component \"adds the missing\n * aria-valuenow\". That is wrong -- Radix already supplies `aria-valuenow`,\n * `aria-valuemin`, `aria-valuemax` and `aria-valuetext`. The missing thing\n * was always the NAME.\n *\n * THE COLOURS ARE NOT THE LEGACY'S. Its track is `bg-primary/20`, an alpha\n * fill that composites against unknown page content and therefore has no\n * fixed contrast pair at all -- over white it is roughly 1.05:1, an\n * invisible boundary. Its indicator is `bg-primary`, 2.44:1 on the page.\n * Here the track is `muted` with a `border` boundary and the fill is\n * `primary-strong`, which puts the fill against the track at 4.61:1 and\n * the track against the page at 5.25:1.\n *\n * Both are in the build-failing contrast contract.\n */\nexport const progressVariants = cva([\"flex w-full flex-col gap-1.5 box-border font-sans\"]);\n\nexport const progressTrackVariants = cva([\n \"relative h-2 w-full overflow-hidden rounded-pill box-border\",\n // muted, NOT `border` as the Switch uses. A switch has no fill; a\n // progress bar does, and primary-strong on a `border` track is 1.01:1 --\n // the two halves are the same grey and the value disappears.\n \"bg-muted border-(length:--border-width) border-border\",\n]);\n\nexport const progressFillVariants = cva(\n [\n \"h-full rounded-pill\",\n // The fill follows the value on the smooth spring and turns success-coloured when it completes (see progress-fill).\n \"progress-fill\",\n ],\n {\n variants: {\n /*\n * What the value MEANS, which for a progress bar is usually a score\n * rather than a completion. The product's bar carried exactly this axis\n * (`default`/`success`/`warning`/`error`/`info`) and reached for it to\n * colour performance metrics.\n *\n * `-strong` throughout: the fill sits on the `muted` track and has to\n * clear the 3:1 non-text floor of WCAG 1.4.11 against it, which is the\n * same reason the track is muted rather than `border` -- primary-strong\n * on a `border` track measures 1.01:1 and the value disappears.\n */\n tone: {\n neutral: \"bg-primary-strong\",\n success: \"bg-success-strong\",\n warning: \"bg-warning-strong\",\n danger: \"bg-destructive-strong\",\n info: \"bg-info-strong\",\n },\n },\n defaultVariants: { tone: \"neutral\" },\n },\n);\n\n/**\n * A determinate or indeterminate progress bar.\n *\n * @example\n * <Progress label=\"Upload\" value={62} />\n * <Progress label=\"Loading results\" />\n */\nexport interface ProgressProps\n extends Omit<AriaProgressBarProps, \"className\" | \"style\" | \"children\">,\n VariantProps<typeof progressFillVariants> {\n /**\n * What is progressing, and the bar's accessible name.\n *\n * **Required.** Both legacy call sites render an unnamed progressbar.\n * Pass `labelHidden` when the surrounding UI already says it.\n */\n label: ReactNode;\n /**\n * Render the label for screen readers only.\n *\n * Both legacy call sites sit beside visible text that was never\n * associated with the bar -- this is how that text becomes the name\n * without printing it twice.\n */\n labelHidden?: boolean;\n /**\n * Print the value beside the label.\n *\n * The text itself comes from React Aria, which already formats it from\n * `value`/`minValue`/`maxValue` and honours `formatOptions` and\n * `valueLabel` -- so a percentage, a currency or \"3 of 7\" are all reachable\n * without a second prop here. It was never RENDERED before: the bar\n * announced its value to assistive tech and showed a sighted reader nothing.\n */\n showValue?: boolean;\n /** Extra classes for the track. */\n trackClassName?: string;\n /** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */\n fillClassName?: string;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/**\n * The printed value. A percentage counts to its new number on the same spring the\n * fill follows, and a check lands beside the final count once the bar is full.\n * Any other text (a price, a step count) is shown as it is.\n */\nfunction ProgressValue({ percentage, valueText }: { percentage: number; valueText: string | undefined }) {\n const percent = Math.round(percentage);\n const counted = useCountTo(percent);\n const done = useAfter(percent >= 100, 240);\n const isPercent = valueText === `${percent}%`;\n return (\n <>\n {isPercent ? `${counted}%` : valueText}\n {isPercent ? (\n <span data-slot=\"progress-done\" className=\"ms-1 inline-flex align-middle text-success-strong\">\n <SwapText swapKey={done ? \"done\" : \"\"} motion=\"glyph\" className=\"inline-flex\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </SwapText>\n </span>\n ) : null}\n </>\n );\n}\n\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n { label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props },\n ref,\n) {\n return (\n <AriaProgressBar\n ref={ref}\n {...props}\n data-slot=\"progress\"\n className={cn(progressVariants(), className)}\n >\n {({ percentage, isIndeterminate, valueText }) => (\n <>\n {/*\n The row exists only when the value is printed. Wrapping the label\n in a flex row unconditionally would change the label's box for\n every existing consumer to no purpose.\n */}\n <div\n data-slot=\"progress-header\"\n className={cnAll(showValue && \"flex items-baseline justify-between gap-2\")}\n >\n <AriaLabel\n data-slot=\"progress-label\"\n className={cnAll(\n \"text-sm leading-(--leading-normal) text-foreground\",\n labelHidden && \"sr-only\",\n )}\n >\n {typeof label === \"string\" ? (\n <SwapText swapKey={label} className=\"block\">\n {label}\n </SwapText>\n ) : (\n label\n )}\n </AriaLabel>\n {showValue ? (\n <span\n data-slot=\"progress-value\"\n // aria-hidden: React Aria already announces the value on the\n // progressbar itself. Without this a screen reader reads it\n // twice, once as the widget's value and once as loose text.\n aria-hidden=\"true\"\n className=\"text-sm leading-(--leading-normal) text-muted-foreground tabular-nums\"\n >\n <ProgressValue percentage={percentage ?? 0} valueText={valueText} />\n </span>\n ) : null}\n </div>\n <div\n data-slot=\"progress-track\"\n className={cn(progressTrackVariants(), trackClassName)}\n >\n {/*\n width, not the legacy translateX(-(100 - value)%). That\n overflowed for value > 100 -- a positive translate slid the\n indicator off to the right, while the announced value stayed\n whatever was passed. React Aria clamps the value, so the two\n can no longer disagree.\n\n An indeterminate bar renders no fill rather than the\n legacy's silent 0%: `value={undefined}` became `(value || 0)`\n there, so a sighted user saw \"0% done\" while a screen reader\n said \"indeterminate\".\n */}\n <div\n data-slot=\"progress-fill\"\n data-indeterminate={isIndeterminate || undefined}\n // A default-toned bar turns success-coloured when it completes; an explicit tone is never overridden.\n data-complete={(!tone || tone === \"neutral\") && !isIndeterminate && (percentage ?? 0) >= 100 ? \"\" : undefined}\n className={cn(progressFillVariants({ tone }), fillClassName)}\n style={{ width: isIndeterminate ? \"0%\" : `${percentage ?? 0}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAa,mBAAmB,IAAI,CAAC,mDAAmD,CAAC;AAEzF,MAAa,wBAAwB,IAAI,CACvC,+DAIA,uDACF,CAAC;AAED,MAAa,uBAAuB,IAClC,CACE,uBAEA,eACF,GACA;CACE,UAAU,EAYR,MAAM;EACJ,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;CACR,EACF;CACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CACF;;;;;;AAkDA,SAAS,cAAc,EAAE,YAAY,aAAoE;CACvG,MAAM,UAAU,KAAK,MAAM,UAAU;CACrC,MAAM,UAAU,WAAW,OAAO;CAClC,MAAM,OAAO,SAAS,WAAW,KAAK,GAAG;CACzC,MAAM,YAAY,cAAc,GAAG,QAAQ;CAC3C,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,YAAY,GAAG,QAAQ,KAAK,WAC5B,YACC,oBAAC,QAAD;EAAM,aAAU;EAAgB,WAAU;EACxC,UAAA,oBAAC,UAAD;GAAU,SAAS,OAAO,SAAS;GAAI,QAAO;GAAQ,WAAU;GAC9D,UAAA,oBAAC,OAAD;IACE,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;IACf,WAAU;IACV,eAAY;IAEZ,UAAA,oBAAC,QAAD,EAAM,GAAE,mBAAoB,CAAA;GACzB,CAAA;EACG,CAAA;CACN,CAAA,IACJ,IACJ,EAAA,CAAA;AAEN;AAEA,MAAa,WAAW,WAA0C,SAAS,SACzE,EAAE,OAAO,aAAa,WAAW,MAAM,gBAAgB,eAAe,WAAW,GAAG,SACpF,KACA;CACA,OACE,oBAACA,aAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,iBAAiB,GAAG,SAAS;EAEzC,WAAA,EAAE,YAAY,iBAAiB,gBAC/B,qBAAA,YAAA,EAAA,UAAA,CAME,qBAAC,OAAD;GACE,aAAU;GACV,WAAW,MAAM,aAAa,2CAA2C;GAF3E,UAAA,CAIE,oBAACC,OAAD;IACE,aAAU;IACV,WAAW,MACT,sDACA,eAAe,SACjB;IAEC,UAAA,OAAO,UAAU,WAChB,oBAAC,UAAD;KAAU,SAAS;KAAO,WAAU;KACjC,UAAA;IACO,CAAA,IAEV;GAEO,CAAA,GACV,YACC,oBAAC,QAAD;IACE,aAAU;IAIV,eAAY;IACZ,WAAU;IAEV,UAAA,oBAAC,eAAD;KAAe,YAAY,cAAc;KAAc;IAAY,CAAA;GAC/D,CAAA,IACJ,IACD;EACL,CAAA,GAAA,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,sBAAsB,GAAG,cAAc;GAcrD,UAAA,oBAAC,OAAD;IACE,aAAU;IACV,sBAAoB,mBAAmB,KAAA;IAEvC,kBAAgB,CAAC,QAAQ,SAAS,cAAc,CAAC,oBAAoB,cAAc,MAAM,MAAM,KAAK,KAAA;IACpG,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,aAAa;IAC3D,OAAO,EAAE,OAAO,kBAAkB,OAAO,GAAG,cAAc,EAAE,GAAG;GAChE,CAAA;EACE,CAAA,CACL,EAAA,CAAA;CAEW,CAAA;AAErB,CAAC"}
|
|
1
|
+
{"version":3,"file":"progress.js","names":["AriaProgressBar","AriaLabel"],"sources":["../../src/progress/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { forwardRef, useId, useMemo, type ReactNode } from \"react\";\nimport {\n Label as AriaLabel,\n ProgressBar as AriaProgressBar,\n useLocale,\n type ProgressBarProps as AriaProgressBarProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useAfter, useCountTo } from \"../lib/count-to.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Progress, ported from the legacy shadcn/Radix component.\n *\n * Census: seven files import it and only TWO render anything --\n * `ResponseOption.tsx` and `LiveQA.tsx`. Both pass `value` and\n * `className` and nothing else.\n *\n * `label` IS REQUIRED, and that is the point. Neither live call site\n * passes `aria-label`, `aria-labelledby` or an `id` a label targets, and\n * `role=\"progressbar\"` does not take its name from content -- so both\n * rendered progress bars are announced as an unnamed progressbar. React\n * Aria supplies the plumbing for three naming routes but no name of its\n * own: `<ProgressBar value={60}/>` is just as nameless there. Making the\n * prop required is what turns it into something you cannot forget.\n *\n * The migration plan said this component \"adds the missing\n * aria-valuenow\". That is wrong -- Radix already supplies `aria-valuenow`,\n * `aria-valuemin`, `aria-valuemax` and `aria-valuetext`. The missing thing\n * was always the NAME.\n *\n * THE COLOURS ARE NOT THE LEGACY'S. Its track is `bg-primary/20`, an alpha\n * fill that composites against unknown page content and therefore has no\n * fixed contrast pair at all -- over white it is roughly 1.05:1, an\n * invisible boundary. Its indicator is `bg-primary`, 2.44:1 on the page.\n * Here the track is `muted` with a `border` boundary and the fill is\n * `primary-strong`, which puts the fill against the track at 4.61:1 and\n * the track against the page at 5.25:1.\n *\n * Both are in the build-failing contrast contract.\n *\n * A TARGET MARKER is opt-in (`target`): a short upright tick that stands\n * proud of the track at the value the bar should have reached -- \"where the\n * goal should be by now\". The tick is decorative; its meaning is read to\n * assistive technology as the bar's description (`targetLabel` plus the\n * formatted value), and `showTargetLabel` also prints it under the tick.\n *\n * `surface=\"inverse\"` is for a bar on the inverted (foreground-coloured)\n * surface, such as a dark summary strip: the track is the inverse text colour\n * mixed into the surface, the default fill and the copy take the inverse text\n * colour, so the bar keeps its contrast where the default greys would vanish.\n */\nexport const progressVariants = cva([\"flex w-full flex-col box-border font-sans\"], {\n variants: {\n /*\n * True when nothing is printed above the track (`labelHidden` and no\n * `showValue`). The header is then an empty row -- its sr-only label is\n * out of flow -- and the column gap would leave 6px of nothing above the\n * bar. The gap lives here and not in the base, so the raw\n * `progressVariants()` output never carries both `gap-1.5` and `gap-0`.\n */\n headerEmpty: { true: \"gap-0\", false: \"gap-1.5\" },\n },\n defaultVariants: { headerEmpty: false },\n});\n\nexport const progressTrackVariants = cva(\n [\"relative h-2 w-full overflow-hidden rounded-pill box-border\", \"border-(length:--border-width)\"],\n {\n variants: {\n /** The surface the bar sits on. */\n surface: {\n // muted, NOT `border` as the Switch uses. A switch has no fill; a\n // progress bar does, and primary-strong on a `border` track is 1.01:1 --\n // the two halves are the same grey and the value disappears.\n default: \"bg-muted border-border\",\n // On the inverted surface: the inverse text colour mixed into that surface, a quiet step off it.\n inverse:\n \"bg-[color-mix(in_oklab,var(--color-text-inverse)_28%,var(--color-foreground))] border-transparent\",\n },\n },\n defaultVariants: { surface: \"default\" },\n },\n);\n\n/**\n * The target tick: 2px wide, standing 4px proud of the track on both sides,\n * with a 1px halo of the surface colour so it reads over the fill as well as\n * over the track. Placed by an inline `inset-inline-start`, so it mirrors in\n * right-to-left text.\n */\nexport const progressTargetVariants = cva(\n \"pointer-events-none absolute -top-1 -bottom-1 w-0.5 -ms-px rounded-pill\",\n {\n variants: {\n surface: {\n default: \"bg-foreground shadow-[0_0_0_1px_var(--color-surface)]\",\n inverse: \"bg-text-inverse shadow-[0_0_0_1px_var(--color-foreground)]\",\n },\n },\n defaultVariants: { surface: \"default\" },\n },\n);\n\nexport const progressFillVariants = cva(\n [\n \"h-full rounded-pill\",\n // The fill follows the value on the smooth spring and turns success-coloured when it completes; indeterminate, it sweeps (see progress-fill).\n \"progress-fill\",\n ],\n {\n variants: {\n /*\n * What the value MEANS, which for a progress bar is usually a score\n * rather than a completion. The product's bar carried exactly this axis\n * (`default`/`success`/`warning`/`error`/`info`) and reached for it to\n * colour performance metrics.\n *\n * `-strong` throughout: the fill sits on the `muted` track and has to\n * clear the 3:1 non-text floor of WCAG 1.4.11 against it, which is the\n * same reason the track is muted rather than `border` -- primary-strong\n * on a `border` track measures 1.01:1 and the value disappears.\n */\n tone: {\n neutral: \"bg-primary-strong\",\n success: \"bg-success-strong\",\n warning: \"bg-warning-strong\",\n danger: \"bg-destructive-strong\",\n info: \"bg-info-strong\",\n },\n },\n defaultVariants: { tone: \"neutral\" },\n },\n);\n\n/**\n * A determinate or indeterminate progress bar.\n *\n * @example\n * <Progress label=\"Upload\" value={62} />\n * <Progress label=\"Loading results\" />\n */\nexport interface ProgressProps\n extends Omit<AriaProgressBarProps, \"className\" | \"style\" | \"children\">,\n VariantProps<typeof progressFillVariants> {\n /**\n * What is progressing, and the bar's accessible name.\n *\n * **Required.** Both legacy call sites render an unnamed progressbar.\n * Pass `labelHidden` when the surrounding UI already says it.\n */\n label: ReactNode;\n /**\n * Render the label for screen readers only.\n *\n * Both legacy call sites sit beside visible text that was never\n * associated with the bar -- this is how that text becomes the name\n * without printing it twice.\n */\n labelHidden?: boolean;\n /**\n * Print the value beside the label.\n *\n * The text itself comes from React Aria, which already formats it from\n * `value`/`minValue`/`maxValue` and honours `formatOptions` and\n * `valueLabel` -- so a percentage, a currency or \"3 of 7\" are all reachable\n * without a second prop here. It was never RENDERED before: the bar\n * announced its value to assistive tech and showed a sighted reader nothing.\n */\n showValue?: boolean;\n /**\n * Mark a full bar as done: a default-toned fill eases to the success\n * colour at 100% and, with `showValue`, a check lands beside the count.\n *\n * Pass `false` for a bar that can reach 100% before the work is finished\n * -- a timer-driven estimate -- so it does not say \"done\" early. The fill\n * keeps its tone and no check is shown. An explicit `tone` is never\n * overridden either way.\n *\n * @default true\n */\n showCompletion?: boolean;\n /**\n * Where the bar should be by now, on the same scale as `value`\n * (`minValue`..`maxValue`, clamped). Draws an upright tick across the track\n * at that point. The tick is decorative; `targetLabel` and the formatted\n * target are read as the bar's description. Leave it out (the default) for\n * no marker and the bar exactly as before. Ignored while indeterminate.\n */\n target?: number;\n /**\n * What the target means, such as \"Expected by now\". Read with the formatted\n * target (\"Expected by now: 40%\") as the bar's description, and printed\n * under the tick with `showTargetLabel`.\n * @default \"Target\"\n */\n targetLabel?: string;\n /**\n * Print `targetLabel` and the target under the tick. It follows the tick and\n * stays inside the bar near either end.\n * @default false\n */\n showTargetLabel?: boolean;\n /**\n * The surface the bar sits on. `inverse` is the inverted, foreground-coloured\n * surface (a dark strip in the light theme, a light one in the dark theme):\n * the track, the default-toned fill, the target tick and the copy take\n * colours that hold their contrast there. Explicit tones keep their colours.\n * @default \"default\"\n */\n surface?: \"default\" | \"inverse\";\n /** Extra classes for the track. */\n trackClassName?: string;\n /** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */\n fillClassName?: string;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/**\n * The printed value. A percentage counts to its new number on the same spring the\n * fill follows, and a check lands beside the final count once the bar is full.\n * Any other text (a price, a step count) is shown as it is.\n */\nfunction ProgressValue({\n percentage,\n valueText,\n showCompletion,\n}: {\n percentage: number;\n valueText: string | undefined;\n showCompletion: boolean;\n}) {\n const percent = Math.round(percentage);\n const counted = useCountTo(percent);\n const done = useAfter(showCompletion && percent >= 100, 240);\n const isPercent = valueText === `${percent}%`;\n return (\n <>\n {isPercent ? `${counted}%` : valueText}\n {isPercent ? (\n <span data-slot=\"progress-done\" className=\"ms-1 inline-flex align-middle text-success-strong\">\n <SwapText swapKey={done ? \"done\" : \"\"} motion=\"glyph\" className=\"inline-flex\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </SwapText>\n </span>\n ) : null}\n </>\n );\n}\n\ninterface Marker {\n /** Where the target sits along the track, 0 to 1. */\n share: number;\n /** \"Target: 40%\": what is read, and printed with showTargetLabel. */\n text: string;\n}\n\n/** The track's frame and the tick. With no marker it renders the track alone, as before. */\nfunction TargetFrame({\n marker,\n surface,\n children,\n}: {\n marker: Marker | null;\n surface: \"default\" | \"inverse\";\n children: ReactNode;\n}) {\n if (!marker) return <>{children}</>;\n return (\n <div data-slot=\"progress-target-frame\" className=\"relative\">\n {children}\n <span\n data-slot=\"progress-target\"\n aria-hidden=\"true\"\n className={progressTargetVariants({ surface })}\n style={{ insetInlineStart: `${marker.share * 100}%` }}\n />\n </div>\n );\n}\n\n/**\n * The target's meaning, the bar's description. Printed, it hangs from a\n * zero-width anchor at the tick: centred on it, or running inward near either\n * end, in either text direction. Otherwise it is for screen readers only.\n */\nfunction TargetText({\n id,\n marker,\n shown,\n inverse,\n}: {\n id: string;\n marker: Marker;\n shown: boolean;\n inverse: boolean;\n}) {\n if (!shown) {\n return (\n <span id={id} className=\"sr-only\">\n {marker.text}\n </span>\n );\n }\n return (\n // aria-hidden: it is read once, as the description, not again as loose text.\n <div data-slot=\"progress-target-label\" aria-hidden=\"true\" className=\"relative h-[1lh] text-xs leading-(--leading-normal)\">\n <span\n className={cnAll(\n \"absolute top-0 flex w-0 whitespace-nowrap\",\n marker.share < 0.2 ? \"justify-start\" : marker.share > 0.8 ? \"justify-end\" : \"justify-center\",\n inverse ? \"text-text-inverse\" : \"text-muted-foreground\",\n )}\n style={{ insetInlineStart: `${marker.share * 100}%` }}\n >\n <span id={id}>{marker.text}</span>\n </span>\n </div>\n );\n}\n\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n {\n label,\n labelHidden,\n showValue,\n showCompletion = true,\n tone,\n target,\n targetLabel = \"Target\",\n showTargetLabel = false,\n surface = \"default\",\n trackClassName,\n fillClassName,\n className,\n ...props\n },\n ref,\n) {\n const { locale } = useLocale();\n const targetId = useId();\n const { minValue = 0, maxValue = 100, formatOptions, isIndeterminate: indeterminate } = props;\n const marker = useMemo(() => {\n if (target === undefined || !Number.isFinite(target) || indeterminate) return null;\n const span = maxValue - minValue;\n const share = span > 0 ? Math.min(1, Math.max(0, (target - minValue) / span)) : 0;\n // Formatted the way React Aria formats the bar's own value: the share under the default percent style, else the value.\n const options = formatOptions ?? { style: \"percent\" };\n const shown = options.style === \"percent\" ? share : Math.min(maxValue, Math.max(minValue, target));\n return { share, text: `${targetLabel}: ${new Intl.NumberFormat(locale, options).format(shown)}` };\n }, [target, indeterminate, minValue, maxValue, formatOptions, locale, targetLabel]);\n const inverse = surface === \"inverse\";\n const describedBy = marker\n ? [props[\"aria-describedby\"], targetId].filter(Boolean).join(\" \")\n : props[\"aria-describedby\"];\n return (\n <AriaProgressBar\n ref={ref}\n {...props}\n aria-describedby={describedBy}\n data-slot=\"progress\"\n data-surface={inverse ? \"inverse\" : undefined}\n className={cn(progressVariants({ headerEmpty: Boolean(labelHidden && !showValue) }), className)}\n >\n {({ percentage, isIndeterminate, valueText }) => (\n <>\n {/*\n The row exists only when the value is printed. Wrapping the label\n in a flex row unconditionally would change the label's box for\n every existing consumer to no purpose.\n */}\n <div\n data-slot=\"progress-header\"\n className={cnAll(showValue && \"flex items-baseline justify-between gap-2\")}\n >\n <AriaLabel\n data-slot=\"progress-label\"\n className={cnAll(\n \"text-sm leading-(--leading-normal)\",\n inverse ? \"text-text-inverse\" : \"text-foreground\",\n labelHidden && \"sr-only\",\n )}\n >\n {typeof label === \"string\" ? (\n <SwapText swapKey={label} className=\"block\">\n {label}\n </SwapText>\n ) : (\n label\n )}\n </AriaLabel>\n {showValue ? (\n <span\n data-slot=\"progress-value\"\n // aria-hidden: React Aria already announces the value on the\n // progressbar itself. Without this a screen reader reads it\n // twice, once as the widget's value and once as loose text.\n aria-hidden=\"true\"\n className={cnAll(\n \"text-sm leading-(--leading-normal) tabular-nums\",\n inverse ? \"text-text-inverse\" : \"text-muted-foreground\",\n )}\n >\n <ProgressValue\n percentage={percentage ?? 0}\n valueText={valueText}\n showCompletion={showCompletion}\n />\n </span>\n ) : null}\n </div>\n {/* The track is wrapped only when there is a marker, so a bar without one keeps its DOM exactly. */}\n <TargetFrame marker={marker} surface={surface}>\n <div\n data-slot=\"progress-track\"\n className={cn(progressTrackVariants({ surface }), trackClassName)}\n >\n {/*\n width, not the legacy translateX(-(100 - value)%). That\n overflowed for value > 100 -- a positive translate slid the\n indicator off to the right, while the announced value stayed\n whatever was passed. React Aria clamps the value, so the two\n can no longer disagree.\n \n An indeterminate bar is not the legacy's silent 0%:\n `value={undefined}` became `(value || 0)` there, so a sighted\n user saw \"0% done\" while a screen reader said \"indeterminate\".\n Its fill is a segment that sweeps the track on a loop (a slow\n pulse of the whole track under reduced motion), sized and moved\n by progress-fill, so it carries no inline width.\n */}\n <div\n data-slot=\"progress-fill\"\n data-indeterminate={isIndeterminate || undefined}\n // A default-toned bar turns success-coloured when it completes, unless showCompletion is off; an explicit tone is never overridden.\n data-complete={\n showCompletion && (!tone || tone === \"neutral\") && !isIndeterminate && (percentage ?? 0) >= 100\n ? \"\"\n : undefined\n }\n className={cn(\n [progressFillVariants({ tone }), inverse && (!tone || tone === \"neutral\") && \"bg-text-inverse\"],\n fillClassName,\n )}\n style={isIndeterminate ? undefined : { width: `${percentage ?? 0}%` }}\n />\n </div>\n </TargetFrame>\n {marker ? <TargetText id={targetId} marker={marker} shown={showTargetLabel} inverse={inverse} /> : null}\n </>\n )}\n </AriaProgressBar>\n );\n});\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Radio, RadioGroup, RadioGroupProps, RadioProps, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
-
export { Radio, RadioGroup, type RadioGroupProps, type RadioProps, radioGroupVariants, radioVariants };
|
|
1
|
+
import { Radio, RadioGroup, RadioGroupProps, RadioGroupVariant, RadioProps, radioCardVariants, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
+
export { Radio, RadioGroup, type RadioGroupProps, type RadioGroupVariant, type RadioProps, radioCardVariants, radioGroupVariants, radioVariants };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Radio, RadioGroup, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
-
export { Radio, RadioGroup, radioGroupVariants, radioVariants };
|
|
1
|
+
import { Radio, RadioGroup, radioCardVariants, radioGroupVariants, radioVariants } from "./radio-group.js";
|
|
2
|
+
export { Radio, RadioGroup, radioCardVariants, radioGroupVariants, radioVariants };
|
|
@@ -19,8 +19,22 @@ import { RadioGroupProps, RadioProps } from "react-aria-components";
|
|
|
19
19
|
* Checkbox's exact pair -- `bg-primary` with a `primary-strong` boundary --
|
|
20
20
|
* so a radio and a checkbox in the same form agree.
|
|
21
21
|
*/
|
|
22
|
-
export declare const radioGroupVariants: (props?:
|
|
22
|
+
export declare const radioGroupVariants: (props?: ({
|
|
23
|
+
variant?: "card" | "default" | null | undefined;
|
|
24
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
23
25
|
export declare const radioVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
26
|
+
/**
|
|
27
|
+
* One choice drawn as a selectable card: a bordered surface the whole of
|
|
28
|
+
* which is the hit target, with the title, an optional description and icon.
|
|
29
|
+
*
|
|
30
|
+
* Selection is shown twice, by the border AND by the dot, so it never rests
|
|
31
|
+
* on colour alone (WCAG 1.4.1). The border reuses StatCard's selected filter
|
|
32
|
+
* pair (`border-primary-strong`, hover darkens an unselected edge), so a
|
|
33
|
+
* selected card and a selected stat card read the same.
|
|
34
|
+
*/
|
|
35
|
+
export declare const radioCardVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
36
|
+
/** Which look the group's radios take. */
|
|
37
|
+
export type RadioGroupVariant = "default" | "card";
|
|
24
38
|
/**
|
|
25
39
|
* A set of mutually exclusive choices.
|
|
26
40
|
*
|
|
@@ -31,6 +45,14 @@ export declare const radioVariants: (props?: import("class-variance-authority/ty
|
|
|
31
45
|
* </RadioGroup>
|
|
32
46
|
*/
|
|
33
47
|
interface RadioGroupProps$1 extends Omit<RadioGroupProps, "className" | "style"> {
|
|
48
|
+
/**
|
|
49
|
+
* `card` draws each choice as a selectable card, with a title, an optional
|
|
50
|
+
* `description` and `icon` on each `Radio`. The keyboard model is the same:
|
|
51
|
+
* one tab stop, arrow keys move and select.
|
|
52
|
+
*
|
|
53
|
+
* @default "default"
|
|
54
|
+
*/
|
|
55
|
+
variant?: RadioGroupVariant;
|
|
34
56
|
/** Extra classes. Merged last, so they win conflicts with ours. */
|
|
35
57
|
className?: string;
|
|
36
58
|
}
|
|
@@ -42,9 +64,17 @@ declare const RadioGroup$1: import("react").ForwardRefExoticComponent<RadioGroup
|
|
|
42
64
|
* <Radio value="pro">Pro</Radio>
|
|
43
65
|
*/
|
|
44
66
|
interface RadioProps$1 extends Omit<RadioProps, "className" | "style" | "children"> {
|
|
45
|
-
/** The label, and this option's accessible name. */
|
|
67
|
+
/** The label, and this option's accessible name. In a card, the title. */
|
|
46
68
|
children?: ReactNode;
|
|
47
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* A line under the title, read as the option's description
|
|
71
|
+
* (`aria-describedby`), not as part of its name. Card variant only; the
|
|
72
|
+
* default variant ignores it.
|
|
73
|
+
*/
|
|
74
|
+
description?: ReactNode;
|
|
75
|
+
/** A decorative icon before the title. Card variant only; hidden from assistive tech. */
|
|
76
|
+
icon?: ReactNode;
|
|
77
|
+
/** Extra classes for the label (the whole card in the card variant). Merged last, so they win conflicts. */
|
|
48
78
|
className?: string;
|
|
49
79
|
}
|
|
50
80
|
declare const Radio$1: import("react").ForwardRefExoticComponent<RadioProps$1 & import("react").RefAttributes<HTMLInputElement>>;
|