@seekalfred/primitives 0.6.0 → 0.7.3
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 +214 -15
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +53 -4
- package/dist/alert/alert.js +100 -11
- 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 +58 -18
- package/dist/alert-dialog/alert-dialog.js +152 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +1 -1
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +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/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +77 -0
- package/dist/carousel/carousel.js +415 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/copy-button/copy-button.d.ts +18 -2
- package/dist/copy-button/copy-button.js +29 -8
- package/dist/copy-button/copy-button.js.map +1 -1
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +86 -0
- package/dist/donut-chart/donut-chart.js +593 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +42 -10
- package/dist/empty-state/empty-state.js +44 -26
- 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.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +50 -26
- package/dist/filter-bar/filter-bar.js +317 -157
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +53 -10
- package/dist/index.js +52 -12
- package/dist/input/input.js +4 -4
- package/dist/input/input.js.map +1 -1
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-shell.js +10 -3
- package/dist/lib/field-shell.js.map +1 -1
- package/dist/lib/field-surface.js +31 -9
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- 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 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +116 -0
- package/dist/line-chart/line-chart.js +740 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +86 -0
- package/dist/money-input/money-input.js +613 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +89 -0
- package/dist/morph-select/morph-select.js +712 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +77 -0
- package/dist/multi-select/multi-select.js +209 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.d.ts +16 -0
- package/dist/password-field/password-field.js +39 -10
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/index.d.ts +2 -2
- package/dist/popover/index.js +2 -2
- package/dist/popover/popover.d.ts +51 -2
- package/dist/popover/popover.js +59 -8
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +16 -2
- package/dist/progress/progress.js +16 -9
- 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 +110 -10
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +212 -62
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +24 -2
- package/dist/search-field/search-field.js +24 -9
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +28 -14
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +22 -10
- package/dist/separator/separator.js +33 -11
- package/dist/separator/separator.js.map +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.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 +58 -5
- package/dist/stat-card/stat-card.js +144 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- 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 +55 -3
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.d.ts +36 -1
- package/dist/time-picker/time-picker.js +35 -7
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +7 -1
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +80 -0
- package/dist/usage-meter/usage-meter.js +458 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
package/dist/popover/popover.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cnAll } from "../lib/cn.js";
|
|
3
|
+
import { Button as Button$1 } from "../button/button.js";
|
|
3
4
|
import { cva } from "class-variance-authority";
|
|
4
|
-
import { forwardRef } from "react";
|
|
5
|
-
import { DialogTrigger as PopoverTrigger, Popover } from "react-aria-components";
|
|
6
|
-
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { forwardRef, useContext } from "react";
|
|
6
|
+
import { DialogTrigger as PopoverTrigger, OverlayTriggerStateContext, Popover, PreviewTrigger } from "react-aria-components";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
//#region src/popover/popover.tsx
|
|
8
9
|
/**
|
|
9
10
|
* Popover, ported from the legacy shadcn/Radix component.
|
|
@@ -26,23 +27,73 @@ import { jsx } from "react/jsx-runtime";
|
|
|
26
27
|
* already exists under another name.
|
|
27
28
|
*/
|
|
28
29
|
const popoverVariants = cva([
|
|
29
|
-
"w-72 rounded-
|
|
30
|
+
"w-72 rounded-panel border-(length:--border-width) border-border p-4",
|
|
30
31
|
"bg-card text-card-foreground shadow-floating",
|
|
31
32
|
"box-border font-sans text-sm leading-(--leading-normal)",
|
|
32
33
|
"outline-none"
|
|
33
34
|
]);
|
|
34
|
-
const Popover$1 = forwardRef(function Popover$2({ children, className, placement = "bottom", offset = 4, entrance = "rise", ...props }, ref) {
|
|
35
|
-
|
|
35
|
+
const Popover$1 = forwardRef(function Popover$2({ children, className, placement = "bottom", offset = 4, entrance = "rise", title, description, actionLabel, onAction, ...props }, ref) {
|
|
36
|
+
const state = useContext(OverlayTriggerStateContext);
|
|
37
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
36
38
|
ref,
|
|
37
39
|
...props,
|
|
38
40
|
placement,
|
|
39
41
|
offset,
|
|
40
42
|
"data-slot": "popover",
|
|
41
43
|
className: cnAll(popoverVariants(), entrance === "rise" && "popover-motion", className),
|
|
42
|
-
children
|
|
44
|
+
children: [
|
|
45
|
+
title == null ? null : /* @__PURE__ */ jsx("h3", {
|
|
46
|
+
"data-slot": "popover-title",
|
|
47
|
+
className: "m-0 text-sm leading-(--leading-tight) font-semibold text-foreground",
|
|
48
|
+
children: title
|
|
49
|
+
}),
|
|
50
|
+
description == null ? null : /* @__PURE__ */ jsx("p", {
|
|
51
|
+
"data-slot": "popover-description",
|
|
52
|
+
className: "m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary",
|
|
53
|
+
children: description
|
|
54
|
+
}),
|
|
55
|
+
actionLabel == null ? null : /* @__PURE__ */ jsx(Button$1, {
|
|
56
|
+
variant: "secondary",
|
|
57
|
+
size: "sm",
|
|
58
|
+
className: "mt-4 w-full",
|
|
59
|
+
onPress: () => {
|
|
60
|
+
onAction?.();
|
|
61
|
+
state?.close();
|
|
62
|
+
},
|
|
63
|
+
children: actionLabel
|
|
64
|
+
}),
|
|
65
|
+
children
|
|
66
|
+
]
|
|
43
67
|
});
|
|
44
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
|
+
}
|
|
45
96
|
//#endregion
|
|
46
|
-
export { Popover$1 as Popover, PopoverTrigger, popoverVariants };
|
|
97
|
+
export { Popover$1 as Popover, PopoverHoverTrigger, PopoverTrigger, popoverVariants };
|
|
47
98
|
|
|
48
99
|
//# sourceMappingURL=popover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.js","names":["Popover","AriaPopover"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Popover as AriaPopover,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\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-
|
|
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"}
|
|
@@ -32,10 +32,12 @@ import { ProgressBarProps } from "react-aria-components";
|
|
|
32
32
|
*
|
|
33
33
|
* Both are in the build-failing contrast contract.
|
|
34
34
|
*/
|
|
35
|
-
export declare const progressVariants: (props?:
|
|
35
|
+
export declare const progressVariants: (props?: ({
|
|
36
|
+
headerEmpty?: boolean | null | undefined;
|
|
37
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
36
38
|
export declare const progressTrackVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
37
39
|
export declare const progressFillVariants: (props?: ({
|
|
38
|
-
tone?: "
|
|
40
|
+
tone?: "success" | "warning" | "danger" | "neutral" | "info" | null | undefined;
|
|
39
41
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
40
42
|
/**
|
|
41
43
|
* A determinate or indeterminate progress bar.
|
|
@@ -70,6 +72,18 @@ export interface ProgressProps extends Omit<ProgressBarProps, "className" | "sty
|
|
|
70
72
|
* announced its value to assistive tech and showed a sighted reader nothing.
|
|
71
73
|
*/
|
|
72
74
|
showValue?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Mark a full bar as done: a default-toned fill eases to the success
|
|
77
|
+
* colour at 100% and, with `showValue`, a check lands beside the count.
|
|
78
|
+
*
|
|
79
|
+
* Pass `false` for a bar that can reach 100% before the work is finished
|
|
80
|
+
* -- a timer-driven estimate -- so it does not say "done" early. The fill
|
|
81
|
+
* keeps its tone and no check is shown. An explicit `tone` is never
|
|
82
|
+
* overridden either way.
|
|
83
|
+
*
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
showCompletion?: boolean;
|
|
73
87
|
/** Extra classes for the track. */
|
|
74
88
|
trackClassName?: string;
|
|
75
89
|
/** Extra classes for the fill -- a per-call-site colour the tone axis cannot express. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
-
import { SwapText } from "../lib/swap-text.js";
|
|
4
3
|
import { useAfter, useCountTo } from "../lib/count-to.js";
|
|
4
|
+
import { SwapText } from "../lib/swap-text.js";
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
6
6
|
import { forwardRef } from "react";
|
|
7
7
|
import { Label, ProgressBar } from "react-aria-components";
|
|
@@ -37,7 +37,13 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
37
37
|
*
|
|
38
38
|
* Both are in the build-failing contrast contract.
|
|
39
39
|
*/
|
|
40
|
-
const progressVariants = cva(["flex w-full flex-col
|
|
40
|
+
const progressVariants = cva(["flex w-full flex-col box-border font-sans"], {
|
|
41
|
+
variants: { headerEmpty: {
|
|
42
|
+
true: "gap-0",
|
|
43
|
+
false: "gap-1.5"
|
|
44
|
+
} },
|
|
45
|
+
defaultVariants: { headerEmpty: false }
|
|
46
|
+
});
|
|
41
47
|
const progressTrackVariants = cva(["relative h-2 w-full overflow-hidden rounded-pill box-border", "bg-muted border-(length:--border-width) border-border"]);
|
|
42
48
|
const progressFillVariants = cva(["h-full rounded-pill", "progress-fill"], {
|
|
43
49
|
variants: { tone: {
|
|
@@ -54,10 +60,10 @@ const progressFillVariants = cva(["h-full rounded-pill", "progress-fill"], {
|
|
|
54
60
|
* fill follows, and a check lands beside the final count once the bar is full.
|
|
55
61
|
* Any other text (a price, a step count) is shown as it is.
|
|
56
62
|
*/
|
|
57
|
-
function ProgressValue({ percentage, valueText }) {
|
|
63
|
+
function ProgressValue({ percentage, valueText, showCompletion }) {
|
|
58
64
|
const percent = Math.round(percentage);
|
|
59
65
|
const counted = useCountTo(percent);
|
|
60
|
-
const done = useAfter(percent >= 100, 240);
|
|
66
|
+
const done = useAfter(showCompletion && percent >= 100, 240);
|
|
61
67
|
const isPercent = valueText === `${percent}%`;
|
|
62
68
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [isPercent ? `${counted}%` : valueText, isPercent ? /* @__PURE__ */ jsx("span", {
|
|
63
69
|
"data-slot": "progress-done",
|
|
@@ -80,12 +86,12 @@ function ProgressValue({ percentage, valueText }) {
|
|
|
80
86
|
})
|
|
81
87
|
}) : null] });
|
|
82
88
|
}
|
|
83
|
-
const Progress = forwardRef(function Progress({ label, labelHidden, showValue, tone, trackClassName, fillClassName, className, ...props }, ref) {
|
|
89
|
+
const Progress = forwardRef(function Progress({ label, labelHidden, showValue, showCompletion = true, tone, trackClassName, fillClassName, className, ...props }, ref) {
|
|
84
90
|
return /* @__PURE__ */ jsx(ProgressBar, {
|
|
85
91
|
ref,
|
|
86
92
|
...props,
|
|
87
93
|
"data-slot": "progress",
|
|
88
|
-
className: cn(progressVariants(), className),
|
|
94
|
+
className: cn(progressVariants({ headerEmpty: Boolean(labelHidden && !showValue) }), className),
|
|
89
95
|
children: ({ percentage, isIndeterminate, valueText }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
90
96
|
"data-slot": "progress-header",
|
|
91
97
|
className: cnAll(showValue && "flex items-baseline justify-between gap-2"),
|
|
@@ -103,7 +109,8 @@ const Progress = forwardRef(function Progress({ label, labelHidden, showValue, t
|
|
|
103
109
|
className: "text-sm leading-(--leading-normal) text-muted-foreground tabular-nums",
|
|
104
110
|
children: /* @__PURE__ */ jsx(ProgressValue, {
|
|
105
111
|
percentage: percentage ?? 0,
|
|
106
|
-
valueText
|
|
112
|
+
valueText,
|
|
113
|
+
showCompletion
|
|
107
114
|
})
|
|
108
115
|
}) : null]
|
|
109
116
|
}), /* @__PURE__ */ jsx("div", {
|
|
@@ -112,9 +119,9 @@ const Progress = forwardRef(function Progress({ label, labelHidden, showValue, t
|
|
|
112
119
|
children: /* @__PURE__ */ jsx("div", {
|
|
113
120
|
"data-slot": "progress-fill",
|
|
114
121
|
"data-indeterminate": isIndeterminate || void 0,
|
|
115
|
-
"data-complete": (!tone || tone === "neutral") && !isIndeterminate && (percentage ?? 0) >= 100 ? "" : void 0,
|
|
122
|
+
"data-complete": showCompletion && (!tone || tone === "neutral") && !isIndeterminate && (percentage ?? 0) >= 100 ? "" : void 0,
|
|
116
123
|
className: cn(progressFillVariants({ tone }), fillClassName),
|
|
117
|
-
style:
|
|
124
|
+
style: isIndeterminate ? void 0 : { width: `${percentage ?? 0}%` }
|
|
118
125
|
})
|
|
119
126
|
})] })
|
|
120
127
|
});
|
|
@@ -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, 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 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\",\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; 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 /** 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\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n {\n label,\n labelHidden,\n showValue,\n showCompletion = true,\n tone,\n trackClassName,\n fillClassName,\n className,\n ...props\n },\n ref,\n) {\n return (\n <AriaProgressBar\n ref={ref}\n {...props}\n data-slot=\"progress\"\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) 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\n percentage={percentage ?? 0}\n valueText={valueText}\n showCompletion={showCompletion}\n />\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 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(progressFillVariants({ tone }), fillClassName)}\n style={isIndeterminate ? undefined : { width: `${percentage ?? 0}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAa,mBAAmB,IAAI,CAAC,2CAA2C,GAAG;CACjF,UAAU,EAQR,aAAa;EAAE,MAAM;EAAS,OAAO;CAAU,EACjD;CACA,iBAAiB,EAAE,aAAa,MAAM;AACxC,CAAC;AAED,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;;;;;;AA8DA,SAAS,cAAc,EACrB,YACA,WACA,kBAKC;CACD,MAAM,UAAU,KAAK,MAAM,UAAU;CACrC,MAAM,UAAU,WAAW,OAAO;CAClC,MAAM,OAAO,SAAS,kBAAkB,WAAW,KAAK,GAAG;CAC3D,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,EACE,OACA,aACA,WACA,iBAAiB,MACjB,MACA,gBACA,eACA,WACA,GAAG,SAEL,KACA;CACA,OACE,oBAACA,aAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,iBAAiB,EAAE,aAAa,QAAQ,eAAe,CAAC,SAAS,EAAE,CAAC,GAAG,SAAS;EAE5F,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;KACE,YAAY,cAAc;KACf;KACK;IACjB,CAAA;GACG,CAAA,IACJ,IACD;EACL,CAAA,GAAA,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,sBAAsB,GAAG,cAAc;GAgBrD,UAAA,oBAAC,OAAD;IACE,aAAU;IACV,sBAAoB,mBAAmB,KAAA;IAEvC,iBACE,mBAAmB,CAAC,QAAQ,SAAS,cAAc,CAAC,oBAAoB,cAAc,MAAM,MACxF,KACA,KAAA;IAEN,WAAW,GAAG,qBAAqB,EAAE,KAAK,CAAC,GAAG,aAAa;IAC3D,OAAO,kBAAkB,KAAA,IAAY,EAAE,OAAO,GAAG,cAAc,EAAE,GAAG;GACrE,CAAA;EACE,CAAA,CACL,EAAA,CAAA;CAEW,CAAA;AAErB,CAAC"}
|
|
@@ -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?: "default" | "card" | 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>>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { cn, cnAll } from "../lib/cn.js";
|
|
3
3
|
import { selectionLabelVariants, selectionRingVariants } from "../lib/selection-control.js";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
|
-
import { forwardRef } from "react";
|
|
5
|
+
import { createContext, forwardRef, useContext, useId } from "react";
|
|
6
6
|
import { Radio, RadioGroup } from "react-aria-components";
|
|
7
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/radio-group/radio-group.tsx
|
|
@@ -24,25 +24,79 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
24
24
|
* Checkbox's exact pair -- `bg-primary` with a `primary-strong` boundary --
|
|
25
25
|
* so a radio and a checkbox in the same form agree.
|
|
26
26
|
*/
|
|
27
|
-
const radioGroupVariants = cva(["
|
|
28
|
-
|
|
27
|
+
const radioGroupVariants = cva(["box-border font-sans"], {
|
|
28
|
+
variants: {
|
|
29
|
+
/** `card` lays the choices out as a grid; add `sm:grid-cols-2` and the like. */
|
|
30
|
+
variant: {
|
|
31
|
+
default: "flex flex-col gap-2",
|
|
32
|
+
card: "grid gap-3"
|
|
33
|
+
} },
|
|
34
|
+
defaultVariants: { variant: "default" }
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* The box and dot, shared by both variants. Only the default variant rings
|
|
38
|
+
* the dot itself: a card rings the whole card, and a second ring on the dot
|
|
39
|
+
* inside it would just be noise.
|
|
40
|
+
*/
|
|
41
|
+
const radioMark = [
|
|
29
42
|
"inline-flex size-4 shrink-0 items-center justify-center",
|
|
30
43
|
"rounded-full border-(length:--border-width) box-border",
|
|
31
|
-
"bg-
|
|
44
|
+
"bg-surface border-control-edge",
|
|
32
45
|
"radio-mark",
|
|
33
46
|
"group-data-[selected]:bg-primary group-data-[selected]:border-primary-strong",
|
|
34
|
-
"group-data-[selected]:text-primary-foreground"
|
|
35
|
-
|
|
47
|
+
"group-data-[selected]:text-primary-foreground"
|
|
48
|
+
];
|
|
49
|
+
const radioVariants = cva([...radioMark, selectionRingVariants()]);
|
|
50
|
+
/**
|
|
51
|
+
* One choice drawn as a selectable card: a bordered surface the whole of
|
|
52
|
+
* which is the hit target, with the title, an optional description and icon.
|
|
53
|
+
*
|
|
54
|
+
* Selection is shown twice, by the border AND by the dot, so it never rests
|
|
55
|
+
* on colour alone (WCAG 1.4.1). The border reuses StatCard's selected filter
|
|
56
|
+
* pair (`border-primary-strong`, hover darkens an unselected edge), so a
|
|
57
|
+
* selected card and a selected stat card read the same.
|
|
58
|
+
*/
|
|
59
|
+
const radioCardVariants = cva([
|
|
60
|
+
"group flex items-start gap-3 p-4",
|
|
61
|
+
"rounded-panel border-(length:--border-width) border-border bg-surface",
|
|
62
|
+
"box-border font-sans text-sm leading-(--leading-normal) text-foreground text-start",
|
|
63
|
+
"cursor-pointer select-none",
|
|
64
|
+
"motion-safe:transition-[border-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle",
|
|
65
|
+
"data-[hovered]:not-data-[selected]:border-muted-foreground",
|
|
66
|
+
"data-[selected]:border-primary-strong",
|
|
67
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)",
|
|
68
|
+
"outline-none",
|
|
69
|
+
"data-[focus-visible]:outline-solid",
|
|
70
|
+
"data-[focus-visible]:outline-(length:--focus-ring-width)",
|
|
71
|
+
"data-[focus-visible]:outline-offset-(--focus-ring-offset)",
|
|
72
|
+
"data-[focus-visible]:outline-(--color-ring)"
|
|
36
73
|
]);
|
|
37
|
-
const
|
|
74
|
+
const radioCardDotVariants = cva([...radioMark, "mt-0.5"]);
|
|
75
|
+
/** The group hands its variant down, so every Radio inside matches it. */
|
|
76
|
+
const RadioVariantContext = createContext("default");
|
|
77
|
+
const RadioGroup$1 = forwardRef(function RadioGroup$2({ className, variant = "default", children, ...props }, ref) {
|
|
38
78
|
return /* @__PURE__ */ jsx(RadioGroup, {
|
|
39
79
|
ref,
|
|
40
80
|
...props,
|
|
41
81
|
"data-slot": "radio-group",
|
|
42
|
-
|
|
82
|
+
"data-variant": variant === "default" ? void 0 : variant,
|
|
83
|
+
className: cn(radioGroupVariants({ variant }), className),
|
|
84
|
+
children: (renderProps) => /* @__PURE__ */ jsx(RadioVariantContext.Provider, {
|
|
85
|
+
value: variant,
|
|
86
|
+
children: typeof children === "function" ? children(renderProps) : children
|
|
87
|
+
})
|
|
43
88
|
});
|
|
44
89
|
});
|
|
45
|
-
const Radio$1 = forwardRef(function Radio
|
|
90
|
+
const Radio$1 = forwardRef(function Radio(props, ref) {
|
|
91
|
+
return useContext(RadioVariantContext) === "card" ? /* @__PURE__ */ jsx(RadioCard, {
|
|
92
|
+
...props,
|
|
93
|
+
ref
|
|
94
|
+
}) : /* @__PURE__ */ jsx(RadioDefault, {
|
|
95
|
+
...props,
|
|
96
|
+
ref
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
const RadioDefault = forwardRef(function RadioDefault({ className, children, description: _description, icon: _icon, ...props }, ref) {
|
|
46
100
|
return /* @__PURE__ */ jsx(Radio, {
|
|
47
101
|
...props,
|
|
48
102
|
inputRef: ref,
|
|
@@ -63,7 +117,53 @@ const Radio$1 = forwardRef(function Radio$2({ className, children, ...props }, r
|
|
|
63
117
|
})] })
|
|
64
118
|
});
|
|
65
119
|
});
|
|
120
|
+
const RadioCard = forwardRef(function RadioCard({ className, children, description, icon, ...props }, ref) {
|
|
121
|
+
const id = useId();
|
|
122
|
+
const titleId = `${id}-title`;
|
|
123
|
+
const descriptionId = `${id}-description`;
|
|
124
|
+
return /* @__PURE__ */ jsx(Radio, {
|
|
125
|
+
"aria-labelledby": children == null || props["aria-label"] != null ? void 0 : titleId,
|
|
126
|
+
"aria-describedby": description == null ? void 0 : descriptionId,
|
|
127
|
+
...props,
|
|
128
|
+
inputRef: ref,
|
|
129
|
+
"data-slot": "radio",
|
|
130
|
+
"data-variant": "card",
|
|
131
|
+
className: cn(radioCardVariants(), className),
|
|
132
|
+
children: ({ isSelected }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
133
|
+
icon == null ? null : /* @__PURE__ */ jsx("span", {
|
|
134
|
+
"aria-hidden": "true",
|
|
135
|
+
"data-slot": "radio-icon",
|
|
136
|
+
className: "mt-0.5 inline-flex shrink-0 text-text-secondary [&_svg]:size-4",
|
|
137
|
+
children: icon
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ jsxs("span", {
|
|
140
|
+
className: "flex min-w-0 flex-1 flex-col gap-1",
|
|
141
|
+
children: [children == null ? null : /* @__PURE__ */ jsx("span", {
|
|
142
|
+
id: titleId,
|
|
143
|
+
"data-slot": "radio-label",
|
|
144
|
+
className: "font-medium",
|
|
145
|
+
children
|
|
146
|
+
}), description == null ? null : /* @__PURE__ */ jsx("span", {
|
|
147
|
+
id: descriptionId,
|
|
148
|
+
"data-slot": "radio-description",
|
|
149
|
+
className: "text-text-secondary",
|
|
150
|
+
children: description
|
|
151
|
+
})]
|
|
152
|
+
}),
|
|
153
|
+
/* @__PURE__ */ jsx("span", {
|
|
154
|
+
"aria-hidden": "true",
|
|
155
|
+
"data-slot": "radio-dot",
|
|
156
|
+
className: radioCardDotVariants(),
|
|
157
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
158
|
+
"data-slot": "radio-indicator",
|
|
159
|
+
"data-selected": isSelected || void 0,
|
|
160
|
+
className: "radio-dot size-1.5 rounded-full bg-current"
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
] })
|
|
164
|
+
});
|
|
165
|
+
});
|
|
66
166
|
//#endregion
|
|
67
|
-
export { Radio$1 as Radio, RadioGroup$1 as RadioGroup, radioGroupVariants, radioVariants };
|
|
167
|
+
export { Radio$1 as Radio, RadioGroup$1 as RadioGroup, radioCardVariants, radioGroupVariants, radioVariants };
|
|
68
168
|
|
|
69
169
|
//# sourceMappingURL=radio-group.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-group.js","names":["RadioGroup","AriaRadioGroup","Radio","AriaRadio"],"sources":["../../src/radio-group/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Radio as AriaRadio,\n RadioGroup as AriaRadioGroup,\n type RadioGroupProps as AriaRadioGroupProps,\n type RadioProps as AriaRadioProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { selectionLabelVariants, selectionRingVariants } from \"../lib/selection-control.js\";\n\n/**\n * RadioGroup, ported from the legacy shadcn/Radix component onto React Aria.\n *\n * Census: 1 file, 3 uses -- `RadioGroup` 1, `RadioGroupItem` 2. The\n * smallest real surface in the migration, so the port is deliberately plain.\n *\n * THE LABEL MOVES INSIDE THE ITEM. Radix's `RadioGroupItem` is a bare\n * button and the caller pairs it with a separate `<Label htmlFor>`; React\n * Aria's `Radio` takes its label as `children` and wires the association\n * itself. That is the one rename a call site has to make, and it removes\n * the id bookkeeping.\n *\n * `border-primary` becomes `border-border` for the same reason it did on\n * Checkbox: brand orange is 2.44:1 on white and cannot legally be the only\n * thing describing a control's edge (WCAG 1.4.11). Selected reuses\n * Checkbox's exact pair -- `bg-primary` with a `primary-strong` boundary --\n * so a radio and a checkbox in the same form agree.\n */\nexport const radioGroupVariants = cva([\n \"flex flex-col gap-2\",\n \"box-border font-sans\",\n]);\n\nexport const radioVariants = cva([\n \"inline-flex size-4 shrink-0 items-center justify-center\",\n \"rounded-full border-(length:--border-width) box-border\",\n \"bg-background border-border\",\n // Colours ease, the mark dips under a press and springs back (see radio-mark).\n \"radio-mark\",\n \"group-data-[selected]:bg-primary group-data-[selected]:border-primary-strong\",\n \"group-data-[selected]:text-primary-foreground\",\n selectionRingVariants(),\n]);\n\n/**\n * A set of mutually exclusive choices.\n *\n * @example\n * <RadioGroup value={plan} onChange={setPlan} aria-label=\"Plan\">\n * <Radio value=\"free\">Free</Radio>\n * <Radio value=\"pro\">Pro</Radio>\n * </RadioGroup>\n */\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, \"className\" | \"style\"> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(function RadioGroup(\n { className, ...props },\n ref,\n) {\n return (\n <AriaRadioGroup\n ref={ref}\n {...props}\n data-slot=\"radio-group\"\n className={cn(radioGroupVariants(), className)}\n />\n );\n});\n\n/**\n * One choice.\n *\n * @example\n * <Radio value=\"pro\">Pro</Radio>\n */\nexport interface RadioProps extends Omit<AriaRadioProps, \"className\" | \"style\" | \"children\"> {\n /** The label, and this option's accessible name. */\n children?: ReactNode;\n /** Extra classes for the label. Merged last, so they win conflicts. */\n className?: string;\n}\n\nexport const Radio = forwardRef<HTMLInputElement, RadioProps>(function Radio(\n { className, children, ...props },\n ref,\n) {\n return (\n <AriaRadio\n {...props}\n // inputRef, not ref: AriaRadio's own ref targets the label it renders\n // around a visually-hidden native input, and a label cannot be\n // focused -- ref.current.focus() would silently no-op. inputRef is\n // React Aria's own escape hatch to the real control.\n inputRef={ref}\n data-slot=\"radio\"\n // justify-start, overriding the shared label's centring: a radio sits\n // in a vertical list where every label must start on the same line,\n // not centre itself in a 24px box.\n className={cn(cnAll(selectionLabelVariants(), \"justify-start\"), className)}\n >\n {({ isSelected }) => (\n <>\n <span aria-hidden=\"true\" data-slot=\"radio-dot\" className={radioVariants()}>\n {/*\n Always there: the state decides whether it shows, so the dot can\n spring in and shrink away. data-selected carries the state to a\n test, which can no longer read it from the dot's presence.\n */}\n <span\n data-slot=\"radio-indicator\"\n data-selected={isSelected || undefined}\n className=\"radio-dot size-1.5 rounded-full bg-current\"\n />\n </span>\n {children == null ? null : <span data-slot=\"radio-label\">{children}</span>}\n </>\n )}\n </AriaRadio>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,qBAAqB,IAAI,CACpC,uBACA,sBACF,CAAC;AAED,MAAa,gBAAgB,IAAI;CAC/B;CACA;CACA;CAEA;CACA;CACA;CACA,sBAAsB;AACxB,CAAC;AAgBD,MAAaA,eAAa,WAA4C,SAASA,aAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAACC,YAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAeD,MAAaC,UAAQ,WAAyC,SAASA,QACrE,EAAE,WAAW,UAAU,GAAG,SAC1B,KACA;CACA,OACE,oBAACC,OAAD;EACE,GAAI;EAKJ,UAAU;EACV,aAAU;EAIV,WAAW,GAAG,MAAM,uBAAuB,GAAG,eAAe,GAAG,SAAS;EAEvE,WAAA,EAAE,iBACF,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAY,WAAW,cAAc;GAMtE,UAAA,oBAAC,QAAD;IACE,aAAU;IACV,iBAAe,cAAc,KAAA;IAC7B,WAAU;GACX,CAAA;EACG,CAAA,GACL,YAAY,OAAO,OAAO,oBAAC,QAAD;GAAM,aAAU;GAAe;EAAe,CAAA,CACzE,EAAA,CAAA;CAEK,CAAA;AAEf,CAAC"}
|
|
1
|
+
{"version":3,"file":"radio-group.js","names":["RadioGroup","AriaRadioGroup","Radio","AriaRadio"],"sources":["../../src/radio-group/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { createContext, forwardRef, useContext, useId, type ReactNode } from \"react\";\nimport {\n Radio as AriaRadio,\n RadioGroup as AriaRadioGroup,\n type RadioGroupProps as AriaRadioGroupProps,\n type RadioProps as AriaRadioProps,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { selectionLabelVariants, selectionRingVariants } from \"../lib/selection-control.js\";\n\n/**\n * RadioGroup, ported from the legacy shadcn/Radix component onto React Aria.\n *\n * Census: 1 file, 3 uses -- `RadioGroup` 1, `RadioGroupItem` 2. The\n * smallest real surface in the migration, so the port is deliberately plain.\n *\n * THE LABEL MOVES INSIDE THE ITEM. Radix's `RadioGroupItem` is a bare\n * button and the caller pairs it with a separate `<Label htmlFor>`; React\n * Aria's `Radio` takes its label as `children` and wires the association\n * itself. That is the one rename a call site has to make, and it removes\n * the id bookkeeping.\n *\n * `border-primary` becomes `border-border` for the same reason it did on\n * Checkbox: brand orange is 2.44:1 on white and cannot legally be the only\n * thing describing a control's edge (WCAG 1.4.11). Selected reuses\n * Checkbox's exact pair -- `bg-primary` with a `primary-strong` boundary --\n * so a radio and a checkbox in the same form agree.\n */\nexport const radioGroupVariants = cva([\"box-border font-sans\"], {\n variants: {\n /** `card` lays the choices out as a grid; add `sm:grid-cols-2` and the like. */\n variant: {\n default: \"flex flex-col gap-2\",\n card: \"grid gap-3\",\n },\n },\n defaultVariants: { variant: \"default\" },\n});\n\n/**\n * The box and dot, shared by both variants. Only the default variant rings\n * the dot itself: a card rings the whole card, and a second ring on the dot\n * inside it would just be noise.\n */\nconst radioMark = [\n \"inline-flex size-4 shrink-0 items-center justify-center\",\n \"rounded-full border-(length:--border-width) box-border\",\n \"bg-surface border-control-edge\",\n // Colours ease, the mark dips under a press and springs back (see radio-mark).\n \"radio-mark\",\n \"group-data-[selected]:bg-primary group-data-[selected]:border-primary-strong\",\n \"group-data-[selected]:text-primary-foreground\",\n];\n\nexport const radioVariants = cva([...radioMark, selectionRingVariants()]);\n\n/**\n * One choice drawn as a selectable card: a bordered surface the whole of\n * which is the hit target, with the title, an optional description and icon.\n *\n * Selection is shown twice, by the border AND by the dot, so it never rests\n * on colour alone (WCAG 1.4.1). The border reuses StatCard's selected filter\n * pair (`border-primary-strong`, hover darkens an unselected edge), so a\n * selected card and a selected stat card read the same.\n */\nexport const radioCardVariants = cva([\n \"group flex items-start gap-3 p-4\",\n \"rounded-panel border-(length:--border-width) border-border bg-surface\",\n \"box-border font-sans text-sm leading-(--leading-normal) text-foreground text-start\",\n \"cursor-pointer select-none\",\n \"motion-safe:transition-[border-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n \"data-[hovered]:not-data-[selected]:border-muted-foreground\",\n \"data-[selected]:border-primary-strong\",\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n // The same ring as selectionRingVariants, on purpose not reused: that helper\n // reads the state from a `group-data-[focus-visible]:` parent, and here the\n // card IS the group, so its own `data-[focus-visible]:` is the state. Keep\n // the two in step. outline-solid is load-bearing: outline-none sets the\n // style the width utility resolves from (see selectionRingVariants).\n \"outline-none\",\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\nconst radioCardDotVariants = cva([...radioMark, \"mt-0.5\"]);\n\n/** Which look the group's radios take. */\nexport type RadioGroupVariant = \"default\" | \"card\";\n\n/** The group hands its variant down, so every Radio inside matches it. */\nconst RadioVariantContext = createContext<RadioGroupVariant>(\"default\");\n\n/**\n * A set of mutually exclusive choices.\n *\n * @example\n * <RadioGroup value={plan} onChange={setPlan} aria-label=\"Plan\">\n * <Radio value=\"free\">Free</Radio>\n * <Radio value=\"pro\">Pro</Radio>\n * </RadioGroup>\n */\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, \"className\" | \"style\"> {\n /**\n * `card` draws each choice as a selectable card, with a title, an optional\n * `description` and `icon` on each `Radio`. The keyboard model is the same:\n * one tab stop, arrow keys move and select.\n *\n * @default \"default\"\n */\n variant?: RadioGroupVariant;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(function RadioGroup(\n { className, variant = \"default\", children, ...props },\n ref,\n) {\n return (\n <AriaRadioGroup\n ref={ref}\n {...props}\n data-slot=\"radio-group\"\n data-variant={variant === \"default\" ? undefined : variant}\n className={cn(radioGroupVariants({ variant }), className)}\n >\n {(renderProps) => (\n <RadioVariantContext.Provider value={variant}>\n {typeof children === \"function\" ? children(renderProps) : children}\n </RadioVariantContext.Provider>\n )}\n </AriaRadioGroup>\n );\n});\n\n/**\n * One choice.\n *\n * @example\n * <Radio value=\"pro\">Pro</Radio>\n */\nexport interface RadioProps extends Omit<AriaRadioProps, \"className\" | \"style\" | \"children\"> {\n /** The label, and this option's accessible name. In a card, the title. */\n children?: ReactNode;\n /**\n * A line under the title, read as the option's description\n * (`aria-describedby`), not as part of its name. Card variant only; the\n * default variant ignores it.\n */\n description?: ReactNode;\n /** A decorative icon before the title. Card variant only; hidden from assistive tech. */\n icon?: ReactNode;\n /** Extra classes for the label (the whole card in the card variant). Merged last, so they win conflicts. */\n className?: string;\n}\n\nexport const Radio = forwardRef<HTMLInputElement, RadioProps>(function Radio(props, ref) {\n const variant = useContext(RadioVariantContext);\n return variant === \"card\" ? <RadioCard {...props} ref={ref} /> : <RadioDefault {...props} ref={ref} />;\n});\n\nconst RadioDefault = forwardRef<HTMLInputElement, RadioProps>(function RadioDefault(\n // description and icon belong to the card; pulled out so they never reach the DOM.\n { className, children, description: _description, icon: _icon, ...props },\n ref,\n) {\n return (\n <AriaRadio\n {...props}\n // inputRef, not ref: AriaRadio's own ref targets the label it renders\n // around a visually-hidden native input, and a label cannot be\n // focused -- ref.current.focus() would silently no-op. inputRef is\n // React Aria's own escape hatch to the real control.\n inputRef={ref}\n data-slot=\"radio\"\n // justify-start, overriding the shared label's centring: a radio sits\n // in a vertical list where every label must start on the same line,\n // not centre itself in a 24px box.\n className={cn(cnAll(selectionLabelVariants(), \"justify-start\"), className)}\n >\n {({ isSelected }) => (\n <>\n <span aria-hidden=\"true\" data-slot=\"radio-dot\" className={radioVariants()}>\n {/*\n Always there: the state decides whether it shows, so the dot can\n spring in and shrink away. data-selected carries the state to a\n test, which can no longer read it from the dot's presence.\n */}\n <span\n data-slot=\"radio-indicator\"\n data-selected={isSelected || undefined}\n className=\"radio-dot size-1.5 rounded-full bg-current\"\n />\n </span>\n {children == null ? null : <span data-slot=\"radio-label\">{children}</span>}\n </>\n )}\n </AriaRadio>\n );\n});\n\nconst RadioCard = forwardRef<HTMLInputElement, RadioProps>(function RadioCard(\n { className, children, description, icon, ...props },\n ref,\n) {\n const id = useId();\n const titleId = `${id}-title`;\n const descriptionId = `${id}-description`;\n return (\n <AriaRadio\n // The title alone names the option; the description describes it. Left\n // to the wrapping <label>, the name would be title and description run\n // together. A caller's own labelling wins: their aria-labelledby or\n // aria-describedby by the spread below, and their aria-label because the\n // title's aria-labelledby is then left off (it would outrank aria-label\n // in the name computation).\n aria-labelledby={children == null || props[\"aria-label\"] != null ? undefined : titleId}\n aria-describedby={description == null ? undefined : descriptionId}\n {...props}\n inputRef={ref}\n data-slot=\"radio\"\n data-variant=\"card\"\n className={cn(radioCardVariants(), className)}\n >\n {({ isSelected }) => (\n <>\n {icon == null ? null : (\n <span\n aria-hidden=\"true\"\n data-slot=\"radio-icon\"\n className=\"mt-0.5 inline-flex shrink-0 text-text-secondary [&_svg]:size-4\"\n >\n {icon}\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {children == null ? null : (\n <span id={titleId} data-slot=\"radio-label\" className=\"font-medium\">\n {children}\n </span>\n )}\n {description == null ? null : (\n <span id={descriptionId} data-slot=\"radio-description\" className=\"text-text-secondary\">\n {description}\n </span>\n )}\n </span>\n <span aria-hidden=\"true\" data-slot=\"radio-dot\" className={radioCardDotVariants()}>\n <span\n data-slot=\"radio-indicator\"\n data-selected={isSelected || undefined}\n className=\"radio-dot size-1.5 rounded-full bg-current\"\n />\n </span>\n </>\n )}\n </AriaRadio>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,qBAAqB,IAAI,CAAC,sBAAsB,GAAG;CAC9D,UAAU;;AAER,SAAS;EACP,SAAS;EACT,MAAM;CACR,EACF;CACA,iBAAiB,EAAE,SAAS,UAAU;AACxC,CAAC;;;;;;AAOD,MAAM,YAAY;CAChB;CACA;CACA;CAEA;CACA;CACA;AACF;AAEA,MAAa,gBAAgB,IAAI,CAAC,GAAG,WAAW,sBAAsB,CAAC,CAAC;;;;;;;;;;AAWxE,MAAa,oBAAoB,IAAI;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAMA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,uBAAuB,IAAI,CAAC,GAAG,WAAW,QAAQ,CAAC;;AAMzD,MAAM,sBAAsB,cAAiC,SAAS;AAwBtE,MAAaA,eAAa,WAA4C,SAASA,aAC7E,EAAE,WAAW,UAAU,WAAW,UAAU,GAAG,SAC/C,KACA;CACA,OACE,oBAACC,YAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,gBAAc,YAAY,YAAY,KAAA,IAAY;EAClD,WAAW,GAAG,mBAAmB,EAAE,QAAQ,CAAC,GAAG,SAAS;EAEtD,WAAA,gBACA,oBAAC,oBAAoB,UAArB;GAA8B,OAAO;GAClC,UAAA,OAAO,aAAa,aAAa,SAAS,WAAW,IAAI;EAC9B,CAAA;CAElB,CAAA;AAEpB,CAAC;AAuBD,MAAaC,UAAQ,WAAyC,SAAS,MAAM,OAAO,KAAK;CAEvF,OADgB,WAAW,mBACd,MAAM,SAAS,oBAAC,WAAD;EAAW,GAAI;EAAY;CAAM,CAAA,IAAI,oBAAC,cAAD;EAAc,GAAI;EAAY;CAAM,CAAA;AACvG,CAAC;AAED,MAAM,eAAe,WAAyC,SAAS,aAErE,EAAE,WAAW,UAAU,aAAa,cAAc,MAAM,OAAO,GAAG,SAClE,KACA;CACA,OACE,oBAACC,OAAD;EACE,GAAI;EAKJ,UAAU;EACV,aAAU;EAIV,WAAW,GAAG,MAAM,uBAAuB,GAAG,eAAe,GAAG,SAAS;EAEvE,WAAA,EAAE,iBACF,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAY,WAAW,cAAc;GAMtE,UAAA,oBAAC,QAAD;IACE,aAAU;IACV,iBAAe,cAAc,KAAA;IAC7B,WAAU;GACX,CAAA;EACG,CAAA,GACL,YAAY,OAAO,OAAO,oBAAC,QAAD;GAAM,aAAU;GAAe;EAAe,CAAA,CACzE,EAAA,CAAA;CAEK,CAAA;AAEf,CAAC;AAED,MAAM,YAAY,WAAyC,SAAS,UAClE,EAAE,WAAW,UAAU,aAAa,MAAM,GAAG,SAC7C,KACA;CACA,MAAM,KAAK,MAAM;CACjB,MAAM,UAAU,GAAG,GAAG;CACtB,MAAM,gBAAgB,GAAG,GAAG;CAC5B,OACE,oBAACA,OAAD;EAOE,mBAAiB,YAAY,QAAQ,MAAM,iBAAiB,OAAO,KAAA,IAAY;EAC/E,oBAAkB,eAAe,OAAO,KAAA,IAAY;EACpD,GAAI;EACJ,UAAU;EACV,aAAU;EACV,gBAAa;EACb,WAAW,GAAG,kBAAkB,GAAG,SAAS;EAE1C,WAAA,EAAE,iBACF,qBAAA,YAAA,EAAA,UAAA;GACG,QAAQ,OAAO,OACd,oBAAC,QAAD;IACE,eAAY;IACZ,aAAU;IACV,WAAU;IAET,UAAA;GACG,CAAA;GAER,qBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA,CACG,YAAY,OAAO,OAClB,oBAAC,QAAD;KAAM,IAAI;KAAS,aAAU;KAAc,WAAU;KAClD;IACG,CAAA,GAEP,eAAe,OAAO,OACrB,oBAAC,QAAD;KAAM,IAAI;KAAe,aAAU;KAAoB,WAAU;KAC9D,UAAA;IACG,CAAA,CAEJ;;GACN,oBAAC,QAAD;IAAM,eAAY;IAAO,aAAU;IAAY,WAAW,qBAAqB;IAC7E,UAAA,oBAAC,QAAD;KACE,aAAU;KACV,iBAAe,cAAc,KAAA;KAC7B,WAAU;IACX,CAAA;GACG,CAAA;EACN,EAAA,CAAA;CAEK,CAAA;AAEf,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/reactions/reactions.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reactions: a reaction bar for comments, posts and release notes. Each pill shows
|
|
5
|
+
* one reaction and how many people chose it; the viewer's own carry the primary
|
|
6
|
+
* tint and a filled glyph (never colour alone). Pressing a pill adds or removes
|
|
7
|
+
* the viewer's reaction, and the add button opens a compact picker that grows out
|
|
8
|
+
* of it. Use it for quick acknowledgement; when people need to say something
|
|
9
|
+
* specific, give them a reply field instead.
|
|
10
|
+
*
|
|
11
|
+
* Adding gives the icon a kick (it overshoots and settles), the count swaps to its
|
|
12
|
+
* new number, a pill that arrives or leaves opens or closes its slot so the others
|
|
13
|
+
* glide, and the bar follows its own height when it wraps. In the picker a shared
|
|
14
|
+
* highlight glides to the hovered or focused option and a caption names it.
|
|
15
|
+
* Arrow keys, Home and End move through the options. A change is announced
|
|
16
|
+
* politely ("Added Like. 3 reactions."). Reduced motion fades.
|
|
17
|
+
*/
|
|
18
|
+
export interface ReactionOption {
|
|
19
|
+
id: string;
|
|
20
|
+
/** The reaction's name, used on the pill, in the picker and in announcements. */
|
|
21
|
+
label: string;
|
|
22
|
+
/** The glyph: an svg that takes `currentColor`, 16px on a pill and 20px in the picker. */
|
|
23
|
+
icon: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export interface Reaction {
|
|
26
|
+
id: string;
|
|
27
|
+
count: number;
|
|
28
|
+
/** Whether the viewer chose it. */
|
|
29
|
+
reacted: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface ReactionsProps {
|
|
32
|
+
/** Every reaction the picker offers, in picker order. */
|
|
33
|
+
options: ReactionOption[];
|
|
34
|
+
/** Controlled reactions. Pills keep this order, and a reaction whose count reaches zero leaves the bar. */
|
|
35
|
+
value?: Reaction[];
|
|
36
|
+
/** The reactions when uncontrolled. */
|
|
37
|
+
defaultValue?: Reaction[];
|
|
38
|
+
/** Called with the next reactions and the change that produced them. */
|
|
39
|
+
onValueChange?: (next: Reaction[], change: {
|
|
40
|
+
id: string;
|
|
41
|
+
reacted: boolean;
|
|
42
|
+
}) => void;
|
|
43
|
+
/** Accessible name for the bar. @default "Reactions" */
|
|
44
|
+
label?: string;
|
|
45
|
+
/** Extra classes for the bar. Merged last. */
|
|
46
|
+
className?: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function Reactions({ options, value, defaultValue, onValueChange, label, className }: ReactionsProps): import("react").JSX.Element;
|
|
49
|
+
//#endregion
|
|
50
|
+
//# sourceMappingURL=reactions.d.ts.map
|