@mitcsutt/kiln-ui 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/assets/fonts/archivo-latin-ext-wdth-italic.woff2 +0 -0
- package/dist/assets/fonts/archivo-latin-ext-wdth-normal.woff2 +0 -0
- package/dist/assets/fonts/archivo-latin-wdth-italic.woff2 +0 -0
- package/dist/assets/fonts/archivo-latin-wdth-normal.woff2 +0 -0
- package/dist/assets/fonts/atkinson-hyperlegible-mono-latin-ext-wght-italic.woff2 +0 -0
- package/dist/assets/fonts/atkinson-hyperlegible-mono-latin-ext-wght-normal.woff2 +0 -0
- package/dist/assets/fonts/atkinson-hyperlegible-mono-latin-wght-italic.woff2 +0 -0
- package/dist/assets/fonts/atkinson-hyperlegible-mono-latin-wght-normal.woff2 +0 -0
- package/dist/assets/fonts/b612-latin-400-italic.woff2 +0 -0
- package/dist/assets/fonts/b612-latin-400-normal.woff2 +0 -0
- package/dist/assets/fonts/b612-latin-700-italic.woff2 +0 -0
- package/dist/assets/fonts/b612-latin-700-normal.woff2 +0 -0
- package/dist/assets/fonts/b612-mono-latin-400-italic.woff2 +0 -0
- package/dist/assets/fonts/b612-mono-latin-400-normal.woff2 +0 -0
- package/dist/assets/fonts/b612-mono-latin-700-italic.woff2 +0 -0
- package/dist/assets/fonts/b612-mono-latin-700-normal.woff2 +0 -0
- package/dist/assets/fonts/golos-text-latin-ext-wght-normal.woff2 +0 -0
- package/dist/assets/fonts/golos-text-latin-wght-normal.woff2 +0 -0
- package/dist/assets/fonts/licenses/archivo.txt +93 -0
- package/dist/assets/fonts/licenses/atkinson-hyperlegible-mono.txt +93 -0
- package/dist/assets/fonts/licenses/b612-mono.txt +93 -0
- package/dist/assets/fonts/licenses/b612.txt +93 -0
- package/dist/assets/fonts/licenses/golos-text.txt +93 -0
- package/dist/assets/fonts/licenses/shantell-sans.txt +93 -0
- package/dist/assets/fonts/shantell-sans-latin-ext-full-italic.woff2 +0 -0
- package/dist/assets/fonts/shantell-sans-latin-ext-full-normal.woff2 +0 -0
- package/dist/assets/fonts/shantell-sans-latin-full-italic.woff2 +0 -0
- package/dist/assets/fonts/shantell-sans-latin-full-normal.woff2 +0 -0
- package/dist/components/actions/Button/Button.d.ts +8 -0
- package/dist/components/actions/Button/Button.js.map +1 -1
- package/dist/components/actions/ChipGroup/ChipGroup.d.ts +9 -0
- package/dist/components/actions/ChipGroup/ChipGroup.js.map +1 -1
- package/dist/components/actions/IconButton/IconButton.d.ts +8 -0
- package/dist/components/actions/IconButton/IconButton.js.map +1 -1
- package/dist/components/actions/ModeToggle/ModeToggle.d.ts +8 -0
- package/dist/components/actions/ModeToggle/ModeToggle.js.map +1 -1
- package/dist/components/actions/SegmentedControl/SegmentedControl.d.ts +9 -0
- package/dist/components/actions/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/components/actions/ToggleChip/ToggleChip.d.ts +9 -0
- package/dist/components/actions/ToggleChip/ToggleChip.js.map +1 -1
- package/dist/components/display/Avatar/Avatar.d.ts +11 -1
- package/dist/components/display/Avatar/Avatar.js.map +1 -1
- package/dist/components/display/AvatarGroup/AvatarGroup.d.ts +7 -0
- package/dist/components/display/AvatarGroup/AvatarGroup.js.map +1 -1
- package/dist/components/display/Badge/Badge.d.ts +7 -0
- package/dist/components/display/Badge/Badge.js.map +1 -1
- package/dist/components/display/Card/Card.js.map +1 -1
- package/dist/components/display/CodeBlock/CodeBlock.d.ts +9 -0
- package/dist/components/display/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/display/DataList/DataList.js.map +1 -1
- package/dist/components/display/Delta/Delta.d.ts +9 -0
- package/dist/components/display/Delta/Delta.js.map +1 -1
- package/dist/components/display/EmptyState/EmptyState.d.ts +8 -0
- package/dist/components/display/EmptyState/EmptyState.js.map +1 -1
- package/dist/components/display/List/List.js.map +1 -1
- package/dist/components/display/Marquee/Marquee.d.ts +8 -0
- package/dist/components/display/Marquee/Marquee.js.map +1 -1
- package/dist/components/display/Media/Media.d.ts +7 -0
- package/dist/components/display/Media/Media.js.map +1 -1
- package/dist/components/display/Meter/Meter.d.ts +9 -0
- package/dist/components/display/Meter/Meter.js.map +1 -1
- package/dist/components/display/Progress/Progress.d.ts +8 -0
- package/dist/components/display/Progress/Progress.js.map +1 -1
- package/dist/components/display/Skeleton/Skeleton.js.map +1 -1
- package/dist/components/display/Stamp/Stamp.d.ts +8 -0
- package/dist/components/display/Stamp/Stamp.js.map +1 -1
- package/dist/components/display/Stat/Stat.d.ts +8 -0
- package/dist/components/display/Stat/Stat.js.map +1 -1
- package/dist/components/display/Table/Table.js.map +1 -1
- package/dist/components/display/Tag/Tag.d.ts +7 -0
- package/dist/components/display/Tag/Tag.js.map +1 -1
- package/dist/components/feedback/Alert/Alert.d.ts +10 -0
- package/dist/components/feedback/Alert/Alert.js.map +1 -1
- package/dist/components/feedback/LiveIndicator/LiveIndicator.d.ts +7 -0
- package/dist/components/feedback/LiveIndicator/LiveIndicator.js.map +1 -1
- package/dist/components/feedback/RelativeTime/RelativeTime.d.ts +8 -0
- package/dist/components/feedback/RelativeTime/RelativeTime.js.map +1 -1
- package/dist/components/feedback/Spinner/Spinner.d.ts +10 -1
- package/dist/components/feedback/Spinner/Spinner.js.map +1 -1
- package/dist/components/feedback/StatusDot/StatusDot.d.ts +10 -1
- package/dist/components/feedback/StatusDot/StatusDot.js.map +1 -1
- package/dist/components/inputs/AmountField/AmountField.d.ts +7 -0
- package/dist/components/inputs/AmountField/AmountField.js.map +1 -1
- package/dist/components/inputs/AmountInput/AmountInput.d.ts +8 -0
- package/dist/components/inputs/AmountInput/AmountInput.js.map +1 -1
- package/dist/components/inputs/Checkbox/Checkbox.d.ts +8 -0
- package/dist/components/inputs/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/inputs/CheckboxField/CheckboxField.d.ts +7 -0
- package/dist/components/inputs/CheckboxField/CheckboxField.js.map +1 -1
- package/dist/components/inputs/CheckboxGroup/CheckboxGroup.d.ts +8 -0
- package/dist/components/inputs/CheckboxGroup/CheckboxGroup.js.map +1 -1
- package/dist/components/inputs/CheckboxGroupField/CheckboxGroupField.d.ts +7 -0
- package/dist/components/inputs/CheckboxGroupField/CheckboxGroupField.js.map +1 -1
- package/dist/components/inputs/ChipGroupField/ChipGroupField.d.ts +7 -0
- package/dist/components/inputs/ChipGroupField/ChipGroupField.js.map +1 -1
- package/dist/components/inputs/ChoiceCards/ChoiceCards.d.ts +9 -0
- package/dist/components/inputs/ChoiceCards/ChoiceCards.js.map +1 -1
- package/dist/components/inputs/ChoiceCardsField/ChoiceCardsField.d.ts +7 -0
- package/dist/components/inputs/ChoiceCardsField/ChoiceCardsField.js.map +1 -1
- package/dist/components/inputs/ColorField/ColorField.d.ts +6 -0
- package/dist/components/inputs/ColorField/ColorField.js.map +1 -1
- package/dist/components/inputs/ColorInput/ColorInput.d.ts +8 -0
- package/dist/components/inputs/ColorInput/ColorInput.js.map +1 -1
- package/dist/components/inputs/Combobox/Combobox.d.ts +9 -0
- package/dist/components/inputs/Combobox/Combobox.js.map +1 -1
- package/dist/components/inputs/ComboboxField/ComboboxField.d.ts +7 -0
- package/dist/components/inputs/ComboboxField/ComboboxField.js.map +1 -1
- package/dist/components/inputs/DateRangeField/DateRangeField.d.ts +7 -0
- package/dist/components/inputs/DateRangeField/DateRangeField.js.map +1 -1
- package/dist/components/inputs/Field/Field.d.ts +26 -0
- package/dist/components/inputs/Field/Field.js.map +1 -1
- package/dist/components/inputs/Fieldset/Fieldset.d.ts +8 -0
- package/dist/components/inputs/Fieldset/Fieldset.js.map +1 -1
- package/dist/components/inputs/FileDrop/FileDrop.d.ts +10 -0
- package/dist/components/inputs/FileDrop/FileDrop.js.map +1 -1
- package/dist/components/inputs/FileField/FileField.d.ts +7 -0
- package/dist/components/inputs/FileField/FileField.js.map +1 -1
- package/dist/components/inputs/Input/Input.d.ts +9 -0
- package/dist/components/inputs/Input/Input.js.map +1 -1
- package/dist/components/inputs/NumberField/NumberField.d.ts +7 -0
- package/dist/components/inputs/NumberField/NumberField.js.map +1 -1
- package/dist/components/inputs/NumberInput/NumberInput.d.ts +8 -0
- package/dist/components/inputs/NumberInput/NumberInput.js.map +1 -1
- package/dist/components/inputs/OneTimeCodeField/OneTimeCodeField.d.ts +7 -0
- package/dist/components/inputs/OneTimeCodeField/OneTimeCodeField.js.map +1 -1
- package/dist/components/inputs/OneTimeCodeInput/OneTimeCodeInput.d.ts +8 -0
- package/dist/components/inputs/OneTimeCodeInput/OneTimeCodeInput.js.map +1 -1
- package/dist/components/inputs/PasswordField/PasswordField.d.ts +7 -0
- package/dist/components/inputs/PasswordField/PasswordField.js.map +1 -1
- package/dist/components/inputs/PasswordInput/PasswordInput.d.ts +10 -0
- package/dist/components/inputs/PasswordInput/PasswordInput.js.map +1 -1
- package/dist/components/inputs/RadioGroup/RadioGroup.d.ts +9 -0
- package/dist/components/inputs/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/components/inputs/RadioGroupField/RadioGroupField.d.ts +8 -0
- package/dist/components/inputs/RadioGroupField/RadioGroupField.js.map +1 -1
- package/dist/components/inputs/RangeSlider/RangeSlider.d.ts +8 -0
- package/dist/components/inputs/RangeSlider/RangeSlider.js.map +1 -1
- package/dist/components/inputs/RangeSliderField/RangeSliderField.d.ts +7 -0
- package/dist/components/inputs/RangeSliderField/RangeSliderField.js.map +1 -1
- package/dist/components/inputs/Rating/Rating.d.ts +8 -0
- package/dist/components/inputs/Rating/Rating.js.map +1 -1
- package/dist/components/inputs/RatingField/RatingField.d.ts +7 -0
- package/dist/components/inputs/RatingField/RatingField.js.map +1 -1
- package/dist/components/inputs/SegmentedField/SegmentedField.d.ts +7 -0
- package/dist/components/inputs/SegmentedField/SegmentedField.js.map +1 -1
- package/dist/components/inputs/Select/Select.d.ts +9 -0
- package/dist/components/inputs/Select/Select.js.map +1 -1
- package/dist/components/inputs/SelectField/SelectField.d.ts +7 -0
- package/dist/components/inputs/SelectField/SelectField.js.map +1 -1
- package/dist/components/inputs/Slider/Slider.d.ts +8 -0
- package/dist/components/inputs/Slider/Slider.js.map +1 -1
- package/dist/components/inputs/SliderField/SliderField.d.ts +7 -0
- package/dist/components/inputs/SliderField/SliderField.js.map +1 -1
- package/dist/components/inputs/Switch/Switch.d.ts +8 -0
- package/dist/components/inputs/Switch/Switch.js.map +1 -1
- package/dist/components/inputs/SwitchField/SwitchField.d.ts +8 -0
- package/dist/components/inputs/SwitchField/SwitchField.js.map +1 -1
- package/dist/components/inputs/TagsField/TagsField.d.ts +6 -0
- package/dist/components/inputs/TagsField/TagsField.js.map +1 -1
- package/dist/components/inputs/TagsInput/TagsInput.d.ts +8 -0
- package/dist/components/inputs/TagsInput/TagsInput.js.map +1 -1
- package/dist/components/inputs/TextField/TextField.d.ts +8 -0
- package/dist/components/inputs/TextField/TextField.js.map +1 -1
- package/dist/components/inputs/Textarea/Textarea.d.ts +8 -0
- package/dist/components/inputs/Textarea/Textarea.js.map +1 -1
- package/dist/components/inputs/TextareaField/TextareaField.d.ts +7 -0
- package/dist/components/inputs/TextareaField/TextareaField.js.map +1 -1
- package/dist/components/layout/ActionBar/ActionBar.d.ts +7 -0
- package/dist/components/layout/ActionBar/ActionBar.js.map +1 -1
- package/dist/components/layout/AppShell/AppShell.js.map +1 -1
- package/dist/components/layout/AspectRatio/AspectRatio.d.ts +8 -0
- package/dist/components/layout/AspectRatio/AspectRatio.js.map +1 -1
- package/dist/components/layout/Box/Box.d.ts +8 -0
- package/dist/components/layout/Box/Box.js.map +1 -1
- package/dist/components/layout/Container/Container.d.ts +17 -0
- package/dist/components/layout/Container/Container.js.map +1 -1
- package/dist/components/layout/Divider/Divider.d.ts +7 -0
- package/dist/components/layout/Divider/Divider.js.map +1 -1
- package/dist/components/layout/Grid/Grid.d.ts +9 -0
- package/dist/components/layout/Grid/Grid.js.map +1 -1
- package/dist/components/layout/Inline/Inline.d.ts +8 -0
- package/dist/components/layout/Inline/Inline.js.map +1 -1
- package/dist/components/layout/Section/Section.d.ts +15 -0
- package/dist/components/layout/Section/Section.js.map +1 -1
- package/dist/components/layout/Split/Split.d.ts +9 -0
- package/dist/components/layout/Split/Split.js.map +1 -1
- package/dist/components/layout/Stack/Stack.d.ts +12 -0
- package/dist/components/layout/Stack/Stack.js.map +1 -1
- package/dist/components/layout/VisuallyHidden/VisuallyHidden.d.ts +7 -0
- package/dist/components/layout/VisuallyHidden/VisuallyHidden.js.map +1 -1
- package/dist/components/navigation/Accordion/Accordion.d.ts +7 -0
- package/dist/components/navigation/Accordion/Accordion.js.map +1 -1
- package/dist/components/navigation/BottomNav/BottomNav.d.ts +9 -0
- package/dist/components/navigation/BottomNav/BottomNav.js.map +1 -1
- package/dist/components/navigation/NavLinks/NavLinks.d.ts +9 -0
- package/dist/components/navigation/NavLinks/NavLinks.js.map +1 -1
- package/dist/components/navigation/Stepper/Stepper.d.ts +7 -0
- package/dist/components/navigation/Stepper/Stepper.js.map +1 -1
- package/dist/components/navigation/Tabs/Tabs.d.ts +8 -0
- package/dist/components/navigation/Tabs/Tabs.js.map +1 -1
- package/dist/components/overlays/Dialog/Dialog.d.ts +13 -0
- package/dist/components/overlays/Dialog/Dialog.js.map +1 -1
- package/dist/components/overlays/DropdownMenu/DropdownMenu.d.ts +8 -0
- package/dist/components/overlays/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/overlays/Popover/Popover.d.ts +7 -0
- package/dist/components/overlays/Popover/Popover.js.map +1 -1
- package/dist/components/overlays/Sheet/Sheet.d.ts +8 -0
- package/dist/components/overlays/Sheet/Sheet.js.map +1 -1
- package/dist/components/overlays/Tooltip/Tooltip.d.ts +8 -0
- package/dist/components/overlays/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/typography/Amount/Amount.d.ts +8 -0
- package/dist/components/typography/Amount/Amount.js.map +1 -1
- package/dist/components/typography/Code/Code.d.ts +8 -0
- package/dist/components/typography/Code/Code.js.map +1 -1
- package/dist/components/typography/Heading/Heading.d.ts +8 -0
- package/dist/components/typography/Heading/Heading.js.map +1 -1
- package/dist/components/typography/Kbd/Kbd.d.ts +6 -0
- package/dist/components/typography/Kbd/Kbd.js.map +1 -1
- package/dist/components/typography/Link/Link.d.ts +9 -0
- package/dist/components/typography/Link/Link.js.map +1 -1
- package/dist/components/typography/Numeral/Numeral.d.ts +8 -0
- package/dist/components/typography/Numeral/Numeral.js.map +1 -1
- package/dist/components/typography/Prose/Prose.d.ts +9 -0
- package/dist/components/typography/Prose/Prose.js.map +1 -1
- package/dist/components/typography/Quote/Quote.d.ts +7 -0
- package/dist/components/typography/Quote/Quote.js.map +1 -1
- package/dist/components/typography/SectionHeader/SectionHeader.d.ts +8 -0
- package/dist/components/typography/SectionHeader/SectionHeader.js.map +1 -1
- package/dist/components/typography/Text/Text.d.ts +8 -0
- package/dist/components/typography/Text/Text.js.map +1 -1
- package/dist/icons/Icon.d.ts +1 -1
- package/dist/icons/Icon.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme/ThemeProvider.d.ts +4 -4
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/themes.d.ts +1 -1
- package/dist/theme/themes.js +15 -3
- package/dist/theme/themes.js.map +1 -1
- package/dist/themes/fiesta.css +1 -1
- package/dist/themes/flightdeck.css +1 -0
- package/dist/themes/ledger.css +1 -1
- package/dist/themes/monograph.css +1 -1
- package/dist/themes/riso.css +1 -0
- package/package.json +4 -2
- package/skills/custom-theme/SKILL.md +45 -43
- package/skills/custom-theme/references/colour.md +1 -1
- package/skills/custom-theme/references/motion.md +2 -2
- package/skills/custom-theme/references/tokens.md +42 -42
- package/skills/custom-theme/references/type.md +13 -10
- package/skills/design-rules/SKILL.md +31 -3
- package/skills/layout-composition/SKILL.md +27 -88
- package/skills/layout-composition/references/action-bar.md +3 -3
- package/skills/layout-composition/references/app-shell.md +10 -3
- package/skills/layout-composition/references/aspect-ratio.md +2 -0
- package/skills/layout-composition/references/box.md +6 -6
- package/skills/layout-composition/references/container.md +14 -12
- package/skills/layout-composition/references/divider.md +4 -4
- package/skills/layout-composition/references/section.md +69 -0
- package/skills/layout-composition/references/visually-hidden.md +3 -3
- package/skills/setup-and-theming/SKILL.md +10 -6
- package/skills/setup-and-theming/references/fiesta.md +3 -1
- package/skills/setup-and-theming/references/flightdeck.md +39 -0
- package/skills/setup-and-theming/references/ledger.md +7 -7
- package/skills/setup-and-theming/references/monograph.md +4 -0
- package/skills/setup-and-theming/references/paper.md +7 -7
- package/skills/setup-and-theming/references/riso.md +32 -0
|
@@ -20,6 +20,14 @@ export interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 't
|
|
|
20
20
|
titleAs?: EmptyStateTitleElement;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
+
* What to show when there's nothing to show yet. A plain title, a line of direction, one action.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* `EmptyState` replaces content that doesn't exist yet: no saved routes, no results for a filter.
|
|
27
|
+
* It's typographic: a title that says what's empty, a sentence that says what to do, and one
|
|
28
|
+
* action that does it.
|
|
29
|
+
*
|
|
30
|
+
* @privateRemarks
|
|
23
31
|
* The view when there's nothing to show yet. Typographic: a plain title, a line of
|
|
24
32
|
* direction, one action.
|
|
25
33
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.js","names":["styles","EmptyState_module_default","EmptyState","forwardRef","Title","rest","cx","className","align","framed","media","title","description","action"],"sources":["../../../../src/components/display/EmptyState/EmptyState.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport styles from './EmptyState.module.css'\n\nexport type EmptyStateTitleElement = 'h1' | 'h2' | 'h3' | 'h4' | 'p'\n\nexport interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** What's empty, said plainly: \"No invoices yet\". */\n title: ReactNode\n /** What happens next, or why it's empty. One or two sentences. */\n description?: ReactNode\n /** The one thing to do about it — usually a single Button. */\n action?: ReactNode\n /** An illustration, photo or figure. Not a stock icon in a circle. */\n media?: ReactNode\n /** Default `start`. `center` is fine when the empty state is the whole view. */\n align?: 'start' | 'center'\n /**\n * Mark out the space with printer's crop marks at the four corners — reads as \"this is\n * where things will go\" without boxing it in.\n */\n framed?: boolean\n /** Heading element for the title. Default `h3`. */\n titleAs?: EmptyStateTitleElement\n}\n\n/**\n * The view when there's nothing to show yet. Typographic: a plain title, a line of\n * direction, one action.\n *\n * <EmptyState title=\"No invoices yet\" description=\"…\" action={<Button>New invoice</Button>} />\n */\nexport const EmptyState = forwardRef<HTMLDivElement, EmptyStateProps>(function EmptyState(\n {\n title,\n description,\n action,\n media,\n align = 'start',\n framed = false,\n titleAs: Title = 'h3',\n className,\n ...rest\n },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cx(styles.empty, className)}\n data-align={align}\n data-framed={framed || undefined}\n {...rest}\n >\n {framed ? <span className={styles.marks} aria-hidden=\"true\" /> : null}\n {media ? <div className={styles.media}>{media}</div> : null}\n <div className={styles.copy}>\n <Title className={styles.title}>{title}</Title>\n {description ? <p className={styles.description}>{description}</p> : null}\n </div>\n {action ? <div className={styles.action}>{action}</div> : null}\n </div>\n )\n})\n"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"EmptyState.js","names":["styles","EmptyState_module_default","EmptyState","forwardRef","Title","rest","cx","className","align","framed","media","title","description","action"],"sources":["../../../../src/components/display/EmptyState/EmptyState.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport styles from './EmptyState.module.css'\n\nexport type EmptyStateTitleElement = 'h1' | 'h2' | 'h3' | 'h4' | 'p'\n\nexport interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** What's empty, said plainly: \"No invoices yet\". */\n title: ReactNode\n /** What happens next, or why it's empty. One or two sentences. */\n description?: ReactNode\n /** The one thing to do about it — usually a single Button. */\n action?: ReactNode\n /** An illustration, photo or figure. Not a stock icon in a circle. */\n media?: ReactNode\n /** Default `start`. `center` is fine when the empty state is the whole view. */\n align?: 'start' | 'center'\n /**\n * Mark out the space with printer's crop marks at the four corners — reads as \"this is\n * where things will go\" without boxing it in.\n */\n framed?: boolean\n /** Heading element for the title. Default `h3`. */\n titleAs?: EmptyStateTitleElement\n}\n\n/**\n * What to show when there's nothing to show yet. A plain title, a line of direction, one action.\n *\n * @remarks\n * `EmptyState` replaces content that doesn't exist yet: no saved routes, no results for a filter.\n * It's typographic: a title that says what's empty, a sentence that says what to do, and one\n * action that does it.\n *\n * @privateRemarks\n * The view when there's nothing to show yet. Typographic: a plain title, a line of\n * direction, one action.\n *\n * <EmptyState title=\"No invoices yet\" description=\"…\" action={<Button>New invoice</Button>} />\n */\nexport const EmptyState = forwardRef<HTMLDivElement, EmptyStateProps>(function EmptyState(\n {\n title,\n description,\n action,\n media,\n align = 'start',\n framed = false,\n titleAs: Title = 'h3',\n className,\n ...rest\n },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cx(styles.empty, className)}\n data-align={align}\n data-framed={framed || undefined}\n {...rest}\n >\n {framed ? <span className={styles.marks} aria-hidden=\"true\" /> : null}\n {media ? <div className={styles.media}>{media}</div> : null}\n <div className={styles.copy}>\n <Title className={styles.title}>{title}</Title>\n {description ? <p className={styles.description}>{description}</p> : null}\n </div>\n {action ? <div className={styles.action}>{action}</div> : null}\n </div>\n )\n})\n"],"mappings":";;;;;AAwCA,IAAaE,IAAaC,EAA4C,SACpE,EACE,UACA,gBACA,WACA,UACA,WAAQ,SACR,YAAS,IACT,SAASC,IAAQ,MACjB,cACA,GAAGC,KAEL,GACA;CACA,OACE,kBAAC,OAAD;EACO;EACL,WAAWC,EAAGN,EAAO,OAAOO,CAAS;EACrC,cAAYC;EACZ,eAAaC,KAAU,KAAA;EACvB,GAAIJ;EALN,UAAA;GAOGI,IAAS,kBAAC,QAAD;IAAM,WAAWT,EAAO;IAAO,eAAY;GAAQ,CAAA,IAAI;GAChEU,IAAQ,kBAAC,OAAD;IAAK,WAAWV,EAAO;IAAQ,UAAAU;GAAW,CAAA,IAAI;GACvD,kBAAC,OAAD;IAAK,WAAWV,EAAO;IAAvB,UAAA,CACE,kBAACI,GAAD;KAAO,WAAWJ,EAAO;KAAQ,UAAAW;IAAa,CAAA,GAC7CC,IAAc,kBAAC,KAAD;KAAG,WAAWZ,EAAO;KAAc,UAAAY;IAAe,CAAA,IAAI,IAClE;;GACJC,IAAS,kBAAC,OAAD;IAAK,WAAWb,EAAO;IAAS,UAAAa;GAAY,CAAA,IAAI;EACvD;;AAET,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"List.js","names":["styles","List_module_default","ListRoot","forwardRef","Comp","rest","cx","className","divided","density","ListItem","Row","asChild","Slot","interactive","selected","highlighted","ListLeading","ListContent","ListDescription","ListTrailing","List"],"sources":["../../../../src/components/display/List/List.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes, type LiHTMLAttributes, type ReactNode } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport styles from './List.module.css'\n\nexport type ListDensity = 'compact' | 'regular'\nexport type ListElement = 'ul' | 'ol'\n\nexport interface ListProps extends HTMLAttributes<HTMLUListElement> {\n /** Hairline rules between rows. Default `true`. */\n divided?: boolean\n /** Row height. Default `regular`. */\n density?: ListDensity\n /** `ol` when order is the content (a leaderboard). Default `ul`. */\n as?: ListElement\n}\n\n/**\n * The workhorse row list: leaderboards, team members, invoices, recent work. Rows are\n * `Leading · Content · Trailing`, divided by hairlines, never boxed in cards.\n *\n * <List><List.Item><List.Leading>1</List.Leading><List.Content>…</List.Content></List.Item></List>\n */\nconst ListRoot = forwardRef<HTMLUListElement, ListProps>(function List(\n { divided = true, density = 'regular', as: Comp = 'ul', className, ...rest },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — ul/ol share HTMLOListElement-compatible attributes; ref type differs only nominally\n ref={ref}\n role=\"list\"\n className={cx(styles.list, className)}\n data-divided={divided || undefined}\n data-density={density}\n {...rest}\n />\n )\n})\n\nexport interface ListItemProps extends LiHTMLAttributes<HTMLLIElement> {\n /** Hover affordance. Implied by `asChild`. */\n interactive?: boolean\n /**\n * Make the whole row one link or button: the single child becomes the row, so there\n * is exactly one interactive element and no nested anchors.\n * `<List.Item asChild><a href=\"/projects/atlas\">…slots…</a></List.Item>`\n */\n asChild?: boolean\n /** The current item (accent-soft fill) — e.g. the open page in a list of links. */\n selected?: boolean\n /** Draw attention to a row with `--color-highlight` — typically \"you\". */\n highlighted?: boolean\n children?: ReactNode\n}\n\nconst ListItem = forwardRef<HTMLLIElement, ListItemProps>(function ListItem(\n {\n interactive = false,\n asChild = false,\n selected = false,\n highlighted = false,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const Row = asChild ? Slot.Root : 'div'\n return (\n <li\n ref={ref}\n className={cx(styles.item, className)}\n data-interactive={interactive || asChild || undefined}\n data-selected={selected || undefined}\n data-highlighted={highlighted || undefined}\n {...rest}\n >\n <Row className={styles.row}>{children}</Row>\n </li>\n )\n})\n\n/** Rank, avatar or icon at the start of the row. Numbers are tabular and right-aligned. */\nconst ListLeading = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListLeading({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.leading, className)} {...rest} />\n },\n)\n\n/** Title (the direct text) and an optional `List.Description` below it. */\nconst ListContent = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListContent({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.content, className)} {...rest} />\n },\n)\n\nconst ListDescription = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListDescription({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.description, className)} {...rest} />\n },\n)\n\n/** Value, meta or actions at the end of the row. Numbers are tabular. */\nconst ListTrailing = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListTrailing({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.trailing, className)} {...rest} />\n },\n)\n\nexport const List = Object.assign(ListRoot, {\n Item: ListItem,\n Leading: ListLeading,\n Content: ListContent,\n Description: ListDescription,\n Trailing: ListTrailing,\n})\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"List.js","names":["styles","List_module_default","ListRoot","forwardRef","Comp","rest","cx","className","divided","density","ListItem","Row","asChild","Slot","interactive","selected","highlighted","ListLeading","ListContent","ListDescription","ListTrailing","List"],"sources":["../../../../src/components/display/List/List.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes, type LiHTMLAttributes, type ReactNode } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport styles from './List.module.css'\n\nexport type ListDensity = 'compact' | 'regular'\nexport type ListElement = 'ul' | 'ol'\n\nexport interface ListProps extends HTMLAttributes<HTMLUListElement> {\n /** Hairline rules between rows. Default `true`. */\n divided?: boolean\n /** Row height. Default `regular`. */\n density?: ListDensity\n /** `ol` when order is the content (a leaderboard). Default `ul`. */\n as?: ListElement\n}\n\n/**\n * The workhorse row list. Leading, content and trailing slots, divided by hairlines, never boxed\n * in cards.\n *\n * @remarks\n * `List` is for rows of similar things: trips, people, invoices, recent activity. Each row has up\n * to three slots: `List.Leading` (an avatar, an icon, a rank), `List.Content` (a title and\n * `List.Description`), and `List.Trailing` (an amount, a time, an action). Rows are divided by\n * hairlines.\n *\n * @privateRemarks\n * The workhorse row list: leaderboards, team members, invoices, recent work. Rows are\n * `Leading · Content · Trailing`, divided by hairlines, never boxed in cards.\n *\n * <List><List.Item><List.Leading>1</List.Leading><List.Content>…</List.Content></List.Item></List>\n */\nconst ListRoot = forwardRef<HTMLUListElement, ListProps>(function List(\n { divided = true, density = 'regular', as: Comp = 'ul', className, ...rest },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — ul/ol share HTMLOListElement-compatible attributes; ref type differs only nominally\n ref={ref}\n role=\"list\"\n className={cx(styles.list, className)}\n data-divided={divided || undefined}\n data-density={density}\n {...rest}\n />\n )\n})\n\nexport interface ListItemProps extends LiHTMLAttributes<HTMLLIElement> {\n /** Hover affordance. Implied by `asChild`. */\n interactive?: boolean\n /**\n * Make the whole row one link or button: the single child becomes the row, so there\n * is exactly one interactive element and no nested anchors.\n * `<List.Item asChild><a href=\"/projects/atlas\">…slots…</a></List.Item>`\n */\n asChild?: boolean\n /** The current item (accent-soft fill) — e.g. the open page in a list of links. */\n selected?: boolean\n /** Draw attention to a row with `--color-highlight` — typically \"you\". */\n highlighted?: boolean\n children?: ReactNode\n}\n\nconst ListItem = forwardRef<HTMLLIElement, ListItemProps>(function ListItem(\n {\n interactive = false,\n asChild = false,\n selected = false,\n highlighted = false,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const Row = asChild ? Slot.Root : 'div'\n return (\n <li\n ref={ref}\n className={cx(styles.item, className)}\n data-interactive={interactive || asChild || undefined}\n data-selected={selected || undefined}\n data-highlighted={highlighted || undefined}\n {...rest}\n >\n <Row className={styles.row}>{children}</Row>\n </li>\n )\n})\n\n/** Rank, avatar or icon at the start of the row. Numbers are tabular and right-aligned. */\nconst ListLeading = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListLeading({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.leading, className)} {...rest} />\n },\n)\n\n/** Title (the direct text) and an optional `List.Description` below it. */\nconst ListContent = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListContent({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.content, className)} {...rest} />\n },\n)\n\nconst ListDescription = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListDescription({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.description, className)} {...rest} />\n },\n)\n\n/** Value, meta or actions at the end of the row. Numbers are tabular. */\nconst ListTrailing = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n function ListTrailing({ className, ...rest }, ref) {\n return <span ref={ref} className={cx(styles.trailing, className)} {...rest} />\n },\n)\n\nexport const List = Object.assign(ListRoot, {\n Item: ListItem,\n Leading: ListLeading,\n Content: ListContent,\n Description: ListDescription,\n Trailing: ListTrailing,\n})\n"],"mappings":";;;;;;AAiCA,IAAME,IAAWC,EAAwC,SACvD,EAAE,aAAU,IAAM,aAAU,WAAW,IAAIC,IAAO,MAAM,cAAW,GAAGC,KACtE,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,MAAK;EACL,WAAWE,EAAGN,EAAO,MAAMO,CAAS;EACpC,gBAAcC,KAAW,KAAA;EACzB,gBAAcC;EACd,GAAIJ;CACL,CAAA;AAEL,CAAC,GAkBKK,IAAWP,EAAyC,SACxD,EACE,iBAAc,IACd,aAAU,IACV,cAAW,IACX,iBAAc,IACd,cACA,aACA,GAAGE,KAEL,GACA;CACA,IAAMM,IAAMC,IAAUC,EAAK,OAAO;CAClC,OACE,kBAAC,MAAD;EACO;EACL,WAAWP,EAAGN,EAAO,MAAMO,CAAS;EACpC,oBAAkBO,KAAeF,KAAW,KAAA;EAC5C,iBAAeG,KAAY,KAAA;EAC3B,oBAAkBC,KAAe,KAAA;EACjC,GAAIX;EAEJ,UAAA,kBAACM,GAAD;GAAK,WAAWX,EAAO;GAAM;EAAc,CAAA;CACzC,CAAA;AAER,CAAC,GAGKiB,IAAcd,EAClB,SAAqB,EAAE,cAAW,GAAGE,KAAQ,GAAK;CAChD,OAAO,kBAAC,QAAD;EAAW;EAAK,WAAWC,EAAGN,EAAO,SAASO,CAAS;EAAG,GAAIF;CAAO,CAAA;AAC9E,CACF,GAGMa,IAAcf,EAClB,SAAqB,EAAE,cAAW,GAAGE,KAAQ,GAAK;CAChD,OAAO,kBAAC,QAAD;EAAW;EAAK,WAAWC,EAAGN,EAAO,SAASO,CAAS;EAAG,GAAIF;CAAO,CAAA;AAC9E,CACF,GAEMc,IAAkBhB,EACtB,SAAyB,EAAE,cAAW,GAAGE,KAAQ,GAAK;CACpD,OAAO,kBAAC,QAAD;EAAW;EAAK,WAAWC,EAAGN,EAAO,aAAaO,CAAS;EAAG,GAAIF;CAAO,CAAA;AAClF,CACF,GAGMe,IAAejB,EACnB,SAAsB,EAAE,cAAW,GAAGE,KAAQ,GAAK;CACjD,OAAO,kBAAC,QAAD;EAAW;EAAK,WAAWC,EAAGN,EAAO,UAAUO,CAAS;EAAG,GAAIF;CAAO,CAAA;AAC/E,CACF,GAEagB,IAAO,OAAO,OAAOnB,GAAU;CAC1C,MAAMQ;CACN,SAASO;CACT,SAASC;CACT,aAAaC;CACb,UAAUC;AACZ,CAAC"}
|
|
@@ -35,6 +35,14 @@ export interface MarqueeProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chil
|
|
|
35
35
|
surface?: MarqueeSurface;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
+
* A horizontal ticker for live updates, with a pause control, that stops for reduced motion.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* `Marquee` scrolls a row of items in a loop, like live departures along a station board. The
|
|
42
|
+
* content is rendered twice for a seamless loop, but the copy is `aria-hidden` and inert, so
|
|
43
|
+
* screen readers and the tab order meet it once.
|
|
44
|
+
*
|
|
45
|
+
* @privateRemarks
|
|
38
46
|
* A horizontal ticker: live status, a features strip, a top-accounts summary.
|
|
39
47
|
*
|
|
40
48
|
* The content is rendered twice for a seamless loop; the copy is `aria-hidden` and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Marquee.js","names":["styles","Marquee_module_default","SPEED","FALLBACK_PER_ITEM","PauseIcon","createIcon","PlayIcon","Marquee","forwardRef","rest","list","items","Children","children","item","paused","setPaused","useState","duration","setDuration","groupRef","useRef","group","measure","width","speed","observer","inertRef","useCallback","seconds","renderSeparator","separator","renderGroup","isValidElement","i","label","cx","className","surface","direction","pauseOnHover","mergeStyles","space","gap","style","pauseControl","p","jsx"],"sources":["../../../../src/components/display/Marquee/Marquee.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { createIcon } from '#icons'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Marquee.module.css'\n\nexport type MarqueeSpeed = 'slow' | 'normal' | 'fast'\nexport type MarqueeDirection = 'left' | 'right'\nexport type MarqueeSurface = 'plain' | 'inverse' | 'accent'\n\nexport interface MarqueeProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n /**\n * The things that scroll. Pass an array (or several children) — each entry becomes\n * one item with a separator after it, so the loop is seamless.\n */\n items?: ReactNode[]\n /** Alternative to `items`: each direct child is one item. */\n children?: ReactNode\n /** Accessible name for the region, e.g. \"Deploy status\". Required: a ticker needs a name. */\n label: string\n /** Travel speed. Measured in px/s once mounted, so long and short tickers feel the same. */\n speed?: MarqueeSpeed\n /** Which way the content travels. Default `left`. */\n direction?: MarqueeDirection\n /** Pause while hovered or while anything inside has focus. Default `true`. */\n pauseOnHover?: boolean\n /**\n * Show a pause/play button at the end of the band (WCAG 2.2.2: moving content needs a\n * way to stop it that works for keyboard and touch). Default `true`.\n */\n pauseControl?: boolean\n /** Space between an item and its separator, as a step on the space scale. Default 5. */\n gap?: Space\n /**\n * Replaces the separator drawn between items. The default is a slanted hairline\n * themed through `--marquee-separator-*`. Pass `null` for none.\n */\n separator?: ReactNode\n /** Band colour. `plain` sits on the page between hairlines; `inverse` and `accent` fill. */\n surface?: MarqueeSurface\n}\n\n/* Pixels per second. Tuned so text stays readable at a glance. */\nconst SPEED: Record<MarqueeSpeed, number> = { slow: 28, normal: 48, fast: 84 }\n/* Seconds per item, used before the track has been measured (and on the server). */\nconst FALLBACK_PER_ITEM: Record<MarqueeSpeed, number> = { slow: 7, normal: 4.5, fast: 2.5 }\n\nconst PauseIcon = createIcon('PauseIcon', <path d=\"M7.5 5.5v9M12.5 5.5v9\" />)\nconst PlayIcon = createIcon('PlayIcon', <path d=\"M7 5.25v9.5L14.75 10z\" />)\n\n/**\n * A horizontal ticker: live status, a features strip, a top-accounts summary.\n *\n * The content is rendered twice for a seamless loop; the copy is `aria-hidden` and\n * `inert`, so screen readers and the tab order see it once. Under\n * `prefers-reduced-motion` the band stops and becomes a scrollable row.\n *\n * <Marquee label=\"Deploy status\" items={[<>Atlas 2.4 deploying</>, …]} surface=\"inverse\" />\n */\nexport const Marquee = forwardRef<HTMLDivElement, MarqueeProps>(function Marquee(\n {\n items,\n children,\n label,\n speed = 'normal',\n direction = 'left',\n pauseOnHover = true,\n pauseControl = true,\n gap = 5,\n separator,\n surface = 'plain',\n className,\n style,\n ...rest\n },\n ref,\n) {\n const list = (items ?? Children.toArray(children)).filter(\n (item) => item !== null && item !== undefined && item !== false,\n )\n const [paused, setPaused] = useState(false)\n const [duration, setDuration] = useState<number | null>(null)\n const groupRef = useRef<HTMLUListElement>(null)\n\n // Measure one copy of the content and derive the loop duration from a constant speed.\n useEffect(() => {\n const group = groupRef.current\n if (!group || typeof ResizeObserver === 'undefined') return\n const measure = () => {\n const width = group.getBoundingClientRect().width\n if (width > 0) setDuration(width / SPEED[speed])\n }\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(group)\n return () => {\n observer.disconnect()\n }\n }, [speed])\n\n // `inert` is only a known prop in React 19, so set the attribute directly (works in 18 too).\n const inertRef = useCallback((node: HTMLUListElement | null) => {\n node?.setAttribute('inert', '')\n }, [])\n\n const seconds = duration ?? Math.max(list.length, 1) * FALLBACK_PER_ITEM[speed]\n\n const renderSeparator = () =>\n separator === undefined ? (\n <span className={styles.rule} />\n ) : separator === null ? null : (\n <span className={styles.custom}>{separator}</span>\n )\n\n const renderGroup = () =>\n list.map((item, i) => (\n <li key={isValidElement(item) && item.key !== null ? item.key : i} className={styles.item}>\n <span className={styles.content}>{item}</span>\n <span className={styles.separator} aria-hidden=\"true\">\n {renderSeparator()}\n </span>\n </li>\n ))\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={label}\n className={cx(styles.marquee, className)}\n data-surface={surface}\n data-direction={direction}\n data-speed={speed}\n data-pause-on-hover={pauseOnHover || undefined}\n data-paused={paused || undefined}\n style={mergeStyles(\n {\n '--marquee-gap': space(gap),\n '--marquee-duration': `${seconds.toFixed(2)}s`,\n } as CSSProperties,\n style,\n )}\n {...rest}\n >\n <div className={styles.viewport}>\n <div className={styles.track}>\n <ul ref={groupRef} role=\"list\" className={styles.group}>\n {renderGroup()}\n </ul>\n <ul\n ref={inertRef}\n role=\"list\"\n className={cx(styles.group, styles.copy)}\n aria-hidden=\"true\"\n >\n {renderGroup()}\n </ul>\n </div>\n </div>\n {pauseControl ? (\n <button\n type=\"button\"\n className={styles.control}\n aria-pressed={paused}\n aria-label={`Pause ${label}`}\n onClick={() => {\n setPaused((p) => !p)\n }}\n >\n {paused ? <PlayIcon /> : <PauseIcon />}\n </button>\n ) : null}\n </div>\n )\n})\n"],"mappings":";;;;;;;;AAuDA,IAAME,IAAsC;CAAE,MAAM;CAAI,QAAQ;CAAI,MAAM;AAAG,GAEvEC,IAAkD;CAAE,MAAM;CAAG,QAAQ;CAAK,MAAM;AAAI,GAEpFC,IAAYC,EAAW,aAAa,kBAAC,QAAD,EAAM,GAAE,wBAAyB,CAAA,CAAC,GACtEC,IAAWD,EAAW,YAAY,kBAAC,QAAD,EAAM,GAAE,wBAAyB,CAAA,CAAC,
|
|
1
|
+
{"version":3,"file":"Marquee.js","names":["styles","Marquee_module_default","SPEED","FALLBACK_PER_ITEM","PauseIcon","createIcon","PlayIcon","Marquee","forwardRef","rest","list","items","Children","children","item","paused","setPaused","useState","duration","setDuration","groupRef","useRef","group","measure","width","speed","observer","inertRef","useCallback","seconds","renderSeparator","separator","renderGroup","isValidElement","i","label","cx","className","surface","direction","pauseOnHover","mergeStyles","space","gap","style","pauseControl","p","jsx"],"sources":["../../../../src/components/display/Marquee/Marquee.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { createIcon } from '#icons'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Marquee.module.css'\n\nexport type MarqueeSpeed = 'slow' | 'normal' | 'fast'\nexport type MarqueeDirection = 'left' | 'right'\nexport type MarqueeSurface = 'plain' | 'inverse' | 'accent'\n\nexport interface MarqueeProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n /**\n * The things that scroll. Pass an array (or several children) — each entry becomes\n * one item with a separator after it, so the loop is seamless.\n */\n items?: ReactNode[]\n /** Alternative to `items`: each direct child is one item. */\n children?: ReactNode\n /** Accessible name for the region, e.g. \"Deploy status\". Required: a ticker needs a name. */\n label: string\n /** Travel speed. Measured in px/s once mounted, so long and short tickers feel the same. */\n speed?: MarqueeSpeed\n /** Which way the content travels. Default `left`. */\n direction?: MarqueeDirection\n /** Pause while hovered or while anything inside has focus. Default `true`. */\n pauseOnHover?: boolean\n /**\n * Show a pause/play button at the end of the band (WCAG 2.2.2: moving content needs a\n * way to stop it that works for keyboard and touch). Default `true`.\n */\n pauseControl?: boolean\n /** Space between an item and its separator, as a step on the space scale. Default 5. */\n gap?: Space\n /**\n * Replaces the separator drawn between items. The default is a slanted hairline\n * themed through `--marquee-separator-*`. Pass `null` for none.\n */\n separator?: ReactNode\n /** Band colour. `plain` sits on the page between hairlines; `inverse` and `accent` fill. */\n surface?: MarqueeSurface\n}\n\n/* Pixels per second. Tuned so text stays readable at a glance. */\nconst SPEED: Record<MarqueeSpeed, number> = { slow: 28, normal: 48, fast: 84 }\n/* Seconds per item, used before the track has been measured (and on the server). */\nconst FALLBACK_PER_ITEM: Record<MarqueeSpeed, number> = { slow: 7, normal: 4.5, fast: 2.5 }\n\nconst PauseIcon = createIcon('PauseIcon', <path d=\"M7.5 5.5v9M12.5 5.5v9\" />)\nconst PlayIcon = createIcon('PlayIcon', <path d=\"M7 5.25v9.5L14.75 10z\" />)\n\n/**\n * A horizontal ticker for live updates, with a pause control, that stops for reduced motion.\n *\n * @remarks\n * `Marquee` scrolls a row of items in a loop, like live departures along a station board. The\n * content is rendered twice for a seamless loop, but the copy is `aria-hidden` and inert, so\n * screen readers and the tab order meet it once.\n *\n * @privateRemarks\n * A horizontal ticker: live status, a features strip, a top-accounts summary.\n *\n * The content is rendered twice for a seamless loop; the copy is `aria-hidden` and\n * `inert`, so screen readers and the tab order see it once. Under\n * `prefers-reduced-motion` the band stops and becomes a scrollable row.\n *\n * <Marquee label=\"Deploy status\" items={[<>Atlas 2.4 deploying</>, …]} surface=\"inverse\" />\n */\nexport const Marquee = forwardRef<HTMLDivElement, MarqueeProps>(function Marquee(\n {\n items,\n children,\n label,\n speed = 'normal',\n direction = 'left',\n pauseOnHover = true,\n pauseControl = true,\n gap = 5,\n separator,\n surface = 'plain',\n className,\n style,\n ...rest\n },\n ref,\n) {\n const list = (items ?? Children.toArray(children)).filter(\n (item) => item !== null && item !== undefined && item !== false,\n )\n const [paused, setPaused] = useState(false)\n const [duration, setDuration] = useState<number | null>(null)\n const groupRef = useRef<HTMLUListElement>(null)\n\n // Measure one copy of the content and derive the loop duration from a constant speed.\n useEffect(() => {\n const group = groupRef.current\n if (!group || typeof ResizeObserver === 'undefined') return\n const measure = () => {\n const width = group.getBoundingClientRect().width\n if (width > 0) setDuration(width / SPEED[speed])\n }\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(group)\n return () => {\n observer.disconnect()\n }\n }, [speed])\n\n // `inert` is only a known prop in React 19, so set the attribute directly (works in 18 too).\n const inertRef = useCallback((node: HTMLUListElement | null) => {\n node?.setAttribute('inert', '')\n }, [])\n\n const seconds = duration ?? Math.max(list.length, 1) * FALLBACK_PER_ITEM[speed]\n\n const renderSeparator = () =>\n separator === undefined ? (\n <span className={styles.rule} />\n ) : separator === null ? null : (\n <span className={styles.custom}>{separator}</span>\n )\n\n const renderGroup = () =>\n list.map((item, i) => (\n <li key={isValidElement(item) && item.key !== null ? item.key : i} className={styles.item}>\n <span className={styles.content}>{item}</span>\n <span className={styles.separator} aria-hidden=\"true\">\n {renderSeparator()}\n </span>\n </li>\n ))\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={label}\n className={cx(styles.marquee, className)}\n data-surface={surface}\n data-direction={direction}\n data-speed={speed}\n data-pause-on-hover={pauseOnHover || undefined}\n data-paused={paused || undefined}\n style={mergeStyles(\n {\n '--marquee-gap': space(gap),\n '--marquee-duration': `${seconds.toFixed(2)}s`,\n } as CSSProperties,\n style,\n )}\n {...rest}\n >\n <div className={styles.viewport}>\n <div className={styles.track}>\n <ul ref={groupRef} role=\"list\" className={styles.group}>\n {renderGroup()}\n </ul>\n <ul\n ref={inertRef}\n role=\"list\"\n className={cx(styles.group, styles.copy)}\n aria-hidden=\"true\"\n >\n {renderGroup()}\n </ul>\n </div>\n </div>\n {pauseControl ? (\n <button\n type=\"button\"\n className={styles.control}\n aria-pressed={paused}\n aria-label={`Pause ${label}`}\n onClick={() => {\n setPaused((p) => !p)\n }}\n >\n {paused ? <PlayIcon /> : <PauseIcon />}\n </button>\n ) : null}\n </div>\n )\n})\n"],"mappings":"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"}
|
|
@@ -22,6 +22,13 @@ export interface MediaProps extends Omit<HTMLAttributes<HTMLElement>, 'children'
|
|
|
22
22
|
imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt' | 'loading'>;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
+
* A framed image with a fixed ratio, lazy loading, a caption, and a calm failure state.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* `Media` frames an image: it holds a ratio while loading, fits the image, loads lazily, and when
|
|
29
|
+
* the image fails it keeps its shape and shows a quiet fallback instead of a broken-image icon.
|
|
30
|
+
*
|
|
31
|
+
* @privateRemarks
|
|
25
32
|
* A framed image: fixed ratio, fitted, lazy by default, with a caption and a graceful
|
|
26
33
|
* failure state. Screenshots, company logos, scanned documents.
|
|
27
34
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Media.js","names":["styles","Media_module_default","Media","forwardRef","rest","failed","setFailed","useState","imgRef","useRef","useEffect","img","src","caption","cx","className","ratio","fit","radius","alt","fallback","imgProps"],"sources":["../../../../src/components/display/Media/Media.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type ImgHTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport styles from './Media.module.css'\n\nexport type MediaRatio = '1/1' | '4/3' | '3/2' | '16/9' | '21/9' | '3/4' | '2/3' | 'auto'\nexport type MediaFit = 'cover' | 'contain'\nexport type MediaRadius = 'none' | 'media' | 'surface'\n\nexport interface MediaProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {\n src: string\n /** Required. Describe the image; pass `\"\"` only when it's purely decorative. */\n alt: string\n /** Frame ratio. The image is fitted inside. Default `auto` (the image's own ratio). */\n ratio?: MediaRatio\n /** Default `cover`. Use `contain` for logos, diagrams and screenshots that must not crop. */\n fit?: MediaFit\n /** Corner role. Default `media`. */\n radius?: MediaRadius\n /** Renders a `<figure>` with this as its `<figcaption>`. */\n caption?: ReactNode\n /** Shown inside the frame if the image fails to load. Default: an empty sunken frame. */\n fallback?: ReactNode\n /** Default `lazy`. Use `eager` for the image above the fold. */\n loading?: 'lazy' | 'eager'\n /** Extra attributes for the `<img>` (srcSet, sizes, width/height, fetchPriority). */\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt' | 'loading'>\n}\n\n/**\n * A framed image: fixed ratio, fitted, lazy by default, with a caption and a graceful\n * failure state. Screenshots, company logos, scanned documents.\n *\n * <Media src=\"/images/dashboard.png\" alt=\"Revenue dashboard, September\" ratio=\"16/9\" caption=\"Dashboard, 2026\" />\n */\nexport const Media = forwardRef<HTMLElement, MediaProps>(function Media(\n {\n src,\n alt,\n ratio = 'auto',\n fit = 'cover',\n radius = 'media',\n caption,\n fallback,\n loading = 'lazy',\n imgProps,\n className,\n ...rest\n },\n ref,\n) {\n const [failed, setFailed] = useState(false)\n const imgRef = useRef<HTMLImageElement>(null)\n\n // Reset when the source changes; also catch an error that fired before hydration.\n useEffect(() => {\n const img = imgRef.current\n setFailed(\n Boolean(\n img?.complete &&\n img.naturalWidth === 0 &&\n typeof img.currentSrc === 'string' &&\n img.currentSrc !== '',\n ),\n )\n }, [src])\n\n const Root = caption !== undefined ? 'figure' : 'div'\n return (\n <Root\n // @ts-expect-error — polymorphic ref across figure/div is safe here\n ref={ref}\n className={cx(styles.media, className)}\n data-ratio={ratio}\n data-fit={fit}\n data-radius={radius}\n data-failed={failed || undefined}\n {...rest}\n >\n <div className={styles.frame}>\n {failed ? (\n <div\n className={styles.fallback}\n role={alt ? 'img' : undefined}\n aria-label={alt || undefined}\n >\n {fallback}\n </div>\n ) : (\n <img\n ref={imgRef}\n className={styles.image}\n src={src}\n alt={alt}\n loading={loading}\n decoding=\"async\"\n onError={() => {\n setFailed(true)\n }}\n {...imgProps}\n />\n )}\n </div>\n {caption !== undefined ? <figcaption className={styles.caption}>{caption}</figcaption> : null}\n </Root>\n )\n})\n"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"Media.js","names":["styles","Media_module_default","Media","forwardRef","rest","failed","setFailed","useState","imgRef","useRef","useEffect","img","src","caption","cx","className","ratio","fit","radius","alt","fallback","imgProps"],"sources":["../../../../src/components/display/Media/Media.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type ImgHTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport styles from './Media.module.css'\n\nexport type MediaRatio = '1/1' | '4/3' | '3/2' | '16/9' | '21/9' | '3/4' | '2/3' | 'auto'\nexport type MediaFit = 'cover' | 'contain'\nexport type MediaRadius = 'none' | 'media' | 'surface'\n\nexport interface MediaProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {\n src: string\n /** Required. Describe the image; pass `\"\"` only when it's purely decorative. */\n alt: string\n /** Frame ratio. The image is fitted inside. Default `auto` (the image's own ratio). */\n ratio?: MediaRatio\n /** Default `cover`. Use `contain` for logos, diagrams and screenshots that must not crop. */\n fit?: MediaFit\n /** Corner role. Default `media`. */\n radius?: MediaRadius\n /** Renders a `<figure>` with this as its `<figcaption>`. */\n caption?: ReactNode\n /** Shown inside the frame if the image fails to load. Default: an empty sunken frame. */\n fallback?: ReactNode\n /** Default `lazy`. Use `eager` for the image above the fold. */\n loading?: 'lazy' | 'eager'\n /** Extra attributes for the `<img>` (srcSet, sizes, width/height, fetchPriority). */\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt' | 'loading'>\n}\n\n/**\n * A framed image with a fixed ratio, lazy loading, a caption, and a calm failure state.\n *\n * @remarks\n * `Media` frames an image: it holds a ratio while loading, fits the image, loads lazily, and when\n * the image fails it keeps its shape and shows a quiet fallback instead of a broken-image icon.\n *\n * @privateRemarks\n * A framed image: fixed ratio, fitted, lazy by default, with a caption and a graceful\n * failure state. Screenshots, company logos, scanned documents.\n *\n * <Media src=\"/images/dashboard.png\" alt=\"Revenue dashboard, September\" ratio=\"16/9\" caption=\"Dashboard, 2026\" />\n */\nexport const Media = forwardRef<HTMLElement, MediaProps>(function Media(\n {\n src,\n alt,\n ratio = 'auto',\n fit = 'cover',\n radius = 'media',\n caption,\n fallback,\n loading = 'lazy',\n imgProps,\n className,\n ...rest\n },\n ref,\n) {\n const [failed, setFailed] = useState(false)\n const imgRef = useRef<HTMLImageElement>(null)\n\n // Reset when the source changes; also catch an error that fired before hydration.\n useEffect(() => {\n const img = imgRef.current\n setFailed(\n Boolean(\n img?.complete &&\n img.naturalWidth === 0 &&\n typeof img.currentSrc === 'string' &&\n img.currentSrc !== '',\n ),\n )\n }, [src])\n\n const Root = caption !== undefined ? 'figure' : 'div'\n return (\n <Root\n // @ts-expect-error — polymorphic ref across figure/div is safe here\n ref={ref}\n className={cx(styles.media, className)}\n data-ratio={ratio}\n data-fit={fit}\n data-radius={radius}\n data-failed={failed || undefined}\n {...rest}\n >\n <div className={styles.frame}>\n {failed ? (\n <div\n className={styles.fallback}\n role={alt ? 'img' : undefined}\n aria-label={alt || undefined}\n >\n {fallback}\n </div>\n ) : (\n <img\n ref={imgRef}\n className={styles.image}\n src={src}\n alt={alt}\n loading={loading}\n decoding=\"async\"\n onError={() => {\n setFailed(true)\n }}\n {...imgProps}\n />\n )}\n </div>\n {caption !== undefined ? <figcaption className={styles.caption}>{caption}</figcaption> : null}\n </Root>\n )\n})\n"],"mappings":";;;;;AAiDA,IAAaE,IAAQC,EAAoC,SACvD,EACE,QACA,QACA,WAAQ,QACR,SAAM,SACN,YAAS,SACT,YACA,aACA,aAAU,QACV,aACA,cACA,GAAGC,KAEL,GACA;CACA,IAAM,CAACC,GAAQC,KAAaC,EAAS,EAAK,GACpCC,IAASC,EAAyB,IAAI;CAgB5C,OAbAC,QAAgB;EACd,IAAMC,IAAMH,EAAO;EACnB,EACE,GACEG,GAAK,YACLA,EAAI,iBAAiB,KACrB,OAAOA,EAAI,cAAe,YAC1BA,EAAI,eAAe,GAEvB;CACF,GAAG,CAACC,CAAG,CAAC,GAIN,kBAFWC,MAAY,KAAA,IAAuB,QAAX,UAEnC;EAEO;EACL,WAAWC,EAAGd,EAAO,OAAOe,CAAS;EACrC,cAAYC;EACZ,YAAUC;EACV,eAAaC;EACb,eAAab,KAAU,KAAA;EACvB,GAAID;EARN,UAAA,CAUE,kBAAC,OAAD;GAAK,WAAWJ,EAAO;GACpB,UAAAK,IACC,kBAAC,OAAD;IACE,WAAWL,EAAO;IAClB,MAAMmB,IAAM,QAAQ,KAAA;IACpB,cAAYA,KAAO,KAAA;IAElB,UAAAC;GACE,CAAA,IAEL,kBAAC,OAAD;IACE,KAAKZ;IACL,WAAWR,EAAO;IACb;IACA;IACI;IACT,UAAS;IACT,eAAe;KACb,EAAU,EAAI;IAChB;IACA,GAAIqB;GACL,CAAA;EAEA,CAAA,GACJR,MAAY,KAAA,IAA4E,OAAhE,kBAAC,cAAD;GAAY,WAAWb,EAAO;GAAU,UAAAa;EAAoB,CAAA,CACjF;;AAEV,CAAC"}
|
|
@@ -22,6 +22,15 @@ export interface MeterProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childr
|
|
|
22
22
|
size?: Size;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
+
* A measurement against a known range, whose tone follows thresholds you set, so the app never
|
|
26
|
+
* picks colours.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* `Meter` shows where a value sits in a range: seats taken, storage used, API requests against a
|
|
30
|
+
* monthly quota. Give it `low`, `high` and `optimum` thresholds and the tone follows the value, so
|
|
31
|
+
* 60% of capacity is calm, 90% is a warning, and over the limit is critical.
|
|
32
|
+
*
|
|
33
|
+
* @privateRemarks
|
|
25
34
|
* A measurement against a known range: usage vs quota, storage used, revenue vs target.
|
|
26
35
|
* Tone follows the thresholds, so 40% of the storage quota is calm, 85% is a warning
|
|
27
36
|
* and 110% is critical without the app deciding colours.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Meter.js","names":["styles","Meter_module_default","Meter","forwardRef","ariaLabel","rest","labelId","useId","range","max","min","clamped","value","fraction","resolvedTone","tone","low","high","optimum","meterTone","text","valueLabel","ticks","segments","i","label","cx","className","size","mergeStyles","style","showValue","at"],"sources":["../../../../src/components/display/Meter/Meter.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size, Tone } from '#utils/tokens'\nimport { meterTone, type MeterThresholds } from './meterTone'\nimport styles from './Meter.module.css'\n\nexport type MeterTone = Extract<\n Tone,\n 'positive' | 'caution' | 'critical' | 'accent' | 'neutral' | 'info'\n>\n\nexport interface MeterProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, MeterThresholds {\n /** The measured amount. May exceed `max` (a quota that has run over): the bar fills and `data-over` is set. */\n value: number\n /** Visible label; also the accessible name. */\n label?: ReactNode\n /** Replaces the default percentage, visibly and as `aria-valuetext`: \"578.4 GB of 680 GB\". */\n valueLabel?: string\n /** Show the value text in the label row. Default `true`. */\n showValue?: boolean\n /**\n * Force a tone. By default it's derived from `low`/`high`/`optimum` exactly like HTML\n * `<meter>` (optimum → positive, one region off → caution, two off → critical), or\n * `accent` when no thresholds are given.\n */\n tone?: MeterTone\n /** Divide the track into this many segments with ticks. */\n segments?: number\n size?: Size\n}\n\n/**\n * A measurement against a known range: usage vs quota, storage used, revenue vs target.\n * Tone follows the thresholds, so 40% of the storage quota is calm, 85% is a warning\n * and 110% is critical without the app deciding colours.\n *\n * <Meter label=\"Storage\" value={578.4} max={680} low={510} high={680} optimum={0}\n * valueLabel=\"578.4 GB of 680 GB\" />\n */\nexport const Meter = forwardRef<HTMLDivElement, MeterProps>(function Meter(\n {\n value,\n min = 0,\n max = 1,\n low,\n high,\n optimum,\n label,\n valueLabel,\n showValue = true,\n tone,\n segments,\n size = 'md',\n className,\n style,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n) {\n const labelId = useId()\n const range = max - min || 1\n const clamped = Math.min(Math.max(value, min), max)\n const fraction = (clamped - min) / range\n const hasThresholds = low !== undefined || high !== undefined || optimum !== undefined\n const resolvedTone: MeterTone =\n tone ?? (hasThresholds ? meterTone(value, { min, max, low, high, optimum }) : 'accent')\n const text = valueLabel ?? `${String(Math.round(((value - min) / range) * 100))}%`\n const ticks =\n segments && segments > 1\n ? Array.from({ length: segments - 1 }, (_, i) => (i + 1) / segments)\n : []\n\n return (\n <div\n ref={ref}\n role=\"meter\"\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={clamped}\n aria-valuetext={text}\n aria-label={ariaLabel}\n aria-labelledby={label && !ariaLabel ? labelId : undefined}\n className={cx(styles.meter, className)}\n data-tone={resolvedTone}\n data-size={size}\n data-over={value > max || undefined}\n data-segmented={ticks.length ? '' : undefined}\n style={mergeStyles({ '--meter-fraction': fraction } as CSSProperties, style)}\n {...rest}\n >\n {label || showValue ? (\n <div className={styles.header}>\n {label ? (\n <span id={labelId} className={styles.label}>\n {label}\n </span>\n ) : null}\n {showValue ? (\n <span className={styles.value} aria-hidden=\"true\">\n {text}\n </span>\n ) : null}\n </div>\n ) : null}\n <div className={styles.track} aria-hidden=\"true\">\n <div className={styles.fill} />\n {ticks.map((at) => (\n <span key={at} className={styles.tick} style={{ '--meter-tick': at } as CSSProperties} />\n ))}\n </div>\n </div>\n )\n})\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"Meter.js","names":["styles","Meter_module_default","Meter","forwardRef","ariaLabel","rest","labelId","useId","range","max","min","clamped","value","fraction","resolvedTone","tone","low","high","optimum","meterTone","text","valueLabel","ticks","segments","i","label","cx","className","size","mergeStyles","style","showValue","at"],"sources":["../../../../src/components/display/Meter/Meter.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size, Tone } from '#utils/tokens'\nimport { meterTone, type MeterThresholds } from './meterTone'\nimport styles from './Meter.module.css'\n\nexport type MeterTone = Extract<\n Tone,\n 'positive' | 'caution' | 'critical' | 'accent' | 'neutral' | 'info'\n>\n\nexport interface MeterProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, MeterThresholds {\n /** The measured amount. May exceed `max` (a quota that has run over): the bar fills and `data-over` is set. */\n value: number\n /** Visible label; also the accessible name. */\n label?: ReactNode\n /** Replaces the default percentage, visibly and as `aria-valuetext`: \"578.4 GB of 680 GB\". */\n valueLabel?: string\n /** Show the value text in the label row. Default `true`. */\n showValue?: boolean\n /**\n * Force a tone. By default it's derived from `low`/`high`/`optimum` exactly like HTML\n * `<meter>` (optimum → positive, one region off → caution, two off → critical), or\n * `accent` when no thresholds are given.\n */\n tone?: MeterTone\n /** Divide the track into this many segments with ticks. */\n segments?: number\n size?: Size\n}\n\n/**\n * A measurement against a known range, whose tone follows thresholds you set, so the app never\n * picks colours.\n *\n * @remarks\n * `Meter` shows where a value sits in a range: seats taken, storage used, API requests against a\n * monthly quota. Give it `low`, `high` and `optimum` thresholds and the tone follows the value, so\n * 60% of capacity is calm, 90% is a warning, and over the limit is critical.\n *\n * @privateRemarks\n * A measurement against a known range: usage vs quota, storage used, revenue vs target.\n * Tone follows the thresholds, so 40% of the storage quota is calm, 85% is a warning\n * and 110% is critical without the app deciding colours.\n *\n * <Meter label=\"Storage\" value={578.4} max={680} low={510} high={680} optimum={0}\n * valueLabel=\"578.4 GB of 680 GB\" />\n */\nexport const Meter = forwardRef<HTMLDivElement, MeterProps>(function Meter(\n {\n value,\n min = 0,\n max = 1,\n low,\n high,\n optimum,\n label,\n valueLabel,\n showValue = true,\n tone,\n segments,\n size = 'md',\n className,\n style,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n) {\n const labelId = useId()\n const range = max - min || 1\n const clamped = Math.min(Math.max(value, min), max)\n const fraction = (clamped - min) / range\n const hasThresholds = low !== undefined || high !== undefined || optimum !== undefined\n const resolvedTone: MeterTone =\n tone ?? (hasThresholds ? meterTone(value, { min, max, low, high, optimum }) : 'accent')\n const text = valueLabel ?? `${String(Math.round(((value - min) / range) * 100))}%`\n const ticks =\n segments && segments > 1\n ? Array.from({ length: segments - 1 }, (_, i) => (i + 1) / segments)\n : []\n\n return (\n <div\n ref={ref}\n role=\"meter\"\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={clamped}\n aria-valuetext={text}\n aria-label={ariaLabel}\n aria-labelledby={label && !ariaLabel ? labelId : undefined}\n className={cx(styles.meter, className)}\n data-tone={resolvedTone}\n data-size={size}\n data-over={value > max || undefined}\n data-segmented={ticks.length ? '' : undefined}\n style={mergeStyles({ '--meter-fraction': fraction } as CSSProperties, style)}\n {...rest}\n >\n {label || showValue ? (\n <div className={styles.header}>\n {label ? (\n <span id={labelId} className={styles.label}>\n {label}\n </span>\n ) : null}\n {showValue ? (\n <span className={styles.value} aria-hidden=\"true\">\n {text}\n </span>\n ) : null}\n </div>\n ) : null}\n <div className={styles.track} aria-hidden=\"true\">\n <div className={styles.fill} />\n {ticks.map((at) => (\n <span key={at} className={styles.tick} style={{ '--meter-tick': at } as CSSProperties} />\n ))}\n </div>\n </div>\n )\n})\n"],"mappings":";;;;;;;AAkDA,IAAaE,IAAQC,EAAuC,SAC1D,EACE,UACA,SAAM,GACN,SAAM,GACN,QACA,SACA,YACA,UACA,eACA,eAAY,IACZ,SACA,aACA,UAAO,MACP,cACA,UACA,cAAcC,GACd,GAAGC,KAEL,GACA;CACA,IAAMC,IAAUC,EAAM,GAChBC,IAAQC,IAAMC,KAAO,GACrBC,IAAU,KAAK,IAAI,KAAK,IAAIC,GAAOF,CAAG,GAAGD,CAAG,GAC5CI,KAAYF,IAAUD,KAAOF,GAE7BM,IACJC,MAFoBC,MAAQ,KAAA,KAAaC,MAAS,KAAA,KAAaC,MAAY,KAAA,IAElDC,EAAUP,GAAO;EAAE;EAAK;EAAK;EAAK;EAAM;CAAQ,CAAC,IAAI,WAC1EQ,IAAOC,KAAc,GAAG,OAAO,KAAK,OAAQT,IAAQF,KAAOF,IAAS,GAAG,CAAC,EAAE,IAC1Ec,IACJC,KAAYA,IAAW,IACnB,MAAM,KAAK,EAAE,QAAQA,IAAW,EAAE,IAAI,GAAG,OAAOC,IAAI,KAAKD,CAAQ,IACjE,CAAC;CAEP,OACE,kBAAC,OAAD;EACO;EACL,MAAK;EACL,iBAAeb;EACf,iBAAeD;EACf,iBAAeE;EACf,kBAAgBS;EAChB,cAAYhB;EACZ,mBAAiBqB,KAAS,CAACrB,IAAYE,IAAU,KAAA;EACjD,WAAWoB,EAAG1B,EAAO,OAAO2B,CAAS;EACrC,aAAWb;EACX,aAAWc;EACX,aAAWhB,IAAQH,KAAO,KAAA;EAC1B,kBAAgBa,EAAM,SAAS,KAAK,KAAA;EACpC,OAAOO,EAAY,EAAE,oBAAoBhB,EAAS,GAAoBiB,CAAK;EAC3E,GAAIzB;EAfN,UAAA,CAiBGoB,KAASM,IACR,kBAAC,OAAD;GAAK,WAAW/B,EAAO;GAAvB,UAAA,CACGyB,IACC,kBAAC,QAAD;IAAM,IAAInB;IAAS,WAAWN,EAAO;IAClC,UAAAyB;GACG,CAAA,IACJ,MACHM,IACC,kBAAC,QAAD;IAAM,WAAW/B,EAAO;IAAO,eAAY;IACxC,UAAAoB;GACG,CAAA,IACJ,IACD;EACH,CAAA,IAAA,MACJ,kBAAC,OAAD;GAAK,WAAWpB,EAAO;GAAO,eAAY;GAA1C,UAAA,CACE,kBAAC,OAAD,EAAK,WAAWA,EAAO,KAAO,CAAA,GAC7BsB,EAAM,KAAK,MACV,kBAAC,QAAD;IAAe,WAAWtB,EAAO;IAAM,OAAO,EAAE,gBAAgBgC,EAAG;GAAqB,GAA7EA,CAA6E,CACzF,CACE;EACF,CAAA,CAAA;;AAET,CAAC"}
|
|
@@ -18,6 +18,14 @@ export interface ProgressProps extends Omit<ComponentPropsWithoutRef<typeof Prog
|
|
|
18
18
|
formatValue?: (value: number, max: number) => string;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
+
* How far along a task is, like an upload or a sync, or that it's under way when nobody knows how
|
|
22
|
+
* far.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* `Progress` shows a task moving toward done. For a quantity against a limit (seats taken, storage
|
|
26
|
+
* used), use {@link Meter | Meter}: a meter is a level, not a journey.
|
|
27
|
+
*
|
|
28
|
+
* @privateRemarks
|
|
21
29
|
* How far along a task is: an upload, an import, a sync in progress.
|
|
22
30
|
* For a quantity against a limit (usage vs quota) use `Meter` instead.
|
|
23
31
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Progress.js","names":["Progress","styles","ProgressPrimitive","Progress_module_default","percent","value","max","Progress$1","forwardRef","ariaLabel","rest","labelId","useId","determinate","clamped","valueText","formatValue","cx","className","tone","size","label","showValue","Progress","v","m","mergeStyles"],"sources":["../../../../src/components/display/Progress/Progress.tsx"],"sourcesContent":["import {\n forwardRef,\n useId,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type ReactNode,\n} from 'react'\nimport { Progress as ProgressPrimitive } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size, Tone } from '#utils/tokens'\nimport styles from './Progress.module.css'\n\nexport interface ProgressProps extends Omit<\n ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>,\n 'value' | 'max' | 'children'\n> {\n /** Current value. `null` or omitted = indeterminate (working, amount unknown). */\n value?: number | null\n /** Default 100. */\n max?: number\n /** Default `accent`. */\n tone?: Tone\n /** Bar thickness. */\n size?: Size\n /** Visible label above the bar; also its accessible name. */\n label?: ReactNode\n /** Show the value at the end of the label row. */\n showValue?: boolean\n /** Formats the visible value and `aria-valuetext`. Default: whole percent. */\n formatValue?: (value: number, max: number) => string\n}\n\nconst percent = (value: number, max: number) => `${String(Math.round((value / max) * 100))}%`\n\n/**\n * How far along a task is: an upload, an import, a sync in progress.\n * For a quantity against a limit (usage vs quota) use `Meter` instead.\n *\n * `ref` and native/ARIA props land on the `progressbar` element; `className` and `style`\n * on the outer wrapper (label row + track).\n */\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n {\n value = null,\n max = 100,\n tone = 'accent',\n size = 'md',\n label,\n showValue = false,\n formatValue = percent,\n className,\n style,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n) {\n const labelId = useId()\n const determinate = value !== null && Number.isFinite(value)\n const clamped = determinate ? Math.min(Math.max(value, 0), max) : 0\n const valueText = determinate ? formatValue(clamped, max) : undefined\n\n return (\n <div className={cx(styles.progress, className)} style={style} data-tone={tone} data-size={size}>\n {label || (showValue && determinate) ? (\n <div className={styles.header}>\n {label ? (\n <span id={labelId} className={styles.label}>\n {label}\n </span>\n ) : null}\n {showValue && determinate ? (\n <span className={styles.value} aria-hidden=\"true\">\n {valueText}\n </span>\n ) : null}\n </div>\n ) : null}\n <ProgressPrimitive.Root\n ref={ref}\n className={styles.track}\n value={determinate ? clamped : null}\n max={max}\n getValueLabel={(v, m) => formatValue(v, m)}\n aria-label={ariaLabel}\n aria-labelledby={label && !ariaLabel ? labelId : undefined}\n {...rest}\n >\n <ProgressPrimitive.Indicator\n className={styles.indicator}\n style={mergeStyles(\n determinate ? ({ '--progress-fraction': clamped / max } as CSSProperties) : undefined,\n )}\n />\n </ProgressPrimitive.Root>\n </div>\n )\n})\n"],"mappings":";;;;;;;AAiCA,IAAMI,KAAW,GAAe,MAAgB,GAAG,OAAO,KAAK,MAAOC,IAAQC,IAAO,GAAG,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"Progress.js","names":["Progress","styles","ProgressPrimitive","Progress_module_default","percent","value","max","Progress$1","forwardRef","ariaLabel","rest","labelId","useId","determinate","clamped","valueText","formatValue","cx","className","tone","size","label","showValue","Progress","v","m","mergeStyles"],"sources":["../../../../src/components/display/Progress/Progress.tsx"],"sourcesContent":["import {\n forwardRef,\n useId,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type ReactNode,\n} from 'react'\nimport { Progress as ProgressPrimitive } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size, Tone } from '#utils/tokens'\nimport styles from './Progress.module.css'\n\nexport interface ProgressProps extends Omit<\n ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>,\n 'value' | 'max' | 'children'\n> {\n /** Current value. `null` or omitted = indeterminate (working, amount unknown). */\n value?: number | null\n /** Default 100. */\n max?: number\n /** Default `accent`. */\n tone?: Tone\n /** Bar thickness. */\n size?: Size\n /** Visible label above the bar; also its accessible name. */\n label?: ReactNode\n /** Show the value at the end of the label row. */\n showValue?: boolean\n /** Formats the visible value and `aria-valuetext`. Default: whole percent. */\n formatValue?: (value: number, max: number) => string\n}\n\nconst percent = (value: number, max: number) => `${String(Math.round((value / max) * 100))}%`\n\n/**\n * How far along a task is, like an upload or a sync, or that it's under way when nobody knows how\n * far.\n *\n * @remarks\n * `Progress` shows a task moving toward done. For a quantity against a limit (seats taken, storage\n * used), use {@link Meter | Meter}: a meter is a level, not a journey.\n *\n * @privateRemarks\n * How far along a task is: an upload, an import, a sync in progress.\n * For a quantity against a limit (usage vs quota) use `Meter` instead.\n *\n * `ref` and native/ARIA props land on the `progressbar` element; `className` and `style`\n * on the outer wrapper (label row + track).\n */\nexport const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(\n {\n value = null,\n max = 100,\n tone = 'accent',\n size = 'md',\n label,\n showValue = false,\n formatValue = percent,\n className,\n style,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n) {\n const labelId = useId()\n const determinate = value !== null && Number.isFinite(value)\n const clamped = determinate ? Math.min(Math.max(value, 0), max) : 0\n const valueText = determinate ? formatValue(clamped, max) : undefined\n\n return (\n <div className={cx(styles.progress, className)} style={style} data-tone={tone} data-size={size}>\n {label || (showValue && determinate) ? (\n <div className={styles.header}>\n {label ? (\n <span id={labelId} className={styles.label}>\n {label}\n </span>\n ) : null}\n {showValue && determinate ? (\n <span className={styles.value} aria-hidden=\"true\">\n {valueText}\n </span>\n ) : null}\n </div>\n ) : null}\n <ProgressPrimitive.Root\n ref={ref}\n className={styles.track}\n value={determinate ? clamped : null}\n max={max}\n getValueLabel={(v, m) => formatValue(v, m)}\n aria-label={ariaLabel}\n aria-labelledby={label && !ariaLabel ? labelId : undefined}\n {...rest}\n >\n <ProgressPrimitive.Indicator\n className={styles.indicator}\n style={mergeStyles(\n determinate ? ({ '--progress-fraction': clamped / max } as CSSProperties) : undefined,\n )}\n />\n </ProgressPrimitive.Root>\n </div>\n )\n})\n"],"mappings":";;;;;;;AAiCA,IAAMI,KAAW,GAAe,MAAgB,GAAG,OAAO,KAAK,MAAOC,IAAQC,IAAO,GAAG,CAAC,EAAE,IAiB9EN,IAAWQ,EAA0C,SAChE,EACE,WAAQ,MACR,SAAM,KACN,UAAO,UACP,UAAO,MACP,UACA,eAAY,IACZ,iBAAcJ,GACd,cACA,UACA,cAAcK,GACd,GAAGC,KAEL,GACA;CACA,IAAMC,IAAUC,EAAM,GAChBC,IAAcR,MAAU,QAAQ,OAAO,SAASA,CAAK,GACrDS,IAAUD,IAAc,KAAK,IAAI,KAAK,IAAIR,GAAO,CAAC,GAAGC,CAAG,IAAI,GAC5DS,IAAYF,IAAcG,EAAYF,GAASR,CAAG,IAAI,KAAA;CAE5D,OACE,kBAAC,OAAD;EAAK,WAAWW,EAAGhB,EAAO,UAAUiB,CAAS;EAAU;EAAO,aAAWC;EAAM,aAAWC;EAA1F,UAAA,CACGC,KAAUC,KAAaT,IACtB,kBAAC,OAAD;GAAK,WAAWZ,EAAO;GAAvB,UAAA,CACGoB,IACC,kBAAC,QAAD;IAAM,IAAIV;IAAS,WAAWV,EAAO;IAClC,UAAAoB;GACG,CAAA,IACJ,MACHC,KAAaT,IACZ,kBAAC,QAAD;IAAM,WAAWZ,EAAO;IAAO,eAAY;IACxC,UAAAc;GACG,CAAA,IACJ,IACD;EACH,CAAA,IAAA,MACJ,kBAACb,EAAkB,MAAnB;GACO;GACL,WAAWD,EAAO;GAClB,OAAOY,IAAcC,IAAU;GAC1B;GACL,gBAAgB,GAAG,MAAME,EAAYQ,GAAGC,CAAC;GACzC,cAAYhB;GACZ,mBAAiBY,KAAS,CAACZ,IAAYE,IAAU,KAAA;GACjD,GAAID;GAEJ,UAAA,kBAACR,EAAkB,WAAnB;IACE,WAAWD,EAAO;IAClB,OAAOyB,EACLb,IAAe,EAAE,uBAAuBC,IAAUR,EAAI,IAAsB,KAAA,CAC9E;GACD,CAAA;EACqB,CAAA,CACrB;;AAET,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Skeleton.js","names":["styles","Skeleton_module_default","FRACTION","widthCss","w","space","heightCss","h","SkeletonRoot","forwardRef","rest","lines","cx","className","radius","mergeStyles","width","height","style","SkeletonText","gap","i","SkeletonCircle","size","Skeleton"],"sources":["../../../../src/components/display/Skeleton/Skeleton.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Skeleton.module.css'\n\n/** A fraction of the container, or a step on the space scale. */\nexport type SkeletonWidth = 'full' | '3/4' | '2/3' | '1/2' | '1/3' | '1/4' | Space\n/** A text line, a heading line, a control, a block — or a step on the space scale. */\nexport type SkeletonHeight = 'text' | 'heading' | 'control' | 'block' | Space\nexport type SkeletonRadius = 'field' | 'surface' | 'media' | 'chip'\n\nconst FRACTION: Record<Exclude<SkeletonWidth, Space>, string> = {\n full: '100%',\n '3/4': '75%',\n '2/3': '66.667%',\n '1/2': '50%',\n '1/3': '33.333%',\n '1/4': '25%',\n}\n\nconst widthCss = (w: SkeletonWidth) => (typeof w === 'number' ? space(w) : FRACTION[w])\nconst heightCss = (h: SkeletonHeight) =>\n typeof h === 'number'\n ? space(h)\n : h === 'text'\n ? '1lh'\n : h === 'heading'\n ? 'calc(var(--text-xl) * var(--leading-snug))'\n : h === 'control'\n ? 'var(--control-md)'\n : 'var(--space-9)'\n\nexport interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {\n /** Default `full`. */\n width?: SkeletonWidth\n /** Default `text` (one line of the surrounding text). */\n height?: SkeletonHeight\n /** Default `field`. */\n radius?: SkeletonRadius\n /** Render a paragraph of this many lines instead of one block (same as `Skeleton.Text`). */\n lines?: number\n}\n\n/**\n * Placeholder for content that's on its way. Every piece is `aria-hidden`: announce\n * loading once on the region that's loading (`aria-busy`, or a `Spinner` label), not per bar.\n *\n * <Skeleton width=\"1/2\" height=\"heading\" />\n * <Skeleton.Text lines={3} />\n * <Skeleton.Circle size=\"md\" />\n */\nconst SkeletonRoot = forwardRef<HTMLDivElement, SkeletonProps>(function Skeleton(\n { width = 'full', height = 'text', radius = 'field', lines, className, style, ...rest },\n ref,\n) {\n if (lines !== undefined)\n return <SkeletonText ref={ref} lines={lines} className={className} style={style} {...rest} />\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cx(styles.skeleton, className)}\n data-radius={radius}\n style={mergeStyles(\n {\n '--skeleton-width': widthCss(width),\n '--skeleton-height': heightCss(height),\n } as CSSProperties,\n style,\n )}\n {...rest}\n />\n )\n})\n\nexport interface SkeletonTextProps extends HTMLAttributes<HTMLDivElement> {\n /** Number of lines. The last one is shorter, like the end of a paragraph. Default 3. */\n lines?: number\n /** Space between lines. Default: the gap of real body text. */\n gap?: Space\n}\n\nconst SkeletonText = forwardRef<HTMLDivElement, SkeletonTextProps>(function SkeletonText(\n { lines = 3, gap, className, style, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cx(styles.text, className)}\n style={mergeStyles(\n gap !== undefined ? ({ '--skeleton-gap': space(gap) } as CSSProperties) : undefined,\n style,\n )}\n {...rest}\n >\n {Array.from({ length: Math.max(1, lines) }, (_, i) => (\n <div key={i} className={cx(styles.skeleton, styles.line)} data-radius=\"field\" />\n ))}\n </div>\n )\n})\n\nexport interface SkeletonCircleProps extends HTMLAttributes<HTMLDivElement> {\n /** Matches control heights: sm 32 · md 40 · lg 48 (× density). Default `md`. */\n size?: 'sm' | 'md' | 'lg'\n}\n\n/** Avatar placeholder. Follows the theme's avatar shape, so it's a rounded square in Monograph. */\nconst SkeletonCircle = forwardRef<HTMLDivElement, SkeletonCircleProps>(function SkeletonCircle(\n { size = 'md', className, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cx(styles.skeleton, styles.circle, className)}\n data-size={size}\n {...rest}\n />\n )\n})\n\nexport const Skeleton = Object.assign(SkeletonRoot, { Text: SkeletonText, Circle: SkeletonCircle })\n"],"mappings":";;;;;;;AAYA,IAAME,IAA0D;CAC9D,MAAM;CACN,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;AACT,GAEMC,KAAY,MAAsB,OAAOC,KAAM,WAAWC,EAAMD,CAAC,IAAIF,EAASE,IAC9EE,KAAa,MACjB,OAAOC,KAAM,WACTF,EAAME,CAAC,IACPA,MAAM,SACJ,QACAA,MAAM,YACJ,+CACAA,MAAM,YACJ,sBACA,
|
|
1
|
+
{"version":3,"file":"Skeleton.js","names":["styles","Skeleton_module_default","FRACTION","widthCss","w","space","heightCss","h","SkeletonRoot","forwardRef","rest","lines","cx","className","radius","mergeStyles","width","height","style","SkeletonText","gap","i","SkeletonCircle","size","Skeleton"],"sources":["../../../../src/components/display/Skeleton/Skeleton.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Skeleton.module.css'\n\n/** A fraction of the container, or a step on the space scale. */\nexport type SkeletonWidth = 'full' | '3/4' | '2/3' | '1/2' | '1/3' | '1/4' | Space\n/** A text line, a heading line, a control, a block — or a step on the space scale. */\nexport type SkeletonHeight = 'text' | 'heading' | 'control' | 'block' | Space\nexport type SkeletonRadius = 'field' | 'surface' | 'media' | 'chip'\n\nconst FRACTION: Record<Exclude<SkeletonWidth, Space>, string> = {\n full: '100%',\n '3/4': '75%',\n '2/3': '66.667%',\n '1/2': '50%',\n '1/3': '33.333%',\n '1/4': '25%',\n}\n\nconst widthCss = (w: SkeletonWidth) => (typeof w === 'number' ? space(w) : FRACTION[w])\nconst heightCss = (h: SkeletonHeight) =>\n typeof h === 'number'\n ? space(h)\n : h === 'text'\n ? '1lh'\n : h === 'heading'\n ? 'calc(var(--text-xl) * var(--leading-snug))'\n : h === 'control'\n ? 'var(--control-md)'\n : 'var(--space-9)'\n\nexport interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {\n /** Default `full`. */\n width?: SkeletonWidth\n /** Default `text` (one line of the surrounding text). */\n height?: SkeletonHeight\n /** Default `field`. */\n radius?: SkeletonRadius\n /** Render a paragraph of this many lines instead of one block (same as `Skeleton.Text`). */\n lines?: number\n}\n\n/**\n * Placeholder shapes for content on its way, hidden from assistive technology.\n *\n * @remarks\n * `Skeleton` holds the shape of content while it loads, so the page doesn't jump when it arrives.\n * `Skeleton.Text` is lines of text, `Skeleton.Circle` is an avatar, and `Skeleton` itself is a\n * block with a `width` and `height`.\n *\n * @privateRemarks\n * Placeholder for content that's on its way. Every piece is `aria-hidden`: announce\n * loading once on the region that's loading (`aria-busy`, or a `Spinner` label), not per bar.\n *\n * <Skeleton width=\"1/2\" height=\"heading\" />\n * <Skeleton.Text lines={3} />\n * <Skeleton.Circle size=\"md\" />\n */\nconst SkeletonRoot = forwardRef<HTMLDivElement, SkeletonProps>(function Skeleton(\n { width = 'full', height = 'text', radius = 'field', lines, className, style, ...rest },\n ref,\n) {\n if (lines !== undefined)\n return <SkeletonText ref={ref} lines={lines} className={className} style={style} {...rest} />\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cx(styles.skeleton, className)}\n data-radius={radius}\n style={mergeStyles(\n {\n '--skeleton-width': widthCss(width),\n '--skeleton-height': heightCss(height),\n } as CSSProperties,\n style,\n )}\n {...rest}\n />\n )\n})\n\nexport interface SkeletonTextProps extends HTMLAttributes<HTMLDivElement> {\n /** Number of lines. The last one is shorter, like the end of a paragraph. Default 3. */\n lines?: number\n /** Space between lines. Default: the gap of real body text. */\n gap?: Space\n}\n\nconst SkeletonText = forwardRef<HTMLDivElement, SkeletonTextProps>(function SkeletonText(\n { lines = 3, gap, className, style, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cx(styles.text, className)}\n style={mergeStyles(\n gap !== undefined ? ({ '--skeleton-gap': space(gap) } as CSSProperties) : undefined,\n style,\n )}\n {...rest}\n >\n {Array.from({ length: Math.max(1, lines) }, (_, i) => (\n <div key={i} className={cx(styles.skeleton, styles.line)} data-radius=\"field\" />\n ))}\n </div>\n )\n})\n\nexport interface SkeletonCircleProps extends HTMLAttributes<HTMLDivElement> {\n /** Matches control heights: sm 32 · md 40 · lg 48 (× density). Default `md`. */\n size?: 'sm' | 'md' | 'lg'\n}\n\n/** Avatar placeholder. Follows the theme's avatar shape, so it's a rounded square in Monograph. */\nconst SkeletonCircle = forwardRef<HTMLDivElement, SkeletonCircleProps>(function SkeletonCircle(\n { size = 'md', className, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cx(styles.skeleton, styles.circle, className)}\n data-size={size}\n {...rest}\n />\n )\n})\n\nexport const Skeleton = Object.assign(SkeletonRoot, { Text: SkeletonText, Circle: SkeletonCircle })\n"],"mappings":";;;;;;;AAYA,IAAME,IAA0D;CAC9D,MAAM;CACN,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;AACT,GAEMC,KAAY,MAAsB,OAAOC,KAAM,WAAWC,EAAMD,CAAC,IAAIF,EAASE,IAC9EE,KAAa,MACjB,OAAOC,KAAM,WACTF,EAAME,CAAC,IACPA,MAAM,SACJ,QACAA,MAAM,YACJ,+CACAA,MAAM,YACJ,sBACA,kBA6BNC,IAAeC,EAA0C,SAC7D,EAAE,WAAQ,QAAQ,YAAS,QAAQ,YAAS,SAAS,UAAO,cAAW,UAAO,GAAGC,KACjF,GACA;CAGA,OAFIC,MAAU,KAAA,IAGZ,kBAAC,OAAD;EACO;EACL,eAAY;EACZ,WAAWC,EAAGZ,EAAO,UAAUa,CAAS;EACxC,eAAaC;EACb,OAAOC,EACL;GACE,oBAAoBZ,EAASa,CAAK;GAClC,qBAAqBV,EAAUW,CAAM;EACvC,GACAC,CACF;EACA,GAAIR;CACL,CAAA,IAfM,kBAACS,GAAD;EAAmB;EAAY;EAAkB;EAAkB;EAAO,GAAIT;CAAO,CAAA;AAiBhG,CAAC,GASKS,IAAeV,EAA8C,SACjE,EAAE,WAAQ,GAAG,QAAK,cAAW,UAAO,GAAGC,KACvC,GACA;CACA,OACE,kBAAC,OAAD;EACO;EACL,eAAY;EACZ,WAAWE,EAAGZ,EAAO,MAAMa,CAAS;EACpC,OAAOE,EACLK,MAAQ,KAAA,IAAkE,KAAA,IAArD,EAAE,kBAAkBf,EAAMe,CAAG,EAAE,GACpDF,CACF;EACA,GAAIR;EAEH,UAAA,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAGC,CAAK,EAAE,IAAI,GAAG,MAC9C,kBAAC,OAAD;GAAa,WAAWC,EAAGZ,EAAO,UAAUA,EAAO,IAAI;GAAG,eAAY;EAAS,GAArEqB,CAAqE,CAChF;CACE,CAAA;AAET,CAAC,GAQKC,IAAiBb,EAAgD,SACrE,EAAE,UAAO,MAAM,cAAW,GAAGC,KAC7B,GACA;CACA,OACE,kBAAC,OAAD;EACO;EACL,eAAY;EACZ,WAAWE,EAAGZ,EAAO,UAAUA,EAAO,QAAQa,CAAS;EACvD,aAAWU;EACX,GAAIb;CACL,CAAA;AAEL,CAAC,GAEYc,IAAW,OAAO,OAAOhB,GAAc;CAAE,MAAMW;CAAc,QAAQG;AAAe,CAAC"}
|
|
@@ -15,6 +15,14 @@ export interface StampProps extends HTMLAttributes<HTMLSpanElement> {
|
|
|
15
15
|
placement?: StampPlacement;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
+
* A rubber-stamp verdict like Paid, Void or Cancelled. A double rule in the display face, knocked
|
|
19
|
+
* off square.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* `Stamp` is a verdict, stamped on the thing it judges: a refunded ticket, a void invoice. It's
|
|
23
|
+
* decorative but deliberate, so use one per object.
|
|
24
|
+
*
|
|
25
|
+
* @privateRemarks
|
|
18
26
|
* A rubber-stamp verdict: "Rejected", "Paid", "Void". A double rule in the display
|
|
19
27
|
* face, knocked slightly off square. Decorative but deliberate — use one per object,
|
|
20
28
|
* on the thing it judges.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stamp.js","names":["styles","Stamp_module_default","Stamp","forwardRef","rest","cx","className","tone","size","placement","mergeStyles","rotate","style"],"sources":["../../../../src/components/display/Stamp/Stamp.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size, Tone } from '#utils/tokens'\nimport styles from './Stamp.module.css'\n\nexport type StampPlacement = 'inline' | 'corner'\n\nexport interface StampProps extends HTMLAttributes<HTMLSpanElement> {\n /** Ink colour. Default `critical` — stamps are usually verdicts. */\n tone?: Tone\n /** Tilt in degrees. Keep it small (−10 to 10); default −6. `0` for a square stamp. */\n rotate?: number\n size?: Size\n /**\n * `inline` (default) sits in the flow. `corner` is pinned to the top-end corner of the\n * nearest positioned ancestor — a <Card> is positioned — so stamping an object never\n * changes its height.\n */\n placement?: StampPlacement\n}\n\n/**\n * A rubber-stamp verdict: \"Rejected\", \"Paid\", \"Void\". A double rule in the display\n * face, knocked slightly off square. Decorative but deliberate — use one per object,\n * on the thing it judges.\n *\n * It reads as its text; pass `aria-label` when the visible word needs more context\n * (\"Invoice 1039 overdue by 12 days\").\n */\nexport const Stamp = forwardRef<HTMLSpanElement, StampProps>(function Stamp(\n { tone = 'critical', rotate = -6, size = 'md', placement = 'inline', className, style, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cx(styles.stamp, className)}\n data-tone={tone}\n data-size={size}\n data-placement={placement === 'corner' ? 'corner' : undefined}\n style={mergeStyles({ '--stamp-rotate': `${String(rotate)}deg` } as CSSProperties, style)}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"Stamp.js","names":["styles","Stamp_module_default","Stamp","forwardRef","rest","cx","className","tone","size","placement","mergeStyles","rotate","style"],"sources":["../../../../src/components/display/Stamp/Stamp.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size, Tone } from '#utils/tokens'\nimport styles from './Stamp.module.css'\n\nexport type StampPlacement = 'inline' | 'corner'\n\nexport interface StampProps extends HTMLAttributes<HTMLSpanElement> {\n /** Ink colour. Default `critical` — stamps are usually verdicts. */\n tone?: Tone\n /** Tilt in degrees. Keep it small (−10 to 10); default −6. `0` for a square stamp. */\n rotate?: number\n size?: Size\n /**\n * `inline` (default) sits in the flow. `corner` is pinned to the top-end corner of the\n * nearest positioned ancestor — a <Card> is positioned — so stamping an object never\n * changes its height.\n */\n placement?: StampPlacement\n}\n\n/**\n * A rubber-stamp verdict like Paid, Void or Cancelled. A double rule in the display face, knocked\n * off square.\n *\n * @remarks\n * `Stamp` is a verdict, stamped on the thing it judges: a refunded ticket, a void invoice. It's\n * decorative but deliberate, so use one per object.\n *\n * @privateRemarks\n * A rubber-stamp verdict: \"Rejected\", \"Paid\", \"Void\". A double rule in the display\n * face, knocked slightly off square. Decorative but deliberate — use one per object,\n * on the thing it judges.\n *\n * It reads as its text; pass `aria-label` when the visible word needs more context\n * (\"Invoice 1039 overdue by 12 days\").\n */\nexport const Stamp = forwardRef<HTMLSpanElement, StampProps>(function Stamp(\n { tone = 'critical', rotate = -6, size = 'md', placement = 'inline', className, style, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cx(styles.stamp, className)}\n data-tone={tone}\n data-size={size}\n data-placement={placement === 'corner' ? 'corner' : undefined}\n style={mergeStyles({ '--stamp-rotate': `${String(rotate)}deg` } as CSSProperties, style)}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;AAsCA,IAAaE,IAAQC,EAAwC,SAC3D,EAAE,UAAO,YAAY,YAAS,IAAI,UAAO,MAAM,eAAY,UAAU,cAAW,UAAO,GAAGC,KAC1F,GACA;CACA,OACE,kBAAC,QAAD;EACO;EACL,WAAWC,EAAGL,EAAO,OAAOM,CAAS;EACrC,aAAWC;EACX,aAAWC;EACX,kBAAgBC,MAAc,WAAW,WAAW,KAAA;EACpD,OAAOC,EAAY,EAAE,kBAAkB,GAAG,OAAOC,CAAM,EAAE,KAAK,GAAoBC,CAAK;EACvF,GAAIR;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -32,6 +32,14 @@ export interface StatProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childre
|
|
|
32
32
|
tone?: StatTone;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
+
* A single labelled figure with its change, set on one baseline. No tile, no icon, no gradient.
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* `Stat` is a number that matters, with its label and how it changed. It's typographic on purpose:
|
|
39
|
+
* label, figure and change on one baseline, in the theme's numeric face. A row of them on a
|
|
40
|
+
* hairline reads better than three tiles with an icon in a tinted circle each.
|
|
41
|
+
*
|
|
42
|
+
* @privateRemarks
|
|
35
43
|
* A single labelled figure. Typographic, not decorative: label, number, and the change
|
|
36
44
|
* on one baseline — no tile, no icon, no gradient.
|
|
37
45
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stat.js","names":["styles","Stat_module_default","Stat","forwardRef","rest","labelId","useId","cx","className","size","rule","tone","label","value","delta","Delta","hint"],"sources":["../../../../src/components/display/Stat/Stat.tsx"],"sourcesContent":["import { forwardRef, useId, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport { Delta, type DeltaDirection, type DeltaTone } from '#components/display/Delta'\nimport styles from './Stat.module.css'\n\nexport type StatSize = 'sm' | 'md' | 'lg' | 'hero'\nexport type StatDeltaDirection = DeltaDirection\nexport type StatDeltaTone = DeltaTone\n/** Colour of the figure. `default` is ink; use a tone only when the number itself is the news. */\nexport type StatTone = 'default' | 'positive' | 'critical' | 'accent'\n\nexport interface StatDelta {\n /** The change, already formatted: \"+$212.40\", \"3 places\", \"4.1%\". */\n value: ReactNode\n direction: StatDeltaDirection\n /**\n * Whether the change is good. Defaults from direction (up → positive, down → critical),\n * so override it when down is good: costs that fell are `direction=\"down\" tone=\"positive\"`.\n */\n tone?: StatDeltaTone\n}\n\nexport interface StatProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n /** What the number is. Sentence case, short: \"Revenue\", \"Seats used\", \"Days left\". */\n label: ReactNode\n /** The figure. Usually `<Amount>`/`<Numeral>`; plain strings are set tabular too. */\n value: ReactNode\n /** Change against a comparison period, shown beside the value. */\n delta?: StatDelta\n /** One line of context under the value: \"of $60,000 target\", \"after 3 sprints\". */\n hint?: ReactNode\n /** Default `md`. `hero` is for the one figure a page is about. */\n size?: StatSize\n /** A hairline above the label — lines up a row of stats like ledger columns. */\n rule?: boolean\n /** Colour of the value: an overrun in `critical`, the live total in `accent`. Default ink. */\n tone?: StatTone\n}\n\n/**\n * A single labelled figure. Typographic, not decorative: label, number, and the change\n * on one baseline — no tile, no icon, no gradient.\n *\n * <Stat label=\"Costs\" value=\"$4,812.40\" delta={{ value: '$212.40', direction: 'up', tone: 'critical' }} />\n */\nexport const Stat = forwardRef<HTMLDivElement, StatProps>(function Stat(\n { label, value, delta, hint, size = 'md', rule = false, tone, className, ...rest },\n ref,\n) {\n const labelId = useId()\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-labelledby={labelId}\n className={cx(styles.stat, className)}\n data-size={size}\n data-rule={rule || undefined}\n data-tone={tone && tone !== 'default' ? tone : undefined}\n {...rest}\n >\n <div id={labelId} className={styles.label}>\n {label}\n </div>\n <div className={styles.figure}>\n <span className={styles.value}>{value}</span>\n {delta ? (\n <Delta className={styles.delta} direction={delta.direction} tone={delta.tone}>\n {delta.value}\n </Delta>\n ) : null}\n </div>\n {hint ? <div className={styles.hint}>{hint}</div> : null}\n </div>\n )\n})\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"Stat.js","names":["styles","Stat_module_default","Stat","forwardRef","rest","labelId","useId","cx","className","size","rule","tone","label","value","delta","Delta","hint"],"sources":["../../../../src/components/display/Stat/Stat.tsx"],"sourcesContent":["import { forwardRef, useId, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport { Delta, type DeltaDirection, type DeltaTone } from '#components/display/Delta'\nimport styles from './Stat.module.css'\n\nexport type StatSize = 'sm' | 'md' | 'lg' | 'hero'\nexport type StatDeltaDirection = DeltaDirection\nexport type StatDeltaTone = DeltaTone\n/** Colour of the figure. `default` is ink; use a tone only when the number itself is the news. */\nexport type StatTone = 'default' | 'positive' | 'critical' | 'accent'\n\nexport interface StatDelta {\n /** The change, already formatted: \"+$212.40\", \"3 places\", \"4.1%\". */\n value: ReactNode\n direction: StatDeltaDirection\n /**\n * Whether the change is good. Defaults from direction (up → positive, down → critical),\n * so override it when down is good: costs that fell are `direction=\"down\" tone=\"positive\"`.\n */\n tone?: StatDeltaTone\n}\n\nexport interface StatProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n /** What the number is. Sentence case, short: \"Revenue\", \"Seats used\", \"Days left\". */\n label: ReactNode\n /** The figure. Usually `<Amount>`/`<Numeral>`; plain strings are set tabular too. */\n value: ReactNode\n /** Change against a comparison period, shown beside the value. */\n delta?: StatDelta\n /** One line of context under the value: \"of $60,000 target\", \"after 3 sprints\". */\n hint?: ReactNode\n /** Default `md`. `hero` is for the one figure a page is about. */\n size?: StatSize\n /** A hairline above the label — lines up a row of stats like ledger columns. */\n rule?: boolean\n /** Colour of the value: an overrun in `critical`, the live total in `accent`. Default ink. */\n tone?: StatTone\n}\n\n/**\n * A single labelled figure with its change, set on one baseline. No tile, no icon, no gradient.\n *\n * @remarks\n * `Stat` is a number that matters, with its label and how it changed. It's typographic on purpose:\n * label, figure and change on one baseline, in the theme's numeric face. A row of them on a\n * hairline reads better than three tiles with an icon in a tinted circle each.\n *\n * @privateRemarks\n * A single labelled figure. Typographic, not decorative: label, number, and the change\n * on one baseline — no tile, no icon, no gradient.\n *\n * <Stat label=\"Costs\" value=\"$4,812.40\" delta={{ value: '$212.40', direction: 'up', tone: 'critical' }} />\n */\nexport const Stat = forwardRef<HTMLDivElement, StatProps>(function Stat(\n { label, value, delta, hint, size = 'md', rule = false, tone, className, ...rest },\n ref,\n) {\n const labelId = useId()\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-labelledby={labelId}\n className={cx(styles.stat, className)}\n data-size={size}\n data-rule={rule || undefined}\n data-tone={tone && tone !== 'default' ? tone : undefined}\n {...rest}\n >\n <div id={labelId} className={styles.label}>\n {label}\n </div>\n <div className={styles.figure}>\n <span className={styles.value}>{value}</span>\n {delta ? (\n <Delta className={styles.delta} direction={delta.direction} tone={delta.tone}>\n {delta.value}\n </Delta>\n ) : null}\n </div>\n {hint ? <div className={styles.hint}>{hint}</div> : null}\n </div>\n )\n})\n"],"mappings":";;;;;;AAqDA,IAAaE,IAAOC,EAAsC,SACxD,EAAE,UAAO,UAAO,UAAO,SAAM,UAAO,MAAM,UAAO,IAAO,SAAM,cAAW,GAAGC,KAC5E,GACA;CACA,IAAMC,IAAUC,EAAM;CACtB,OACE,kBAAC,OAAD;EACO;EACL,MAAK;EACL,mBAAiBD;EACjB,WAAWE,EAAGP,EAAO,MAAMQ,CAAS;EACpC,aAAWC;EACX,aAAWC,KAAQ,KAAA;EACnB,aAAWC,KAAQA,MAAS,YAAYA,IAAO,KAAA;EAC/C,GAAIP;EARN,UAAA;GAUE,kBAAC,OAAD;IAAK,IAAIC;IAAS,WAAWL,EAAO;IACjC,UAAAY;GACE,CAAA;GACL,kBAAC,OAAD;IAAK,WAAWZ,EAAO;IAAvB,UAAA,CACE,kBAAC,QAAD;KAAM,WAAWA,EAAO;KAAQ,UAAAa;IAAY,CAAA,GAC3CC,IACC,kBAACC,GAAD;KAAO,WAAWf,EAAO;KAAO,WAAWc,EAAM;KAAW,MAAMA,EAAM;KACrE,UAAAA,EAAM;IACF,CAAA,IACL,IACD;;GACJE,IAAO,kBAAC,OAAD;IAAK,WAAWhB,EAAO;IAAO,UAAAgB;GAAU,CAAA,IAAI;EACjD;;AAET,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Table.js","names":["styles","Table_module_default","TableRoot","forwardRef","rest","stickyHeader","label","cx","className","density","variant","striped","TableHead","TableBody","TableFoot","TableRow","highlighted","interactive","ARIA_SORT","TableHeaderCell","sortable","sort","onSort","visibilityClass","align","numeric","width","children","jsx","ChevronUpIcon","ChevronDownIcon","TableCell","shared","rowHeader","thRest","TableCaption","Table"],"sources":["../../../../src/components/display/Table/Table.tsx"],"sourcesContent":["import {\n forwardRef,\n type HTMLAttributes,\n type TableHTMLAttributes,\n type TdHTMLAttributes,\n type ThHTMLAttributes,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport { ChevronDownIcon, ChevronUpIcon } from '#icons'\nimport styles from './Table.module.css'\n\nexport type TableDensity = 'compact' | 'regular'\nexport type TableVariant = 'rules' | 'plain'\nexport type TableAlign = 'start' | 'center' | 'end'\nexport type TableSort = 'asc' | 'desc' | 'none'\n/** `fill` takes the spare width (the name column); `min` shrinks to its content. */\nexport type TableColumnWidth = 'fill' | 'min'\n\nexport interface TableProps extends TableHTMLAttributes<HTMLTableElement> {\n /** Row height. Default `regular`. */\n density?: TableDensity\n /** `rules` (default): a hairline under every row. `plain`: only the header rule. */\n variant?: TableVariant\n /** Alternate row fills. Off by default — rules usually read better. */\n striped?: boolean\n /**\n * Keep the header visible while rows scroll. The wrapper becomes the scroll container\n * (capped by `--table-max-height`, default 70vh), since a horizontally scrolling wrapper\n * would otherwise trap a page-sticky header.\n */\n stickyHeader?: boolean\n /**\n * Name for the scroll container. When set, the wrapper becomes a focusable, labelled\n * region so keyboard users can scroll a wide table.\n */\n label?: string\n}\n\n/**\n * A semantic, styled data table: project boards, ledgers, audit logs. The wrapper scrolls\n * horizontally on narrow screens; `ref` and native props go to the `<table>`.\n *\n * <Table><Table.Head><Table.Row><Table.HeaderCell>Project</Table.HeaderCell>…</Table.Row></Table.Head>…</Table>\n */\nconst TableRoot = forwardRef<HTMLTableElement, TableProps>(function Table(\n {\n density = 'regular',\n variant = 'rules',\n striped = false,\n stickyHeader = false,\n label,\n className,\n ...rest\n },\n ref,\n) {\n return (\n <div\n className={styles.scroll}\n data-kiln-component=\"\"\n data-sticky-header={stickyHeader || undefined}\n role={label ? 'region' : undefined}\n aria-label={label}\n tabIndex={label ? 0 : undefined}\n >\n <table\n ref={ref}\n className={cx(styles.table, className)}\n data-density={density}\n data-variant={variant}\n data-striped={striped || undefined}\n data-sticky-header={stickyHeader || undefined}\n {...rest}\n />\n </div>\n )\n})\n\nconst TableHead = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableHead({ className, ...rest }, ref) {\n return <thead ref={ref} className={cx(styles.head, className)} {...rest} />\n },\n)\n\nconst TableBody = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableBody({ className, ...rest }, ref) {\n return <tbody ref={ref} className={cx(styles.body, className)} {...rest} />\n },\n)\n\n/** Totals row(s). Set above a strong rule. */\nconst TableFoot = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableFoot({ className, ...rest }, ref) {\n return <tfoot ref={ref} className={cx(styles.foot, className)} {...rest} />\n },\n)\n\nexport interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {\n /** `--color-highlight` fill — \"you\", your project, the circled line. */\n highlighted?: boolean\n /** Hover fill for rows that respond to clicks (put the real link in a cell). */\n interactive?: boolean\n}\n\nconst TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(function TableRow(\n { highlighted = false, interactive = false, className, ...rest },\n ref,\n) {\n return (\n <tr\n ref={ref}\n className={cx(styles.row, className)}\n data-highlighted={highlighted || undefined}\n data-interactive={interactive || undefined}\n {...rest}\n />\n )\n})\n\nexport interface TableHeaderCellProps\n extends Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align' | 'width'>, VisibilityProps {\n align?: TableAlign\n /** Numeric column: right-aligned with tabular figures. */\n numeric?: boolean\n /** Column sizing: `fill` takes the spare width, `min` shrinks to fit. Default: automatic. */\n width?: TableColumnWidth\n /** Current sort of this column. With `onSort`, the label becomes a button and `aria-sort` is set. */\n sort?: TableSort\n /** Called when the sort button is pressed. Decide the next direction yourself. */\n onSort?: () => void\n}\n\nconst ARIA_SORT = { asc: 'ascending', desc: 'descending', none: 'none' } as const\n\nconst TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellProps>(\n function TableHeaderCell(\n {\n align,\n numeric = false,\n width,\n sort,\n onSort,\n hideBelow,\n hideAbove,\n scope = 'col',\n className,\n children,\n ...rest\n },\n ref,\n ) {\n const sortable = sort !== undefined && onSort !== undefined\n return (\n <th\n ref={ref}\n scope={scope}\n className={cx(styles.headerCell, visibilityClass({ hideBelow, hideAbove }), className)}\n data-align={align ?? (numeric ? 'end' : undefined)}\n data-numeric={numeric || undefined}\n data-width={width}\n data-sort={sortable ? sort : undefined}\n aria-sort={sort !== undefined ? ARIA_SORT[sort] : undefined}\n {...rest}\n >\n {sortable ? (\n <button type=\"button\" className={styles.sortButton} onClick={onSort}>\n {children}\n <span className={styles.sortGlyph} aria-hidden=\"true\">\n {sort === 'asc' ? <ChevronUpIcon /> : <ChevronDownIcon />}\n </span>\n </button>\n ) : (\n children\n )}\n </th>\n )\n },\n)\n\n/**\n * Hide a column on small screens: put the same `hideBelow` on its HeaderCell and on\n * every Cell in that column (cells are `display: none`, so the columns stay aligned).\n */\nexport interface TableCellProps\n extends Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'>, VisibilityProps {\n align?: TableAlign\n /** Figures: right-aligned, tabular, in the theme's numeric face (mono in Ledger). */\n numeric?: boolean\n /** Render as `<th scope=\"row\">` — the cell that names the row (project, client). */\n rowHeader?: boolean\n}\n\nconst TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(function TableCell(\n { align, numeric = false, rowHeader = false, hideBelow, hideAbove, className, ...rest },\n ref,\n) {\n const shared = {\n className: cx(styles.cell, visibilityClass({ hideBelow, hideAbove }), className),\n 'data-align': align ?? (numeric ? 'end' : undefined),\n 'data-numeric': numeric || undefined,\n 'data-row-header': rowHeader || undefined,\n }\n if (rowHeader) {\n const { scope = 'row', ...thRest } = rest as ThHTMLAttributes<HTMLTableCellElement>\n return <th ref={ref} scope={scope} {...shared} {...thRest} />\n }\n return <td ref={ref} {...shared} {...rest} />\n})\n\nconst TableCaption = forwardRef<HTMLTableCaptionElement, HTMLAttributes<HTMLTableCaptionElement>>(\n function TableCaption({ className, ...rest }, ref) {\n return <caption ref={ref} className={cx(styles.caption, className)} {...rest} />\n },\n)\n\nexport const Table = Object.assign(TableRoot, {\n Head: TableHead,\n Body: TableBody,\n Foot: TableFoot,\n Row: TableRow,\n HeaderCell: TableHeaderCell,\n Cell: TableCell,\n Caption: TableCaption,\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Table.js","names":["styles","Table_module_default","TableRoot","forwardRef","rest","stickyHeader","label","cx","className","density","variant","striped","TableHead","TableBody","TableFoot","TableRow","highlighted","interactive","ARIA_SORT","TableHeaderCell","sortable","sort","onSort","visibilityClass","align","numeric","width","children","jsx","ChevronUpIcon","ChevronDownIcon","TableCell","shared","rowHeader","thRest","TableCaption","Table"],"sources":["../../../../src/components/display/Table/Table.tsx"],"sourcesContent":["import {\n forwardRef,\n type HTMLAttributes,\n type TableHTMLAttributes,\n type TdHTMLAttributes,\n type ThHTMLAttributes,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport { ChevronDownIcon, ChevronUpIcon } from '#icons'\nimport styles from './Table.module.css'\n\nexport type TableDensity = 'compact' | 'regular'\nexport type TableVariant = 'rules' | 'plain'\nexport type TableAlign = 'start' | 'center' | 'end'\nexport type TableSort = 'asc' | 'desc' | 'none'\n/** `fill` takes the spare width (the name column); `min` shrinks to its content. */\nexport type TableColumnWidth = 'fill' | 'min'\n\nexport interface TableProps extends TableHTMLAttributes<HTMLTableElement> {\n /** Row height. Default `regular`. */\n density?: TableDensity\n /** `rules` (default): a hairline under every row. `plain`: only the header rule. */\n variant?: TableVariant\n /** Alternate row fills. Off by default — rules usually read better. */\n striped?: boolean\n /**\n * Keep the header visible while rows scroll. The wrapper becomes the scroll container\n * (capped by `--table-max-height`, default 70vh), since a horizontally scrolling wrapper\n * would otherwise trap a page-sticky header.\n */\n stickyHeader?: boolean\n /**\n * Name for the scroll container. When set, the wrapper becomes a focusable, labelled\n * region so keyboard users can scroll a wide table.\n */\n label?: string\n}\n\n/**\n * A semantic data table with numeric columns that line up, sortable headers and a footer for\n * totals.\n *\n * @remarks\n * `Table` is a real `<table>` with Kiln's styles: rules between rows, numeric columns in tabular\n * figures, and a wrapper that scrolls sideways on narrow screens instead of breaking the layout.\n *\n * ## Density, stripes and sticky headers\n *\n * `density=\"compact\"` for dense data, `striped` for alternating fills (rules usually read better),\n * and `stickyHeader` to keep the header in view while the rows scroll inside the table.\n *\n * @privateRemarks\n * A semantic, styled data table: project boards, ledgers, audit logs. The wrapper scrolls\n * horizontally on narrow screens; `ref` and native props go to the `<table>`.\n *\n * <Table><Table.Head><Table.Row><Table.HeaderCell>Project</Table.HeaderCell>…</Table.Row></Table.Head>…</Table>\n */\nconst TableRoot = forwardRef<HTMLTableElement, TableProps>(function Table(\n {\n density = 'regular',\n variant = 'rules',\n striped = false,\n stickyHeader = false,\n label,\n className,\n ...rest\n },\n ref,\n) {\n return (\n <div\n className={styles.scroll}\n data-kiln-component=\"\"\n data-sticky-header={stickyHeader || undefined}\n role={label ? 'region' : undefined}\n aria-label={label}\n tabIndex={label ? 0 : undefined}\n >\n <table\n ref={ref}\n className={cx(styles.table, className)}\n data-density={density}\n data-variant={variant}\n data-striped={striped || undefined}\n data-sticky-header={stickyHeader || undefined}\n {...rest}\n />\n </div>\n )\n})\n\nconst TableHead = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableHead({ className, ...rest }, ref) {\n return <thead ref={ref} className={cx(styles.head, className)} {...rest} />\n },\n)\n\nconst TableBody = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableBody({ className, ...rest }, ref) {\n return <tbody ref={ref} className={cx(styles.body, className)} {...rest} />\n },\n)\n\n/** Totals row(s). Set above a strong rule. */\nconst TableFoot = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableFoot({ className, ...rest }, ref) {\n return <tfoot ref={ref} className={cx(styles.foot, className)} {...rest} />\n },\n)\n\nexport interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {\n /** `--color-highlight` fill — \"you\", your project, the circled line. */\n highlighted?: boolean\n /** Hover fill for rows that respond to clicks (put the real link in a cell). */\n interactive?: boolean\n}\n\nconst TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(function TableRow(\n { highlighted = false, interactive = false, className, ...rest },\n ref,\n) {\n return (\n <tr\n ref={ref}\n className={cx(styles.row, className)}\n data-highlighted={highlighted || undefined}\n data-interactive={interactive || undefined}\n {...rest}\n />\n )\n})\n\nexport interface TableHeaderCellProps\n extends Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align' | 'width'>, VisibilityProps {\n align?: TableAlign\n /** Numeric column: right-aligned with tabular figures. */\n numeric?: boolean\n /** Column sizing: `fill` takes the spare width, `min` shrinks to fit. Default: automatic. */\n width?: TableColumnWidth\n /** Current sort of this column. With `onSort`, the label becomes a button and `aria-sort` is set. */\n sort?: TableSort\n /** Called when the sort button is pressed. Decide the next direction yourself. */\n onSort?: () => void\n}\n\nconst ARIA_SORT = { asc: 'ascending', desc: 'descending', none: 'none' } as const\n\nconst TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellProps>(\n function TableHeaderCell(\n {\n align,\n numeric = false,\n width,\n sort,\n onSort,\n hideBelow,\n hideAbove,\n scope = 'col',\n className,\n children,\n ...rest\n },\n ref,\n ) {\n const sortable = sort !== undefined && onSort !== undefined\n return (\n <th\n ref={ref}\n scope={scope}\n className={cx(styles.headerCell, visibilityClass({ hideBelow, hideAbove }), className)}\n data-align={align ?? (numeric ? 'end' : undefined)}\n data-numeric={numeric || undefined}\n data-width={width}\n data-sort={sortable ? sort : undefined}\n aria-sort={sort !== undefined ? ARIA_SORT[sort] : undefined}\n {...rest}\n >\n {sortable ? (\n <button type=\"button\" className={styles.sortButton} onClick={onSort}>\n {children}\n <span className={styles.sortGlyph} aria-hidden=\"true\">\n {sort === 'asc' ? <ChevronUpIcon /> : <ChevronDownIcon />}\n </span>\n </button>\n ) : (\n children\n )}\n </th>\n )\n },\n)\n\n/**\n * Hide a column on small screens: put the same `hideBelow` on its HeaderCell and on\n * every Cell in that column (cells are `display: none`, so the columns stay aligned).\n */\nexport interface TableCellProps\n extends Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'>, VisibilityProps {\n align?: TableAlign\n /** Figures: right-aligned, tabular, in the theme's numeric face (mono in Ledger). */\n numeric?: boolean\n /** Render as `<th scope=\"row\">` — the cell that names the row (project, client). */\n rowHeader?: boolean\n}\n\nconst TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(function TableCell(\n { align, numeric = false, rowHeader = false, hideBelow, hideAbove, className, ...rest },\n ref,\n) {\n const shared = {\n className: cx(styles.cell, visibilityClass({ hideBelow, hideAbove }), className),\n 'data-align': align ?? (numeric ? 'end' : undefined),\n 'data-numeric': numeric || undefined,\n 'data-row-header': rowHeader || undefined,\n }\n if (rowHeader) {\n const { scope = 'row', ...thRest } = rest as ThHTMLAttributes<HTMLTableCellElement>\n return <th ref={ref} scope={scope} {...shared} {...thRest} />\n }\n return <td ref={ref} {...shared} {...rest} />\n})\n\nconst TableCaption = forwardRef<HTMLTableCaptionElement, HTMLAttributes<HTMLTableCaptionElement>>(\n function TableCaption({ className, ...rest }, ref) {\n return <caption ref={ref} className={cx(styles.caption, className)} {...rest} />\n },\n)\n\nexport const Table = Object.assign(TableRoot, {\n Head: TableHead,\n Body: TableBody,\n Foot: TableFoot,\n Row: TableRow,\n HeaderCell: TableHeaderCell,\n Cell: TableCell,\n Caption: TableCaption,\n})\n"],"mappings":"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"}
|
|
@@ -12,6 +12,13 @@ export interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'>
|
|
|
12
12
|
asChild?: boolean;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
+
* Quiet metadata, like a category, a facility or a group, in a wrapping list that can be edited.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* A `Tag` labels a thing with metadata, and a `TagList` lays several out as a real list that
|
|
19
|
+
* wraps. Tags are quieter than {@link Badge | badges}, which are for status.
|
|
20
|
+
*
|
|
21
|
+
* @privateRemarks
|
|
15
22
|
* Quiet metadata: a category, a stack item, a group. Quieter than `Badge`, which is
|
|
16
23
|
* for status. Group several in a `TagList`.
|
|
17
24
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.js","names":["styles","Tag_module_default","Tag","forwardRef","rest","Comp","asChild","Slot","canRemove","onRemove","label","removeLabel","children","cx","className","color","CloseIcon","TagList","Children","isValidElement","child"],"sources":["../../../../src/components/display/Tag/Tag.tsx"],"sourcesContent":["import { Children, forwardRef, isValidElement, type HTMLAttributes, type MouseEvent } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { CloseIcon } from '#icons'\nimport styles from './Tag.module.css'\n\n/** Categorical colour slot → `--color-cat-N`. */\nexport type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\n\nexport interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {\n /** A categorical colour (category, person, topic). Omit for a neutral tag. */\n color?: TagColor\n /** Renders a remove button after the label. Not rendered when `asChild`. */\n onRemove?: (event: MouseEvent<HTMLButtonElement>) => void\n /** Accessible name for the remove button. Default `Remove <label>` for text labels. */\n removeLabel?: string\n /** Render the single child (e.g. a filter link) as the tag. Incompatible with `onRemove`. */\n asChild?: boolean\n}\n\n/**\n * Quiet metadata: a category, a stack item, a group. Quieter than `Badge`, which is\n * for status. Group several in a `TagList`.\n */\nexport const Tag = forwardRef<HTMLSpanElement, TagProps>(function Tag(\n { color, onRemove, removeLabel, asChild = false, className, children, ...rest },\n ref,\n) {\n const Comp = asChild ? Slot.Root : 'span'\n const canRemove = Boolean(onRemove) && !asChild\n const label = removeLabel ?? (typeof children === 'string' ? `Remove ${children}` : 'Remove')\n return (\n <Comp\n ref={ref}\n className={cx(styles.tag, className)}\n data-color={color}\n data-removable={canRemove || undefined}\n {...rest}\n >\n <Slot.Slottable>{children}</Slot.Slottable>\n {canRemove ? (\n <button type=\"button\" className={styles.remove} aria-label={label} onClick={onRemove}>\n <CloseIcon />\n </button>\n ) : null}\n </Comp>\n )\n})\n\nexport type TagListProps = HTMLAttributes<HTMLUListElement>\n\n/** A wrapping list of tags. Each child is wrapped in an `<li>`. */\nexport const TagList = forwardRef<HTMLUListElement, TagListProps>(function TagList(\n { className, children, ...rest },\n ref,\n) {\n return (\n <ul ref={ref} role=\"list\" className={cx(styles.list, className)} {...rest}>\n {Children.map(children, (child) =>\n isValidElement(child) || typeof child === 'string' || typeof child === 'number' ? (\n <li key={isValidElement(child) ? child.key : undefined}>{child}</li>\n ) : null,\n )}\n </ul>\n )\n})\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"Tag.js","names":["styles","Tag_module_default","Tag","forwardRef","rest","Comp","asChild","Slot","canRemove","onRemove","label","removeLabel","children","cx","className","color","CloseIcon","TagList","Children","isValidElement","child"],"sources":["../../../../src/components/display/Tag/Tag.tsx"],"sourcesContent":["import { Children, forwardRef, isValidElement, type HTMLAttributes, type MouseEvent } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { CloseIcon } from '#icons'\nimport styles from './Tag.module.css'\n\n/** Categorical colour slot → `--color-cat-N`. */\nexport type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\n\nexport interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {\n /** A categorical colour (category, person, topic). Omit for a neutral tag. */\n color?: TagColor\n /** Renders a remove button after the label. Not rendered when `asChild`. */\n onRemove?: (event: MouseEvent<HTMLButtonElement>) => void\n /** Accessible name for the remove button. Default `Remove <label>` for text labels. */\n removeLabel?: string\n /** Render the single child (e.g. a filter link) as the tag. Incompatible with `onRemove`. */\n asChild?: boolean\n}\n\n/**\n * Quiet metadata, like a category, a facility or a group, in a wrapping list that can be edited.\n *\n * @remarks\n * A `Tag` labels a thing with metadata, and a `TagList` lays several out as a real list that\n * wraps. Tags are quieter than {@link Badge | badges}, which are for status.\n *\n * @privateRemarks\n * Quiet metadata: a category, a stack item, a group. Quieter than `Badge`, which is\n * for status. Group several in a `TagList`.\n */\nexport const Tag = forwardRef<HTMLSpanElement, TagProps>(function Tag(\n { color, onRemove, removeLabel, asChild = false, className, children, ...rest },\n ref,\n) {\n const Comp = asChild ? Slot.Root : 'span'\n const canRemove = Boolean(onRemove) && !asChild\n const label = removeLabel ?? (typeof children === 'string' ? `Remove ${children}` : 'Remove')\n return (\n <Comp\n ref={ref}\n className={cx(styles.tag, className)}\n data-color={color}\n data-removable={canRemove || undefined}\n {...rest}\n >\n <Slot.Slottable>{children}</Slot.Slottable>\n {canRemove ? (\n <button type=\"button\" className={styles.remove} aria-label={label} onClick={onRemove}>\n <CloseIcon />\n </button>\n ) : null}\n </Comp>\n )\n})\n\nexport type TagListProps = HTMLAttributes<HTMLUListElement>\n\n/** A wrapping list of tags. Each child is wrapped in an `<li>`. */\nexport const TagList = forwardRef<HTMLUListElement, TagListProps>(function TagList(\n { className, children, ...rest },\n ref,\n) {\n return (\n <ul ref={ref} role=\"list\" className={cx(styles.list, className)} {...rest}>\n {Children.map(children, (child) =>\n isValidElement(child) || typeof child === 'string' || typeof child === 'number' ? (\n <li key={isValidElement(child) ? child.key : undefined}>{child}</li>\n ) : null,\n )}\n </ul>\n )\n})\n"],"mappings":";;;;;;;AA+BA,IAAaE,IAAMC,EAAsC,SACvD,EAAE,UAAO,aAAU,gBAAa,aAAU,IAAO,cAAW,aAAU,GAAGC,KACzE,GACA;CACA,IAAMC,IAAOC,IAAUC,EAAK,OAAO,QAC7BC,IAAY,EAAQC,KAAa,CAACH,GAClCI,IAAQC,MAAgB,OAAOC,KAAa,WAAW,UAAUA,MAAa;CACpF,OACE,kBAACP,GAAD;EACO;EACL,WAAWQ,EAAGb,EAAO,KAAKc,CAAS;EACnC,cAAYC;EACZ,kBAAgBP,KAAa,KAAA;EAC7B,GAAIJ;EALN,UAAA,CAOE,kBAACG,EAAK,WAAN,EAAiB,YAAyB,CAAA,GACzCC,IACC,kBAAC,UAAD;GAAQ,MAAK;GAAS,WAAWR,EAAO;GAAQ,cAAYU;GAAO,SAASD;GAC1E,UAAA,kBAACO,GAAD,CAAY,CAAA;EACN,CAAA,IACN,IACA;;AAEV,CAAC,GAKYC,IAAUd,EAA2C,SAChE,EAAE,cAAW,aAAU,GAAGC,KAC1B,GACA;CACA,OACE,kBAAC,MAAD;EAAS;EAAK,MAAK;EAAO,WAAWS,EAAGb,EAAO,MAAMc,CAAS;EAAG,GAAIV;EAClE,UAAAc,EAAS,IAAIN,IAAW,MACvBO,EAAeC,CAAK,KAAK,OAAOA,KAAU,YAAY,OAAOA,KAAU,WACrE,kBAAC,MAAD,EAAA,UAAyDA,EAAU,GAA1DD,EAAeC,CAAK,IAAIA,EAAM,MAAM,KAAA,CAAsB,IACjE,IACN;CACE,CAAA;AAER,CAAC"}
|
|
@@ -22,6 +22,16 @@ export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'
|
|
|
22
22
|
dismissLabel?: string;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
+
* An inline message about the thing next to it. A hairline frame, the tone in its glyph, never a
|
|
26
|
+
* coloured stripe.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* `Alert` tells the reader something about the content around it: an error loading it, a change to
|
|
30
|
+
* it, a success. It's a full hairline frame with the tone in the glyph and a short tab on the top
|
|
31
|
+
* edge, and its title in the heading face. It isn't a tinted box by default, and it never has a
|
|
32
|
+
* coloured stripe down the left edge.
|
|
33
|
+
*
|
|
34
|
+
* @privateRemarks
|
|
25
35
|
* An inline message about the thing next to it. A full hairline frame, the tone in the
|
|
26
36
|
* glyph and a short tab on the top edge, the title in the heading face — never a tinted
|
|
27
37
|
* box by default, never a coloured stripe down the left edge.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.js","names":["styles","Alert_module_default","GLYPH","InfoIcon","CircleCheckIcon","WarningIcon","ErrorIcon","Alert","forwardRef","rest","titleId","useId","glyph","icon","tone","role","title","cx","className","variant","onDismiss","children","action","dismissLabel","CloseIcon"],"sources":["../../../../src/components/feedback/Alert/Alert.tsx"],"sourcesContent":["import { forwardRef, useId, type HTMLAttributes, type ReactNode } from 'react'\nimport { CircleCheckIcon, CloseIcon, ErrorIcon, InfoIcon, WarningIcon } from '#icons'\nimport { cx } from '#utils/cx'\nimport styles from './Alert.module.css'\n\nexport type AlertTone = 'info' | 'positive' | 'caution' | 'critical' | 'neutral'\nexport type AlertVariant = 'soft' | 'outline'\n\nexport interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Meaning. `critical` and `caution` interrupt (role=\"alert\"); the rest are polite (role=\"status\"). */\n tone?: AlertTone\n /**\n * `outline` (default): a neutral hairline frame on the surface — the tone shows only in\n * the glyph and a short tab on the top edge. `soft`: the louder form, a tinted fill with a\n * tone keyline, for notices that must not be missed.\n */\n variant?: AlertVariant\n /** Short summary, read first. */\n title?: ReactNode\n /** Replaces the tone glyph. Pass `null` to drop it. */\n icon?: ReactNode\n /** A follow-up action, e.g. `<Button size=\"sm\" variant=\"outline\">Retry</Button>`. */\n action?: ReactNode\n /** Shows a dismiss button that calls this. */\n onDismiss?: () => void\n /** Accessible name of the dismiss button. Default \"Dismiss\". */\n dismissLabel?: string\n}\n\nconst GLYPH: Record<AlertTone, ReactNode> = {\n info: <InfoIcon />,\n neutral: <InfoIcon />,\n positive: <CircleCheckIcon />,\n caution: <WarningIcon />,\n critical: <ErrorIcon />,\n}\n\n/**\n * An inline message about the thing next to it. A full hairline frame, the tone in the\n * glyph and a short tab on the top edge, the title in the heading face — never a tinted\n * box by default, never a coloured stripe down the left edge.\n *\n * <Alert tone=\"critical\" title=\"Couldn't load invoices\" action={<Button …>Retry</Button>}>\n * The billing service didn't answer. We'll try again in 30 seconds.\n * </Alert>\n */\nexport const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(\n {\n tone = 'info',\n variant = 'outline',\n title,\n icon,\n action,\n onDismiss,\n dismissLabel = 'Dismiss',\n role,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const titleId = useId()\n const glyph = icon === undefined ? GLYPH[tone] : icon\n return (\n <div\n ref={ref}\n role={role ?? (tone === 'critical' || tone === 'caution' ? 'alert' : 'status')}\n aria-labelledby={title ? titleId : undefined}\n className={cx(styles.alert, className)}\n data-kiln-component=\"\"\n data-tone={tone}\n data-variant={variant}\n data-dismissible={onDismiss ? '' : undefined}\n {...rest}\n >\n {glyph ? (\n <span className={styles.glyph} aria-hidden=\"true\">\n {glyph}\n </span>\n ) : null}\n <div className={styles.body}>\n {title ? (\n <p id={titleId} className={styles.title}>\n {title}\n </p>\n ) : null}\n {children ? <div className={styles.description}>{children}</div> : null}\n {action ? <div className={styles.action}>{action}</div> : null}\n </div>\n {onDismiss ? (\n <button\n type=\"button\"\n className={styles.dismiss}\n aria-label={dismissLabel}\n onClick={onDismiss}\n >\n <CloseIcon />\n </button>\n ) : null}\n </div>\n )\n})\n"],"mappings":";;;;;;AA6BA,IAAME,IAAsC;CAC1C,MAAM,kBAACC,GAAD,CAAW,CAAA;CACjB,SAAS,kBAACA,GAAD,CAAW,CAAA;CACpB,UAAU,kBAACC,GAAD,CAAkB,CAAA;CAC5B,SAAS,kBAACC,GAAD,CAAc,CAAA;CACvB,UAAU,kBAACC,GAAD,CAAY,CAAA;AACxB,
|
|
1
|
+
{"version":3,"file":"Alert.js","names":["styles","Alert_module_default","GLYPH","InfoIcon","CircleCheckIcon","WarningIcon","ErrorIcon","Alert","forwardRef","rest","titleId","useId","glyph","icon","tone","role","title","cx","className","variant","onDismiss","children","action","dismissLabel","CloseIcon"],"sources":["../../../../src/components/feedback/Alert/Alert.tsx"],"sourcesContent":["import { forwardRef, useId, type HTMLAttributes, type ReactNode } from 'react'\nimport { CircleCheckIcon, CloseIcon, ErrorIcon, InfoIcon, WarningIcon } from '#icons'\nimport { cx } from '#utils/cx'\nimport styles from './Alert.module.css'\n\nexport type AlertTone = 'info' | 'positive' | 'caution' | 'critical' | 'neutral'\nexport type AlertVariant = 'soft' | 'outline'\n\nexport interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Meaning. `critical` and `caution` interrupt (role=\"alert\"); the rest are polite (role=\"status\"). */\n tone?: AlertTone\n /**\n * `outline` (default): a neutral hairline frame on the surface — the tone shows only in\n * the glyph and a short tab on the top edge. `soft`: the louder form, a tinted fill with a\n * tone keyline, for notices that must not be missed.\n */\n variant?: AlertVariant\n /** Short summary, read first. */\n title?: ReactNode\n /** Replaces the tone glyph. Pass `null` to drop it. */\n icon?: ReactNode\n /** A follow-up action, e.g. `<Button size=\"sm\" variant=\"outline\">Retry</Button>`. */\n action?: ReactNode\n /** Shows a dismiss button that calls this. */\n onDismiss?: () => void\n /** Accessible name of the dismiss button. Default \"Dismiss\". */\n dismissLabel?: string\n}\n\nconst GLYPH: Record<AlertTone, ReactNode> = {\n info: <InfoIcon />,\n neutral: <InfoIcon />,\n positive: <CircleCheckIcon />,\n caution: <WarningIcon />,\n critical: <ErrorIcon />,\n}\n\n/**\n * An inline message about the thing next to it. A hairline frame, the tone in its glyph, never a\n * coloured stripe.\n *\n * @remarks\n * `Alert` tells the reader something about the content around it: an error loading it, a change to\n * it, a success. It's a full hairline frame with the tone in the glyph and a short tab on the top\n * edge, and its title in the heading face. It isn't a tinted box by default, and it never has a\n * coloured stripe down the left edge.\n *\n * @privateRemarks\n * An inline message about the thing next to it. A full hairline frame, the tone in the\n * glyph and a short tab on the top edge, the title in the heading face — never a tinted\n * box by default, never a coloured stripe down the left edge.\n *\n * <Alert tone=\"critical\" title=\"Couldn't load invoices\" action={<Button …>Retry</Button>}>\n * The billing service didn't answer. We'll try again in 30 seconds.\n * </Alert>\n */\nexport const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(\n {\n tone = 'info',\n variant = 'outline',\n title,\n icon,\n action,\n onDismiss,\n dismissLabel = 'Dismiss',\n role,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const titleId = useId()\n const glyph = icon === undefined ? GLYPH[tone] : icon\n return (\n <div\n ref={ref}\n role={role ?? (tone === 'critical' || tone === 'caution' ? 'alert' : 'status')}\n aria-labelledby={title ? titleId : undefined}\n className={cx(styles.alert, className)}\n data-kiln-component=\"\"\n data-tone={tone}\n data-variant={variant}\n data-dismissible={onDismiss ? '' : undefined}\n {...rest}\n >\n {glyph ? (\n <span className={styles.glyph} aria-hidden=\"true\">\n {glyph}\n </span>\n ) : null}\n <div className={styles.body}>\n {title ? (\n <p id={titleId} className={styles.title}>\n {title}\n </p>\n ) : null}\n {children ? <div className={styles.description}>{children}</div> : null}\n {action ? <div className={styles.action}>{action}</div> : null}\n </div>\n {onDismiss ? (\n <button\n type=\"button\"\n className={styles.dismiss}\n aria-label={dismissLabel}\n onClick={onDismiss}\n >\n <CloseIcon />\n </button>\n ) : null}\n </div>\n )\n})\n"],"mappings":";;;;;;AA6BA,IAAME,IAAsC;CAC1C,MAAM,kBAACC,GAAD,CAAW,CAAA;CACjB,SAAS,kBAACA,GAAD,CAAW,CAAA;CACpB,UAAU,kBAACC,GAAD,CAAkB,CAAA;CAC5B,SAAS,kBAACC,GAAD,CAAc,CAAA;CACvB,UAAU,kBAACC,GAAD,CAAY,CAAA;AACxB,GAqBaC,IAAQC,EAAuC,SAC1D,EACE,UAAO,QACP,aAAU,WACV,UACA,SACA,WACA,cACA,kBAAe,WACf,SACA,cACA,aACA,GAAGC,KAEL,GACA;CACA,IAAMC,IAAUC,EAAM,GAChBC,IAAQC,MAAS,KAAA,IAAYX,EAAMY,KAAQD;CACjD,OACE,kBAAC,OAAD;EACO;EACL,MAAME,MAASD,MAAS,cAAcA,MAAS,YAAY,UAAU;EACrE,mBAAiBE,IAAQN,IAAU,KAAA;EACnC,WAAWO,EAAGjB,EAAO,OAAOkB,CAAS;EACrC,uBAAoB;EACpB,aAAWJ;EACX,gBAAcK;EACd,oBAAkBC,IAAY,KAAK,KAAA;EACnC,GAAIX;EATN,UAAA;GAWGG,IACC,kBAAC,QAAD;IAAM,WAAWZ,EAAO;IAAO,eAAY;IACxC,UAAAY;GACG,CAAA,IACJ;GACJ,kBAAC,OAAD;IAAK,WAAWZ,EAAO;IAAvB,UAAA;KACGgB,IACC,kBAAC,KAAD;MAAG,IAAIN;MAAS,WAAWV,EAAO;MAC/B,UAAAgB;KACA,CAAA,IACD;KACHK,IAAW,kBAAC,OAAD;MAAK,WAAWrB,EAAO;MAAc;KAAc,CAAA,IAAI;KAClEsB,IAAS,kBAAC,OAAD;MAAK,WAAWtB,EAAO;MAAS,UAAAsB;KAAY,CAAA,IAAI;IACvD;;GACJF,IACC,kBAAC,UAAD;IACE,MAAK;IACL,WAAWpB,EAAO;IAClB,cAAYuB;IACZ,SAASH;IAET,UAAA,kBAACI,GAAD,CAAY,CAAA;GACN,CAAA,IACN;EACD;;AAET,CAAC"}
|
|
@@ -13,6 +13,13 @@ export interface LiveIndicatorProps extends HTMLAttributes<HTMLSpanElement> {
|
|
|
13
13
|
size?: Exclude<Size, 'lg'>;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
+
* This is happening now. A dot with a slow ring and a short label.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* `LiveIndicator` marks something in progress: live tracking, a sync, a recording. The ring pulses
|
|
20
|
+
* slowly and stops under reduced motion.
|
|
21
|
+
*
|
|
22
|
+
* @privateRemarks
|
|
16
23
|
* "This is happening now." A dot with a slow ring and a short label — for a live call,
|
|
17
24
|
* an in-progress sync, a recording.
|
|
18
25
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveIndicator.js","names":["styles","LiveIndicator_module_default","LiveIndicator","forwardRef","rest","cx","className","tone","variant","size","pulse","label"],"sources":["../../../../src/components/feedback/LiveIndicator/LiveIndicator.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport type { Size, Tone } from '#utils/tokens'\nimport styles from './LiveIndicator.module.css'\n\nexport type LiveIndicatorVariant = 'inline' | 'pill'\n\nexport interface LiveIndicatorProps extends HTMLAttributes<HTMLSpanElement> {\n /** What's live, or how far in: \"Live\", \"72'\", \"Recording\". Default \"Live\". */\n label?: ReactNode\n /** Colour of the dot (and the label in `inline`). Default `accent` — live *is* the accent's job. */\n tone?: Tone\n /** A ring that swells out of the dot. Stills under reduced motion. Default `true`. */\n pulse?: boolean\n /** `inline` sits in running text; `pill` is a keylined chip for cards and list rows. */\n variant?: LiveIndicatorVariant\n size?: Exclude<Size, 'lg'>\n}\n\n/**\n * \"This is happening now.\" A dot with a slow ring and a short label — for a live call,\n * an in-progress sync, a recording.\n *\n * Deliberately not a live region: a minute counter that announces itself every minute is\n * noise. If the change matters, announce it where it happens.\n */\nexport const LiveIndicator = forwardRef<HTMLSpanElement, LiveIndicatorProps>(function LiveIndicator(\n {\n label = 'Live',\n tone = 'accent',\n pulse = true,\n variant = 'inline',\n size = 'md',\n className,\n ...rest\n },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cx(styles.live, className)}\n data-tone={tone}\n data-variant={variant}\n data-size={size}\n data-pulse={pulse || undefined}\n {...rest}\n >\n <span className={styles.dot} aria-hidden=\"true\" />\n <span className={styles.label}>{label}</span>\n </span>\n )\n})\n"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"LiveIndicator.js","names":["styles","LiveIndicator_module_default","LiveIndicator","forwardRef","rest","cx","className","tone","variant","size","pulse","label"],"sources":["../../../../src/components/feedback/LiveIndicator/LiveIndicator.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport type { Size, Tone } from '#utils/tokens'\nimport styles from './LiveIndicator.module.css'\n\nexport type LiveIndicatorVariant = 'inline' | 'pill'\n\nexport interface LiveIndicatorProps extends HTMLAttributes<HTMLSpanElement> {\n /** What's live, or how far in: \"Live\", \"72'\", \"Recording\". Default \"Live\". */\n label?: ReactNode\n /** Colour of the dot (and the label in `inline`). Default `accent` — live *is* the accent's job. */\n tone?: Tone\n /** A ring that swells out of the dot. Stills under reduced motion. Default `true`. */\n pulse?: boolean\n /** `inline` sits in running text; `pill` is a keylined chip for cards and list rows. */\n variant?: LiveIndicatorVariant\n size?: Exclude<Size, 'lg'>\n}\n\n/**\n * This is happening now. A dot with a slow ring and a short label.\n *\n * @remarks\n * `LiveIndicator` marks something in progress: live tracking, a sync, a recording. The ring pulses\n * slowly and stops under reduced motion.\n *\n * @privateRemarks\n * \"This is happening now.\" A dot with a slow ring and a short label — for a live call,\n * an in-progress sync, a recording.\n *\n * Deliberately not a live region: a minute counter that announces itself every minute is\n * noise. If the change matters, announce it where it happens.\n */\nexport const LiveIndicator = forwardRef<HTMLSpanElement, LiveIndicatorProps>(function LiveIndicator(\n {\n label = 'Live',\n tone = 'accent',\n pulse = true,\n variant = 'inline',\n size = 'md',\n className,\n ...rest\n },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cx(styles.live, className)}\n data-tone={tone}\n data-variant={variant}\n data-size={size}\n data-pulse={pulse || undefined}\n {...rest}\n >\n <span className={styles.dot} aria-hidden=\"true\" />\n <span className={styles.label}>{label}</span>\n </span>\n )\n})\n"],"mappings":";;;;;AAiCA,IAAaE,IAAgBC,EAAgD,SAC3E,EACE,WAAQ,QACR,UAAO,UACP,WAAQ,IACR,aAAU,UACV,UAAO,MACP,cACA,GAAGC,KAEL,GACA;CACA,OACE,kBAAC,QAAD;EACO;EACL,WAAWC,EAAGL,EAAO,MAAMM,CAAS;EACpC,aAAWC;EACX,gBAAcC;EACd,aAAWC;EACX,cAAYC,KAAS,KAAA;EACrB,GAAIN;EAPN,UAAA,CASE,kBAAC,QAAD;GAAM,WAAWJ,EAAO;GAAK,eAAY;EAAQ,CAAA,GACjD,kBAAC,QAAD;GAAM,WAAWA,EAAO;GAAQ,UAAAW;EAAY,CAAA,CACxC;;AAEV,CAAC"}
|
|
@@ -13,6 +13,14 @@ export interface RelativeTimeProps extends Omit<TimeHTMLAttributes<HTMLTimeEleme
|
|
|
13
13
|
prefix?: ReactNode;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
+
* How long ago, or how long until. A time element that keeps itself current and renders safely on
|
|
17
|
+
* the server.
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* `RelativeTime` renders a `<time>` that reads "3 mins ago" or "in 2 hours", and updates itself
|
|
21
|
+
* (every 30 seconds by default). The full date is always in its `title`.
|
|
22
|
+
*
|
|
23
|
+
* @privateRemarks
|
|
16
24
|
* A `<time>` that says how long ago (or until) something happened, and keeps itself current.
|
|
17
25
|
*
|
|
18
26
|
* SSR: the server and the first client render show the absolute date (no clock is read
|