@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-group.js","names":["AriaButton","Menu","MenuItem"],"sources":["../../src/button-group/button-group.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type FocusEvent,\n type KeyboardEvent,\n type PointerEvent,\n type ReactNode,\n} from \"react\";\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport {\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n useMorphWidth,\n prefersReducedMotion,\n} from \"../lib/motion.js\";\nimport { Menu, MenuItem, MenuTrigger } from \"../menu/menu.js\";\n\nexport type ButtonGroupVariant = \"outline\" | \"solid\";\nexport type ButtonGroupSize = \"sm\" | \"md\";\nexport type ButtonGroupOrientation = \"horizontal\" | \"vertical\";\n\nexport interface ButtonGroupItem {\n /** Stable key for the segment. */\n id: string;\n /** Visible text and the accessible name. Changing it crossfades in place, so \"Share\" can answer \"Copied\". */\n label: string;\n /** Every other label this segment can show, such as [\"Copied\"]. The segment sizes to the widest, so a label change never resizes it. */\n reserve?: string[];\n /** An icon before the label. Swapping it for another morphs; re-rendering the same icon does not. Mark it `aria-hidden`. */\n icon?: ReactNode;\n /** Shows only the icon; the label becomes the accessible name and the tooltip. */\n iconOnly?: boolean;\n /** Visible content in place of the label, such as a live value. The label stays the accessible name. */\n content?: ReactNode;\n onSelect?: () => void;\n /** Renders the segment as a link. */\n href?: string;\n isDisabled?: boolean;\n}\n\nexport interface ButtonGroupMenuItem {\n id: string;\n label: string;\n /** An icon before the label. Mark it `aria-hidden`. */\n icon?: ReactNode;\n onSelect?: () => void;\n href?: string;\n isDisabled?: boolean;\n /** Colours the item as a destructive action. */\n destructive?: boolean;\n}\n\nexport interface ButtonGroupMenu {\n /** Accessible name and tooltip of the chevron segment, such as \"More actions\". */\n label: string;\n items: ButtonGroupMenuItem[];\n}\n\nexport interface ButtonGroupProps {\n items: ButtonGroupItem[];\n /** Adds a trailing chevron segment that opens these actions in a menu aligned to the group's edge. */\n menu?: ButtonGroupMenu;\n /** Accessible name of the group, such as \"Document actions\". */\n label: string;\n /** @default \"outline\" */\n variant?: ButtonGroupVariant;\n /** @default \"md\" */\n size?: ButtonGroupSize;\n /** @default \"horizontal\" */\n orientation?: ButtonGroupOrientation;\n /** When the row does not fit its container, segments with an icon drop their label and keep the icon. Horizontal only. @default true */\n collapseLabels?: boolean;\n /** Disables every segment and the menu. */\n isDisabled?: boolean;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** Key of the chevron segment. Plain text, because the HTML parser rewrites control characters in server-rendered attributes. */\nconst MENU = \"button-group-menu\";\n\n/** Names the icon element, so swapping Link for Check morphs while a re-render of the same icon stays still. */\nfunction iconKey(node: ReactNode): string {\n if (!isValidElement(node))\n return typeof node === \"string\" || typeof node === \"number\" ? String(node) : \"\";\n const type = node.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type?.displayName ?? type?.name ?? \"icon\");\n}\n\nconst PHASE =\n \"col-start-1 row-start-1 inline-flex items-center gap-2 whitespace-nowrap group-data-[size=sm]/bg:gap-1.5\";\n\nfunction LeavingPhase({\n id,\n onDone,\n children,\n}: {\n id: number;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n className={cn(PHASE + \" motion-safe:animate-label-out motion-reduce:animate-fade-out\")}\n >\n {children}\n </span>\n );\n}\n\n/**\n * Icon and label of one segment. Every reserved label sits invisibly in the same\n * grid cell, so the segment is as wide as its widest state and a new label\n * crossfades inside a fixed box: it rises in from a soft blur while the old one\n * lifts away. A label nobody reserved still never snaps: the slot springs to its\n * width.\n */\nfunction SegmentContent({ item }: { item: ButtonGroupItem }) {\n const contentRef = useRef<HTMLSpanElement>(null);\n const phaseRef = useRef<HTMLSpanElement>(null);\n const glyph = iconKey(item.icon);\n const key =\n glyph + \"|\" + (item.content !== undefined ? \"\\u0000content\" : item.iconOnly ? \"\" : item.label);\n useMorphWidth(contentRef, key);\n\n const collapsible = item.icon ? \"group-data-[compact]/bg:hidden\" : \"\";\n const node = (\n <>\n {item.icon ? (\n <span className=\"inline-flex flex-none [&_svg]:size-(--icon-sm)\">{item.icon}</span>\n ) : null}\n {item.content !== undefined ? (\n <span className=\"inline-flex items-center tabular-nums\">{item.content}</span>\n ) : item.iconOnly ? null : (\n <span className={cn(\"inline-block \" + collapsible)}>{item.label}</span>\n )}\n </>\n );\n const { leaving, changed, remove } = useLeavingItems(key, node, () => phaseRef.current);\n const sizers =\n item.iconOnly || item.content !== undefined\n ? []\n : [...new Set([item.label, ...(item.reserve ?? [])])];\n\n return (\n <span\n className=\"button-slot [--morph-clip:inset(-50%_calc(var(--spacing)*-3))]\"\n aria-hidden=\"true\"\n >\n <span ref={contentRef} className=\"inline-grid flex-none items-center justify-items-center\">\n {sizers.map((text) => (\n <span key={text} className={cn(PHASE + \" pointer-events-none invisible\")}>\n {item.icon ? <span className=\"inline-block size-4 flex-none\" /> : null}\n <span className={cn(\"inline-block \" + collapsible)}>{text}</span>\n </span>\n ))}\n {leaving.map((entry) => (\n <LeavingPhase key={entry.id} id={entry.id} onDone={remove}>\n {entry.value}\n </LeavingPhase>\n ))}\n <span\n key={key}\n ref={phaseRef}\n className={cn(\n PHASE + (changed ? \" motion-safe:animate-label-in motion-reduce:animate-fade-in\" : \"\"),\n )}\n >\n {node}\n </span>\n </span>\n </span>\n );\n}\n\nconst inside = (node: HTMLElement, x: number, y: number) => {\n const rect = node.getBoundingClientRect();\n return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;\n};\n/** Segments are found by their data-key, so the group needs no ref per segment. */\nconst segmentsIn = (root: HTMLElement | null) =>\n Array.from(root?.querySelectorAll<HTMLElement>(\":scope > [data-key]\") ?? []);\nconst segmentIn = (root: HTMLElement | null, key: string | null) =>\n key === null ? undefined : segmentsIn(root).find((node) => node.dataset.key === key);\n/** The segment's box inside the group at sub-pixel precision, undoing any scale an ancestor applies. */\nfunction boxOf(group: HTMLElement, node: HTMLElement) {\n const outer = group.getBoundingClientRect();\n const inner = node.getBoundingClientRect();\n // offsetWidth is rounded to whole pixels, so only a real transform (more than a pixel apart) counts as scale.\n const scale =\n group.offsetWidth && Math.abs(outer.width - group.offsetWidth) > 1\n ? outer.width / group.offsetWidth\n : 1;\n return [\n (inner.left - outer.left) / scale - group.clientLeft + group.scrollLeft,\n (inner.top - outer.top) / scale - group.clientTop + group.scrollTop,\n inner.width / scale,\n inner.height / scale,\n ] as const;\n}\nconst inert = (node: HTMLElement) =>\n (node as HTMLButtonElement).disabled || node.getAttribute(\"aria-disabled\") === \"true\";\n\nconst TRAVEL =\n \"translate var(--duration-snappy) var(--ease-snappy), width var(--duration-snappy) var(--ease-snappy), height var(--duration-snappy) var(--ease-snappy), opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE = \"opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE_INSTANT = \"opacity var(--duration-instant) var(--ease-settle)\";\n\nconst focusRing =\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-(--color-ring)\";\n\n/**\n * Related actions joined into one surface: a shared border and radius, hairline\n * dividers, no gaps. One soft highlight glides between segments under the\n * pointer or keyboard focus, the pressed segment answers in place, and an\n * optional chevron segment opens more actions in a menu aligned to the group's\n * edge.\n *\n * Tab visits every segment in order; the arrow keys along the orientation, Home\n * and End also move between them. A segment's label can change in place (\"Share\"\n * answers \"Copied\") without resizing it, and a label that changes right after its\n * segment is pressed is announced once.\n *\n * @example\n * <ButtonGroup label=\"Document actions\" items={[{ id: \"edit\", label: \"Edit\" }, { id: \"share\", label: \"Share\", reserve: [\"Copied\"] }]} />\n */\nexport function ButtonGroup({\n items,\n menu,\n label,\n variant = \"outline\",\n size = \"md\",\n orientation = \"horizontal\",\n collapseLabels = true,\n isDisabled = false,\n className,\n}: ButtonGroupProps) {\n const root = useRef<HTMLDivElement>(null);\n const highlight = useRef<HTMLSpanElement>(null);\n const vertical = orientation === \"vertical\";\n\n // The highlight sits on the pointer's segment, else the pressed one (touch), else the open menu's chevron, else keyboard focus.\n const [hover, setHover] = useState<string | null>(null);\n const [pressed, setPressed] = useState<string | null>(null);\n const [focused, setFocused] = useState<string | null>(null);\n const [menuOpen, setMenuOpen] = useState(false);\n const active = hover ?? pressed ?? (menuOpen ? MENU : null) ?? focused;\n const keys = [...items.map((item) => item.id), ...(menu ? [MENU] : [])];\n const activeIndex = active ? keys.indexOf(active) : -1;\n const inertKey = String(isDisabled) + items.map((item) => (item.isDisabled ? 1 : 0)).join(\"\");\n\n const shown = useRef<string | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const el = highlight.current;\n const group = root.current;\n if (!el || !group) return;\n const reduced = prefersReducedMotion();\n const node = segmentIn(group, active);\n // Hovering a disabled segment shows nothing; focus on one still shows where the keyboard is.\n if (!node || (inert(node) && active !== focused)) {\n if (shown.current) {\n el.style.transition = reduced ? FADE_INSTANT : FADE;\n el.style.opacity = \"0\";\n }\n shown.current = null;\n return;\n }\n const [x, y, width, height] = boxOf(group, node);\n // Arriving from nowhere, it appears in place; only moves between segments travel.\n const arriving = !shown.current || reduced;\n el.style.transition = arriving ? (reduced ? FADE_INSTANT : FADE) : TRAVEL;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n el.style.opacity = \"1\";\n shown.current = active;\n // inertKey re-checks a segment that turns disabled under a still pointer.\n }, [active, inertKey, focused]);\n\n // Segments resize when a label morphs or labels collapse; the highlight stays locked to its segment.\n const keyList = keys.join(\"\\u0000\");\n useEffect(() => {\n const group = root.current;\n const el = highlight.current;\n if (!group || !el || typeof ResizeObserver === \"undefined\") return;\n // Work is deferred a frame: a callback that changes layout inside the observer's own notification reports a loop error.\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const node = segmentIn(group, shown.current);\n if (!node) return;\n const [x, y, width, height] = boxOf(group, node);\n el.style.transition = FADE;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n });\n });\n observer.observe(group);\n segmentsIn(group).forEach((node) => observer.observe(node));\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [keyList]);\n\n // Labels collapse to icons when the row overflows, and come back once the container is wide enough for the full row again.\n const [overflowing, setCompact] = useState(false);\n const fullWidth = useRef(0);\n const blockedAt = useRef(0);\n const collapsible =\n collapseLabels &&\n !vertical &&\n items.some((item) => item.icon && !item.iconOnly && item.content === undefined);\n const compact = collapsible && overflowing;\n useEffect(() => {\n const group = root.current;\n const parent = group?.parentElement;\n if (!group || !parent || !collapsible || typeof ResizeObserver === \"undefined\") return;\n const check = () => {\n const box = getComputedStyle(parent);\n const available =\n parent.clientWidth - parseFloat(box.paddingLeft) - parseFloat(box.paddingRight);\n if (group.dataset.compact === undefined) {\n if (group.scrollWidth > group.clientWidth + 1) {\n fullWidth.current = group.scrollWidth + 2;\n blockedAt.current = available;\n setCompact(true);\n }\n } else if (available > blockedAt.current && available >= fullWidth.current) setCompact(false);\n };\n // Collapsing changes the layout being observed, so the check runs a frame later (see the highlight observer).\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(check);\n });\n observer.observe(group);\n observer.observe(parent);\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [collapsible, keyList]);\n\n // Pressing tracks the segment under the finger, so touch gets the same highlight a pointer gets on hover.\n useEffect(() => {\n if (!pressed) return;\n const release = () => setPressed(null);\n window.addEventListener(\"pointerup\", release);\n window.addEventListener(\"pointercancel\", release);\n return () => {\n window.removeEventListener(\"pointerup\", release);\n window.removeEventListener(\"pointercancel\", release);\n };\n }, [pressed]);\n\n const hit = (event: PointerEvent) => {\n for (const node of segmentsIn(root.current))\n if (inside(node, event.clientX, event.clientY))\n return inert(node) ? null : (node.dataset.key ?? null);\n return null;\n };\n const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"touch\") setHover(hit(event));\n };\n const onPointerLeave = () => setHover(null);\n const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {\n if (event.button === 0) setPressed(hit(event));\n };\n\n // Keyboard focus shows the highlight; a mouse click focuses without it, so nothing stays lit after the pointer leaves.\n const onFocus = (event: FocusEvent<HTMLDivElement>) => {\n const target = event.target as HTMLElement;\n const key = target.dataset.key;\n if (key === undefined) return;\n let visible = true;\n try {\n visible = target.matches(\":focus-visible\");\n } catch {\n /* older engines */\n }\n setFocused(visible ? key : null);\n };\n const onBlur = (event: FocusEvent<HTMLDivElement>) => {\n if (!root.current?.contains(event.relatedTarget)) setFocused(null);\n };\n\n // Tab visits every segment in order; arrow keys along the orientation, Home and End also move between them.\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.defaultPrevented) return;\n // Disabled segments stay focusable (aria-disabled), so the keyboard can find them and hear why nothing happens.\n const list = segmentsIn(root.current).filter((node) => !(node as HTMLButtonElement).disabled);\n const index = list.findIndex((node) => node === document.activeElement);\n if (index < 0) return;\n const rtl = !vertical && getComputedStyle(root.current!).direction === \"rtl\";\n const forward = vertical ? \"ArrowDown\" : rtl ? \"ArrowLeft\" : \"ArrowRight\";\n const backward = vertical ? \"ArrowUp\" : rtl ? \"ArrowRight\" : \"ArrowLeft\";\n const last = list.length - 1;\n const target =\n event.key === forward\n ? index === last\n ? 0\n : index + 1\n : event.key === backward\n ? index === 0\n ? last\n : index - 1\n : event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? last\n : -1;\n if (target < 0) return;\n event.preventDefault();\n list[target]!.focus();\n };\n\n // A label that changes right after its segment is pressed, such as \"Copied\", is announced once; the quiet revert is not.\n const [announcement, setAnnouncement] = useState(\"\");\n const labels = useRef<Map<string, string> | null>(null);\n const activated = useRef({ key: \"\", at: 0 });\n const labelList = items.map((item) => item.id + \"\\u0000\" + item.label).join(\"\\u0001\");\n useEffect(() => {\n const previous = labels.current;\n labels.current = new Map(items.map((item) => [item.id, item.label]));\n if (!previous) return;\n const recent = performance.now() - activated.current.at < 1000;\n const changed = items.filter(\n (item) =>\n recent &&\n item.id === activated.current.key &&\n previous.has(item.id) &&\n previous.get(item.id) !== item.label &&\n item.content === undefined,\n );\n\n if (changed.length) setAnnouncement(changed.map((item) => item.label).join(\", \"));\n // labelList carries the only part of items this effect reads.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [labelList]);\n\n const select = (item: ButtonGroupItem) => {\n activated.current = { key: item.id, at: performance.now() };\n item.onSelect?.();\n };\n const quiet = (index: number) =>\n activeIndex >= 0 && (index === activeIndex || index === activeIndex + 1) ? \"\" : undefined;\n\n const segments = items.map((item, index) => {\n const off = item.isDisabled && !isDisabled;\n const named = item.iconOnly || item.content !== undefined || (compact && !!item.icon);\n const common = {\n className: cn(\"button-group-segment \" + focusRing),\n \"data-key\": item.id,\n \"data-quiet\": quiet(index),\n \"data-icon-only\": item.iconOnly ? \"\" : undefined,\n \"data-collapsible\":\n item.icon && !item.iconOnly && item.content === undefined ? \"\" : undefined,\n \"aria-label\": named ? item.label : undefined,\n title: item.iconOnly || (compact && item.icon) ? item.label : undefined,\n };\n const body = (\n <span className=\"button-group-content\">\n <SegmentContent item={item} />\n </span>\n );\n const name = named ? null : <span className=\"sr-only\">{item.label}</span>;\n if (item.href && !off && !isDisabled)\n return (\n <a key={item.id} {...common} href={item.href} onClick={() => select(item)}>\n {body}\n {name}\n </a>\n );\n return (\n <button\n key={item.id}\n {...common}\n type=\"button\"\n disabled={isDisabled}\n aria-disabled={off || undefined}\n onClick={off ? undefined : () => select(item)}\n >\n {body}\n {name}\n </button>\n );\n });\n\n return (\n <div\n ref={root}\n role=\"group\"\n aria-label={label}\n aria-disabled={isDisabled || undefined}\n data-orientation={orientation}\n data-variant={variant}\n data-size={size}\n data-compact={compact ? \"\" : undefined}\n onPointerMove={onPointerMove}\n onPointerLeave={onPointerLeave}\n onPointerDown={onPointerDown}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n className={cn(\"button-group group/bg\", className)}\n >\n <span\n ref={highlight}\n aria-hidden=\"true\"\n data-pressed={pressed && pressed === active ? \"\" : undefined}\n className=\"button-group-highlight\"\n />\n {segments}\n {menu ? (\n <MenuTrigger onOpenChange={setMenuOpen}>\n <AriaButton\n data-key={MENU}\n data-trigger=\"\"\n data-quiet={quiet(items.length)}\n aria-label={menu.label}\n isDisabled={isDisabled}\n className={cn(\"button-group-segment group/trigger \" + focusRing)}\n >\n <span className=\"button-group-content\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className=\"size-4 motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </AriaButton>\n <Menu\n placement={vertical ? \"bottom start\" : \"bottom end\"}\n offset={6}\n shouldFocusWrap\n className={cn(\"menu-motion \" + (vertical ? \"[--menu-ox:left]\" : \"[--menu-ox:right]\"))}\n >\n {menu.items.map((action, index) => (\n <MenuItem\n key={action.id}\n id={action.id}\n textValue={action.label}\n href={action.href}\n isDisabled={action.isDisabled}\n onAction={action.onSelect}\n data-i={Math.min(index, 4)}\n className={cn(\n \"menu-item-motion gap-2.5\" +\n (action.destructive\n ? \" text-destructive-strong data-[focused]:bg-[color-mix(in_oklab,var(--color-destructive-strong)_8%,var(--color-card))]\"\n : \"\"),\n )}\n >\n {action.icon ? <span aria-hidden=\"true\">{action.icon}</span> : null}\n {action.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n ) : null}\n <span className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button-group.js","names":["AriaButton","Menu","MenuItem"],"sources":["../../src/button-group/button-group.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type FocusEvent,\n type KeyboardEvent,\n type PointerEvent,\n type ReactNode,\n} from \"react\";\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport {\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n useMorphWidth,\n prefersReducedMotion,\n} from \"../lib/motion.js\";\nimport { Menu, MenuItem, MenuTrigger } from \"../menu/menu.js\";\n\nexport type ButtonGroupVariant = \"outline\" | \"solid\";\nexport type ButtonGroupSize = \"sm\" | \"md\";\nexport type ButtonGroupOrientation = \"horizontal\" | \"vertical\";\n\nexport interface ButtonGroupItem {\n /** Stable key for the segment. */\n id: string;\n /** Visible text and the accessible name. Changing it crossfades in place, so \"Share\" can answer \"Copied\". */\n label: string;\n /** Every other label this segment can show, such as [\"Copied\"]. The segment sizes to the widest, so a label change never resizes it. */\n reserve?: string[];\n /** An icon before the label. Swapping it for another morphs; re-rendering the same icon does not. Mark it `aria-hidden`. */\n icon?: ReactNode;\n /** Shows only the icon; the label becomes the accessible name and the tooltip. */\n iconOnly?: boolean;\n /** Visible content in place of the label, such as a live value. The label stays the accessible name. */\n content?: ReactNode;\n onSelect?: () => void;\n /** Renders the segment as a link. */\n href?: string;\n isDisabled?: boolean;\n}\n\nexport interface ButtonGroupMenuItem {\n id: string;\n label: string;\n /** An icon before the label. Mark it `aria-hidden`. */\n icon?: ReactNode;\n onSelect?: () => void;\n href?: string;\n isDisabled?: boolean;\n /** Colours the item as a destructive action. */\n destructive?: boolean;\n}\n\nexport interface ButtonGroupMenu {\n /** Accessible name and tooltip of the chevron segment, such as \"More actions\". */\n label: string;\n items: ButtonGroupMenuItem[];\n}\n\nexport interface ButtonGroupProps {\n items: ButtonGroupItem[];\n /** Adds a trailing chevron segment that opens these actions in a menu aligned to the group's edge. */\n menu?: ButtonGroupMenu;\n /** Accessible name of the group, such as \"Document actions\". */\n label: string;\n /** @default \"outline\" */\n variant?: ButtonGroupVariant;\n /** @default \"md\" */\n size?: ButtonGroupSize;\n /** @default \"horizontal\" */\n orientation?: ButtonGroupOrientation;\n /** When the row does not fit its container, segments with an icon drop their label and keep the icon. Horizontal only. @default true */\n collapseLabels?: boolean;\n /** Disables every segment and the menu. */\n isDisabled?: boolean;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** Key of the chevron segment. Plain text, because the HTML parser rewrites control characters in server-rendered attributes. */\nconst MENU = \"button-group-menu\";\n\n/** Names the icon element, so swapping Link for Check morphs while a re-render of the same icon stays still. */\nfunction iconKey(node: ReactNode): string {\n if (!isValidElement(node))\n return typeof node === \"string\" || typeof node === \"number\" ? String(node) : \"\";\n const type = node.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type?.displayName ?? type?.name ?? \"icon\");\n}\n\nconst PHASE =\n \"col-start-1 row-start-1 inline-flex items-center gap-2 whitespace-nowrap group-data-[size=sm]/bg:gap-1.5\";\n\nfunction LeavingPhase({\n id,\n onDone,\n children,\n}: {\n id: number;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n className={cn(PHASE + \" motion-safe:animate-label-out motion-reduce:animate-fade-out\")}\n >\n {children}\n </span>\n );\n}\n\n/**\n * Icon and label of one segment. Every reserved label sits invisibly in the same\n * grid cell, so the segment is as wide as its widest state and a new label\n * crossfades inside a fixed box: it rises in from a soft blur while the old one\n * lifts away. A label nobody reserved still never snaps: the slot springs to its\n * width.\n */\nfunction SegmentContent({ item }: { item: ButtonGroupItem }) {\n const contentRef = useRef<HTMLSpanElement>(null);\n const phaseRef = useRef<HTMLSpanElement>(null);\n const glyph = iconKey(item.icon);\n const key =\n glyph + \"|\" + (item.content !== undefined ? \"\\u0000content\" : item.iconOnly ? \"\" : item.label);\n useMorphWidth(contentRef, key);\n\n const collapsible = item.icon ? \"group-data-[compact]/bg:hidden\" : \"\";\n const node = (\n <>\n {item.icon ? (\n <span className=\"inline-flex flex-none [&_svg]:size-(--icon-sm)\">{item.icon}</span>\n ) : null}\n {item.content !== undefined ? (\n <span className=\"inline-flex items-center tabular-nums\">{item.content}</span>\n ) : item.iconOnly ? null : (\n <span className={cn(\"inline-block \" + collapsible)}>{item.label}</span>\n )}\n </>\n );\n const { leaving, changed, remove } = useLeavingItems(key, node, () => phaseRef.current);\n const sizers =\n item.iconOnly || item.content !== undefined\n ? []\n : [...new Set([item.label, ...(item.reserve ?? [])])];\n\n return (\n <span\n className=\"button-slot [--morph-clip:inset(-50%_calc(var(--spacing)*-3))]\"\n aria-hidden=\"true\"\n >\n <span ref={contentRef} className=\"inline-grid flex-none items-center justify-items-center\">\n {sizers.map((text) => (\n <span key={text} className={cn(PHASE + \" pointer-events-none invisible\")}>\n {item.icon ? <span className=\"inline-block size-4 flex-none\" /> : null}\n <span className={cn(\"inline-block \" + collapsible)}>{text}</span>\n </span>\n ))}\n {leaving.map((entry) => (\n <LeavingPhase key={entry.id} id={entry.id} onDone={remove}>\n {entry.value}\n </LeavingPhase>\n ))}\n <span\n key={key}\n ref={phaseRef}\n className={cn(\n PHASE + (changed ? \" motion-safe:animate-label-in motion-reduce:animate-fade-in\" : \"\"),\n )}\n >\n {node}\n </span>\n </span>\n </span>\n );\n}\n\nconst inside = (node: HTMLElement, x: number, y: number) => {\n const rect = node.getBoundingClientRect();\n return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;\n};\n/** Segments are found by their data-key, so the group needs no ref per segment. */\nconst segmentsIn = (root: HTMLElement | null) =>\n Array.from(root?.querySelectorAll<HTMLElement>(\":scope > [data-key]\") ?? []);\nconst segmentIn = (root: HTMLElement | null, key: string | null) =>\n key === null ? undefined : segmentsIn(root).find((node) => node.dataset.key === key);\n/** The segment's box inside the group at sub-pixel precision, undoing any scale an ancestor applies. */\nfunction boxOf(group: HTMLElement, node: HTMLElement) {\n const outer = group.getBoundingClientRect();\n const inner = node.getBoundingClientRect();\n // offsetWidth is rounded to whole pixels, so only a real transform (more than a pixel apart) counts as scale.\n const scale =\n group.offsetWidth && Math.abs(outer.width - group.offsetWidth) > 1\n ? outer.width / group.offsetWidth\n : 1;\n return [\n (inner.left - outer.left) / scale - group.clientLeft + group.scrollLeft,\n (inner.top - outer.top) / scale - group.clientTop + group.scrollTop,\n inner.width / scale,\n inner.height / scale,\n ] as const;\n}\nconst inert = (node: HTMLElement) =>\n (node as HTMLButtonElement).disabled || node.getAttribute(\"aria-disabled\") === \"true\";\n\nconst TRAVEL =\n \"translate var(--duration-snappy) var(--ease-snappy), width var(--duration-snappy) var(--ease-snappy), height var(--duration-snappy) var(--ease-snappy), opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE = \"opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE_INSTANT = \"opacity var(--duration-instant) var(--ease-settle)\";\n\nconst focusRing =\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-(--color-ring)\";\n\n/**\n * Related actions joined into one surface: a shared border and radius, hairline\n * dividers, no gaps. One soft highlight glides between segments under the\n * pointer or keyboard focus, the pressed segment answers in place, and an\n * optional chevron segment opens more actions in a menu aligned to the group's\n * edge.\n *\n * Tab visits every segment in order; the arrow keys along the orientation, Home\n * and End also move between them. A segment's label can change in place (\"Share\"\n * answers \"Copied\") without resizing it, and a label that changes right after its\n * segment is pressed is announced once.\n *\n * @example\n * <ButtonGroup label=\"Document actions\" items={[{ id: \"edit\", label: \"Edit\" }, { id: \"share\", label: \"Share\", reserve: [\"Copied\"] }]} />\n */\nexport function ButtonGroup({\n items,\n menu,\n label,\n variant = \"outline\",\n size = \"md\",\n orientation = \"horizontal\",\n collapseLabels = true,\n isDisabled = false,\n className,\n}: ButtonGroupProps) {\n const root = useRef<HTMLDivElement>(null);\n const highlight = useRef<HTMLSpanElement>(null);\n const vertical = orientation === \"vertical\";\n\n // The highlight sits on the pointer's segment, else the pressed one (touch), else the open menu's chevron, else keyboard focus.\n const [hover, setHover] = useState<string | null>(null);\n const [pressed, setPressed] = useState<string | null>(null);\n const [focused, setFocused] = useState<string | null>(null);\n const [menuOpen, setMenuOpen] = useState(false);\n const active = hover ?? pressed ?? (menuOpen ? MENU : null) ?? focused;\n const keys = [...items.map((item) => item.id), ...(menu ? [MENU] : [])];\n const activeIndex = active ? keys.indexOf(active) : -1;\n const inertKey = String(isDisabled) + items.map((item) => (item.isDisabled ? 1 : 0)).join(\"\");\n\n const shown = useRef<string | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const el = highlight.current;\n const group = root.current;\n if (!el || !group) return;\n const reduced = prefersReducedMotion();\n const node = segmentIn(group, active);\n // Hovering a disabled segment shows nothing; focus on one still shows where the keyboard is.\n if (!node || (inert(node) && active !== focused)) {\n if (shown.current) {\n el.style.transition = reduced ? FADE_INSTANT : FADE;\n el.style.opacity = \"0\";\n }\n shown.current = null;\n return;\n }\n const [x, y, width, height] = boxOf(group, node);\n // Arriving from nowhere, it appears in place; only moves between segments travel.\n const arriving = !shown.current || reduced;\n el.style.transition = arriving ? (reduced ? FADE_INSTANT : FADE) : TRAVEL;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n el.style.opacity = \"1\";\n shown.current = active;\n // inertKey re-checks a segment that turns disabled under a still pointer.\n }, [active, inertKey, focused]);\n\n // Segments resize when a label morphs or labels collapse; the highlight stays locked to its segment.\n const keyList = keys.join(\"\\u0000\");\n useEffect(() => {\n const group = root.current;\n const el = highlight.current;\n if (!group || !el || typeof ResizeObserver === \"undefined\") return;\n // Work is deferred a frame: a callback that changes layout inside the observer's own notification reports a loop error.\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const node = segmentIn(group, shown.current);\n if (!node) return;\n const [x, y, width, height] = boxOf(group, node);\n el.style.transition = FADE;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n });\n });\n observer.observe(group);\n segmentsIn(group).forEach((node) => observer.observe(node));\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [keyList]);\n\n // Labels collapse to icons when the row overflows, and come back once the container is wide enough for the full row again.\n const [overflowing, setCompact] = useState(false);\n const fullWidth = useRef(0);\n const blockedAt = useRef(0);\n const collapsible =\n collapseLabels &&\n !vertical &&\n items.some((item) => item.icon && !item.iconOnly && item.content === undefined);\n const compact = collapsible && overflowing;\n useEffect(() => {\n const group = root.current;\n const parent = group?.parentElement;\n if (!group || !parent || !collapsible || typeof ResizeObserver === \"undefined\") return;\n const check = () => {\n const box = getComputedStyle(parent);\n const available =\n parent.clientWidth - parseFloat(box.paddingLeft) - parseFloat(box.paddingRight);\n if (group.dataset.compact === undefined) {\n if (group.scrollWidth > group.clientWidth + 1) {\n fullWidth.current = group.scrollWidth + 2;\n blockedAt.current = available;\n setCompact(true);\n }\n } else if (available > blockedAt.current && available >= fullWidth.current) setCompact(false);\n };\n // Collapsing changes the layout being observed, so the check runs a frame later (see the highlight observer).\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(check);\n });\n observer.observe(group);\n observer.observe(parent);\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [collapsible, keyList]);\n\n // Pressing tracks the segment under the finger, so touch gets the same highlight a pointer gets on hover.\n useEffect(() => {\n if (!pressed) return;\n const release = () => setPressed(null);\n window.addEventListener(\"pointerup\", release);\n window.addEventListener(\"pointercancel\", release);\n return () => {\n window.removeEventListener(\"pointerup\", release);\n window.removeEventListener(\"pointercancel\", release);\n };\n }, [pressed]);\n\n const hit = (event: PointerEvent) => {\n for (const node of segmentsIn(root.current))\n if (inside(node, event.clientX, event.clientY))\n return inert(node) ? null : (node.dataset.key ?? null);\n return null;\n };\n const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"touch\") setHover(hit(event));\n };\n const onPointerLeave = () => setHover(null);\n const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {\n if (event.button === 0) setPressed(hit(event));\n };\n\n // Keyboard focus shows the highlight; a mouse click focuses without it, so nothing stays lit after the pointer leaves.\n const onFocus = (event: FocusEvent<HTMLDivElement>) => {\n const target = event.target as HTMLElement;\n const key = target.dataset.key;\n if (key === undefined) return;\n let visible = true;\n try {\n visible = target.matches(\":focus-visible\");\n } catch {\n /* older engines */\n }\n setFocused(visible ? key : null);\n };\n const onBlur = (event: FocusEvent<HTMLDivElement>) => {\n if (!root.current?.contains(event.relatedTarget)) setFocused(null);\n };\n\n // Tab visits every segment in order; arrow keys along the orientation, Home and End also move between them.\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.defaultPrevented) return;\n // Disabled segments stay focusable (aria-disabled), so the keyboard can find them and hear why nothing happens.\n const list = segmentsIn(root.current).filter((node) => !(node as HTMLButtonElement).disabled);\n const index = list.findIndex((node) => node === document.activeElement);\n if (index < 0) return;\n const rtl = !vertical && getComputedStyle(root.current!).direction === \"rtl\";\n const forward = vertical ? \"ArrowDown\" : rtl ? \"ArrowLeft\" : \"ArrowRight\";\n const backward = vertical ? \"ArrowUp\" : rtl ? \"ArrowRight\" : \"ArrowLeft\";\n const last = list.length - 1;\n const target =\n event.key === forward\n ? index === last\n ? 0\n : index + 1\n : event.key === backward\n ? index === 0\n ? last\n : index - 1\n : event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? last\n : -1;\n if (target < 0) return;\n event.preventDefault();\n list[target]!.focus();\n };\n\n // A label that changes right after its segment is pressed, such as \"Copied\", is announced once; the quiet revert is not.\n const [announcement, setAnnouncement] = useState(\"\");\n const labels = useRef<Map<string, string> | null>(null);\n const activated = useRef({ key: \"\", at: 0 });\n const labelList = items.map((item) => item.id + \"\\u0000\" + item.label).join(\"\\u0001\");\n useEffect(() => {\n const previous = labels.current;\n labels.current = new Map(items.map((item) => [item.id, item.label]));\n if (!previous) return;\n const recent = performance.now() - activated.current.at < 1000;\n const changed = items.filter(\n (item) =>\n recent &&\n item.id === activated.current.key &&\n previous.has(item.id) &&\n previous.get(item.id) !== item.label &&\n item.content === undefined,\n );\n\n if (changed.length) setAnnouncement(changed.map((item) => item.label).join(\", \"));\n // labelList carries the only part of items this effect reads.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [labelList]);\n\n const select = (item: ButtonGroupItem) => {\n activated.current = { key: item.id, at: performance.now() };\n item.onSelect?.();\n };\n const quiet = (index: number) =>\n activeIndex >= 0 && (index === activeIndex || index === activeIndex + 1) ? \"\" : undefined;\n\n const segments = items.map((item, index) => {\n const off = item.isDisabled && !isDisabled;\n const named = item.iconOnly || item.content !== undefined || (compact && !!item.icon);\n const common = {\n className: cn(\"button-group-segment \" + focusRing),\n \"data-key\": item.id,\n \"data-quiet\": quiet(index),\n \"data-icon-only\": item.iconOnly ? \"\" : undefined,\n \"data-collapsible\":\n item.icon && !item.iconOnly && item.content === undefined ? \"\" : undefined,\n \"aria-label\": named ? item.label : undefined,\n title: item.iconOnly || (compact && item.icon) ? item.label : undefined,\n };\n const body = (\n <span className=\"button-group-content\">\n <SegmentContent item={item} />\n </span>\n );\n const name = named ? null : <span className=\"sr-only\">{item.label}</span>;\n if (item.href && !off && !isDisabled)\n return (\n <a key={item.id} {...common} href={item.href} onClick={() => select(item)}>\n {body}\n {name}\n </a>\n );\n return (\n <button\n key={item.id}\n {...common}\n type=\"button\"\n disabled={isDisabled}\n aria-disabled={off || undefined}\n onClick={off ? undefined : () => select(item)}\n >\n {body}\n {name}\n </button>\n );\n });\n\n return (\n <div\n ref={root}\n role=\"group\"\n aria-label={label}\n aria-disabled={isDisabled || undefined}\n data-orientation={orientation}\n data-variant={variant}\n data-size={size}\n data-compact={compact ? \"\" : undefined}\n onPointerMove={onPointerMove}\n onPointerLeave={onPointerLeave}\n onPointerDown={onPointerDown}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n className={cn(\"button-group group/bg\", className)}\n >\n <span\n ref={highlight}\n aria-hidden=\"true\"\n data-pressed={pressed && pressed === active ? \"\" : undefined}\n className=\"button-group-highlight\"\n />\n {segments}\n {menu ? (\n <MenuTrigger onOpenChange={setMenuOpen}>\n <AriaButton\n data-key={MENU}\n data-trigger=\"\"\n data-quiet={quiet(items.length)}\n aria-label={menu.label}\n isDisabled={isDisabled}\n className={cn(\"button-group-segment group/trigger \" + focusRing)}\n >\n <span className=\"button-group-content\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className=\"size-4 motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </AriaButton>\n <Menu placement={vertical ? \"bottom start\" : \"bottom end\"} offset={6} shouldFocusWrap>\n {menu.items.map((action) => (\n <MenuItem\n key={action.id}\n id={action.id}\n textValue={action.label}\n href={action.href}\n isDisabled={action.isDisabled}\n onAction={action.onSelect}\n destructive={action.destructive}\n >\n {action.icon ? (\n <span\n aria-hidden=\"true\"\n className={action.destructive ? \"inline-flex\" : \"inline-flex text-text-secondary\"}\n >\n {action.icon}\n </span>\n ) : null}\n {action.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n ) : null}\n <span className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -50,7 +50,7 @@ export declare const calendarClearVariants: (props?: import("class-variance-auth
|
|
|
50
50
|
* is a strikethrough.
|
|
51
51
|
*/
|
|
52
52
|
export declare const calendarCellVariants: (props?: ({
|
|
53
|
-
selection?: "
|
|
53
|
+
selection?: "range" | "single" | "band" | null | undefined;
|
|
54
54
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
55
55
|
interface CalendarSharedProps {
|
|
56
56
|
/**
|
package/dist/card/card.d.ts
CHANGED
|
@@ -28,7 +28,10 @@ import { ComponentPropsWithoutRef } from "react";
|
|
|
28
28
|
* source text, so a class assembled at runtime is invisible to it and would
|
|
29
29
|
* simply never be emitted.
|
|
30
30
|
*/
|
|
31
|
-
export declare const cardVariants: (props?:
|
|
31
|
+
export declare const cardVariants: (props?: ({
|
|
32
|
+
flush?: boolean | null | undefined;
|
|
33
|
+
highlighted?: boolean | null | undefined;
|
|
34
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
32
35
|
/**
|
|
33
36
|
* Padding is inline-only on the sections and block-only on the shell.
|
|
34
37
|
*
|
|
@@ -60,6 +63,24 @@ export interface CardProps extends ComponentPropsWithoutRef<"div"> {
|
|
|
60
63
|
* Leave it off for a card that is not a target: a static card should not move.
|
|
61
64
|
*/
|
|
62
65
|
interactive?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* A featured card: a brand gradient edge in place of the hairline. Use it for the
|
|
68
|
+
* one card a view wants the eye to land on (a recommended action, the suggested
|
|
69
|
+
* plan), never for several at once. The edge is decoration, so say WHY the card
|
|
70
|
+
* is featured in its content too: colour alone does not reach a screen reader.
|
|
71
|
+
* @default false
|
|
72
|
+
*/
|
|
73
|
+
highlighted?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Edge-to-edge content: no shell padding and no gap between the parts, and the
|
|
76
|
+
* content is clipped to the rounded border. The outline, radius, surface and
|
|
77
|
+
* shadow stay. For a table, list or full-bleed chart that must meet the card's
|
|
78
|
+
* top and bottom. The header and footer keep a 24px block inset so their text
|
|
79
|
+
* clears the corner curve, and every section keeps its inline padding: a
|
|
80
|
+
* section's fill already reaches the side edges.
|
|
81
|
+
* @default false
|
|
82
|
+
*/
|
|
83
|
+
flush?: boolean;
|
|
63
84
|
}
|
|
64
85
|
export declare const Card: import("react").ForwardRefExoticComponent<CardProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
65
86
|
/** Title and description, stacked. */
|
package/dist/card/card.js
CHANGED
|
@@ -33,11 +33,59 @@ import { jsx } from "react/jsx-runtime";
|
|
|
33
33
|
* simply never be emitted.
|
|
34
34
|
*/
|
|
35
35
|
const cardVariants = cva([
|
|
36
|
-
"flex flex-col
|
|
37
|
-
"rounded-
|
|
36
|
+
"flex flex-col",
|
|
37
|
+
"rounded-panel border-(length:--border-width) border-border",
|
|
38
38
|
"bg-card text-card-foreground shadow-surface",
|
|
39
39
|
"box-border font-sans"
|
|
40
|
-
]
|
|
40
|
+
], {
|
|
41
|
+
variants: {
|
|
42
|
+
/**
|
|
43
|
+
* The block padding and the gap live here, not in the base, so `flush`
|
|
44
|
+
* REPLACES them rather than relying on a merge to drop them: the
|
|
45
|
+
* exported `cardVariants({ flush: true })` is correct on its own.
|
|
46
|
+
*
|
|
47
|
+
* Flush keeps the outline, radius, surface and shadow, and clips its
|
|
48
|
+
* content to the inside of the rounded border: without the clip a filled
|
|
49
|
+
* child (a table header, a chart) shows square corners over the curve and
|
|
50
|
+
* covers the left and right hairline. `overflow: clip`, not `hidden`, so
|
|
51
|
+
* the card does not become a scroll container and a sticky table header
|
|
52
|
+
* inside it still sticks to the page. The sections keep their inline
|
|
53
|
+
* padding, so text and a section's fill stay clear of the corner curve
|
|
54
|
+
* (the fill still meets the side edges: background paints the padding).
|
|
55
|
+
*
|
|
56
|
+
* The header and footer hold text, so under flush they keep the 24px
|
|
57
|
+
* block inset the shell gave up. Without it the title and the footer line
|
|
58
|
+
* sat against the top and bottom borders, inside the corner curve, and
|
|
59
|
+
* the clip cut their first glyphs. The inset is a custom property both
|
|
60
|
+
* sections read, set per variant (never left undefined), so a nested
|
|
61
|
+
* default card resets it, and a section's own `py-*` still wins by merge.
|
|
62
|
+
* CardContent stays block-flush: a table or chart meets the edges.
|
|
63
|
+
*/
|
|
64
|
+
flush: {
|
|
65
|
+
false: "gap-6 py-6 [--card-section-py:--spacing(0)]",
|
|
66
|
+
true: "gap-0 py-0 overflow-clip [--card-section-py:--spacing(6)]"
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* A featured card: the hairline is replaced by a brand gradient edge,
|
|
70
|
+
* painted by the `card-highlighted` utility in styles.css. The border
|
|
71
|
+
* stays (transparent), so a highlighted card has exactly the box of a
|
|
72
|
+
* plain one and a list of mixed cards does not shift.
|
|
73
|
+
*/
|
|
74
|
+
highlighted: {
|
|
75
|
+
false: "",
|
|
76
|
+
true: "card-highlighted border-transparent"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
compoundVariants: [{
|
|
80
|
+
flush: true,
|
|
81
|
+
highlighted: true,
|
|
82
|
+
class: "card-highlighted-flush"
|
|
83
|
+
}],
|
|
84
|
+
defaultVariants: {
|
|
85
|
+
flush: false,
|
|
86
|
+
highlighted: false
|
|
87
|
+
}
|
|
88
|
+
});
|
|
41
89
|
/**
|
|
42
90
|
* Padding is inline-only on the sections and block-only on the shell.
|
|
43
91
|
*
|
|
@@ -45,18 +93,20 @@ const cardVariants = cva([
|
|
|
45
93
|
* consumer run a divider or a full-bleed image edge to edge, which a single
|
|
46
94
|
* `p-6` on the shell would make impossible.
|
|
47
95
|
*/
|
|
48
|
-
const cardHeaderVariants = cva("flex flex-col gap-1.5 px-6");
|
|
96
|
+
const cardHeaderVariants = cva("flex flex-col gap-1.5 px-6 py-(--card-section-py)");
|
|
49
97
|
const cardTitleVariants = cva(["text-base leading-(--leading-none) font-semibold", "m-0"]);
|
|
50
98
|
const cardDescriptionVariants = cva(["text-sm leading-(--leading-normal) text-muted-foreground"]);
|
|
51
99
|
const cardContentVariants = cva("px-6");
|
|
52
|
-
const cardFooterVariants = cva("flex items-center px-6");
|
|
53
|
-
const Card = forwardRef(function Card({ className, interactive = false, onPointerEnter, onPointerLeave, ...props }, ref) {
|
|
100
|
+
const cardFooterVariants = cva("flex items-center px-6 py-(--card-section-py)");
|
|
101
|
+
const Card = forwardRef(function Card({ className, interactive = false, highlighted = false, flush = false, onPointerEnter, onPointerLeave, ...props }, ref) {
|
|
54
102
|
const [pointed, setPointed] = useState(false);
|
|
55
103
|
return /* @__PURE__ */ jsx("div", {
|
|
56
104
|
ref,
|
|
57
105
|
...props,
|
|
58
106
|
"data-slot": "card",
|
|
59
107
|
"data-interactive": interactive || void 0,
|
|
108
|
+
"data-highlighted": highlighted || void 0,
|
|
109
|
+
"data-flush": flush || void 0,
|
|
60
110
|
"data-pointed": interactive && pointed || void 0,
|
|
61
111
|
onPointerEnter: (event) => {
|
|
62
112
|
onPointerEnter?.(event);
|
|
@@ -66,7 +116,10 @@ const Card = forwardRef(function Card({ className, interactive = false, onPointe
|
|
|
66
116
|
onPointerLeave?.(event);
|
|
67
117
|
setPointed(false);
|
|
68
118
|
},
|
|
69
|
-
className: cnAll(cardVariants(
|
|
119
|
+
className: cnAll(cardVariants({
|
|
120
|
+
flush,
|
|
121
|
+
highlighted
|
|
122
|
+
}), interactive && "card-lift", className)
|
|
70
123
|
});
|
|
71
124
|
});
|
|
72
125
|
const CardHeader = forwardRef(function CardHeader({ className, ...props }, ref) {
|
package/dist/card/card.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useState, type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\n\n/**\n * Card, ported from the legacy shadcn component.\n *\n * Presentational, so there is no React Aria base -- React Aria ships no card,\n * and a container that is not focusable, selectable or interactive needs no\n * behaviour to inherit.\n *\n * Five parts, from six. The census (24 consumer files, the most-used\n * container in the app) reads: Card 55, CardContent 40, CardHeader 21,\n * CardTitle 19, CardDescription 8, CardFooter 2, **CardAction 0**.\n *\n * Dropping `CardAction` takes three things with it, which is most of the\n * legacy header's complexity: the `grid`, the `grid-rows-[auto_auto]`, and\n * the `has-data-[slot=card-action]:grid-cols-[1fr_auto]` rule that existed\n * only to reserve a column for it. With no action to place, a header is a\n * stack.\n *\n * Two more legacy rules are gone. `[.border-b]:pb-6` and `[.border-t]:pt-6`\n * added padding when the CONSUMER happened to add a border class -- clever,\n * but action at a distance through a class name we do not own, and the census\n * found zero application call sites relying on it (one call site passes\n * `border-b-0`, which removes a border it never had). `@container/card-header`\n * named a container nothing queried.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const cardVariants = cva([\n \"flex flex-col gap-6\",\n \"rounded-xl border-(length:--border-width) border-border py-6\",\n \"bg-card text-card-foreground shadow-surface\",\n\n // box-border and font-sans are not boilerplate. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none: content-box\n // would add the padding to any width they set (the Alert shipped\n // overflowing its container by 46px), and an unstyled document renders\n // text in a serif (the Accordion shipped its panel that way). Declaring\n // the family HERE rather than on each part means one declaration covers\n // the whole subtree by inheritance.\n \"box-border font-sans\",\n]);\n\n/**\n * Padding is inline-only on the sections and block-only on the shell.\n *\n * That split is load-bearing rather than stylistic: it is what lets a\n * consumer run a divider or a full-bleed image edge to edge, which a single\n * `p-6` on the shell would make impossible.\n */\nexport const cardHeaderVariants = cva(\"flex flex-col gap-1.5 px-6\");\n\nexport const cardTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-base leading-(--leading-none) font-semibold\",\n // The user-agent <h3> ships a 1em block margin and font-size 1.17em. The\n // legacy component was a <div> and never met either.\n \"m-0\",\n]);\n\nexport const cardDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n]);\n\nexport const cardContentVariants = cva(\"px-6\");\n\nexport const cardFooterVariants = cva(\"flex items-center px-6\");\n\n/**\n * A content container.\n *\n * @example\n * <Card>\n * <CardHeader>\n * <CardTitle>Learning Progress</CardTitle>\n * <CardDescription>Across all active courses.</CardDescription>\n * </CardHeader>\n * <CardContent>Twelve of eighteen modules complete.</CardContent>\n * </Card>\n */\nexport interface CardProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Make the card respond to a pointer: it floats 2px on a spring while it is pointed\n * at, its border darkens and its shadow deepens. Mouse and pen only; touch never lifts.\n * Leave it off for a card that is not a target: a static card should not move.\n */\n interactive?: boolean;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { className, interactive = false, onPointerEnter, onPointerLeave, ...props },\n ref,\n) {\n // Pointed at by a mouse or pen. Touch never lifts, so it is read from the pointer, not :hover (which a tap leaves stuck).\n const [pointed, setPointed] = useState(false);\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card\"\n data-interactive={interactive || undefined}\n data-pointed={(interactive && pointed) || undefined}\n onPointerEnter={(event) => {\n onPointerEnter?.(event);\n if (interactive && event.pointerType !== \"touch\") setPointed(true);\n }}\n onPointerLeave={(event) => {\n onPointerLeave?.(event);\n setPointed(false);\n }}\n className={cnAll(cardVariants(), interactive && \"card-lift\", className)}\n />\n );\n});\n\n/** Title and description, stacked. */\nexport type CardHeaderProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(function CardHeader(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-header\"\n className={cn(cardHeaderVariants(), className)}\n />\n );\n});\n\n/**\n * The card's heading.\n *\n * A REAL heading, unlike the legacy `<div class=\"font-semibold\">`. None of\n * the 19 legacy call sites wrapped one, so 19 card titles were invisible to\n * screen-reader heading navigation -- the same shape of finding as the Avatar\n * port's missing `alt`, and the migration is when it gets answered.\n */\nexport interface CardTitleProps extends ComponentPropsWithoutRef<\"h3\"> {\n /**\n * Heading level.\n *\n * `3` by default, because a card almost always sits under a section\n * heading. It is a prop rather than a constant because the correct level\n * is a fact about the PAGE, and a fixed one would be wrong somewhere; the\n * level changes semantics only, never the rendered size.\n *\n * @default 3\n */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(function CardTitle(\n { className, level = 3, ...props },\n ref,\n) {\n // Static union, not a template literal: `h${level}` is a string React\n // accepts but TypeScript cannot narrow to an intrinsic element, and the\n // cast that silences it would also accept `h7`.\n const Heading = ({ 1: \"h1\", 2: \"h2\", 3: \"h3\", 4: \"h4\", 5: \"h5\", 6: \"h6\" } as const)[level];\n\n return (\n <Heading\n ref={ref}\n {...props}\n data-slot=\"card-title\"\n className={cn(cardTitleVariants(), className)}\n />\n );\n});\n\n/** Secondary text under the title. */\nexport type CardDescriptionProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardDescription = forwardRef<HTMLDivElement, CardDescriptionProps>(\n function CardDescription({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-description\"\n className={cn(cardDescriptionVariants(), className)}\n />\n );\n },\n);\n\n/** The card's body. */\nexport type CardContentProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardContent = forwardRef<HTMLDivElement, CardContentProps>(function CardContent(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-content\"\n className={cn(cardContentVariants(), className)}\n />\n );\n});\n\n/** Actions or metadata at the foot of the card. */\nexport type CardFooterProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(function CardFooter(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-footer\"\n className={cn(cardFooterVariants(), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,eAAe,IAAI;CAC9B;CACA;CACA;CASA;AACF,CAAC;;;;;;;;AASD,MAAa,qBAAqB,IAAI,4BAA4B;AAElE,MAAa,oBAAoB,IAAI,CAGnC,oDAGA,KACF,CAAC;AAED,MAAa,0BAA0B,IAAI,CACzC,0DACF,CAAC;AAED,MAAa,sBAAsB,IAAI,MAAM;AAE7C,MAAa,qBAAqB,IAAI,wBAAwB;AAuB9D,MAAa,OAAO,WAAsC,SAAS,KACjE,EAAE,WAAW,cAAc,OAAO,gBAAgB,gBAAgB,GAAG,SACrE,KACA;CAEA,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,oBAAkB,eAAe,KAAA;EACjC,gBAAe,eAAe,WAAY,KAAA;EAC1C,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,IAAI,eAAe,MAAM,gBAAgB,SAAS,WAAW,IAAI;EACnE;EACA,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,WAAW,KAAK;EAClB;EACA,WAAW,MAAM,aAAa,GAAG,eAAe,aAAa,SAAS;CACvE,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAwBD,MAAa,YAAY,WAA+C,SAAS,UAC/E,EAAE,WAAW,QAAQ,GAAG,GAAG,SAC3B,KACA;CAIA,MAAM,UAAW;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,EAAY;CAEpF,OACE,oBAAC,SAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,kBAAkB,GAAG,SAAS;CAC7C,CAAA;AAEL,CAAC;AAKD,MAAa,kBAAkB,WAC7B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,wBAAwB,GAAG,SAAS;CACnD,CAAA;AAEL,CACF;AAKA,MAAa,cAAc,WAA6C,SAAS,YAC/E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;CAC/C,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC"}
|
|
1
|
+
{"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useState, type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\n\n/**\n * Card, ported from the legacy shadcn component.\n *\n * Presentational, so there is no React Aria base -- React Aria ships no card,\n * and a container that is not focusable, selectable or interactive needs no\n * behaviour to inherit.\n *\n * Five parts, from six. The census (24 consumer files, the most-used\n * container in the app) reads: Card 55, CardContent 40, CardHeader 21,\n * CardTitle 19, CardDescription 8, CardFooter 2, **CardAction 0**.\n *\n * Dropping `CardAction` takes three things with it, which is most of the\n * legacy header's complexity: the `grid`, the `grid-rows-[auto_auto]`, and\n * the `has-data-[slot=card-action]:grid-cols-[1fr_auto]` rule that existed\n * only to reserve a column for it. With no action to place, a header is a\n * stack.\n *\n * Two more legacy rules are gone. `[.border-b]:pb-6` and `[.border-t]:pt-6`\n * added padding when the CONSUMER happened to add a border class -- clever,\n * but action at a distance through a class name we do not own, and the census\n * found zero application call sites relying on it (one call site passes\n * `border-b-0`, which removes a border it never had). `@container/card-header`\n * named a container nothing queried.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const cardVariants = cva(\n [\n \"flex flex-col\",\n \"rounded-panel border-(length:--border-width) border-border\",\n \"bg-card text-card-foreground shadow-surface\",\n\n // box-border and font-sans are not boilerplate. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none: content-box\n // would add the padding to any width they set (the Alert shipped\n // overflowing its container by 46px), and an unstyled document renders\n // text in a serif (the Accordion shipped its panel that way). Declaring\n // the family HERE rather than on each part means one declaration covers\n // the whole subtree by inheritance.\n \"box-border font-sans\",\n ],\n {\n variants: {\n /**\n * The block padding and the gap live here, not in the base, so `flush`\n * REPLACES them rather than relying on a merge to drop them: the\n * exported `cardVariants({ flush: true })` is correct on its own.\n *\n * Flush keeps the outline, radius, surface and shadow, and clips its\n * content to the inside of the rounded border: without the clip a filled\n * child (a table header, a chart) shows square corners over the curve and\n * covers the left and right hairline. `overflow: clip`, not `hidden`, so\n * the card does not become a scroll container and a sticky table header\n * inside it still sticks to the page. The sections keep their inline\n * padding, so text and a section's fill stay clear of the corner curve\n * (the fill still meets the side edges: background paints the padding).\n *\n * The header and footer hold text, so under flush they keep the 24px\n * block inset the shell gave up. Without it the title and the footer line\n * sat against the top and bottom borders, inside the corner curve, and\n * the clip cut their first glyphs. The inset is a custom property both\n * sections read, set per variant (never left undefined), so a nested\n * default card resets it, and a section's own `py-*` still wins by merge.\n * CardContent stays block-flush: a table or chart meets the edges.\n */\n flush: {\n false: \"gap-6 py-6 [--card-section-py:--spacing(0)]\",\n true: \"gap-0 py-0 overflow-clip [--card-section-py:--spacing(6)]\",\n },\n /**\n * A featured card: the hairline is replaced by a brand gradient edge,\n * painted by the `card-highlighted` utility in styles.css. The border\n * stays (transparent), so a highlighted card has exactly the box of a\n * plain one and a list of mixed cards does not shift.\n */\n highlighted: {\n false: \"\",\n true: \"card-highlighted border-transparent\",\n },\n },\n compoundVariants: [\n // The flush clip would cut away the gradient edge, which is painted in\n // the border area outside the padding box. See card-highlighted-flush.\n { flush: true, highlighted: true, class: \"card-highlighted-flush\" },\n ],\n defaultVariants: { flush: false, highlighted: false },\n },\n);\n\n/**\n * Padding is inline-only on the sections and block-only on the shell.\n *\n * That split is load-bearing rather than stylistic: it is what lets a\n * consumer run a divider or a full-bleed image edge to edge, which a single\n * `p-6` on the shell would make impossible.\n */\nexport const cardHeaderVariants = cva(\"flex flex-col gap-1.5 px-6 py-(--card-section-py)\");\n\nexport const cardTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-base leading-(--leading-none) font-semibold\",\n // The user-agent <h3> ships a 1em block margin and font-size 1.17em. The\n // legacy component was a <div> and never met either.\n \"m-0\",\n]);\n\nexport const cardDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n]);\n\nexport const cardContentVariants = cva(\"px-6\");\n\nexport const cardFooterVariants = cva(\"flex items-center px-6 py-(--card-section-py)\");\n\n/**\n * A content container.\n *\n * @example\n * <Card>\n * <CardHeader>\n * <CardTitle>Learning Progress</CardTitle>\n * <CardDescription>Across all active courses.</CardDescription>\n * </CardHeader>\n * <CardContent>Twelve of eighteen modules complete.</CardContent>\n * </Card>\n */\nexport interface CardProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Make the card respond to a pointer: it floats 2px on a spring while it is pointed\n * at, its border darkens and its shadow deepens. Mouse and pen only; touch never lifts.\n * Leave it off for a card that is not a target: a static card should not move.\n */\n interactive?: boolean;\n /**\n * A featured card: a brand gradient edge in place of the hairline. Use it for the\n * one card a view wants the eye to land on (a recommended action, the suggested\n * plan), never for several at once. The edge is decoration, so say WHY the card\n * is featured in its content too: colour alone does not reach a screen reader.\n * @default false\n */\n highlighted?: boolean;\n /**\n * Edge-to-edge content: no shell padding and no gap between the parts, and the\n * content is clipped to the rounded border. The outline, radius, surface and\n * shadow stay. For a table, list or full-bleed chart that must meet the card's\n * top and bottom. The header and footer keep a 24px block inset so their text\n * clears the corner curve, and every section keeps its inline padding: a\n * section's fill already reaches the side edges.\n * @default false\n */\n flush?: boolean;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n {\n className,\n interactive = false,\n highlighted = false,\n flush = false,\n onPointerEnter,\n onPointerLeave,\n ...props\n },\n ref,\n) {\n // Pointed at by a mouse or pen. Touch never lifts, so it is read from the pointer, not :hover (which a tap leaves stuck).\n const [pointed, setPointed] = useState(false);\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card\"\n data-interactive={interactive || undefined}\n data-highlighted={highlighted || undefined}\n data-flush={flush || undefined}\n data-pointed={(interactive && pointed) || undefined}\n onPointerEnter={(event) => {\n onPointerEnter?.(event);\n if (interactive && event.pointerType !== \"touch\") setPointed(true);\n }}\n onPointerLeave={(event) => {\n onPointerLeave?.(event);\n setPointed(false);\n }}\n className={cnAll(cardVariants({ flush, highlighted }), interactive && \"card-lift\", className)}\n />\n );\n});\n\n/** Title and description, stacked. */\nexport type CardHeaderProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(function CardHeader(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-header\"\n className={cn(cardHeaderVariants(), className)}\n />\n );\n});\n\n/**\n * The card's heading.\n *\n * A REAL heading, unlike the legacy `<div class=\"font-semibold\">`. None of\n * the 19 legacy call sites wrapped one, so 19 card titles were invisible to\n * screen-reader heading navigation -- the same shape of finding as the Avatar\n * port's missing `alt`, and the migration is when it gets answered.\n */\nexport interface CardTitleProps extends ComponentPropsWithoutRef<\"h3\"> {\n /**\n * Heading level.\n *\n * `3` by default, because a card almost always sits under a section\n * heading. It is a prop rather than a constant because the correct level\n * is a fact about the PAGE, and a fixed one would be wrong somewhere; the\n * level changes semantics only, never the rendered size.\n *\n * @default 3\n */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(function CardTitle(\n { className, level = 3, ...props },\n ref,\n) {\n // Static union, not a template literal: `h${level}` is a string React\n // accepts but TypeScript cannot narrow to an intrinsic element, and the\n // cast that silences it would also accept `h7`.\n const Heading = ({ 1: \"h1\", 2: \"h2\", 3: \"h3\", 4: \"h4\", 5: \"h5\", 6: \"h6\" } as const)[level];\n\n return (\n <Heading\n ref={ref}\n {...props}\n data-slot=\"card-title\"\n className={cn(cardTitleVariants(), className)}\n />\n );\n});\n\n/** Secondary text under the title. */\nexport type CardDescriptionProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardDescription = forwardRef<HTMLDivElement, CardDescriptionProps>(\n function CardDescription({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-description\"\n className={cn(cardDescriptionVariants(), className)}\n />\n );\n },\n);\n\n/** The card's body. */\nexport type CardContentProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardContent = forwardRef<HTMLDivElement, CardContentProps>(function CardContent(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-content\"\n className={cn(cardContentVariants(), className)}\n />\n );\n});\n\n/** Actions or metadata at the foot of the card. */\nexport type CardFooterProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(function CardFooter(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-footer\"\n className={cn(cardFooterVariants(), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,eAAe,IAC1B;CACE;CACA;CACA;CASA;AACF,GACA;CACE,UAAU;;;;;;;;;;;;;;;;;;;;;;;EAuBR,OAAO;GACL,OAAO;GACP,MAAM;EACR;;;;;;;EAOA,aAAa;GACX,OAAO;GACP,MAAM;EACR;CACF;CACA,kBAAkB,CAGhB;EAAE,OAAO;EAAM,aAAa;EAAM,OAAO;CAAyB,CACpE;CACA,iBAAiB;EAAE,OAAO;EAAO,aAAa;CAAM;AACtD,CACF;;;;;;;;AASA,MAAa,qBAAqB,IAAI,mDAAmD;AAEzF,MAAa,oBAAoB,IAAI,CAGnC,oDAGA,KACF,CAAC;AAED,MAAa,0BAA0B,IAAI,CACzC,0DACF,CAAC;AAED,MAAa,sBAAsB,IAAI,MAAM;AAE7C,MAAa,qBAAqB,IAAI,+CAA+C;AAyCrF,MAAa,OAAO,WAAsC,SAAS,KACjE,EACE,WACA,cAAc,OACd,cAAc,OACd,QAAQ,OACR,gBACA,gBACA,GAAG,SAEL,KACA;CAEA,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,oBAAkB,eAAe,KAAA;EACjC,oBAAkB,eAAe,KAAA;EACjC,cAAY,SAAS,KAAA;EACrB,gBAAe,eAAe,WAAY,KAAA;EAC1C,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,IAAI,eAAe,MAAM,gBAAgB,SAAS,WAAW,IAAI;EACnE;EACA,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,WAAW,KAAK;EAClB;EACA,WAAW,MAAM,aAAa;GAAE;GAAO;EAAY,CAAC,GAAG,eAAe,aAAa,SAAS;CAC7F,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAwBD,MAAa,YAAY,WAA+C,SAAS,UAC/E,EAAE,WAAW,QAAQ,GAAG,GAAG,SAC3B,KACA;CAIA,MAAM,UAAW;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,EAAY;CAEpF,OACE,oBAAC,SAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,kBAAkB,GAAG,SAAS;CAC7C,CAAA;AAEL,CAAC;AAKD,MAAa,kBAAkB,WAC7B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,wBAAwB,GAAG,SAAS;CACnD,CAAA;AAEL,CACF;AAKA,MAAa,cAAc,WAA6C,SAAS,YAC/E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;CAC/C,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/card-stack/card-stack.d.ts
|
|
3
|
+
export type CardDecision = "left" | "right";
|
|
4
|
+
/**
|
|
5
|
+
* CardStack: a deck of cards reviewed one at a time. Drag or flick the top card
|
|
6
|
+
* left or right, or use the buttons and the arrow keys. The cards behind peek
|
|
7
|
+
* out below it and rise as it leaves; a card in the hand tips the way it is
|
|
8
|
+
* moving and shows a stamp (the action it will take); let go early and it springs
|
|
9
|
+
* home, let go far enough, or fast enough, and it is thrown. Undo brings the
|
|
10
|
+
* last card back from where it left, and Start over deals the deck again.
|
|
11
|
+
*
|
|
12
|
+
* Use it for quick triage where each item deserves a moment of attention:
|
|
13
|
+
* shortlisting destinations, candidates or design options. Use a list or a
|
|
14
|
+
* table when people need to compare items side by side.
|
|
15
|
+
*
|
|
16
|
+
* Keyboard: the stack takes one tab stop; Left and Right decide the top card,
|
|
17
|
+
* Backspace (or Ctrl or Cmd with Z) undoes. Every decision is announced politely
|
|
18
|
+
* and the buttons do the same, so nothing needs a drag. Reduced motion fades the
|
|
19
|
+
* card out and back instead of throwing it.
|
|
20
|
+
*/
|
|
21
|
+
export interface CardStackProps<T> {
|
|
22
|
+
items: T[];
|
|
23
|
+
/** A stable key for an item. */
|
|
24
|
+
getKey: (item: T) => string;
|
|
25
|
+
/** A short name for the card, used in its accessible label and announcements, such as "Lisbon". */
|
|
26
|
+
getLabel: (item: T) => string;
|
|
27
|
+
/** The card's content. It fills the card, whose size the stack sets. */
|
|
28
|
+
renderCard: (item: T) => ReactNode;
|
|
29
|
+
/** Called when a card is decided. */
|
|
30
|
+
onDecide?: (item: T, decision: CardDecision) => void;
|
|
31
|
+
/** Called when a decision is undone. */
|
|
32
|
+
onUndo?: (item: T, decision: CardDecision) => void;
|
|
33
|
+
/** Called when the deck is dealt again. */
|
|
34
|
+
onReset?: () => void;
|
|
35
|
+
/** Action names for each side, used on the buttons and the stamps that appear while dragging. @default { left: "Pass", right: "Keep" } */
|
|
36
|
+
labels?: {
|
|
37
|
+
left: string;
|
|
38
|
+
right: string;
|
|
39
|
+
};
|
|
40
|
+
/** Past tense of each action for announcements, such as "skipped" and "shortlisted". Without it a decision reads "Lisbon: Keep". */
|
|
41
|
+
outcomes?: {
|
|
42
|
+
left: string;
|
|
43
|
+
right: string;
|
|
44
|
+
};
|
|
45
|
+
/** Accessible name for the stack. @default "Cards" */
|
|
46
|
+
label?: string;
|
|
47
|
+
/** Shown once every card has been decided. Receives a function that brings all cards back. */
|
|
48
|
+
renderEmpty?: (reset: () => void) => ReactNode;
|
|
49
|
+
/** Extra classes for the stack. Merged last. */
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function CardStack<T>({ items, getKey, getLabel, renderCard, onDecide, onUndo, onReset, labels, outcomes, label, renderEmpty, className }: CardStackProps<T>): import("react").JSX.Element;
|
|
53
|
+
//#endregion
|
|
54
|
+
//# sourceMappingURL=card-stack.d.ts.map
|