@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-heatmap.js","names":["Tabs","AriaTabPanel","TabList","Tab"],"sources":["../../src/activity-heatmap/activity-heatmap.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type PointerEvent,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { useCountTo } from \"../lib/count-to.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { TabPanel as AriaTabPanel } from \"react-aria-components\";\n\nimport { Tab, TabList, Tabs } from \"../tabs/tabs.js\";\n\n/**\n * ActivityHeatmap: one square per day, weeks as columns, four tints of the\n * primary chart colour for how busy a day was. Use it when rhythm, streaks and\n * quiet weeks matter more than exact comparisons; use a bar chart when the\n * exact comparison is the point.\n *\n * The squares wave in once, diagonally, when the grid is seen. A tooltip\n * glides between squares on hover or keyboard focus and its words rise in. A\n * new range recolours the grid in a sweep instead of redrawing it, and the\n * total rolls to its new number. The legend's squares preview a level on the\n * grid (the others step back) and pin it on press.\n *\n * It sits in a card (`framed`, on by default). A year switch (`periods`) sits\n * beside the total, and a detail line under the legend says what the hovered,\n * focused or selected day holds (`describeDay`).\n *\n * The grid is a `grid` of `gridcell`s with one tab stop: arrow keys move by\n * day (up and down) and by week (left and right), Home and End jump to the\n * ends, Enter selects. A visually hidden table carries every day.\n */\nexport interface ActivityDay {\n /** Calendar day as YYYY-MM-DD. */\n date: string;\n count: number;\n}\n\n/** What `describeDay` is given for one day. */\nexport interface ActivityDayDetail {\n /** The day as YYYY-MM-DD. */\n date: string;\n count: number;\n /** The day spelled out, such as \"Tuesday, March 18\". The year is added when the range spans more than one. */\n label: string;\n /** The nouns for the count, as passed in `unit`. */\n unit: { one: string; other: string };\n}\n\nexport interface ActivityHeatmapProps {\n /** One entry per day, oldest first. Days missing inside the range count as zero. */\n days: ActivityDay[];\n /** Accessible name for the grid, such as \"Contributions in 2025\". */\n label: string;\n /** Finishes the summary line: \"1,284 contributions in {period}\". */\n period: string;\n /** Nouns for the count. @default { one: \"contribution\", other: \"contributions\" } */\n unit?: { one: string; other: string };\n /** Upper bounds for levels one to three; anything above the last is level four. Defaults to quarters of the busiest day. */\n thresholds?: [number, number, number];\n /** @default 0 (Sunday) */\n weekStartsOn?: 0 | 1;\n /** Controlled selected day, as YYYY-MM-DD. */\n selectedDate?: string | null;\n defaultSelectedDate?: string | null;\n /** Called with the day's YYYY-MM-DD when a day is pressed or chosen with Enter. */\n onSelectDate?: (date: string) => void;\n /** Controls beside the summary. */\n actions?: ReactNode;\n /**\n * The ranges to switch between, such as [\"2025\", \"2024\", \"2023\"], shown as a segmented switch beside the summary with the one\n * equal to `period` selected. You own the data: when `onPeriodChange` fires, pass the new `days`, `period` and `label`.\n */\n periods?: string[];\n /** Called with the chosen entry of `periods`. */\n onPeriodChange?: (period: string) => void;\n /** Accessible name for the switch. @default \"Period\" */\n periodsLabel?: string;\n /**\n * The sentence under the legend about the hovered, focused or selected day. Return the whole sentence, so it can carry data\n * the heatmap does not hold. Pass `null` to remove the line. The default is date and count only:\n * \"Tuesday, March 18: 4 contributions.\"\n */\n describeDay?: ((day: ActivityDayDetail) => string) | null;\n /** The detail line when no day is hovered, focused or selected. @default \"Hover or select a day for details.\" */\n detailHint?: string;\n /** Draws the card around it: border, panel radius, surface and padding. Turn it off to place the heatmap inside a card of your own. @default true */\n framed?: boolean;\n /** Shown instead of the grid when there are no days. @default \"No activity yet\" */\n emptyLabel?: string;\n /** Formatting locale. Fixed by default so server and client render the same labels. @default \"en-US\" */\n locale?: string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\ntype Model = {\n start: number;\n length: number;\n lead: number;\n weeks: number;\n total: number;\n counts: number[];\n levels: number[];\n thresholds: [number, number, number];\n months: { month: number; col: number; label: string }[];\n /** How many days in the range sit at each level. */\n perLevel: number[];\n};\ntype Tip = { index: number; key: string; primary: string; secondary: string; value: number };\n\nconst DAY = 86_400_000;\nconst LEVELS = [0, 1, 2, 3, 4] as const;\n/** Total wave travel in ms, kept under the system's stagger budget however many weeks the range spans. */\nconst WAVE = 420;\nconst toUtc = (iso: string) => Date.parse(`${iso}T00:00:00Z`);\nconst toIso = (time: number) => new Date(time).toISOString().slice(0, 10);\nconst CONTRIBUTIONS = { one: \"contribution\", other: \"contributions\" };\nconst noun = (count: number, unit: { one: string; other: string }) =>\n count === 1 ? unit.one : unit.other;\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\n\nfunction buildModel(\n days: ActivityDay[],\n weekStartsOn: 0 | 1,\n thresholds: [number, number, number] | undefined,\n locale: string,\n): Model {\n const dates = days.map((day) => toUtc(day.date)).filter(Number.isFinite);\n const start = dates.length ? Math.min(...dates) : toUtc(\"2025-01-01\");\n const length = dates.length ? Math.round((Math.max(...dates) - start) / DAY) + 1 : 0;\n const counts = new Array<number>(length).fill(0);\n for (const day of days) {\n const index = Math.round((toUtc(day.date) - start) / DAY);\n if (index >= 0 && index < length) counts[index]! += Math.max(0, day.count);\n }\n const max = counts.reduce((a, b) => Math.max(a, b), 0);\n const bounds =\n thresholds ??\n ([\n Math.max(1, Math.ceil(max * 0.25)),\n Math.max(2, Math.ceil(max * 0.5)),\n Math.max(3, Math.ceil(max * 0.75)),\n ] as [number, number, number]);\n const levels = counts.map((count) =>\n count <= 0 ? 0 : count <= bounds[0] ? 1 : count <= bounds[1] ? 2 : count <= bounds[2] ? 3 : 4,\n );\n const lead = (new Date(start).getUTCDay() - weekStartsOn + 7) % 7;\n const weeks = Math.ceil((lead + length) / 7);\n const monthName = new Intl.DateTimeFormat(locale, { month: \"short\", timeZone: \"UTC\" });\n const months: Model[\"months\"] = [];\n for (let index = 0; index < length; index++) {\n const date = new Date(start + index * DAY);\n if (index === 0 || date.getUTCDate() === 1) {\n months.push({\n month: date.getUTCFullYear() * 12 + date.getUTCMonth(),\n col: Math.floor((lead + index) / 7),\n label: monthName.format(date),\n });\n }\n }\n // A partial first month keeps its label only when there is room before the next one.\n if (months.length > 1 && months[1]!.col - months[0]!.col < 3) months.shift();\n const perLevel = [0, 0, 0, 0, 0];\n for (const level of levels) perLevel[level]!++;\n return {\n start,\n length,\n lead,\n weeks,\n total: counts.reduce((a, b) => a + b, 0),\n counts,\n levels,\n thresholds: bounds,\n months,\n perLevel,\n };\n}\n\n/** The total counts to each new number on the smooth spring. */\nfunction Total({ value, format }: { value: number; format: Intl.NumberFormat }) {\n return <>{format.format(useCountTo(value))}</>;\n}\n\n/**\n * The year switch is a set of tabs, and a tab names the panel it controls: the grid is that panel. Both wrappers add no box of\n * their own (`contents`), and with no `periods` they are not there at all.\n */\nfunction PeriodShell({\n periods,\n period,\n onPeriodChange,\n children,\n}: {\n periods: string[] | undefined;\n period: string;\n onPeriodChange: ((period: string) => void) | undefined;\n children: ReactNode;\n}) {\n if (!periods?.length) return <>{children}</>;\n return (\n <Tabs\n selectedKey={period}\n onSelectionChange={(key) => onPeriodChange?.(String(key))}\n className=\"contents\"\n >\n {children}\n </Tabs>\n );\n}\n\nfunction PeriodPanel({\n periods,\n period,\n children,\n}: {\n periods: string[] | undefined;\n period: string;\n children: ReactNode;\n}) {\n if (!periods?.length) return <>{children}</>;\n return (\n <AriaTabPanel id={period} className=\"contents\">\n {children}\n </AriaTabPanel>\n );\n}\n\nexport const ActivityHeatmap = forwardRef<HTMLDivElement, ActivityHeatmapProps>(\n function ActivityHeatmap(\n {\n days,\n label,\n period,\n unit: unitProp = CONTRIBUTIONS,\n thresholds,\n weekStartsOn = 0,\n selectedDate,\n defaultSelectedDate = null,\n onSelectDate,\n actions,\n periods,\n onPeriodChange,\n periodsLabel = \"Period\",\n describeDay,\n detailHint = \"Hover or select a day for details.\",\n framed = true,\n emptyLabel = \"No activity yet\",\n locale = \"en-US\",\n className,\n },\n ref,\n ) {\n const id = useId();\n // Keyed by its words, so an inline unit object does not rebuild every cell on each render.\n const unit = useMemo(\n () => ({ one: unitProp.one, other: unitProp.other }),\n [unitProp.one, unitProp.other],\n );\n const root = useRef<HTMLDivElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, root), [ref]);\n const plot = useRef<HTMLDivElement>(null);\n const grid = useRef<HTMLDivElement>(null);\n const scroller = useRef<HTMLDivElement>(null);\n const ring = useRef<HTMLSpanElement>(null);\n const tipNode = useRef<HTMLDivElement>(null);\n const tipBody = useRef<HTMLSpanElement>(null);\n const measure = useRef<HTMLSpanElement>(null);\n\n const model = useMemo(\n () => buildModel(days, weekStartsOn, thresholds, locale),\n [days, weekStartsOn, thresholds, locale],\n );\n const formats = useMemo(\n () => ({\n long: new Intl.DateTimeFormat(locale, {\n weekday: \"long\",\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n timeZone: \"UTC\",\n }),\n short: new Intl.DateTimeFormat(locale, {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n timeZone: \"UTC\",\n }),\n day: new Intl.DateTimeFormat(locale, {\n weekday: \"long\",\n month: \"long\",\n day: \"numeric\",\n timeZone: \"UTC\",\n }),\n weekday: new Intl.DateTimeFormat(locale, { weekday: \"short\", timeZone: \"UTC\" }),\n number: new Intl.NumberFormat(locale),\n }),\n [locale],\n );\n\n // A new range sweeps forward when it is later than the last one and back when it is earlier.\n const [range, setRange] = useState({ start: model.start, direction: 1 });\n if (range.start !== model.start)\n setRange({ start: model.start, direction: model.start > range.start ? 1 : -1 });\n\n // The squares wave in once a third of the grid is in view.\n const [reveal, setReveal] = useState<\"hidden\" | \"revealing\" | \"done\">(\"hidden\");\n useEffect(() => {\n const node = plot.current;\n if (reveal !== \"hidden\" || !node) return;\n if (prefersReducedMotion() || typeof IntersectionObserver === \"undefined\") {\n const frame = requestAnimationFrame(() => setReveal(\"done\"));\n return () => cancelAnimationFrame(frame);\n }\n let frame = 0;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry?.isIntersecting) return;\n observer.disconnect();\n frame = requestAnimationFrame(() => setReveal(\"revealing\"));\n },\n { threshold: 0.35 },\n );\n observer.observe(node);\n return () => {\n observer.disconnect();\n cancelAnimationFrame(frame);\n };\n }, [reveal]);\n useEffect(() => {\n if (reveal !== \"revealing\") return;\n const timer = window.setTimeout(() => setReveal(\"done\"), WAVE + 700);\n return () => window.clearTimeout(timer);\n }, [reveal]);\n\n const [ownSelected, setOwnSelected] = useState<string | null>(defaultSelectedDate);\n const selected = selectedDate !== undefined ? selectedDate : ownSelected;\n const selectedIndex = selected ? Math.round((toUtc(selected) - model.start) / DAY) : -1;\n const hasSelection = selectedIndex >= 0 && selectedIndex < model.length;\n const select = (index: number) => {\n const date = toIso(model.start + index * DAY);\n if (selectedDate === undefined) setOwnSelected(date);\n onSelectDate?.(date);\n };\n\n // The one tab stop: the focused day, else the selected day, else the latest.\n const [focusIndex, setFocusIndex] = useState<number | null>(null);\n const tabIndexDay =\n focusIndex !== null && focusIndex < model.length\n ? focusIndex\n : hasSelection\n ? selectedIndex\n : model.length - 1;\n\n // Legend: hovering or focusing a level previews it on the grid, pressing pins it. The caption says how many days sit at that level.\n const [preview, setPreview] = useState<number | null>(null);\n const [pinned, setPinned] = useState<number | null>(null);\n const [legendFocus, setLegendFocus] = useState(0);\n const previewTimer = useRef(0);\n const highlight = preview ?? pinned;\n const previewLevel = (level: number | null) => {\n window.clearTimeout(previewTimer.current);\n if (level !== null) setPreview(level);\n else previewTimer.current = window.setTimeout(() => setPreview(null), 90);\n };\n useEffect(() => () => window.clearTimeout(previewTimer.current), []);\n\n // Tooltip: one bubble for the whole grid. It jumps into place when it opens and glides between squares after that.\n const [tip, setTip] = useState<Tip | null>(null);\n const [open, setOpen] = useState(false);\n const [change, setChange] = useState(1);\n const [session, setSession] = useState(0);\n // The day under the pointer or focus, for the detail line. It lets go when the bubble does.\n const [active, setActive] = useState<number | null>(null);\n const openRef = useRef(false);\n const hideTimer = useRef(0);\n useEffect(() => () => window.clearTimeout(hideTimer.current), []);\n\n const tipFor = (index: number): Tip => {\n const count = model.counts[index]!;\n const date = new Date(model.start + index * DAY);\n return {\n index,\n key: `day-${toIso(date.getTime())}`,\n primary: count\n ? `${formats.number.format(count)} ${noun(count, unit)}`\n : `No ${unit.other}`,\n secondary: formats.short.format(date),\n value: count,\n };\n };\n const [a, b, c] = model.thresholds;\n const ranges = [\n `no ${unit.other}`,\n a === 1 ? `1 ${unit.one}` : `1 to ${a} ${unit.other}`,\n `${a + 1} to ${b} ${unit.other}`,\n `${b + 1} to ${c} ${unit.other}`,\n `${c + 1} or more ${unit.other}`,\n ];\n const shortRanges = [\n `no ${unit.other}`,\n a === 1 ? `1 ${unit.one}` : `1–${a} ${unit.other}`,\n `${a + 1}–${b} ${unit.other}`,\n `${b + 1}–${c} ${unit.other}`,\n `${c + 1}+ ${unit.other}`,\n ];\n const dayCount =\n highlight === null\n ? \"\"\n : `${formats.number.format(model.perLevel[highlight]!)} ${model.perLevel[highlight] === 1 ? \"day\" : \"days\"}`;\n const caption = highlight === null ? \"\" : `${dayCount} with ${shortRanges[highlight]}`;\n\n const show = (index: number) => {\n window.clearTimeout(hideTimer.current);\n const next = tipFor(index);\n if (!openRef.current) setSession((current) => current + 1);\n setChange(tip && openRef.current ? Math.sign(next.value - tip.value) || 1 : 1);\n setTip(next);\n setOpen(true);\n setActive(index);\n };\n const hideSoon = (delay = 110) => {\n window.clearTimeout(hideTimer.current);\n hideTimer.current = window.setTimeout(() => {\n setOpen(false);\n setActive(null);\n }, delay);\n };\n\n // The bubble sits above its square; it never leaves the root sideways, and it stays pinned to its square while a narrow grid scrolls.\n useIsomorphicLayoutEffect(() => {\n const node = root.current;\n const bubble = tipNode.current;\n const body = tipBody.current;\n const sizer = measure.current;\n if (!tip || !node || !bubble || !body || !sizer) return;\n const place = (glide: boolean) => {\n const cell = grid.current?.querySelector<HTMLElement>(`[data-index=\"${tip.index}\"]`);\n if (!cell) return;\n const box = node.getBoundingClientRect();\n const at = cell.getBoundingClientRect();\n const width = Math.ceil(sizer.getBoundingClientRect().width);\n const half = width / 2 + 13;\n // Measured from the padding box, which is where the absolutely placed bubble starts when the card has a border.\n const x = clamp(\n at.left - box.left - node.clientLeft + at.width / 2,\n half + 2,\n node.clientWidth - half - 2,\n );\n bubble.toggleAttribute(\"data-glide\", glide);\n bubble.style.translate = `${x}px ${at.top - box.top - node.clientTop}px`;\n body.toggleAttribute(\"data-glide\", glide);\n body.style.width = `${width}px`;\n };\n place(open && openRef.current && !prefersReducedMotion());\n openRef.current = open;\n if (!open) return;\n const follow = () => place(false);\n const frame = scroller.current;\n frame?.addEventListener(\"scroll\", follow, { passive: true });\n return () => frame?.removeEventListener(\"scroll\", follow);\n }, [tip, open]);\n\n const indexOf = (element: Element | null) => {\n const cell = element?.closest<HTMLElement>(\"[data-index]\");\n return cell && grid.current?.contains(cell)\n ? { cell, index: Number(cell.dataset.index) }\n : null;\n };\n const focusDay = (index: number) =>\n grid.current?.querySelector<HTMLElement>(`[data-index=\"${index}\"]`)?.focus();\n\n const onGridPointerOver = (event: PointerEvent<HTMLDivElement>) => {\n const hit = indexOf(event.target as Element);\n if (hit) show(hit.index);\n };\n const onGridPointerLeave = (event: PointerEvent<HTMLDivElement>) => {\n // A keyboard user keeps the bubble on the focused day. A tap focuses its day too, so the bubble stays until focus moves on;\n // where a tap does not focus, it lingers long enough to read.\n const focused = indexOf(document.activeElement);\n if (focused?.cell.matches(\":focus-visible\")) show(focused.index);\n else hideSoon(event.pointerType === \"touch\" ? 2400 : 110);\n };\n const onGridFocus = (event: FocusEvent<HTMLDivElement>) => {\n const hit = indexOf(event.target);\n if (!hit) return;\n setFocusIndex(hit.index);\n show(hit.index);\n };\n const onGridBlur = (event: FocusEvent<HTMLDivElement>) => {\n if (!grid.current?.contains(event.relatedTarget)) hideSoon(0);\n };\n const onGridKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const hit = indexOf(event.target as Element);\n if (!hit) return;\n const moves: Record<string, number> = {\n ArrowUp: hit.index - 1,\n ArrowDown: hit.index + 1,\n ArrowLeft: hit.index - 7,\n ArrowRight: hit.index + 7,\n Home: 0,\n End: model.length - 1,\n };\n if (event.key in moves) {\n event.preventDefault();\n focusDay(clamp(moves[event.key]!, 0, model.length - 1));\n } else if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(hit.index);\n } else if (event.key === \"Escape\" && open) {\n event.preventDefault();\n setOpen(false);\n }\n };\n\n const onLegendKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"Escape\") {\n previewLevel(null);\n return;\n }\n const moves: Record<string, number> = {\n ArrowLeft: legendFocus - 1,\n ArrowRight: legendFocus + 1,\n Home: 0,\n End: 4,\n };\n if (!(event.key in moves)) return;\n event.preventDefault();\n const next = clamp(moves[event.key]!, 0, 4);\n root.current?.querySelector<HTMLElement>(`[data-level-key=\"${next}\"]`)?.focus();\n };\n\n // The selection ring glides between squares. When it first appears it fades in where it lands instead of flying in from its last spot.\n const ringShown = hasSelection && reveal !== \"hidden\";\n const ringWasShown = useRef(ringShown);\n useIsomorphicLayoutEffect(() => {\n const node = ring.current;\n if (node && ringShown && !ringWasShown.current) {\n node.style.transitionProperty = \"opacity\";\n void node.offsetWidth;\n node.style.transitionProperty = \"\";\n }\n ringWasShown.current = ringShown;\n }, [ringShown]);\n\n const maxDiagonal = Math.max(1, model.weeks - 1 + 6);\n const step = Math.min(12, WAVE / maxDiagonal);\n const cells = useMemo(\n () =>\n Array.from({ length: 7 }, (_, row) => (\n <div\n key={row}\n role=\"row\"\n // A row with no day in range (a short range) has nothing to read.\n aria-hidden={\n Array.from({ length: model.weeks }, (_, col) => col * 7 + row - model.lead).some(\n (index) => index >= 0 && index < model.length,\n )\n ? undefined\n : true\n }\n className=\"activity-heatmap-row\"\n >\n {Array.from({ length: model.weeks }, (_, col) => {\n const index = col * 7 + row - model.lead;\n const inRange = index >= 0 && index < model.length;\n const wave = {\n \"--wave\": `${Math.round((col + row) * step)}ms`,\n \"--wave-back\": `${Math.round((maxDiagonal - col - row) * step)}ms`,\n } as CSSProperties;\n if (!inRange)\n return (\n <span\n key={col}\n className=\"activity-heatmap-cell\"\n data-empty=\"\"\n style={wave}\n aria-hidden=\"true\"\n />\n );\n const count = model.counts[index]!;\n const date = new Date(model.start + index * DAY);\n return (\n <span\n key={col}\n role=\"gridcell\"\n className=\"activity-heatmap-cell\"\n style={wave}\n data-index={index}\n data-level={model.levels[index]}\n tabIndex={index === tabIndexDay ? 0 : -1}\n aria-selected={index === selectedIndex}\n aria-label={`${count ? formats.number.format(count) : \"No\"} ${noun(count, unit)}, ${formats.long.format(date)}`}\n onClick={() => select(index)}\n />\n );\n })}\n </div>\n )),\n // `select` only reads props and model, both already dependencies.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [\n model,\n step,\n maxDiagonal,\n tabIndexDay,\n selectedIndex,\n formats,\n unit,\n onSelectDate,\n selectedDate,\n ],\n );\n\n const weekdayLabels = useMemo(\n () =>\n Array.from({ length: 7 }, (_, row) => {\n const weekday = (row + weekStartsOn) % 7;\n // January 4, 1970 was a Sunday.\n return weekday % 2 === 1 ? formats.weekday.format(new Date((3 + weekday) * DAY)) : \"\";\n }),\n [formats, weekStartsOn],\n );\n\n // The detail line follows the day under pointer or focus, and rests on the selected day.\n const detailIndex =\n active !== null && active < model.length ? active : hasSelection ? selectedIndex : null;\n const spansYears =\n model.length > 0 &&\n new Date(model.start).getUTCFullYear() !==\n new Date(model.start + (model.length - 1) * DAY).getUTCFullYear();\n let detailText = detailHint;\n if (describeDay !== null && detailIndex !== null) {\n const at = new Date(model.start + detailIndex * DAY);\n const count = model.counts[detailIndex]!;\n const dayLabel = spansYears ? formats.long.format(at) : formats.day.format(at);\n detailText = describeDay\n ? describeDay({ date: toIso(at.getTime()), count, label: dayLabel, unit })\n : `${dayLabel}: ${count ? `${formats.number.format(count)} ${noun(count, unit)}` : `no ${unit.other}`}.`;\n }\n\n const selectedCol = hasSelection ? Math.floor((selectedIndex + model.lead) / 7) : 0;\n const selectedRow = hasSelection ? (selectedIndex + model.lead) % 7 : 0;\n const summary = `${formats.number.format(model.total)} ${noun(model.total, unit)} in ${period}`;\n const header = unit.other.charAt(0).toUpperCase() + unit.other.slice(1);\n\n return (\n <div\n ref={refs}\n data-slot=\"activity-heatmap\"\n data-framed={framed || undefined}\n className={cn(\"activity-heatmap\", className)}\n >\n <PeriodShell periods={periods} period={period} onPeriodChange={onPeriodChange}>\n <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-x-4 gap-y-3\">\n <p className=\"m-0 flex min-w-0 flex-wrap items-baseline gap-x-1 text-sm tabular-nums text-text-secondary\">\n <span aria-hidden=\"true\" className=\"whitespace-nowrap font-medium text-foreground\">\n <Total value={model.total} format={formats.number} /> {noun(model.total, unit)}\n </span>\n <span aria-hidden=\"true\" className=\"whitespace-nowrap\">\n in{\" \"}\n <SwapText swapKey={period} className=\"inline-flex\">\n {period}\n </SwapText>\n </span>\n <span className=\"sr-only\" role=\"status\">\n {summary}\n </span>\n </p>\n {actions || periods?.length ? (\n <div className=\"flex flex-none items-center gap-3\">\n {actions}\n {periods?.length ? (\n <TabList aria-label={periodsLabel}>\n {periods.map((item) => (\n <Tab key={item} id={item}>\n {item}\n </Tab>\n ))}\n </TabList>\n ) : null}\n </div>\n ) : null}\n </div>\n\n {model.length === 0 ? (\n <p className=\"m-0 text-sm text-muted-foreground\">{emptyLabel}</p>\n ) : null}\n <PeriodPanel periods={periods} period={period}>\n <div className=\"activity-heatmap-body\" hidden={model.length === 0}>\n <div className=\"activity-heatmap-weekdays\" aria-hidden=\"true\">\n {weekdayLabels.map((text, row) => (\n <span key={row} className=\"activity-heatmap-weekday\">\n {text}\n </span>\n ))}\n </div>\n <div ref={scroller} className=\"activity-heatmap-scroller\">\n <div\n className=\"activity-heatmap-canvas\"\n style={{ \"--weeks\": model.weeks } as CSSProperties}\n >\n <div className=\"activity-heatmap-months\" aria-hidden=\"true\">\n {model.months.map((month, order) => (\n <span\n key={`${month.month % 12}-${model.months.findIndex((item) => item.month % 12 === month.month % 12) === order ? 0 : 1}`}\n className=\"activity-heatmap-month\"\n style={{ \"--col\": month.col } as CSSProperties}\n >\n {month.label}\n </span>\n ))}\n </div>\n <div ref={plot} className=\"relative\">\n <div\n ref={grid}\n role=\"grid\"\n aria-label={label}\n aria-readonly=\"true\"\n aria-describedby={`${id}-legend`}\n className=\"activity-heatmap-grid\"\n data-reveal={reveal}\n data-direction={range.direction < 0 ? \"back\" : undefined}\n data-highlight={highlight ?? undefined}\n onPointerOver={onGridPointerOver}\n onPointerLeave={onGridPointerLeave}\n onFocus={onGridFocus}\n onBlur={onGridBlur}\n onKeyDown={onGridKeyDown}\n >\n {cells}\n </div>\n <span\n ref={ring}\n aria-hidden=\"true\"\n data-shown={ringShown || undefined}\n className=\"activity-heatmap-ring\"\n style={{ \"--col\": selectedCol, \"--row\": selectedRow } as CSSProperties}\n />\n </div>\n </div>\n </div>\n </div>\n </PeriodPanel>\n\n <div className=\"flex min-w-0 items-center justify-between gap-3 text-xs text-text-secondary\">\n <span id={`${id}-legend`} className=\"sr-only\">\n Darker squares mean more {unit.other}. Levels: {ranges.join(\", \")}.\n </span>\n <span aria-live=\"polite\" className=\"activity-heatmap-caption\">\n <span aria-hidden=\"true\">\n <SwapText swapKey={caption} className=\"block\">\n <span className=\"activity-heatmap-caption-long\">{caption}</span>\n <span className=\"activity-heatmap-caption-short\">{dayCount}</span>\n </SwapText>\n </span>\n <span className=\"sr-only\">{caption}</span>\n </span>\n <span className=\"flex flex-none items-center gap-2\">\n <span aria-hidden=\"true\" className=\"leading-none\">\n Less\n </span>\n <div\n role=\"group\"\n aria-label=\"Highlight days by level\"\n onKeyDown={onLegendKeyDown}\n className=\"flex\"\n >\n {LEVELS.map((level) => (\n <button\n key={level}\n type=\"button\"\n className=\"activity-heatmap-swatch\"\n data-level-key={level}\n data-level={level}\n tabIndex={level === legendFocus ? 0 : -1}\n aria-pressed={pinned === level}\n aria-label={`Highlight days with ${ranges[level]}`}\n onClick={() => setPinned((current) => (current === level ? null : level))}\n onPointerEnter={() => previewLevel(level)}\n onPointerLeave={() => previewLevel(null)}\n onFocus={() => {\n setLegendFocus(level);\n previewLevel(level);\n }}\n onBlur={() => previewLevel(null)}\n />\n ))}\n </div>\n <span aria-hidden=\"true\" className=\"leading-none\">\n More\n </span>\n </span>\n </div>\n\n {describeDay !== null && model.length > 0 ? (\n <p className=\"activity-heatmap-detail\">\n <SwapText swapKey={detailText} className=\"block\">\n {detailText}\n </SwapText>\n </p>\n ) : null}\n\n <div\n ref={tipNode}\n aria-hidden=\"true\"\n data-open={open && tip ? \"\" : undefined}\n className=\"activity-heatmap-tip\"\n >\n <div className=\"activity-heatmap-bubble\">\n <span ref={tipBody} className=\"activity-heatmap-tip-body\">\n <span\n ref={measure}\n className=\"activity-heatmap-tip-lines absolute start-0 top-0 invisible\"\n >\n <span className=\"activity-heatmap-tip-primary\">{tip?.primary}</span>\n <span className=\"activity-heatmap-tip-secondary\">{tip?.secondary}</span>\n </span>\n <SwapText key={session} swapKey={tip?.key ?? \"\"} dir={change} className=\"block\">\n <span className=\"activity-heatmap-tip-lines\">\n <span className=\"activity-heatmap-tip-primary\">{tip?.primary}</span>\n <span className=\"activity-heatmap-tip-secondary\">{tip?.secondary}</span>\n </span>\n </SwapText>\n </span>\n </div>\n </div>\n\n <div className=\"sr-only\" hidden={model.length === 0}>\n <table>\n <caption>{label}</caption>\n <thead>\n <tr>\n <th scope=\"col\">Date</th>\n <th scope=\"col\">{header}</th>\n </tr>\n </thead>\n <tbody>\n {model.counts.map((count, index) => (\n <tr key={index}>\n <th scope=\"row\">{formats.long.format(new Date(model.start + index * DAY))}</th>\n <td>{formats.number.format(count)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </PeriodShell>\n </div>\n );\n },\n);\n"],"mappings":"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"}
|
package/dist/alert/alert.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VariantProps } from "class-variance-authority";
|
|
2
|
-
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
import { AriaRole, ComponentPropsWithoutRef, ReactNode, SVGProps } from "react";
|
|
3
3
|
//#region src/alert/alert.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Alert, ported from the legacy shadcn component.
|
|
@@ -24,14 +24,42 @@ import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
|
24
24
|
* problems rather than porting them.
|
|
25
25
|
*/
|
|
26
26
|
export declare const alertVariants: (props?: ({
|
|
27
|
-
variant?: "default" | "
|
|
27
|
+
variant?: "destructive" | "success" | "warning" | "default" | "info" | null | undefined;
|
|
28
28
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
29
|
+
/**
|
|
30
|
+
* The icon's colour: the state's own hue on every variant. Inside an Alert the
|
|
31
|
+
* destructive red is also the whole alert's text colour, so it changes nothing
|
|
32
|
+
* there; outside one it is what colours the icon.
|
|
33
|
+
*
|
|
34
|
+
* Exported with the status icons so a notice that is not an `Alert` (a row, a
|
|
35
|
+
* banner of the app's own) can show a tone the same way the alert does.
|
|
36
|
+
*/
|
|
37
|
+
export declare const alertIconVariants: (props?: ({
|
|
38
|
+
variant?: "destructive" | "success" | "warning" | "default" | "info" | null | undefined;
|
|
39
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
40
|
+
/**
|
|
41
|
+
* The built-in glyph of each variant, drawn with currentColor so the icon
|
|
42
|
+
* wrapper's colour paints it. Each is its own component so a variant change
|
|
43
|
+
* crossfades the icon (see iconKey).
|
|
44
|
+
*
|
|
45
|
+
* Exported as AlertInfoIcon, AlertSuccessIcon, AlertWarningIcon and
|
|
46
|
+
* AlertDestructiveIcon. They are decorative (`aria-hidden`) and `--icon-sm`
|
|
47
|
+
* by default; extra props, such as a `className` with a size, land on the svg.
|
|
48
|
+
* Given an `aria-label` or `aria-labelledby` (with `role="img"`), an icon is
|
|
49
|
+
* no longer hidden, and an explicit `aria-hidden` always wins.
|
|
50
|
+
*/
|
|
51
|
+
type StatusIconProps = Omit<SVGProps<SVGSVGElement>, "children">;
|
|
52
|
+
declare function InfoGlyph(props: StatusIconProps): import("react").JSX.Element;
|
|
53
|
+
declare function SuccessGlyph(props: StatusIconProps): import("react").JSX.Element;
|
|
54
|
+
declare function WarningGlyph(props: StatusIconProps): import("react").JSX.Element;
|
|
55
|
+
declare function DestructiveGlyph(props: StatusIconProps): import("react").JSX.Element;
|
|
29
56
|
/**
|
|
30
57
|
* An inline status message.
|
|
31
58
|
*
|
|
32
59
|
* Announced by assistive technology the moment it renders, via `role="alert"`.
|
|
33
|
-
* That makes it the wrong
|
|
34
|
-
* merely styled as a notice --
|
|
60
|
+
* That makes it the wrong default for content that is present on load and
|
|
61
|
+
* merely styled as a notice -- pass `role="note"` for those, or
|
|
62
|
+
* `role="status"` for an outcome that should be announced politely.
|
|
35
63
|
*
|
|
36
64
|
* @example
|
|
37
65
|
* <Alert variant="destructive" icon={<WarningIcon aria-hidden />} title="Save failed">
|
|
@@ -43,6 +71,10 @@ export interface AlertProps extends Omit<ComponentPropsWithoutRef<"div">, "title
|
|
|
43
71
|
* Leading glyph. Mark it `aria-hidden` -- the message text is the
|
|
44
72
|
* announcement, and an unhidden graphic makes the alert announce twice.
|
|
45
73
|
*
|
|
74
|
+
* Left out, the alert shows its variant's own icon (info, check, warning
|
|
75
|
+
* triangle, cross), coloured for the state and centred on the first line of
|
|
76
|
+
* text. Pass `null` for no icon, or your own glyph to replace it.
|
|
77
|
+
*
|
|
46
78
|
* A prop rather than a positioned child: the legacy `[&>svg]` selectors
|
|
47
79
|
* only worked when the icon was the first element, and broke in RTL.
|
|
48
80
|
*/
|
|
@@ -76,7 +108,24 @@ export interface AlertProps extends Omit<ComponentPropsWithoutRef<"div">, "title
|
|
|
76
108
|
onDismiss?: () => void;
|
|
77
109
|
/** The Dismiss button's accessible name. @default "Dismiss" */
|
|
78
110
|
dismissLabel?: string;
|
|
111
|
+
/**
|
|
112
|
+
* How assistive technology treats the message.
|
|
113
|
+
*
|
|
114
|
+
* - `alert`: announced assertively the moment it renders. For an error or a
|
|
115
|
+
* warning that appears after the user did something.
|
|
116
|
+
* - `status`: a live region announced politely, after what the user is
|
|
117
|
+
* hearing. For an outcome reported after an action ("Saved").
|
|
118
|
+
* - `note`: not live. For a notice that is simply there when the page loads;
|
|
119
|
+
* an alert there would interrupt the page's own announcement.
|
|
120
|
+
*
|
|
121
|
+
* Typed as any ARIA role, as the inherited `role` was, so existing callers
|
|
122
|
+
* still compile; those three are the ones an inline notice should use.
|
|
123
|
+
*
|
|
124
|
+
* @default "alert"
|
|
125
|
+
*/
|
|
126
|
+
role?: "alert" | "status" | "note" | AriaRole;
|
|
79
127
|
}
|
|
80
128
|
export declare const Alert: import("react").ForwardRefExoticComponent<AlertProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
81
129
|
//#endregion
|
|
130
|
+
export { DestructiveGlyph as AlertDestructiveIcon, InfoGlyph as AlertInfoIcon, type StatusIconProps as AlertStatusIconProps, SuccessGlyph as AlertSuccessIcon, WarningGlyph as AlertWarningIcon };
|
|
82
131
|
//# sourceMappingURL=alert.d.ts.map
|
package/dist/alert/alert.js
CHANGED
|
@@ -9,6 +9,17 @@ import { Button } from "react-aria-components";
|
|
|
9
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
//#region src/alert/alert.tsx
|
|
11
11
|
/**
|
|
12
|
+
* `info` is `default` said by name: a call site that means "information" should
|
|
13
|
+
* not depend on `default` happening to look like it. Every per-variant map below
|
|
14
|
+
* derives `info` here, from its `default`, so the two cannot drift.
|
|
15
|
+
*/
|
|
16
|
+
function withInfo(map) {
|
|
17
|
+
return {
|
|
18
|
+
...map,
|
|
19
|
+
info: map.default
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
12
23
|
* Alert, ported from the legacy shadcn component.
|
|
13
24
|
*
|
|
14
25
|
* There is no React Aria base: RAC ships no static inline alert (its Toast
|
|
@@ -32,15 +43,91 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
32
43
|
*/
|
|
33
44
|
const alertVariants = cva([
|
|
34
45
|
"box-border flex w-full items-start gap-(--density-control-gap)",
|
|
35
|
-
"rounded-
|
|
46
|
+
"rounded-panel border-(length:--border-width) p-(--density-control-pad-md)",
|
|
36
47
|
"font-sans text-sm leading-(--leading-normal)"
|
|
37
48
|
], {
|
|
38
|
-
variants: { variant: {
|
|
39
|
-
default: "bg-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
variants: { variant: withInfo({
|
|
50
|
+
default: "bg-surface text-foreground border-border",
|
|
51
|
+
success: "bg-surface text-foreground border-border",
|
|
52
|
+
warning: "bg-surface text-foreground border-border",
|
|
53
|
+
destructive: "bg-surface text-destructive-strong border-destructive-strong"
|
|
54
|
+
}) },
|
|
55
|
+
defaultVariants: { variant: "default" }
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* The icon's colour: the state's own hue on every variant. Inside an Alert the
|
|
59
|
+
* destructive red is also the whole alert's text colour, so it changes nothing
|
|
60
|
+
* there; outside one it is what colours the icon.
|
|
61
|
+
*
|
|
62
|
+
* Exported with the status icons so a notice that is not an `Alert` (a row, a
|
|
63
|
+
* banner of the app's own) can show a tone the same way the alert does.
|
|
64
|
+
*/
|
|
65
|
+
const alertIconVariants = cva("flex h-lh shrink-0 items-center [&_svg]:block [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-(--icon-sm)", {
|
|
66
|
+
variants: { variant: withInfo({
|
|
67
|
+
default: "text-info-strong",
|
|
68
|
+
success: "text-success-strong",
|
|
69
|
+
warning: "text-warning-strong",
|
|
70
|
+
destructive: "text-destructive-strong"
|
|
71
|
+
}) },
|
|
42
72
|
defaultVariants: { variant: "default" }
|
|
43
73
|
});
|
|
74
|
+
function StatusGlyph({ children, className, ...props }) {
|
|
75
|
+
const labelled = props["aria-label"] !== void 0 || props["aria-labelledby"] !== void 0;
|
|
76
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
77
|
+
"aria-hidden": labelled ? void 0 : "true",
|
|
78
|
+
...props,
|
|
79
|
+
className: cn("size-(--icon-sm)", className),
|
|
80
|
+
viewBox: "0 0 16 16",
|
|
81
|
+
fill: "none",
|
|
82
|
+
stroke: "currentColor",
|
|
83
|
+
strokeWidth: "1.5",
|
|
84
|
+
strokeLinecap: "round",
|
|
85
|
+
strokeLinejoin: "round",
|
|
86
|
+
children
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function InfoGlyph(props) {
|
|
90
|
+
return /* @__PURE__ */ jsxs(StatusGlyph, {
|
|
91
|
+
...props,
|
|
92
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
93
|
+
cx: "8",
|
|
94
|
+
cy: "8",
|
|
95
|
+
r: "6.25"
|
|
96
|
+
}), /* @__PURE__ */ jsx("path", { d: "M8 7.25v3.5M8 5h.01" })]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function SuccessGlyph(props) {
|
|
100
|
+
return /* @__PURE__ */ jsxs(StatusGlyph, {
|
|
101
|
+
...props,
|
|
102
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
103
|
+
cx: "8",
|
|
104
|
+
cy: "8",
|
|
105
|
+
r: "6.25"
|
|
106
|
+
}), /* @__PURE__ */ jsx("path", { d: "m5.25 8.25 1.9 1.9 3.6-3.9" })]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function WarningGlyph(props) {
|
|
110
|
+
return /* @__PURE__ */ jsxs(StatusGlyph, {
|
|
111
|
+
...props,
|
|
112
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M6.9 2.6 1.9 11.4a1.2 1.2 0 0 0 1 1.8h10.2a1.2 1.2 0 0 0 1-1.8L9.1 2.6a1.2 1.2 0 0 0-2.2 0Z" }), /* @__PURE__ */ jsx("path", { d: "M8 6.4v2.6M8 11.2h.01" })]
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function DestructiveGlyph(props) {
|
|
116
|
+
return /* @__PURE__ */ jsxs(StatusGlyph, {
|
|
117
|
+
...props,
|
|
118
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
119
|
+
cx: "8",
|
|
120
|
+
cy: "8",
|
|
121
|
+
r: "6.25"
|
|
122
|
+
}), /* @__PURE__ */ jsx("path", { d: "m6 6 4 4M10 6l-4 4" })]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const statusGlyphs = withInfo({
|
|
126
|
+
default: /* @__PURE__ */ jsx(InfoGlyph, {}),
|
|
127
|
+
success: /* @__PURE__ */ jsx(SuccessGlyph, {}),
|
|
128
|
+
warning: /* @__PURE__ */ jsx(WarningGlyph, {}),
|
|
129
|
+
destructive: /* @__PURE__ */ jsx(DestructiveGlyph, {})
|
|
130
|
+
});
|
|
44
131
|
/** A string title or description swaps as a whole when its text changes. */
|
|
45
132
|
function Swappable({ value, children }) {
|
|
46
133
|
if (typeof value !== "string" && typeof value !== "number") return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
@@ -68,8 +155,9 @@ function DismissGlyph() {
|
|
|
68
155
|
children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" })
|
|
69
156
|
});
|
|
70
157
|
}
|
|
71
|
-
const Alert = forwardRef(function Alert({ className, variant, icon, title, headingLevel = 3, children, open, onDismiss, dismissLabel = "Dismiss", ...props }, ref) {
|
|
158
|
+
const Alert = forwardRef(function Alert({ className, variant, icon: iconProp, title, headingLevel = 3, children, open, onDismiss, dismissLabel = "Dismiss", role = "alert", ...props }, ref) {
|
|
72
159
|
const Title = `h${headingLevel}`;
|
|
160
|
+
const icon = iconProp === void 0 ? statusGlyphs[variant ?? "default"] : iconProp;
|
|
73
161
|
const frame = useRef(null);
|
|
74
162
|
const content = useRef(null);
|
|
75
163
|
useHeightFollow(frame, content, `${typeof title === "string" ? title : ""}\n${children === void 0 ? "" : "details"}\n${typeof children === "string" || typeof children === "number" ? String(children) : ""}`);
|
|
@@ -103,13 +191,13 @@ const Alert = forwardRef(function Alert({ className, variant, icon, title, headi
|
|
|
103
191
|
const alert = /* @__PURE__ */ jsxs("div", {
|
|
104
192
|
ref,
|
|
105
193
|
...props,
|
|
106
|
-
role
|
|
194
|
+
role,
|
|
107
195
|
"data-slot": "alert",
|
|
108
196
|
className: cn(alertVariants({ variant }), className),
|
|
109
197
|
children: [
|
|
110
|
-
icon ===
|
|
198
|
+
icon === null || icon === false ? null : /* @__PURE__ */ jsx("span", {
|
|
111
199
|
"data-slot": "alert-icon",
|
|
112
|
-
className:
|
|
200
|
+
className: alertIconVariants({ variant }),
|
|
113
201
|
children: /* @__PURE__ */ jsx(SwapText, {
|
|
114
202
|
swapKey: iconKey(icon),
|
|
115
203
|
motion: "glyph",
|
|
@@ -125,13 +213,14 @@ const Alert = forwardRef(function Alert({ className, variant, icon, title, headi
|
|
|
125
213
|
ref: content,
|
|
126
214
|
children: [title === void 0 ? null : /* @__PURE__ */ jsx(Title, {
|
|
127
215
|
"data-slot": "alert-title",
|
|
128
|
-
className: "mb-1 text-sm leading-(--leading-
|
|
216
|
+
className: "m-0 mb-1 text-sm leading-(--leading-normal) font-medium",
|
|
129
217
|
children: /* @__PURE__ */ jsx(Swappable, {
|
|
130
218
|
value: title,
|
|
131
219
|
children: title
|
|
132
220
|
})
|
|
133
221
|
}), children === void 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
134
222
|
"data-slot": "alert-description",
|
|
223
|
+
className: title === void 0 ? void 0 : "text-xs leading-(--leading-normal) text-text-secondary",
|
|
135
224
|
children: /* @__PURE__ */ jsx(Swappable, {
|
|
136
225
|
value: children,
|
|
137
226
|
children
|
|
@@ -160,6 +249,6 @@ const Alert = forwardRef(function Alert({ className, variant, icon, title, headi
|
|
|
160
249
|
});
|
|
161
250
|
});
|
|
162
251
|
//#endregion
|
|
163
|
-
export { Alert, alertVariants };
|
|
252
|
+
export { Alert, DestructiveGlyph as AlertDestructiveIcon, InfoGlyph as AlertInfoIcon, SuccessGlyph as AlertSuccessIcon, WarningGlyph as AlertWarningIcon, alertIconVariants, alertVariants };
|
|
164
253
|
|
|
165
254
|
//# sourceMappingURL=alert.js.map
|
package/dist/alert/alert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert.js","names":["AriaButton"],"sources":["../../src/alert/alert.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n forwardRef,\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from \"react\";\n\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { useHeightFollow } from \"../lib/height-follow.js\";\nimport { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Alert, ported from the legacy shadcn component.\n *\n * There is no React Aria base: RAC ships no static inline alert (its Toast\n * API is transient and queue-driven), so this is a styled div with\n * role=\"alert\". Because it is not RAC-backed, its props follow\n * ARCHITECTURE section 7 rule 1 -- VariantProps & ComponentPropsWithoutRef --\n * rather than the Button's Omit-from-React-Aria departure, which exists only\n * to stop RAC-ignored props being advertised.\n *\n * Every string below is a STATIC literal. Tailwind scans source text, so a\n * class assembled at runtime is invisible to it and would never be emitted.\n *\n * THE LAYOUT IS THE BIGGEST CHANGE. The legacy component positioned its icon\n * with `[&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4`, pushed the text over\n * with `[&>svg~*]:pl-7`, and nudged it back with `[&>svg+div]:translate-y-[-3px]`.\n * All three are physical properties or magic numbers: `left` and `pl` do not\n * mirror in RTL, so the icon stayed left while the text indented the wrong\n * side, and `translate-y-[-3px]` is a bracket value that `check:hardcoding`\n * rejects outright. Flex with an explicit `icon` prop deletes all three\n * problems rather than porting them.\n */\nexport const alertVariants = cva(\n [\n // box-border is not boilerplate. Tailwind Preflight sets box-sizing\n // globally and the legacy app had it; this library's reset is OPT-IN, so\n // a consumer may load none and the browser default content-box applies.\n // w-full is width:100%, so the padding and border are then ADDED to it\n // and the alert overflows its container by exactly 46px at comfortable\n // density. Measured, not theorised: 346px inside a 300px parent.\n \"box-border flex w-full items-start gap-(--density-control-gap)\",\n \"rounded-lg border-(length:--border-width) p-(--density-control-pad-md)\",\n // font-sans is load-bearing. The library does not style the consumer's\n // page, so anything that does not set its own face inherits the host's\n // default -- which is a serif in an unstyled document. The accordion\n // panel shipped exactly that way and it was caught by eye, not by a test.\n \"font-sans text-sm leading-(--leading-normal)\",\n ],\n {\n variants: {\n variant: {\n default: \"bg-background text-foreground border-border\",\n // destructive-STRONG, not destructive. The fill token is 4.04:1 on\n // the dark page; this role exists so the text and border clear the\n // 4.5 floor in both themes. The legacy `border-destructive/50` was\n // also an opacity modifier, which cannot be contrast-checked at all\n // because the result depends on whatever sits behind it.\n destructive: \"bg-background text-destructive-strong border-destructive-strong\",\n },\n },\n defaultVariants: { variant: \"default\" },\n },\n);\n\n/**\n * An inline status message.\n *\n * Announced by assistive technology the moment it renders, via `role=\"alert\"`.\n * That makes it the wrong component for content that is present on load and\n * merely styled as a notice -- use a plain region for those.\n *\n * @example\n * <Alert variant=\"destructive\" icon={<WarningIcon aria-hidden />} title=\"Save failed\">\n * Your changes were not saved.\n * </Alert>\n */\nexport interface AlertProps\n // The native `title` attribute is omitted so this component can use the\n // name for its heading. That attribute renders a browser tooltip, which is\n // not keyboard reachable and is not announced reliably -- on a role=\"alert\"\n // it is worth nothing, and silently shadowing it with a ReactNode would be\n // a type error waiting for the first consumer who tried both.\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"title\">, VariantProps<typeof alertVariants> {\n /**\n * Leading glyph. Mark it `aria-hidden` -- the message text is the\n * announcement, and an unhidden graphic makes the alert announce twice.\n *\n * A prop rather than a positioned child: the legacy `[&>svg]` selectors\n * only worked when the icon was the first element, and broke in RTL.\n */\n icon?: ReactNode;\n /**\n * Optional heading. Replaces the legacy `AlertTitle` part.\n *\n * Omit it for a single-line alert; most call sites do.\n */\n title?: ReactNode;\n /**\n * Heading level for {@link AlertProps.title}.\n *\n * The legacy `AlertTitle` was always an `<h5>`, which breaks the document\n * outline anywhere the alert is not nested five deep.\n *\n * @default 3\n */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n /**\n * Whether the alert is shown. Controlled: hiding it collapses its height and\n * fades it out, and showing it again grows it back. An alert that is open on\n * first render does not animate. Leave it out for an alert that is always there.\n */\n open?: boolean;\n /**\n * Adds a Dismiss button. Pressed, a controlled alert (`open` given) only calls\n * this and waits to be told `open={false}`; an uncontrolled alert (no `open`)\n * collapses first and then calls it.\n */\n onDismiss?: () => void;\n /** The Dismiss button's accessible name. @default \"Dismiss\" */\n dismissLabel?: string;\n}\n\n/** A string title or description swaps as a whole when its text changes. */\nfunction Swappable({ value, children }: { value: ReactNode; children: ReactNode }) {\n if (typeof value !== \"string\" && typeof value !== \"number\") return <>{children}</>;\n return (\n <SwapText swapKey={String(value)} className=\"block\">\n {children}\n </SwapText>\n );\n}\n\n/** The name an icon swaps under: a different icon component crossfades; the same one re-rendering stays still. */\nfunction iconKey(icon: ReactNode) {\n if (!isValidElement(icon)) return \"icon\";\n const type = icon.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type.displayName ?? type.name ?? \"icon\");\n}\n\nfunction DismissGlyph() {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n aria-hidden=\"true\"\n className=\"size-4\"\n >\n <path d=\"M4 4l8 8M12 4l-8 8\" />\n </svg>\n );\n}\n\nexport const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(\n {\n className,\n variant,\n icon,\n title,\n headingLevel = 3,\n children,\n open,\n onDismiss,\n dismissLabel = \"Dismiss\",\n ...props\n },\n ref,\n) {\n const Title = `h${headingLevel}` as const;\n const frame = useRef<HTMLDivElement>(null);\n const content = useRef<HTMLDivElement>(null);\n const text = typeof children === \"string\" || typeof children === \"number\" ? String(children) : \"\";\n useHeightFollow(\n frame,\n content,\n `${typeof title === \"string\" ? title : \"\"}\\n${children === undefined ? \"\" : \"details\"}\\n${text}`,\n );\n\n // Presence. Without `open` or `onDismiss` the alert is simply there.\n const managed = open !== undefined || onDismiss !== undefined;\n const [dismissed, setDismissed] = useState(false);\n const wanted = open ?? !dismissed;\n const [rendered, setRendered] = useState(wanted);\n const [shown, setShown] = useState(wanted);\n if (wanted && !rendered) setRendered(true);\n if (!wanted && shown) setShown(false);\n useEffect(() => {\n // A newly shown alert mounts collapsed and opens a frame later, so it grows rather than appears.\n if (wanted && rendered && !shown) {\n const frameId = requestAnimationFrame(() => setShown(true));\n return () => cancelAnimationFrame(frameId);\n }\n }, [wanted, rendered, shown]);\n const leaving = rendered && !wanted;\n const finish = useCallback(() => {\n setRendered(false);\n if (open === undefined) onDismiss?.();\n }, [onDismiss, open]);\n useExitBackstop(leaving, finish, 900);\n useIsomorphicLayoutEffect(() => {\n if (leaving && prefersReducedMotion()) finish();\n }, [leaving, finish]);\n if (managed && !rendered) return null;\n\n const alert = (\n <div\n ref={ref}\n {...props}\n role=\"alert\"\n data-slot=\"alert\"\n className={cn(alertVariants({ variant }), className)}\n >\n {/*\n The icon is decorative or labelled by the consumer; this wrapper adds\n no name. It keeps the icon from shrinking and sizes a bare svg to the\n icon scale, unless the consumer sized it themselves.\n */}\n {icon === undefined ? null : (\n <span\n data-slot=\"alert-icon\"\n className=\"grid shrink-0 [&>svg]:pointer-events-none [&>svg:not([class*='size-'])]:size-(--icon-sm)\"\n >\n <SwapText swapKey={iconKey(icon)} motion=\"glyph\" className=\"grid place-items-center\">\n {icon}\n </SwapText>\n </span>\n )}\n {/* The copy column follows its content's height, so longer or shorter text never snaps the alert. */}\n <div ref={frame} data-slot=\"alert-body\" className=\"height-follow min-w-0 flex-1\">\n <div ref={content}>\n {title === undefined ? null : (\n <Title\n data-slot=\"alert-title\"\n className=\"mb-1 text-sm leading-(--leading-tight) font-medium\"\n >\n <Swappable value={title}>{title}</Swappable>\n </Title>\n )}\n {children === undefined ? null : (\n <div data-slot=\"alert-description\">\n <Swappable value={children}>{children}</Swappable>\n </div>\n )}\n </div>\n </div>\n {onDismiss ? (\n <AriaButton\n data-slot=\"alert-dismiss\"\n aria-label={typeof title === \"string\" ? `${dismissLabel}: ${title}` : dismissLabel}\n onPress={() => (open === undefined ? setDismissed(true) : onDismiss())}\n className=\"calendar-nav-motion -my-1 -me-1.5 grid size-7 shrink-0 cursor-pointer place-items-center rounded-full border-0 bg-transparent text-muted-foreground outline-none data-[hovered]:bg-muted data-[hovered]:text-foreground 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)\"\n >\n <DismissGlyph />\n </AriaButton>\n ) : null}\n </div>\n );\n if (!managed) return alert;\n return (\n <div\n data-slot=\"alert-presence\"\n data-shown={shown || undefined}\n onTransitionEnd={(event) => {\n if (leaving && event.target === event.currentTarget && event.propertyName === \"grid-template-rows\")\n finish();\n }}\n className=\"alert-presence\"\n >\n <div>{alert}</div>\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,gBAAgB,IAC3B;CAOE;CACA;CAKA;AACF,GACA;CACE,UAAU,EACR,SAAS;EACP,SAAS;EAMT,aAAa;CACf,EACF;CACA,iBAAiB,EAAE,SAAS,UAAU;AACxC,CACF;;AA6DA,SAAS,UAAU,EAAE,OAAO,YAAuD;CACjF,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU,OAAO,oBAAA,YAAA,EAAG,SAAW,CAAA;CACjF,OACE,oBAAC,UAAD;EAAU,SAAS,OAAO,KAAK;EAAG,WAAU;EACzC;CACO,CAAA;AAEd;;AAGA,SAAS,QAAQ,MAAiB;CAChC,IAAI,CAAC,eAAe,IAAI,GAAG,OAAO;CAClC,MAAM,OAAO,KAAK;CAClB,OAAO,OAAO,SAAS,WAAW,OAAQ,KAAK,eAAe,KAAK,QAAQ;AAC7E;AAEA,SAAS,eAAe;CACtB,OACE,oBAAC,OAAD;EACE,SAAQ;EACR,MAAK;EACL,QAAO;EACP,aAAY;EACZ,eAAc;EACd,eAAY;EACZ,WAAU;EAEV,UAAA,oBAAC,QAAD,EAAM,GAAE,qBAAsB,CAAA;CAC3B,CAAA;AAET;AAEA,MAAa,QAAQ,WAAuC,SAAS,MACnE,EACE,WACA,SACA,MACA,OACA,eAAe,GACf,UACA,MACA,WACA,eAAe,WACf,GAAG,SAEL,KACA;CACA,MAAM,QAAQ,IAAI;CAClB,MAAM,QAAQ,OAAuB,IAAI;CACzC,MAAM,UAAU,OAAuB,IAAI;CAE3C,gBACE,OACA,SACA,GAAG,OAAO,UAAU,WAAW,QAAQ,GAAG,IAAI,aAAa,KAAA,IAAY,KAAK,UAAU,IAJ3E,OAAO,aAAa,YAAY,OAAO,aAAa,WAAW,OAAO,QAAQ,IAAI,IAK/F;CAGA,MAAM,UAAU,SAAS,KAAA,KAAa,cAAc,KAAA;CACpD,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAChD,MAAM,SAAS,QAAQ,CAAC;CACxB,MAAM,CAAC,UAAU,eAAe,SAAS,MAAM;CAC/C,MAAM,CAAC,OAAO,YAAY,SAAS,MAAM;CACzC,IAAI,UAAU,CAAC,UAAU,YAAY,IAAI;CACzC,IAAI,CAAC,UAAU,OAAO,SAAS,KAAK;CACpC,gBAAgB;EAEd,IAAI,UAAU,YAAY,CAAC,OAAO;GAChC,MAAM,UAAU,4BAA4B,SAAS,IAAI,CAAC;GAC1D,aAAa,qBAAqB,OAAO;EAC3C;CACF,GAAG;EAAC;EAAQ;EAAU;CAAK,CAAC;CAC5B,MAAM,UAAU,YAAY,CAAC;CAC7B,MAAM,SAAS,kBAAkB;EAC/B,YAAY,KAAK;EACjB,IAAI,SAAS,KAAA,GAAW,YAAY;CACtC,GAAG,CAAC,WAAW,IAAI,CAAC;CACpB,gBAAgB,SAAS,QAAQ,GAAG;CACpC,gCAAgC;EAC9B,IAAI,WAAW,qBAAqB,GAAG,OAAO;CAChD,GAAG,CAAC,SAAS,MAAM,CAAC;CACpB,IAAI,WAAW,CAAC,UAAU,OAAO;CAEjC,MAAM,QACJ,qBAAC,OAAD;EACO;EACL,GAAI;EACJ,MAAK;EACL,aAAU;EACV,WAAW,GAAG,cAAc,EAAE,QAAQ,CAAC,GAAG,SAAS;EALrD,UAAA;GAYG,SAAS,KAAA,IAAY,OACpB,oBAAC,QAAD;IACE,aAAU;IACV,WAAU;IAEV,UAAA,oBAAC,UAAD;KAAU,SAAS,QAAQ,IAAI;KAAG,QAAO;KAAQ,WAAU;KACxD,UAAA;IACO,CAAA;GACN,CAAA;GAGR,oBAAC,OAAD;IAAK,KAAK;IAAO,aAAU;IAAa,WAAU;IAChD,UAAA,qBAAC,OAAD;KAAK,KAAK;KAAV,UAAA,CACG,UAAU,KAAA,IAAY,OACrB,oBAAC,OAAD;MACE,aAAU;MACV,WAAU;MAEV,UAAA,oBAAC,WAAD;OAAW,OAAO;OAAQ,UAAA;MAAiB,CAAA;KACtC,CAAA,GAER,aAAa,KAAA,IAAY,OACxB,oBAAC,OAAD;MAAK,aAAU;MACb,UAAA,oBAAC,WAAD;OAAW,OAAO;OAAW;MAAoB,CAAA;KAC9C,CAAA,CAEJ;;GACF,CAAA;GACJ,YACC,oBAACA,QAAD;IACE,aAAU;IACV,cAAY,OAAO,UAAU,WAAW,GAAG,aAAa,IAAI,UAAU;IACtE,eAAgB,SAAS,KAAA,IAAY,aAAa,IAAI,IAAI,UAAU;IACpE,WAAU;IAEV,UAAA,oBAAC,cAAD,CAAe,CAAA;GACL,CAAA,IACV;EACD;;CAEP,IAAI,CAAC,SAAS,OAAO;CACrB,OACE,oBAAC,OAAD;EACE,aAAU;EACV,cAAY,SAAS,KAAA;EACrB,kBAAkB,UAAU;GAC1B,IAAI,WAAW,MAAM,WAAW,MAAM,iBAAiB,MAAM,iBAAiB,sBAC5E,OAAO;EACX;EACA,WAAU;EAEV,UAAA,oBAAC,OAAD,EAAA,UAAM,MAAW,CAAA;CACd,CAAA;AAET,CAAC"}
|
|
1
|
+
{"version":3,"file":"alert.js","names":["AriaButton"],"sources":["../../src/alert/alert.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n forwardRef,\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type AriaRole,\n type ComponentPropsWithoutRef,\n type ReactNode,\n type SVGProps,\n} from \"react\";\n\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { useHeightFollow } from \"../lib/height-follow.js\";\nimport { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * `info` is `default` said by name: a call site that means \"information\" should\n * not depend on `default` happening to look like it. Every per-variant map below\n * derives `info` here, from its `default`, so the two cannot drift.\n */\nfunction withInfo<T extends { default: unknown }>(map: T): T & { info: T[\"default\"] } {\n return { ...map, info: map.default };\n}\n\n/**\n * Alert, ported from the legacy shadcn component.\n *\n * There is no React Aria base: RAC ships no static inline alert (its Toast\n * API is transient and queue-driven), so this is a styled div with\n * role=\"alert\". Because it is not RAC-backed, its props follow\n * ARCHITECTURE section 7 rule 1 -- VariantProps & ComponentPropsWithoutRef --\n * rather than the Button's Omit-from-React-Aria departure, which exists only\n * to stop RAC-ignored props being advertised.\n *\n * Every string below is a STATIC literal. Tailwind scans source text, so a\n * class assembled at runtime is invisible to it and would never be emitted.\n *\n * THE LAYOUT IS THE BIGGEST CHANGE. The legacy component positioned its icon\n * with `[&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4`, pushed the text over\n * with `[&>svg~*]:pl-7`, and nudged it back with `[&>svg+div]:translate-y-[-3px]`.\n * All three are physical properties or magic numbers: `left` and `pl` do not\n * mirror in RTL, so the icon stayed left while the text indented the wrong\n * side, and `translate-y-[-3px]` is a bracket value that `check:hardcoding`\n * rejects outright. Flex with an explicit `icon` prop deletes all three\n * problems rather than porting them.\n */\nexport const alertVariants = cva(\n [\n // box-border is not boilerplate. Tailwind Preflight sets box-sizing\n // globally and the legacy app had it; this library's reset is OPT-IN, so\n // a consumer may load none and the browser default content-box applies.\n // w-full is width:100%, so the padding and border are then ADDED to it\n // and the alert overflows its container by exactly 46px at comfortable\n // density. Measured, not theorised: 346px inside a 300px parent.\n \"box-border flex w-full items-start gap-(--density-control-gap)\",\n \"rounded-panel border-(length:--border-width) p-(--density-control-pad-md)\",\n // font-sans is load-bearing. The library does not style the consumer's\n // page, so anything that does not set its own face inherits the host's\n // default -- which is a serif in an unstyled document. The accordion\n // panel shipped exactly that way and it was caught by eye, not by a test.\n \"font-sans text-sm leading-(--leading-normal)\",\n ],\n {\n variants: {\n variant: withInfo({\n default: \"bg-surface text-foreground border-border\",\n // The status variants paint only their icon; the card stays neutral\n // (dark title, grey description, light border), like the reference\n // Sync status alert. See alertIconVariants.\n success: \"bg-surface text-foreground border-border\",\n warning: \"bg-surface text-foreground border-border\",\n // destructive-STRONG, not destructive. The fill token is 4.04:1 on\n // the dark page; this role exists so the text and border clear the\n // 4.5 floor in both themes. The legacy `border-destructive/50` was\n // also an opacity modifier, which cannot be contrast-checked at all\n // because the result depends on whatever sits behind it.\n destructive: \"bg-surface text-destructive-strong border-destructive-strong\",\n }),\n },\n defaultVariants: { variant: \"default\" },\n },\n);\n\n/**\n * The icon's colour: the state's own hue on every variant. Inside an Alert the\n * destructive red is also the whole alert's text colour, so it changes nothing\n * there; outside one it is what colours the icon.\n *\n * Exported with the status icons so a notice that is not an `Alert` (a row, a\n * banner of the app's own) can show a tone the same way the alert does.\n */\nexport const alertIconVariants = cva(\n \"flex h-lh shrink-0 items-center [&_svg]:block [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n {\n variants: {\n variant: withInfo({\n default: \"text-info-strong\",\n success: \"text-success-strong\",\n warning: \"text-warning-strong\",\n destructive: \"text-destructive-strong\",\n }),\n },\n defaultVariants: { variant: \"default\" },\n },\n);\n\n/**\n * The built-in glyph of each variant, drawn with currentColor so the icon\n * wrapper's colour paints it. Each is its own component so a variant change\n * crossfades the icon (see iconKey).\n *\n * Exported as AlertInfoIcon, AlertSuccessIcon, AlertWarningIcon and\n * AlertDestructiveIcon. They are decorative (`aria-hidden`) and `--icon-sm`\n * by default; extra props, such as a `className` with a size, land on the svg.\n * Given an `aria-label` or `aria-labelledby` (with `role=\"img\"`), an icon is\n * no longer hidden, and an explicit `aria-hidden` always wins.\n */\ntype StatusIconProps = Omit<SVGProps<SVGSVGElement>, \"children\">;\nfunction StatusGlyph({\n children,\n className,\n ...props\n}: StatusIconProps & { children: ReactNode }) {\n const labelled = props[\"aria-label\"] !== undefined || props[\"aria-labelledby\"] !== undefined;\n return (\n <svg\n // Before the spread, so a caller's own aria-hidden wins; a labelled icon\n // is meant to be read, so it is not hidden by default.\n aria-hidden={labelled ? undefined : \"true\"}\n {...props}\n // Sized on its own, so an exported icon outside an alert is not 0x0; the\n // same --icon-sm the alert's icon slot gives an unsized svg. A caller's\n // size class wins.\n className={cn(\"size-(--icon-sm)\", className)}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n {children}\n </svg>\n );\n}\nfunction InfoGlyph(props: StatusIconProps) {\n return (\n <StatusGlyph {...props}>\n <circle cx=\"8\" cy=\"8\" r=\"6.25\" />\n <path d=\"M8 7.25v3.5M8 5h.01\" />\n </StatusGlyph>\n );\n}\nfunction SuccessGlyph(props: StatusIconProps) {\n return (\n <StatusGlyph {...props}>\n <circle cx=\"8\" cy=\"8\" r=\"6.25\" />\n <path d=\"m5.25 8.25 1.9 1.9 3.6-3.9\" />\n </StatusGlyph>\n );\n}\nfunction WarningGlyph(props: StatusIconProps) {\n return (\n <StatusGlyph {...props}>\n <path d=\"M6.9 2.6 1.9 11.4a1.2 1.2 0 0 0 1 1.8h10.2a1.2 1.2 0 0 0 1-1.8L9.1 2.6a1.2 1.2 0 0 0-2.2 0Z\" />\n <path d=\"M8 6.4v2.6M8 11.2h.01\" />\n </StatusGlyph>\n );\n}\nfunction DestructiveGlyph(props: StatusIconProps) {\n return (\n <StatusGlyph {...props}>\n <circle cx=\"8\" cy=\"8\" r=\"6.25\" />\n <path d=\"m6 6 4 4M10 6l-4 4\" />\n </StatusGlyph>\n );\n}\nexport {\n InfoGlyph as AlertInfoIcon,\n SuccessGlyph as AlertSuccessIcon,\n WarningGlyph as AlertWarningIcon,\n DestructiveGlyph as AlertDestructiveIcon,\n};\nexport type { StatusIconProps as AlertStatusIconProps };\n\nconst statusGlyphs = withInfo({\n default: <InfoGlyph />,\n success: <SuccessGlyph />,\n warning: <WarningGlyph />,\n destructive: <DestructiveGlyph />,\n});\n\n/**\n * An inline status message.\n *\n * Announced by assistive technology the moment it renders, via `role=\"alert\"`.\n * That makes it the wrong default for content that is present on load and\n * merely styled as a notice -- pass `role=\"note\"` for those, or\n * `role=\"status\"` for an outcome that should be announced politely.\n *\n * @example\n * <Alert variant=\"destructive\" icon={<WarningIcon aria-hidden />} title=\"Save failed\">\n * Your changes were not saved.\n * </Alert>\n */\nexport interface AlertProps\n // The native `title` attribute is omitted so this component can use the\n // name for its heading. That attribute renders a browser tooltip, which is\n // not keyboard reachable and is not announced reliably -- on a role=\"alert\"\n // it is worth nothing, and silently shadowing it with a ReactNode would be\n // a type error waiting for the first consumer who tried both.\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"title\">, VariantProps<typeof alertVariants> {\n /**\n * Leading glyph. Mark it `aria-hidden` -- the message text is the\n * announcement, and an unhidden graphic makes the alert announce twice.\n *\n * Left out, the alert shows its variant's own icon (info, check, warning\n * triangle, cross), coloured for the state and centred on the first line of\n * text. Pass `null` for no icon, or your own glyph to replace it.\n *\n * A prop rather than a positioned child: the legacy `[&>svg]` selectors\n * only worked when the icon was the first element, and broke in RTL.\n */\n icon?: ReactNode;\n /**\n * Optional heading. Replaces the legacy `AlertTitle` part.\n *\n * Omit it for a single-line alert; most call sites do.\n */\n title?: ReactNode;\n /**\n * Heading level for {@link AlertProps.title}.\n *\n * The legacy `AlertTitle` was always an `<h5>`, which breaks the document\n * outline anywhere the alert is not nested five deep.\n *\n * @default 3\n */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n /**\n * Whether the alert is shown. Controlled: hiding it collapses its height and\n * fades it out, and showing it again grows it back. An alert that is open on\n * first render does not animate. Leave it out for an alert that is always there.\n */\n open?: boolean;\n /**\n * Adds a Dismiss button. Pressed, a controlled alert (`open` given) only calls\n * this and waits to be told `open={false}`; an uncontrolled alert (no `open`)\n * collapses first and then calls it.\n */\n onDismiss?: () => void;\n /** The Dismiss button's accessible name. @default \"Dismiss\" */\n dismissLabel?: string;\n /**\n * How assistive technology treats the message.\n *\n * - `alert`: announced assertively the moment it renders. For an error or a\n * warning that appears after the user did something.\n * - `status`: a live region announced politely, after what the user is\n * hearing. For an outcome reported after an action (\"Saved\").\n * - `note`: not live. For a notice that is simply there when the page loads;\n * an alert there would interrupt the page's own announcement.\n *\n * Typed as any ARIA role, as the inherited `role` was, so existing callers\n * still compile; those three are the ones an inline notice should use.\n *\n * @default \"alert\"\n */\n role?: \"alert\" | \"status\" | \"note\" | AriaRole;\n}\n\n/** A string title or description swaps as a whole when its text changes. */\nfunction Swappable({ value, children }: { value: ReactNode; children: ReactNode }) {\n if (typeof value !== \"string\" && typeof value !== \"number\") return <>{children}</>;\n return (\n <SwapText swapKey={String(value)} className=\"block\">\n {children}\n </SwapText>\n );\n}\n\n/** The name an icon swaps under: a different icon component crossfades; the same one re-rendering stays still. */\nfunction iconKey(icon: ReactNode) {\n if (!isValidElement(icon)) return \"icon\";\n const type = icon.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type.displayName ?? type.name ?? \"icon\");\n}\n\nfunction DismissGlyph() {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n aria-hidden=\"true\"\n className=\"size-4\"\n >\n <path d=\"M4 4l8 8M12 4l-8 8\" />\n </svg>\n );\n}\n\nexport const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(\n {\n className,\n variant,\n icon: iconProp,\n title,\n headingLevel = 3,\n children,\n open,\n onDismiss,\n dismissLabel = \"Dismiss\",\n role = \"alert\",\n ...props\n },\n ref,\n) {\n const Title = `h${headingLevel}` as const;\n const icon = iconProp === undefined ? statusGlyphs[variant ?? \"default\"] : iconProp;\n const frame = useRef<HTMLDivElement>(null);\n const content = useRef<HTMLDivElement>(null);\n const text = typeof children === \"string\" || typeof children === \"number\" ? String(children) : \"\";\n useHeightFollow(\n frame,\n content,\n `${typeof title === \"string\" ? title : \"\"}\\n${children === undefined ? \"\" : \"details\"}\\n${text}`,\n );\n\n // Presence. Without `open` or `onDismiss` the alert is simply there.\n const managed = open !== undefined || onDismiss !== undefined;\n const [dismissed, setDismissed] = useState(false);\n const wanted = open ?? !dismissed;\n const [rendered, setRendered] = useState(wanted);\n const [shown, setShown] = useState(wanted);\n if (wanted && !rendered) setRendered(true);\n if (!wanted && shown) setShown(false);\n useEffect(() => {\n // A newly shown alert mounts collapsed and opens a frame later, so it grows rather than appears.\n if (wanted && rendered && !shown) {\n const frameId = requestAnimationFrame(() => setShown(true));\n return () => cancelAnimationFrame(frameId);\n }\n }, [wanted, rendered, shown]);\n const leaving = rendered && !wanted;\n const finish = useCallback(() => {\n setRendered(false);\n if (open === undefined) onDismiss?.();\n }, [onDismiss, open]);\n useExitBackstop(leaving, finish, 900);\n useIsomorphicLayoutEffect(() => {\n if (leaving && prefersReducedMotion()) finish();\n }, [leaving, finish]);\n if (managed && !rendered) return null;\n\n const alert = (\n <div\n ref={ref}\n {...props}\n role={role}\n data-slot=\"alert\"\n className={cn(alertVariants({ variant }), className)}\n >\n {/*\n The icon is decorative or labelled by the consumer; this wrapper adds\n no name. It keeps the icon from shrinking and sizes a bare svg to the\n icon scale, unless the consumer sized it themselves.\n */}\n {icon === null || icon === false ? null : (\n <span data-slot=\"alert-icon\" className={alertIconVariants({ variant })}>\n <SwapText swapKey={iconKey(icon)} motion=\"glyph\" className=\"grid place-items-center\">\n {icon}\n </SwapText>\n </span>\n )}\n {/* The copy column follows its content's height, so longer or shorter text never snaps the alert. */}\n <div ref={frame} data-slot=\"alert-body\" className=\"height-follow min-w-0 flex-1\">\n <div ref={content}>\n {title === undefined ? null : (\n <Title\n data-slot=\"alert-title\"\n className=\"m-0 mb-1 text-sm leading-(--leading-normal) font-medium\"\n >\n <Swappable value={title}>{title}</Swappable>\n </Title>\n )}\n {children === undefined ? null : (\n <div\n data-slot=\"alert-description\"\n className={title === undefined ? undefined : \"text-xs leading-(--leading-normal) text-text-secondary\"}\n >\n <Swappable value={children}>{children}</Swappable>\n </div>\n )}\n </div>\n </div>\n {onDismiss ? (\n <AriaButton\n data-slot=\"alert-dismiss\"\n aria-label={typeof title === \"string\" ? `${dismissLabel}: ${title}` : dismissLabel}\n onPress={() => (open === undefined ? setDismissed(true) : onDismiss())}\n className=\"calendar-nav-motion -my-1 -me-1.5 grid size-7 shrink-0 cursor-pointer place-items-center rounded-full border-0 bg-transparent text-muted-foreground outline-none data-[hovered]:bg-muted data-[hovered]:text-foreground 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)\"\n >\n <DismissGlyph />\n </AriaButton>\n ) : null}\n </div>\n );\n if (!managed) return alert;\n return (\n <div\n data-slot=\"alert-presence\"\n data-shown={shown || undefined}\n onTransitionEnd={(event) => {\n if (leaving && event.target === event.currentTarget && event.propertyName === \"grid-template-rows\")\n finish();\n }}\n className=\"alert-presence\"\n >\n <div>{alert}</div>\n </div>\n );\n});\n"],"mappings":"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"}
|
package/dist/alert/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Alert, AlertProps, alertVariants } from "./alert.js";
|
|
2
|
-
export { Alert, type AlertProps, alertVariants };
|
|
1
|
+
import { Alert, AlertDestructiveIcon as DestructiveGlyph, AlertInfoIcon as InfoGlyph, AlertProps, AlertStatusIconProps as StatusIconProps, AlertSuccessIcon as SuccessGlyph, AlertWarningIcon as WarningGlyph, alertIconVariants, alertVariants } from "./alert.js";
|
|
2
|
+
export { Alert, DestructiveGlyph as AlertDestructiveIcon, InfoGlyph as AlertInfoIcon, type AlertProps, type StatusIconProps as AlertStatusIconProps, SuccessGlyph as AlertSuccessIcon, WarningGlyph as AlertWarningIcon, alertIconVariants, alertVariants };
|
package/dist/alert/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Alert, alertVariants } from "./alert.js";
|
|
2
|
-
export { Alert, alertVariants };
|
|
1
|
+
import { Alert, AlertDestructiveIcon as DestructiveGlyph, AlertInfoIcon as InfoGlyph, AlertSuccessIcon as SuccessGlyph, AlertWarningIcon as WarningGlyph, alertIconVariants, alertVariants } from "./alert.js";
|
|
2
|
+
export { Alert, DestructiveGlyph as AlertDestructiveIcon, InfoGlyph as AlertInfoIcon, SuccessGlyph as AlertSuccessIcon, WarningGlyph as AlertWarningIcon, alertIconVariants, alertVariants };
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import { ButtonProps } from "../button/button.js";
|
|
1
2
|
import { DialogProps } from "../dialog/dialog.js";
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
4
|
//#region src/alert-dialog/alert-dialog.d.ts
|
|
4
5
|
/**
|
|
5
6
|
* AlertDialog: a confirmation that must be answered.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Built from `Dialog`'s own overlay and panel styles and the same React Aria
|
|
9
|
+
* parts, but with its own layout: a header (title, a grey subtitle and a
|
|
10
|
+
* circular close button), a thin divider, the description sentence, an
|
|
11
|
+
* optional `body` of rich content, then the actions.
|
|
12
|
+
* The legacy `alert-dialog.tsx` was a 157-line clone of `dialog.tsx`; this
|
|
13
|
+
* shares the tokens and the motion classes instead, so the two cannot drift
|
|
14
|
+
* on radius, scrim or entrance.
|
|
12
15
|
*
|
|
13
16
|
* Census: 2 files, all eight parts used twice each, `AlertDialogPortal`,
|
|
14
17
|
* `AlertDialogOverlay` and `AlertDialogTrigger` never rendered externally.
|
|
@@ -19,35 +22,72 @@ import { ReactNode } from "react";
|
|
|
19
22
|
* `aria-describedby` ONLY for this role (useDialog.mjs:29), so a
|
|
20
23
|
* confirmation built from a plain Dialog loses the sentence explaining
|
|
21
24
|
* what is about to happen.
|
|
22
|
-
* - `closeButton={false}`. An X in the corner is a third answer to a
|
|
23
|
-
* two-answer question, and it is ambiguous: it reads as "cancel" but
|
|
24
|
-
* looks like "dismiss".
|
|
25
25
|
* - `isDismissable={false}`. Clicking the page must not resolve a
|
|
26
26
|
* destructive confirmation. Escape still cancels, which is the APG
|
|
27
27
|
* behaviour and what Radix does.
|
|
28
|
+
* - THE CLOSE (X) IS CANCEL. It is `slot="close"`, the same dismiss wiring as
|
|
29
|
+
* a Cancel button, so it goes through the same `onOpenChange(false)` and
|
|
30
|
+
* focus returns to the trigger. It is not a third answer: it is the
|
|
31
|
+
* header's copy of the second one.
|
|
28
32
|
* - `description` is REQUIRED. A confirmation with no explanation is the
|
|
29
33
|
* thing this component exists to prevent, and the role only associates it
|
|
30
|
-
* when it is there.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* when it is there. It is the sentence under the divider.
|
|
35
|
+
*
|
|
36
|
+
* THE TWO ACTIONS MUST NEVER LOOK ALIKE. `AlertDialogCancel` is the secondary
|
|
37
|
+
* button (white, light border) and cancels through `slot="close"`;
|
|
38
|
+
* `AlertDialogAction` is the primary button painted destructive red, because a
|
|
39
|
+
* confirmation here is a destructive one. The legacy `AlertDialogAction`
|
|
40
|
+
* defaulted to the gradient primary and BOTH call sites overrode it back to
|
|
41
|
+
* destructive by hand (`data-table.tsx:146`, `calendar-view.tsx:638`); the red
|
|
42
|
+
* is the default now, so nobody fights it. Plain `Button`s still work in the
|
|
43
|
+
* actions slot for anything else.
|
|
38
44
|
*/
|
|
39
|
-
export interface AlertDialogProps extends Omit<DialogProps, "role" | "closeButton" | "
|
|
45
|
+
export interface AlertDialogProps extends Omit<DialogProps, "role" | "closeButton" | "isDismissable"> {
|
|
40
46
|
/**
|
|
41
|
-
* What is about to happen, and whether it can be undone.
|
|
47
|
+
* What is about to happen, and whether it can be undone. Rendered as the
|
|
48
|
+
* sentence under the divider, above the `body` slot (if any) and the actions.
|
|
42
49
|
*
|
|
43
50
|
* **Required**, unlike on `Dialog`. `role="alertdialog"` is what makes
|
|
44
51
|
* React Aria wire this to `aria-describedby`, so it is both the sentence
|
|
45
52
|
* the user needs and the one the screen reader announces with the name.
|
|
46
53
|
*/
|
|
47
54
|
description: ReactNode;
|
|
55
|
+
/** A short grey line under the title, in the header. */
|
|
56
|
+
subtitle?: ReactNode;
|
|
57
|
+
/**
|
|
58
|
+
* Rich content between the description and the actions: a plan comparison,
|
|
59
|
+
* a before/after diff, a quoted reply, a form field or a choice to make.
|
|
60
|
+
*
|
|
61
|
+
* It is NOT part of the accessible description: the screen reader announces
|
|
62
|
+
* the one `description` sentence with the name, and the body is read in
|
|
63
|
+
* place or as focus reaches its controls (each with its own label). So the
|
|
64
|
+
* `description` must say everything needed to answer, including that a
|
|
65
|
+
* choice or field below is required ("Pick what happens to your data.").
|
|
66
|
+
* `false`, `true` and `""` (as from `cond && <X />`) render no body.
|
|
67
|
+
*/
|
|
68
|
+
body?: ReactNode;
|
|
48
69
|
/** The actions. Give the cancel button `slot="close"`. */
|
|
49
70
|
children?: ReactNode;
|
|
50
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* The cancel action: the secondary button, wired to dismiss the dialog.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* <AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
77
|
+
*/
|
|
78
|
+
export declare const AlertDialogCancel: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
79
|
+
/**
|
|
80
|
+
* The confirm action: the primary button in the destructive red.
|
|
81
|
+
*
|
|
82
|
+
* It is the primary button with its fill, hover, pressed and label roles
|
|
83
|
+
* pointed at the destructive tokens, so it keeps the primary's press motion
|
|
84
|
+
* and shadow. Variables on the element, rather than a competing background
|
|
85
|
+
* class, so no stylesheet order decides which colour wins.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* <AlertDialogAction onPress={remove}>Delete</AlertDialogAction>
|
|
89
|
+
*/
|
|
90
|
+
export declare const AlertDialogAction: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
51
91
|
export declare const AlertDialog: import("react").ForwardRefExoticComponent<AlertDialogProps & import("react").RefAttributes<HTMLElement>>;
|
|
52
92
|
//#endregion
|
|
53
93
|
//# sourceMappingURL=alert-dialog.d.ts.map
|