@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
|
@@ -3,6 +3,12 @@ import { type TagsInputProps } from '../TagsInput/index.js';
|
|
|
3
3
|
export interface TagsFieldProps extends FieldLabelProps, Omit<TagsInputProps, 'invalid' | keyof FieldLabelProps> {
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
+
* A labelled tags input with description, warning and error.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* `TagsField` is a {@link Field | Field} around a {@link TagsInput | TagsInput}.
|
|
10
|
+
*
|
|
11
|
+
* @privateRemarks
|
|
6
12
|
* Label + TagsInput + description + warning + error. TagsInput props (`value`,
|
|
7
13
|
* `delimiters`, `maxTags`…) and the ref go to the text input; `className`/`style` go to
|
|
8
14
|
* the field wrapper. `onBlur` fires once focus leaves the whole control.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagsField.js","names":["TagsField","forwardRef","fieldProps","rest","splitFieldLabelProps","props","inputProps","Field","id","TagsInput"],"sources":["../../../../src/components/inputs/TagsField/TagsField.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport { Field, type FieldLabelProps } from '#components/inputs/Field'\nimport { TagsInput, type TagsInputProps } from '#components/inputs/TagsInput'\nimport { splitFieldLabelProps } from '#components/inputs/internal/fieldProps'\n\nexport interface TagsFieldProps\n extends FieldLabelProps, Omit<TagsInputProps, 'invalid' | keyof FieldLabelProps> {}\n\n/**\n * Label + TagsInput + description + warning + error. TagsInput props (`value`,\n * `delimiters`, `maxTags`…) and the ref go to the text input; `className`/`style` go to\n * the field wrapper. `onBlur` fires once focus leaves the whole control.\n *\n * <TagsField label=\"Labels\" description=\"Press Enter or comma to add\" maxTags={8} />\n */\nexport const TagsField = forwardRef<HTMLInputElement, TagsFieldProps>(\n function TagsField(props, ref) {\n const [fieldProps, rest] = splitFieldLabelProps(props)\n const { id, disabled, className, style, ...inputProps } = rest\n return (\n <Field {...fieldProps} htmlFor={id} disabled={disabled} className={className} style={style}>\n <TagsInput ref={ref} id={id} disabled={disabled} {...inputProps} />\n </Field>\n )\n },\n)\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"TagsField.js","names":["TagsField","forwardRef","fieldProps","rest","splitFieldLabelProps","props","inputProps","Field","id","TagsInput"],"sources":["../../../../src/components/inputs/TagsField/TagsField.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport { Field, type FieldLabelProps } from '#components/inputs/Field'\nimport { TagsInput, type TagsInputProps } from '#components/inputs/TagsInput'\nimport { splitFieldLabelProps } from '#components/inputs/internal/fieldProps'\n\nexport interface TagsFieldProps\n extends FieldLabelProps, Omit<TagsInputProps, 'invalid' | keyof FieldLabelProps> {}\n\n/**\n * A labelled tags input with description, warning and error.\n *\n * @remarks\n * `TagsField` is a {@link Field | Field} around a {@link TagsInput | TagsInput}.\n *\n * @privateRemarks\n * Label + TagsInput + description + warning + error. TagsInput props (`value`,\n * `delimiters`, `maxTags`…) and the ref go to the text input; `className`/`style` go to\n * the field wrapper. `onBlur` fires once focus leaves the whole control.\n *\n * <TagsField label=\"Labels\" description=\"Press Enter or comma to add\" maxTags={8} />\n */\nexport const TagsField = forwardRef<HTMLInputElement, TagsFieldProps>(\n function TagsField(props, ref) {\n const [fieldProps, rest] = splitFieldLabelProps(props)\n const { id, disabled, className, style, ...inputProps } = rest\n return (\n <Field {...fieldProps} htmlFor={id} disabled={disabled} className={className} style={style}>\n <TagsInput ref={ref} id={id} disabled={disabled} {...inputProps} />\n </Field>\n )\n },\n)\n"],"mappings":";;;;;;AAqBA,IAAaA,IAAYC,EACvB,SAAmB,GAAO,GAAK;CAC7B,IAAM,CAACC,GAAYC,KAAQC,EAAqBC,CAAK,GAC/C,EAAE,OAAI,aAAU,cAAW,UAAO,GAAGC,MAAeH;CAC1D,OACE,kBAACI,GAAD;EAAO,GAAIL;EAAY,SAASM;EAAc;EAAqB;EAAkB;EACnF,UAAA,kBAACC,GAAD;GAAgB;GAAS;GAAc;GAAU,GAAIH;EAAa,CAAA;CAC7D,CAAA;AAEX,CACF"}
|
|
@@ -25,6 +25,14 @@ export interface TagsInputProps extends Omit<InputProps, 'value' | 'defaultValue
|
|
|
25
25
|
onBlur?: FocusEventHandler<HTMLElement>;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
+
* Free-form tags in one box. Enter or a comma adds one, Backspace removes the last.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* `TagsInput` collects short free-form values: type and press Enter (or a comma) to add one, press
|
|
32
|
+
* Backspace in an empty box to remove the last, or use each chip's remove button. Pasting splits
|
|
33
|
+
* on the delimiters. With `name`, each tag submits as its own hidden input.
|
|
34
|
+
*
|
|
35
|
+
* @privateRemarks
|
|
28
36
|
* Free-form tags in one box: type and press Enter (or a comma) to add, Backspace on an
|
|
29
37
|
* empty box removes the last, each chip has a remove button. Paste splits on the
|
|
30
38
|
* delimiters. The ref and native input props go to the text input; `className`/`style`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagsInput.js","names":["control","styles","control_module_default","TagsInput_module_default","DEFAULT_DELIMITERS","KEY_DELIMITERS","defaultRemoveLabel","tag","escapeRegExp","text","splitter","parts","delimiters","d","TagsInput","markFieldAware","forwardRef","valueProp","invalidProp","idProp","disabledProp","requiredProp","readOnlyProp","describedByProp","ariaInvalid","rest","field","useResolvedField","interactive","inputRef","useRef","rootRef","composedRef","useMergedRefs","ref","tags","setTags","useControllableState","defaultValue","onValueChange","next","draft","setDraft","useState","split","clean","normalise","commit","raw","candidates","onReject","allowDuplicates","maxTags","commitDraft","removeAt","i","index","handleChange","event","last","handleKeyDown","onKeyDown","handlePaste","onPaste","input","start","end","combined","cx","className","size","target","onBlur","leading","TagList","Tag","removeLabel","htmlSize","placeholder","inputMode","numeric","trailing","name"],"sources":["../../../../src/components/inputs/TagsInput/TagsInput.tsx"],"sourcesContent":["import {\n forwardRef,\n useRef,\n useState,\n type ChangeEvent,\n type ClipboardEvent,\n type FocusEvent,\n type FocusEventHandler,\n type KeyboardEvent,\n type PointerEvent,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { Tag, TagList } from '#components/display/Tag'\nimport type { InputProps } from '#components/inputs/Input'\nimport { useControllableState } from '#components/inputs/Combobox/useControllableState'\nimport { markFieldAware, useResolvedField } from '#components/inputs/internal/FieldContext'\nimport { useMergedRefs } from '#components/inputs/internal/refs'\nimport control from '#components/inputs/internal/control.module.css'\nimport styles from './TagsInput.module.css'\n\nexport type TagsRejectReason = 'duplicate' | 'max' | 'empty'\n\nexport interface TagsInputProps extends Omit<\n InputProps,\n 'value' | 'defaultValue' | 'onChange' | 'type' | 'onBlur'\n> {\n value?: readonly string[]\n defaultValue?: readonly string[]\n onValueChange?: (tags: string[]) => void\n /**\n * What ends a tag: single characters (`','`, `';'`, `' '`) and/or the keys `'Enter'` and\n * `'Tab'`. Character delimiters also split pasted text; `'Enter'` makes pasted line\n * breaks split too. Default `[',', 'Enter']`.\n */\n delimiters?: readonly string[]\n /** Further tags are rejected (`onReject(tag, 'max')`) once there are this many. */\n maxTags?: number\n /** Default `false`: a tag already in the list is rejected (`'duplicate'`). */\n allowDuplicates?: boolean\n /** `trim` (default) trims whitespace; `lowercase` trims and lower-cases; `none` keeps the text as typed. */\n normalise?: 'none' | 'trim' | 'lowercase'\n /** A typed tag wasn't added. */\n onReject?: (tag: string, reason: TagsRejectReason) => void\n /** Accessible name of a chip's remove button. Default `Remove <tag>`. */\n removeLabel?: (tag: string) => string\n /** Fires once focus leaves the whole control (input and chip buttons). */\n onBlur?: FocusEventHandler<HTMLElement>\n}\n\nconst DEFAULT_DELIMITERS: readonly string[] = [',', 'Enter']\nconst KEY_DELIMITERS = new Set(['Enter', 'Tab'])\nconst defaultRemoveLabel = (tag: string) => `Remove ${tag}`\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/** Splits text on the character delimiters (and line breaks when Enter is a delimiter). */\nfunction splitter(delimiters: readonly string[]): RegExp | null {\n const parts = delimiters.filter((d) => !KEY_DELIMITERS.has(d)).map(escapeRegExp)\n if (delimiters.includes('Enter')) parts.push('\\\\r?\\\\n')\n return parts.length > 0 ? new RegExp(parts.join('|')) : null\n}\n\n/**\n * Free-form tags in one box: type and press Enter (or a comma) to add, Backspace on an\n * empty box removes the last, each chip has a remove button. Paste splits on the\n * delimiters. The ref and native input props go to the text input; `className`/`style`\n * go to the box. With `name`, each tag is submitted as its own hidden input.\n *\n * <TagsInput placeholder=\"Add a label\" defaultValue={['Design']} maxTags={8} />\n */\nexport const TagsInput = markFieldAware(\n forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n delimiters = DEFAULT_DELIMITERS,\n maxTags,\n allowDuplicates = false,\n normalise = 'trim',\n onReject,\n removeLabel = defaultRemoveLabel,\n size = 'md',\n leading,\n trailing,\n invalid: invalidProp,\n numeric = false,\n htmlSize,\n id: idProp,\n disabled: disabledProp,\n required: requiredProp,\n readOnly: readOnlyProp,\n inputMode,\n name,\n form,\n placeholder,\n className,\n style,\n onKeyDown,\n onPaste,\n onBlur,\n 'aria-describedby': describedByProp,\n 'aria-invalid': ariaInvalid,\n ...rest\n },\n ref,\n ) {\n const field = useResolvedField({\n id: idProp,\n describedBy: describedByProp,\n invalid: invalidProp,\n ariaInvalid,\n required: requiredProp,\n disabled: disabledProp,\n readOnly: readOnlyProp,\n })\n const interactive = !field.disabled && !field.readOnly\n const inputRef = useRef<HTMLInputElement>(null)\n const rootRef = useRef<HTMLDivElement>(null)\n const composedRef = useMergedRefs(ref, inputRef)\n\n const [tags, setTags] = useControllableState<readonly string[]>(\n valueProp,\n defaultValue ?? [],\n (next) => onValueChange?.([...next]),\n )\n const [draft, setDraft] = useState('')\n const split = splitter(delimiters)\n\n const clean = (text: string) =>\n normalise === 'none'\n ? text\n : normalise === 'lowercase'\n ? text.trim().toLowerCase()\n : text.trim()\n\n /** Adds every candidate it can, reporting the rest. Returns whether anything was added. */\n const commit = (candidates: readonly string[]): boolean => {\n if (!interactive) return false\n const next = [...tags]\n for (const raw of candidates) {\n const tag = clean(raw)\n if (tag.trim() === '') {\n if (raw !== '') onReject?.(raw, 'empty')\n continue\n }\n if (!allowDuplicates && next.includes(tag)) {\n onReject?.(tag, 'duplicate')\n continue\n }\n if (maxTags !== undefined && next.length >= maxTags) {\n onReject?.(tag, 'max')\n continue\n }\n next.push(tag)\n }\n if (next.length === tags.length) return false\n setTags(next)\n return true\n }\n\n const commitDraft = () => {\n if (draft === '') return\n commit([draft])\n setDraft('')\n }\n\n const removeAt = (index: number) => {\n if (!interactive) return\n setTags(tags.filter((_, i) => i !== index))\n }\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n const text = event.target.value\n // Typed (or IME/mobile-inserted) delimiter characters end a tag.\n if (split?.test(text)) {\n const parts = text.split(split)\n const last = parts.pop() ?? ''\n commit(parts)\n setDraft(last)\n return\n }\n setDraft(text)\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event)\n if (event.defaultPrevented || event.nativeEvent.isComposing) return\n const isDelimiter =\n delimiters.includes(event.key) && (KEY_DELIMITERS.has(event.key) || event.key.length === 1)\n if (isDelimiter && interactive) {\n if (event.key === 'Tab') {\n // Tab still moves focus; only a non-empty draft becomes a tag.\n commitDraft()\n return\n }\n if (draft === '' && event.key === 'Enter') return // let Enter submit the form\n event.preventDefault()\n commitDraft()\n return\n }\n if (event.key === 'Backspace' && draft === '' && interactive && tags.length > 0) {\n event.preventDefault()\n removeAt(tags.length - 1)\n }\n }\n\n const handlePaste = (event: ClipboardEvent<HTMLInputElement>) => {\n onPaste?.(event)\n if (event.defaultPrevented || !interactive || !split) return\n const text = event.clipboardData.getData('text')\n if (!split.test(text)) return\n event.preventDefault()\n const input = event.currentTarget\n const start = input.selectionStart ?? draft.length\n const end = input.selectionEnd ?? draft.length\n const combined = draft.slice(0, start) + text + draft.slice(end)\n commit(combined.split(split))\n setDraft('')\n }\n\n /** Focus left the whole control: a half-typed tag is kept, not lost. */\n const handleBlur = (event: FocusEvent<HTMLDivElement>) => {\n const next = event.relatedTarget as Node | null\n if (next && rootRef.current?.contains(next)) return\n commitDraft()\n onBlur?.(event)\n }\n\n // Clicking the box's padding focuses the input, like one native control.\n const handlePointerDown = (event: PointerEvent<HTMLDivElement>) => {\n const target = event.target as HTMLElement\n if (target === inputRef.current || target.closest('button, a, input')) return\n event.preventDefault()\n inputRef.current?.focus()\n }\n\n return (\n <div\n ref={rootRef}\n className={cx(control.box, styles.root, className)}\n style={style}\n data-size={size}\n data-invalid={field.invalid || undefined}\n data-disabled={field.disabled || undefined}\n data-readonly={field.readOnly || undefined}\n onPointerDown={handlePointerDown}\n onBlur={handleBlur}\n >\n {leading != null ? (\n <span className={styles.adornment} data-slot=\"leading\">\n {leading}\n </span>\n ) : null}\n <div className={styles.values}>\n {tags.length > 0 ? (\n <TagList className={styles.chips}>\n {tags.map((tag, index) => (\n <Tag\n // Duplicates are allowed when asked for, so the index is part of the key.\n key={`${String(index)}:${tag}`}\n className={styles.chip}\n onRemove={\n interactive\n ? () => {\n removeAt(index)\n inputRef.current?.focus()\n }\n : undefined\n }\n removeLabel={removeLabel(tag)}\n >\n {tag}\n </Tag>\n ))}\n </TagList>\n ) : null}\n <input\n ref={composedRef}\n id={field.id}\n type=\"text\"\n className={styles.input}\n value={draft}\n size={htmlSize}\n placeholder={tags.length === 0 ? placeholder : undefined}\n disabled={field.disabled}\n readOnly={field.readOnly}\n inputMode={inputMode ?? (numeric ? 'decimal' : undefined)}\n enterKeyHint=\"enter\"\n aria-describedby={field.describedBy}\n aria-invalid={field.invalid || undefined}\n aria-required={field.required || undefined}\n aria-busy={field.busy || undefined}\n {...rest}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n />\n </div>\n {trailing != null ? (\n <span className={styles.adornment} data-slot=\"trailing\">\n {trailing}\n </span>\n ) : null}\n {name !== undefined\n ? tags.map((tag, index) => (\n <input\n key={`${String(index)}:${tag}`}\n type=\"hidden\"\n name={name}\n value={tag}\n form={form}\n disabled={field.disabled || undefined}\n />\n ))\n : null}\n </div>\n )\n }),\n)\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TagsInput.js","names":["control","styles","control_module_default","TagsInput_module_default","DEFAULT_DELIMITERS","KEY_DELIMITERS","defaultRemoveLabel","tag","escapeRegExp","text","splitter","parts","delimiters","d","TagsInput","markFieldAware","forwardRef","valueProp","invalidProp","idProp","disabledProp","requiredProp","readOnlyProp","describedByProp","ariaInvalid","rest","field","useResolvedField","interactive","inputRef","useRef","rootRef","composedRef","useMergedRefs","ref","tags","setTags","useControllableState","defaultValue","onValueChange","next","draft","setDraft","useState","split","clean","normalise","commit","raw","candidates","onReject","allowDuplicates","maxTags","commitDraft","removeAt","i","index","handleChange","event","last","handleKeyDown","onKeyDown","handlePaste","onPaste","input","start","end","combined","cx","className","size","target","onBlur","leading","TagList","Tag","removeLabel","htmlSize","placeholder","inputMode","numeric","trailing","name"],"sources":["../../../../src/components/inputs/TagsInput/TagsInput.tsx"],"sourcesContent":["import {\n forwardRef,\n useRef,\n useState,\n type ChangeEvent,\n type ClipboardEvent,\n type FocusEvent,\n type FocusEventHandler,\n type KeyboardEvent,\n type PointerEvent,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { Tag, TagList } from '#components/display/Tag'\nimport type { InputProps } from '#components/inputs/Input'\nimport { useControllableState } from '#components/inputs/Combobox/useControllableState'\nimport { markFieldAware, useResolvedField } from '#components/inputs/internal/FieldContext'\nimport { useMergedRefs } from '#components/inputs/internal/refs'\nimport control from '#components/inputs/internal/control.module.css'\nimport styles from './TagsInput.module.css'\n\nexport type TagsRejectReason = 'duplicate' | 'max' | 'empty'\n\nexport interface TagsInputProps extends Omit<\n InputProps,\n 'value' | 'defaultValue' | 'onChange' | 'type' | 'onBlur'\n> {\n value?: readonly string[]\n defaultValue?: readonly string[]\n onValueChange?: (tags: string[]) => void\n /**\n * What ends a tag: single characters (`','`, `';'`, `' '`) and/or the keys `'Enter'` and\n * `'Tab'`. Character delimiters also split pasted text; `'Enter'` makes pasted line\n * breaks split too. Default `[',', 'Enter']`.\n */\n delimiters?: readonly string[]\n /** Further tags are rejected (`onReject(tag, 'max')`) once there are this many. */\n maxTags?: number\n /** Default `false`: a tag already in the list is rejected (`'duplicate'`). */\n allowDuplicates?: boolean\n /** `trim` (default) trims whitespace; `lowercase` trims and lower-cases; `none` keeps the text as typed. */\n normalise?: 'none' | 'trim' | 'lowercase'\n /** A typed tag wasn't added. */\n onReject?: (tag: string, reason: TagsRejectReason) => void\n /** Accessible name of a chip's remove button. Default `Remove <tag>`. */\n removeLabel?: (tag: string) => string\n /** Fires once focus leaves the whole control (input and chip buttons). */\n onBlur?: FocusEventHandler<HTMLElement>\n}\n\nconst DEFAULT_DELIMITERS: readonly string[] = [',', 'Enter']\nconst KEY_DELIMITERS = new Set(['Enter', 'Tab'])\nconst defaultRemoveLabel = (tag: string) => `Remove ${tag}`\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/** Splits text on the character delimiters (and line breaks when Enter is a delimiter). */\nfunction splitter(delimiters: readonly string[]): RegExp | null {\n const parts = delimiters.filter((d) => !KEY_DELIMITERS.has(d)).map(escapeRegExp)\n if (delimiters.includes('Enter')) parts.push('\\\\r?\\\\n')\n return parts.length > 0 ? new RegExp(parts.join('|')) : null\n}\n\n/**\n * Free-form tags in one box. Enter or a comma adds one, Backspace removes the last.\n *\n * @remarks\n * `TagsInput` collects short free-form values: type and press Enter (or a comma) to add one, press\n * Backspace in an empty box to remove the last, or use each chip's remove button. Pasting splits\n * on the delimiters. With `name`, each tag submits as its own hidden input.\n *\n * @privateRemarks\n * Free-form tags in one box: type and press Enter (or a comma) to add, Backspace on an\n * empty box removes the last, each chip has a remove button. Paste splits on the\n * delimiters. The ref and native input props go to the text input; `className`/`style`\n * go to the box. With `name`, each tag is submitted as its own hidden input.\n *\n * <TagsInput placeholder=\"Add a label\" defaultValue={['Design']} maxTags={8} />\n */\nexport const TagsInput = markFieldAware(\n forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n delimiters = DEFAULT_DELIMITERS,\n maxTags,\n allowDuplicates = false,\n normalise = 'trim',\n onReject,\n removeLabel = defaultRemoveLabel,\n size = 'md',\n leading,\n trailing,\n invalid: invalidProp,\n numeric = false,\n htmlSize,\n id: idProp,\n disabled: disabledProp,\n required: requiredProp,\n readOnly: readOnlyProp,\n inputMode,\n name,\n form,\n placeholder,\n className,\n style,\n onKeyDown,\n onPaste,\n onBlur,\n 'aria-describedby': describedByProp,\n 'aria-invalid': ariaInvalid,\n ...rest\n },\n ref,\n ) {\n const field = useResolvedField({\n id: idProp,\n describedBy: describedByProp,\n invalid: invalidProp,\n ariaInvalid,\n required: requiredProp,\n disabled: disabledProp,\n readOnly: readOnlyProp,\n })\n const interactive = !field.disabled && !field.readOnly\n const inputRef = useRef<HTMLInputElement>(null)\n const rootRef = useRef<HTMLDivElement>(null)\n const composedRef = useMergedRefs(ref, inputRef)\n\n const [tags, setTags] = useControllableState<readonly string[]>(\n valueProp,\n defaultValue ?? [],\n (next) => onValueChange?.([...next]),\n )\n const [draft, setDraft] = useState('')\n const split = splitter(delimiters)\n\n const clean = (text: string) =>\n normalise === 'none'\n ? text\n : normalise === 'lowercase'\n ? text.trim().toLowerCase()\n : text.trim()\n\n /** Adds every candidate it can, reporting the rest. Returns whether anything was added. */\n const commit = (candidates: readonly string[]): boolean => {\n if (!interactive) return false\n const next = [...tags]\n for (const raw of candidates) {\n const tag = clean(raw)\n if (tag.trim() === '') {\n if (raw !== '') onReject?.(raw, 'empty')\n continue\n }\n if (!allowDuplicates && next.includes(tag)) {\n onReject?.(tag, 'duplicate')\n continue\n }\n if (maxTags !== undefined && next.length >= maxTags) {\n onReject?.(tag, 'max')\n continue\n }\n next.push(tag)\n }\n if (next.length === tags.length) return false\n setTags(next)\n return true\n }\n\n const commitDraft = () => {\n if (draft === '') return\n commit([draft])\n setDraft('')\n }\n\n const removeAt = (index: number) => {\n if (!interactive) return\n setTags(tags.filter((_, i) => i !== index))\n }\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n const text = event.target.value\n // Typed (or IME/mobile-inserted) delimiter characters end a tag.\n if (split?.test(text)) {\n const parts = text.split(split)\n const last = parts.pop() ?? ''\n commit(parts)\n setDraft(last)\n return\n }\n setDraft(text)\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event)\n if (event.defaultPrevented || event.nativeEvent.isComposing) return\n const isDelimiter =\n delimiters.includes(event.key) && (KEY_DELIMITERS.has(event.key) || event.key.length === 1)\n if (isDelimiter && interactive) {\n if (event.key === 'Tab') {\n // Tab still moves focus; only a non-empty draft becomes a tag.\n commitDraft()\n return\n }\n if (draft === '' && event.key === 'Enter') return // let Enter submit the form\n event.preventDefault()\n commitDraft()\n return\n }\n if (event.key === 'Backspace' && draft === '' && interactive && tags.length > 0) {\n event.preventDefault()\n removeAt(tags.length - 1)\n }\n }\n\n const handlePaste = (event: ClipboardEvent<HTMLInputElement>) => {\n onPaste?.(event)\n if (event.defaultPrevented || !interactive || !split) return\n const text = event.clipboardData.getData('text')\n if (!split.test(text)) return\n event.preventDefault()\n const input = event.currentTarget\n const start = input.selectionStart ?? draft.length\n const end = input.selectionEnd ?? draft.length\n const combined = draft.slice(0, start) + text + draft.slice(end)\n commit(combined.split(split))\n setDraft('')\n }\n\n /** Focus left the whole control: a half-typed tag is kept, not lost. */\n const handleBlur = (event: FocusEvent<HTMLDivElement>) => {\n const next = event.relatedTarget as Node | null\n if (next && rootRef.current?.contains(next)) return\n commitDraft()\n onBlur?.(event)\n }\n\n // Clicking the box's padding focuses the input, like one native control.\n const handlePointerDown = (event: PointerEvent<HTMLDivElement>) => {\n const target = event.target as HTMLElement\n if (target === inputRef.current || target.closest('button, a, input')) return\n event.preventDefault()\n inputRef.current?.focus()\n }\n\n return (\n <div\n ref={rootRef}\n className={cx(control.box, styles.root, className)}\n style={style}\n data-size={size}\n data-invalid={field.invalid || undefined}\n data-disabled={field.disabled || undefined}\n data-readonly={field.readOnly || undefined}\n onPointerDown={handlePointerDown}\n onBlur={handleBlur}\n >\n {leading != null ? (\n <span className={styles.adornment} data-slot=\"leading\">\n {leading}\n </span>\n ) : null}\n <div className={styles.values}>\n {tags.length > 0 ? (\n <TagList className={styles.chips}>\n {tags.map((tag, index) => (\n <Tag\n // Duplicates are allowed when asked for, so the index is part of the key.\n key={`${String(index)}:${tag}`}\n className={styles.chip}\n onRemove={\n interactive\n ? () => {\n removeAt(index)\n inputRef.current?.focus()\n }\n : undefined\n }\n removeLabel={removeLabel(tag)}\n >\n {tag}\n </Tag>\n ))}\n </TagList>\n ) : null}\n <input\n ref={composedRef}\n id={field.id}\n type=\"text\"\n className={styles.input}\n value={draft}\n size={htmlSize}\n placeholder={tags.length === 0 ? placeholder : undefined}\n disabled={field.disabled}\n readOnly={field.readOnly}\n inputMode={inputMode ?? (numeric ? 'decimal' : undefined)}\n enterKeyHint=\"enter\"\n aria-describedby={field.describedBy}\n aria-invalid={field.invalid || undefined}\n aria-required={field.required || undefined}\n aria-busy={field.busy || undefined}\n {...rest}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n />\n </div>\n {trailing != null ? (\n <span className={styles.adornment} data-slot=\"trailing\">\n {trailing}\n </span>\n ) : null}\n {name !== undefined\n ? tags.map((tag, index) => (\n <input\n key={`${String(index)}:${tag}`}\n type=\"hidden\"\n name={name}\n value={tag}\n form={form}\n disabled={field.disabled || undefined}\n />\n ))\n : null}\n </div>\n )\n }),\n)\n"],"mappings":"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"}
|
|
@@ -10,6 +10,14 @@ export interface TextFieldProps extends FieldLabelProps, Omit<InputProps, 'inval
|
|
|
10
10
|
showCount?: boolean;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
+
* A labelled text input with description, error, warning and an optional character count.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* `TextField` is a {@link Field | Field} around an {@link Input | Input}: the label, description,
|
|
17
|
+
* error and warning wired to the control in one component. Input props (`value`, `type`,
|
|
18
|
+
* `autoComplete`, `leading`, `numeric`) and the ref go to the `<input>`.
|
|
19
|
+
*
|
|
20
|
+
* @privateRemarks
|
|
13
21
|
* Label + Input + description + error in one. Input props (`value`, `type`, `leading`,
|
|
14
22
|
* `numeric`…) and the ref go to the input; `className`/`style` go to the field wrapper.
|
|
15
23
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.js","names":["TextField","forwardRef","fieldProps","rest","splitFieldLabelProps","props","inputRest","autoId","useId","countId","id","internalValue","setInternalValue","useState","currentValue","handleChange","event","onChange","onValueChange","showCounter","showCount","maxLength","remaining","nearLimit","Field","Input","FieldCount"],"sources":["../../../../src/components/inputs/TextField/TextField.tsx"],"sourcesContent":["import { forwardRef, useId, useState, type ChangeEvent } from 'react'\nimport { Field, type FieldLabelProps } from '#components/inputs/Field'\nimport { Input, type InputProps } from '#components/inputs/Input'\nimport { splitFieldLabelProps } from '#components/inputs/internal/fieldProps'\nimport { FieldCount } from '#components/inputs/internal/messages'\n\nexport interface TextFieldProps extends FieldLabelProps, Omit<InputProps, 'invalid'> {\n /** Called with the new string on every change (alongside the native `onChange`). */\n onValueChange?: (value: string) => void\n /**\n * Shows a \"12 / 280\" character counter under the field (needs `maxLength`). Announced\n * politely only once the remaining count is within 10% of the limit.\n */\n showCount?: boolean\n}\n\n/**\n * Label + Input + description + error in one. Input props (`value`, `type`, `leading`,\n * `numeric`…) and the ref go to the input; `className`/`style` go to the field wrapper.\n *\n * <TextField label=\"Amount\" numeric leading=\"$\" trailing=\"AUD\" error={errors.amount} />\n */\nexport const TextField = forwardRef<HTMLInputElement, TextFieldProps>(\n function TextField(props, ref) {\n const [fieldProps, rest] = splitFieldLabelProps(props)\n const {\n id,\n disabled,\n className,\n style,\n onChange,\n onValueChange,\n showCount,\n maxLength,\n ...inputRest\n } = rest\n const autoId = useId()\n const controlId = id ?? `${autoId}control`\n const countId = `${controlId}-count`\n\n const [internalValue, setInternalValue] = useState(() =>\n typeof inputRest.defaultValue === 'string' ? inputRest.defaultValue : '',\n )\n const currentValue = typeof inputRest.value === 'string' ? inputRest.value : internalValue\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n setInternalValue(event.target.value)\n onChange?.(event)\n onValueChange?.(event.target.value)\n }\n\n const showCounter = showCount === true && maxLength != null\n const remaining = maxLength != null ? maxLength - currentValue.length : 0\n const nearLimit = maxLength != null && remaining <= Math.ceil(maxLength * 0.1)\n\n return (\n <Field {...fieldProps} htmlFor={id} disabled={disabled} className={className} style={style}>\n <Input\n ref={ref}\n id={id}\n disabled={disabled}\n maxLength={maxLength}\n onChange={handleChange}\n aria-describedby={showCounter ? countId : undefined}\n {...inputRest}\n />\n {showCounter ? (\n <FieldCount id={countId} live={nearLimit}>\n {currentValue.length} / {maxLength}\n </FieldCount>\n ) : null}\n </Field>\n )\n },\n)\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"TextField.js","names":["TextField","forwardRef","fieldProps","rest","splitFieldLabelProps","props","inputRest","autoId","useId","countId","id","internalValue","setInternalValue","useState","currentValue","handleChange","event","onChange","onValueChange","showCounter","showCount","maxLength","remaining","nearLimit","Field","Input","FieldCount"],"sources":["../../../../src/components/inputs/TextField/TextField.tsx"],"sourcesContent":["import { forwardRef, useId, useState, type ChangeEvent } from 'react'\nimport { Field, type FieldLabelProps } from '#components/inputs/Field'\nimport { Input, type InputProps } from '#components/inputs/Input'\nimport { splitFieldLabelProps } from '#components/inputs/internal/fieldProps'\nimport { FieldCount } from '#components/inputs/internal/messages'\n\nexport interface TextFieldProps extends FieldLabelProps, Omit<InputProps, 'invalid'> {\n /** Called with the new string on every change (alongside the native `onChange`). */\n onValueChange?: (value: string) => void\n /**\n * Shows a \"12 / 280\" character counter under the field (needs `maxLength`). Announced\n * politely only once the remaining count is within 10% of the limit.\n */\n showCount?: boolean\n}\n\n/**\n * A labelled text input with description, error, warning and an optional character count.\n *\n * @remarks\n * `TextField` is a {@link Field | Field} around an {@link Input | Input}: the label, description,\n * error and warning wired to the control in one component. Input props (`value`, `type`,\n * `autoComplete`, `leading`, `numeric`) and the ref go to the `<input>`.\n *\n * @privateRemarks\n * Label + Input + description + error in one. Input props (`value`, `type`, `leading`,\n * `numeric`…) and the ref go to the input; `className`/`style` go to the field wrapper.\n *\n * <TextField label=\"Amount\" numeric leading=\"$\" trailing=\"AUD\" error={errors.amount} />\n */\nexport const TextField = forwardRef<HTMLInputElement, TextFieldProps>(\n function TextField(props, ref) {\n const [fieldProps, rest] = splitFieldLabelProps(props)\n const {\n id,\n disabled,\n className,\n style,\n onChange,\n onValueChange,\n showCount,\n maxLength,\n ...inputRest\n } = rest\n const autoId = useId()\n const controlId = id ?? `${autoId}control`\n const countId = `${controlId}-count`\n\n const [internalValue, setInternalValue] = useState(() =>\n typeof inputRest.defaultValue === 'string' ? inputRest.defaultValue : '',\n )\n const currentValue = typeof inputRest.value === 'string' ? inputRest.value : internalValue\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n setInternalValue(event.target.value)\n onChange?.(event)\n onValueChange?.(event.target.value)\n }\n\n const showCounter = showCount === true && maxLength != null\n const remaining = maxLength != null ? maxLength - currentValue.length : 0\n const nearLimit = maxLength != null && remaining <= Math.ceil(maxLength * 0.1)\n\n return (\n <Field {...fieldProps} htmlFor={id} disabled={disabled} className={className} style={style}>\n <Input\n ref={ref}\n id={id}\n disabled={disabled}\n maxLength={maxLength}\n onChange={handleChange}\n aria-describedby={showCounter ? countId : undefined}\n {...inputRest}\n />\n {showCounter ? (\n <FieldCount id={countId} live={nearLimit}>\n {currentValue.length} / {maxLength}\n </FieldCount>\n ) : null}\n </Field>\n )\n },\n)\n"],"mappings":";;;;;;;AA8BA,IAAaA,IAAYC,EACvB,SAAmB,GAAO,GAAK;CAC7B,IAAM,CAACC,GAAYC,KAAQC,EAAqBC,CAAK,GAC/C,EACJ,OACA,aACA,cACA,UACA,aACA,kBACA,cACA,cACA,GAAGC,MACDH,GACEI,IAASC,EAAM,GAEfC,IAAU,GADEC,KAAM,GAAGH,EAAO,SACL,SAEvB,CAACI,GAAeC,KAAoBC,QACxC,OAAOP,EAAU,gBAAiB,WAAWA,EAAU,eAAe,EACxE,GACMQ,IAAe,OAAOR,EAAU,SAAU,WAAWA,EAAU,QAAQK,GAEvEI,KAAgB,MAAyC;EAG7D,AAFAH,EAAiBI,EAAM,OAAO,KAAK,GACnCC,IAAWD,CAAK,GAChBE,IAAgBF,EAAM,OAAO,KAAK;CACpC,GAEMG,IAAcC,MAAc,MAAQC,KAAa,MACjDC,IAAYD,KAAa,OAAyC,IAAlCA,IAAYP,EAAa,QACzDS,IAAYF,KAAa,QAAQC,KAAa,KAAK,KAAKD,IAAY,EAAG;CAE7E,OACE,kBAACG,GAAD;EAAO,GAAItB;EAAY,SAASQ;EAAc;EAAqB;EAAkB;EAArF,UAAA,CACE,kBAACe,GAAD;GACO;GACD;GACM;GACC;GACX,UAAUV;GACV,oBAAkBI,IAAcV,IAAU,KAAA;GAC1C,GAAIH;EACL,CAAA,GACAa,IACC,kBAACO,GAAD;GAAY,IAAIjB;GAAS,MAAMc;GAA/B,UAAA;IACGT,EAAa;IAAO;IAAIO;GACf;EACV,CAAA,IAAA,IACC;;AAEX,CACF"}
|
|
@@ -13,6 +13,14 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
|
|
|
13
13
|
maxRows?: number;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
+
* Multi-line text in the same box as Input, growing with its content if you let it.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* `Textarea` shares Input's box, states and focus ring. `autoResize` grows it with its content
|
|
20
|
+
* from `rows` up to `maxRows`, then it scrolls. For a labelled textarea with a character count,
|
|
21
|
+
* use {@link TextareaField | TextareaField}.
|
|
22
|
+
*
|
|
23
|
+
* @privateRemarks
|
|
16
24
|
* Multi-line text. Same box, states and focus ring as `Input`.
|
|
17
25
|
*
|
|
18
26
|
* <Textarea autoResize rows={2} maxRows={8} />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.js","names":["control","styles","control_module_default","Textarea_module_default","supportsFieldSizing","Textarea","markFieldAware","forwardRef","invalidProp","idProp","disabledProp","requiredProp","describedByProp","ariaInvalid","rest","innerRef","useRef","composedRef","useMergedRefs","ref","field","useResolvedField","useEffect","el","autoResize","fit","border","cx","className","mergeStyles","rows","maxRows","style","size"],"sources":["../../../../src/components/inputs/Textarea/Textarea.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n type CSSProperties,\n type TextareaHTMLAttributes,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size } from '#utils/tokens'\nimport { markFieldAware, useResolvedField } from '#components/inputs/internal/FieldContext'\nimport { useMergedRefs } from '#components/inputs/internal/refs'\nimport control from '#components/inputs/internal/control.module.css'\nimport styles from './Textarea.module.css'\n\nexport interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n size?: Size\n /** Critical border + `aria-invalid`. Inside a `<Field error>` this is set for you. */\n invalid?: boolean\n /**\n * Grow with the content instead of scrolling. `rows` becomes the minimum height and\n * `maxRows` the cap. Uses CSS `field-sizing: content`, with a small script fallback.\n */\n autoResize?: boolean\n /** With `autoResize`: stop growing after this many rows and scroll instead. */\n maxRows?: number\n}\n\nfunction supportsFieldSizing(): boolean {\n return (\n typeof CSS !== 'undefined' &&\n typeof CSS.supports === 'function' &&\n CSS.supports('field-sizing', 'content')\n )\n}\n\n/**\n * Multi-line text. Same box, states and focus ring as `Input`.\n *\n * <Textarea autoResize rows={2} maxRows={8} />\n */\nexport const Textarea = markFieldAware(\n forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n size = 'md',\n invalid: invalidProp,\n autoResize = false,\n rows = 3,\n maxRows,\n id: idProp,\n disabled: disabledProp,\n required: requiredProp,\n readOnly,\n className,\n style,\n 'aria-describedby': describedByProp,\n 'aria-invalid': ariaInvalid,\n ...rest\n },\n ref,\n ) {\n const innerRef = useRef<HTMLTextAreaElement>(null)\n const composedRef = useMergedRefs(ref, innerRef)\n const field = useResolvedField({\n id: idProp,\n describedBy: describedByProp,\n invalid: invalidProp,\n ariaInvalid,\n disabled: disabledProp,\n readOnly,\n })\n\n // Fallback for browsers without field-sizing: size to scrollHeight on every input.\n useEffect(() => {\n const el = innerRef.current\n if (!autoResize || !el || supportsFieldSizing()) return\n const fit = () => {\n el.style.height = 'auto'\n const border = el.offsetHeight - el.clientHeight\n el.style.height = `${String(el.scrollHeight + border)}px`\n }\n fit()\n el.addEventListener('input', fit)\n return () => {\n el.removeEventListener('input', fit)\n }\n }, [autoResize, rest.value])\n\n return (\n <textarea\n ref={composedRef}\n id={field.id}\n rows={rows}\n className={cx(control.box, styles.textarea, className)}\n style={mergeStyles(\n autoResize ? ({ '--_rows': rows, '--_max-rows': maxRows } as CSSProperties) : undefined,\n style,\n )}\n data-size={size}\n data-auto-resize={autoResize || undefined}\n data-max-rows={autoResize && maxRows ? '' : undefined}\n data-invalid={field.invalid || undefined}\n data-disabled={field.disabled || undefined}\n data-readonly={field.readOnly || undefined}\n disabled={field.disabled}\n readOnly={field.readOnly}\n required={requiredProp ?? field.required}\n aria-describedby={field.describedBy}\n aria-invalid={field.invalid || undefined}\n aria-required={field.required || undefined}\n aria-busy={field.busy || undefined}\n {...rest}\n />\n )\n }),\n)\n"],"mappings":";;;;;;;;;AA4BA,SAASI,IAA+B;CACtC,OACE,OAAO,MAAQ,OACf,OAAO,IAAI,YAAa,cACxB,IAAI,SAAS,gBAAgB,SAAS;AAE1C;
|
|
1
|
+
{"version":3,"file":"Textarea.js","names":["control","styles","control_module_default","Textarea_module_default","supportsFieldSizing","Textarea","markFieldAware","forwardRef","invalidProp","idProp","disabledProp","requiredProp","describedByProp","ariaInvalid","rest","innerRef","useRef","composedRef","useMergedRefs","ref","field","useResolvedField","useEffect","el","autoResize","fit","border","cx","className","mergeStyles","rows","maxRows","style","size"],"sources":["../../../../src/components/inputs/Textarea/Textarea.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n type CSSProperties,\n type TextareaHTMLAttributes,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport type { Size } from '#utils/tokens'\nimport { markFieldAware, useResolvedField } from '#components/inputs/internal/FieldContext'\nimport { useMergedRefs } from '#components/inputs/internal/refs'\nimport control from '#components/inputs/internal/control.module.css'\nimport styles from './Textarea.module.css'\n\nexport interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n size?: Size\n /** Critical border + `aria-invalid`. Inside a `<Field error>` this is set for you. */\n invalid?: boolean\n /**\n * Grow with the content instead of scrolling. `rows` becomes the minimum height and\n * `maxRows` the cap. Uses CSS `field-sizing: content`, with a small script fallback.\n */\n autoResize?: boolean\n /** With `autoResize`: stop growing after this many rows and scroll instead. */\n maxRows?: number\n}\n\nfunction supportsFieldSizing(): boolean {\n return (\n typeof CSS !== 'undefined' &&\n typeof CSS.supports === 'function' &&\n CSS.supports('field-sizing', 'content')\n )\n}\n\n/**\n * Multi-line text in the same box as Input, growing with its content if you let it.\n *\n * @remarks\n * `Textarea` shares Input's box, states and focus ring. `autoResize` grows it with its content\n * from `rows` up to `maxRows`, then it scrolls. For a labelled textarea with a character count,\n * use {@link TextareaField | TextareaField}.\n *\n * @privateRemarks\n * Multi-line text. Same box, states and focus ring as `Input`.\n *\n * <Textarea autoResize rows={2} maxRows={8} />\n */\nexport const Textarea = markFieldAware(\n forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(\n {\n size = 'md',\n invalid: invalidProp,\n autoResize = false,\n rows = 3,\n maxRows,\n id: idProp,\n disabled: disabledProp,\n required: requiredProp,\n readOnly,\n className,\n style,\n 'aria-describedby': describedByProp,\n 'aria-invalid': ariaInvalid,\n ...rest\n },\n ref,\n ) {\n const innerRef = useRef<HTMLTextAreaElement>(null)\n const composedRef = useMergedRefs(ref, innerRef)\n const field = useResolvedField({\n id: idProp,\n describedBy: describedByProp,\n invalid: invalidProp,\n ariaInvalid,\n disabled: disabledProp,\n readOnly,\n })\n\n // Fallback for browsers without field-sizing: size to scrollHeight on every input.\n useEffect(() => {\n const el = innerRef.current\n if (!autoResize || !el || supportsFieldSizing()) return\n const fit = () => {\n el.style.height = 'auto'\n const border = el.offsetHeight - el.clientHeight\n el.style.height = `${String(el.scrollHeight + border)}px`\n }\n fit()\n el.addEventListener('input', fit)\n return () => {\n el.removeEventListener('input', fit)\n }\n }, [autoResize, rest.value])\n\n return (\n <textarea\n ref={composedRef}\n id={field.id}\n rows={rows}\n className={cx(control.box, styles.textarea, className)}\n style={mergeStyles(\n autoResize ? ({ '--_rows': rows, '--_max-rows': maxRows } as CSSProperties) : undefined,\n style,\n )}\n data-size={size}\n data-auto-resize={autoResize || undefined}\n data-max-rows={autoResize && maxRows ? '' : undefined}\n data-invalid={field.invalid || undefined}\n data-disabled={field.disabled || undefined}\n data-readonly={field.readOnly || undefined}\n disabled={field.disabled}\n readOnly={field.readOnly}\n required={requiredProp ?? field.required}\n aria-describedby={field.describedBy}\n aria-invalid={field.invalid || undefined}\n aria-required={field.required || undefined}\n aria-busy={field.busy || undefined}\n {...rest}\n />\n )\n }),\n)\n"],"mappings":";;;;;;;;;AA4BA,SAASI,IAA+B;CACtC,OACE,OAAO,MAAQ,OACf,OAAO,IAAI,YAAa,cACxB,IAAI,SAAS,gBAAgB,SAAS;AAE1C;AAeA,IAAaC,IAAWC,EACtBC,EAA+C,SAC7C,EACE,UAAO,MACP,SAASC,GACT,gBAAa,IACb,UAAO,GACP,YACA,IAAIC,GACJ,UAAUC,GACV,UAAUC,GACV,aACA,cACA,UACA,oBAAoBC,GACpB,gBAAgBC,GAChB,GAAGC,KAEL,GACA;CACA,IAAMC,IAAWC,EAA4B,IAAI,GAC3CC,IAAcC,EAAcC,GAAKJ,CAAQ,GACzCK,IAAQC,EAAiB;EAC7B,IAAIZ;EACJ,aAAaG;EACb,SAASJ;EACT;EACA,UAAUE;EACV;CACF,CAAC;CAkBD,OAfAY,QAAgB;EACd,IAAMC,IAAKR,EAAS;EACpB,IAAI,CAACS,KAAc,CAACD,KAAMnB,EAAoB,GAAG;EACjD,IAAMqB,UAAY;GAChB,EAAG,MAAM,SAAS;GAClB,IAAMC,IAASH,EAAG,eAAeA,EAAG;GACpC,EAAG,MAAM,SAAS,GAAG,OAAOA,EAAG,eAAeG,CAAM,EAAE;EACxD;EAGA,OAFAD,EAAI,GACJF,EAAG,iBAAiB,SAASE,CAAG,SACnB;GACX,EAAG,oBAAoB,SAASA,CAAG;EACrC;CACF,GAAG,CAACD,GAAYV,EAAK,KAAK,CAAC,GAGzB,kBAAC,YAAD;EACE,KAAKG;EACL,IAAIG,EAAM;EACJ;EACN,WAAWO,EAAG3B,EAAQ,KAAKC,EAAO,UAAU2B,CAAS;EACrD,OAAOC,EACLL,IAAc;GAAE,WAAWM;GAAM,eAAeC;EAAQ,IAAsB,KAAA,GAC9EC,CACF;EACA,aAAWC;EACX,oBAAkBT,KAAc,KAAA;EAChC,iBAAeA,KAAcO,IAAU,KAAK,KAAA;EAC5C,gBAAcX,EAAM,WAAW,KAAA;EAC/B,iBAAeA,EAAM,YAAY,KAAA;EACjC,iBAAeA,EAAM,YAAY,KAAA;EACjC,UAAUA,EAAM;EAChB,UAAUA,EAAM;EAChB,UAAUT,KAAgBS,EAAM;EAChC,oBAAkBA,EAAM;EACxB,gBAAcA,EAAM,WAAW,KAAA;EAC/B,iBAAeA,EAAM,YAAY,KAAA;EACjC,aAAWA,EAAM,QAAQ,KAAA;EACzB,GAAIN;CACL,CAAA;AAEL,CAAC,CACH"}
|
|
@@ -10,6 +10,13 @@ export interface TextareaFieldProps extends FieldLabelProps, Omit<TextareaProps,
|
|
|
10
10
|
showCount?: boolean;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
+
* A labelled multi-line text field that can grow with its content and count characters.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* `TextareaField` is a {@link Field | Field} around a {@link Textarea | Textarea}. Textarea props
|
|
17
|
+
* and the ref go to the `<textarea>`.
|
|
18
|
+
*
|
|
19
|
+
* @privateRemarks
|
|
13
20
|
* Label + Textarea + description + error. Textarea props and the ref go to the
|
|
14
21
|
* `<textarea>`; `className`/`style` go to the field wrapper.
|
|
15
22
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextareaField.js","names":["TextareaField","forwardRef","fieldProps","rest","splitFieldLabelProps","props","textareaRest","autoId","useId","countId","id","internalValue","setInternalValue","useState","currentValue","handleChange","event","onChange","onValueChange","showCounter","showCount","maxLength","remaining","nearLimit","Field","Textarea","FieldCount"],"sources":["../../../../src/components/inputs/TextareaField/TextareaField.tsx"],"sourcesContent":["import { forwardRef, useId, useState, type ChangeEvent } from 'react'\nimport { Field, type FieldLabelProps } from '#components/inputs/Field'\nimport { Textarea, type TextareaProps } from '#components/inputs/Textarea'\nimport { splitFieldLabelProps } from '#components/inputs/internal/fieldProps'\nimport { FieldCount } from '#components/inputs/internal/messages'\n\nexport interface TextareaFieldProps extends FieldLabelProps, Omit<TextareaProps, 'invalid'> {\n /** Called with the new string on every change (alongside the native `onChange`). */\n onValueChange?: (value: string) => void\n /**\n * Shows a \"12 / 280\" character counter under the field (needs `maxLength`). Announced\n * politely only once the remaining count is within 10% of the limit.\n */\n showCount?: boolean\n}\n\n/**\n * Label + Textarea + description + error. Textarea props and the ref go to the\n * `<textarea>`; `className`/`style` go to the field wrapper.\n *\n * <TextareaField label=\"Notes\" autoResize rows={2} optional />\n */\nexport const TextareaField = forwardRef<HTMLTextAreaElement, TextareaFieldProps>(\n function TextareaField(props, ref) {\n const [fieldProps, rest] = splitFieldLabelProps(props)\n const {\n id,\n disabled,\n className,\n style,\n onChange,\n onValueChange,\n showCount,\n maxLength,\n ...textareaRest\n } = rest\n const autoId = useId()\n const controlId = id ?? `${autoId}control`\n const countId = `${controlId}-count`\n\n const [internalValue, setInternalValue] = useState(() =>\n typeof textareaRest.defaultValue === 'string' ? textareaRest.defaultValue : '',\n )\n const currentValue = typeof textareaRest.value === 'string' ? textareaRest.value : internalValue\n\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(event.target.value)\n onChange?.(event)\n onValueChange?.(event.target.value)\n }\n\n const showCounter = showCount === true && maxLength != null\n const remaining = maxLength != null ? maxLength - currentValue.length : 0\n const nearLimit = maxLength != null && remaining <= Math.ceil(maxLength * 0.1)\n\n return (\n <Field {...fieldProps} htmlFor={id} disabled={disabled} className={className} style={style}>\n <Textarea\n ref={ref}\n id={id}\n disabled={disabled}\n maxLength={maxLength}\n onChange={handleChange}\n aria-describedby={showCounter ? countId : undefined}\n {...textareaRest}\n />\n {showCounter ? (\n <FieldCount id={countId} live={nearLimit}>\n {currentValue.length} / {maxLength}\n </FieldCount>\n ) : null}\n </Field>\n )\n },\n)\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"TextareaField.js","names":["TextareaField","forwardRef","fieldProps","rest","splitFieldLabelProps","props","textareaRest","autoId","useId","countId","id","internalValue","setInternalValue","useState","currentValue","handleChange","event","onChange","onValueChange","showCounter","showCount","maxLength","remaining","nearLimit","Field","Textarea","FieldCount"],"sources":["../../../../src/components/inputs/TextareaField/TextareaField.tsx"],"sourcesContent":["import { forwardRef, useId, useState, type ChangeEvent } from 'react'\nimport { Field, type FieldLabelProps } from '#components/inputs/Field'\nimport { Textarea, type TextareaProps } from '#components/inputs/Textarea'\nimport { splitFieldLabelProps } from '#components/inputs/internal/fieldProps'\nimport { FieldCount } from '#components/inputs/internal/messages'\n\nexport interface TextareaFieldProps extends FieldLabelProps, Omit<TextareaProps, 'invalid'> {\n /** Called with the new string on every change (alongside the native `onChange`). */\n onValueChange?: (value: string) => void\n /**\n * Shows a \"12 / 280\" character counter under the field (needs `maxLength`). Announced\n * politely only once the remaining count is within 10% of the limit.\n */\n showCount?: boolean\n}\n\n/**\n * A labelled multi-line text field that can grow with its content and count characters.\n *\n * @remarks\n * `TextareaField` is a {@link Field | Field} around a {@link Textarea | Textarea}. Textarea props\n * and the ref go to the `<textarea>`.\n *\n * @privateRemarks\n * Label + Textarea + description + error. Textarea props and the ref go to the\n * `<textarea>`; `className`/`style` go to the field wrapper.\n *\n * <TextareaField label=\"Notes\" autoResize rows={2} optional />\n */\nexport const TextareaField = forwardRef<HTMLTextAreaElement, TextareaFieldProps>(\n function TextareaField(props, ref) {\n const [fieldProps, rest] = splitFieldLabelProps(props)\n const {\n id,\n disabled,\n className,\n style,\n onChange,\n onValueChange,\n showCount,\n maxLength,\n ...textareaRest\n } = rest\n const autoId = useId()\n const controlId = id ?? `${autoId}control`\n const countId = `${controlId}-count`\n\n const [internalValue, setInternalValue] = useState(() =>\n typeof textareaRest.defaultValue === 'string' ? textareaRest.defaultValue : '',\n )\n const currentValue = typeof textareaRest.value === 'string' ? textareaRest.value : internalValue\n\n const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(event.target.value)\n onChange?.(event)\n onValueChange?.(event.target.value)\n }\n\n const showCounter = showCount === true && maxLength != null\n const remaining = maxLength != null ? maxLength - currentValue.length : 0\n const nearLimit = maxLength != null && remaining <= Math.ceil(maxLength * 0.1)\n\n return (\n <Field {...fieldProps} htmlFor={id} disabled={disabled} className={className} style={style}>\n <Textarea\n ref={ref}\n id={id}\n disabled={disabled}\n maxLength={maxLength}\n onChange={handleChange}\n aria-describedby={showCounter ? countId : undefined}\n {...textareaRest}\n />\n {showCounter ? (\n <FieldCount id={countId} live={nearLimit}>\n {currentValue.length} / {maxLength}\n </FieldCount>\n ) : null}\n </Field>\n )\n },\n)\n"],"mappings":";;;;;;;AA6BA,IAAaA,IAAgBC,EAC3B,SAAuB,GAAO,GAAK;CACjC,IAAM,CAACC,GAAYC,KAAQC,EAAqBC,CAAK,GAC/C,EACJ,OACA,aACA,cACA,UACA,aACA,kBACA,cACA,cACA,GAAGC,MACDH,GACEI,IAASC,EAAM,GAEfC,IAAU,GADEC,KAAM,GAAGH,EAAO,SACL,SAEvB,CAACI,GAAeC,KAAoBC,QACxC,OAAOP,EAAa,gBAAiB,WAAWA,EAAa,eAAe,EAC9E,GACMQ,IAAe,OAAOR,EAAa,SAAU,WAAWA,EAAa,QAAQK,GAE7EI,KAAgB,MAA4C;EAGhE,AAFAH,EAAiBI,EAAM,OAAO,KAAK,GACnCC,IAAWD,CAAK,GAChBE,IAAgBF,EAAM,OAAO,KAAK;CACpC,GAEMG,IAAcC,MAAc,MAAQC,KAAa,MACjDC,IAAYD,KAAa,OAAyC,IAAlCA,IAAYP,EAAa,QACzDS,IAAYF,KAAa,QAAQC,KAAa,KAAK,KAAKD,IAAY,EAAG;CAE7E,OACE,kBAACG,GAAD;EAAO,GAAItB;EAAY,SAASQ;EAAc;EAAqB;EAAkB;EAArF,UAAA,CACE,kBAACe,GAAD;GACO;GACD;GACM;GACC;GACX,UAAUV;GACV,oBAAkBI,IAAcV,IAAU,KAAA;GAC1C,GAAIH;EACL,CAAA,GACAa,IACC,kBAACO,GAAD;GAAY,IAAIjB;GAAS,MAAMc;GAA/B,UAAA;IACGT,EAAa;IAAO;IAAIO;GACf;EACV,CAAA,IAAA,IACC;;AAEX,CACF"}
|
|
@@ -19,6 +19,13 @@ export interface ActionBarProps extends HTMLAttributes<HTMLElement>, VisibilityP
|
|
|
19
19
|
as?: ActionBarElement;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
+
* The row of actions at the end of a form or dialog. Cancel, save, continue.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* `ActionBar` is a row of buttons with consistent spacing and alignment: `end` by default,
|
|
26
|
+
* `between` to push a destructive action to the other side, `start` to follow a form's left edge.
|
|
27
|
+
*
|
|
28
|
+
* @privateRemarks
|
|
22
29
|
* A row of form/dialog actions — Cancel, Save, Submit. Composes with `Button`; doesn't
|
|
23
30
|
* lay out anything else itself.
|
|
24
31
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionBar.js","names":["styles","ActionBar_module_default","ActionBar","forwardRef","Comp","rest","cx","visibilityClass","className","align","sticky","mergeStyles","responsiveVars","gap","space","style"],"sources":["../../../../src/components/layout/ActionBar/ActionBar.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport styles from './ActionBar.module.css'\n\nexport type ActionBarElement = 'div' | 'footer'\nexport type ActionBarAlign = 'start' | 'end' | 'between'\n\nexport interface ActionBarProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Where the actions sit on the row. Default `end`. */\n align?: ActionBarAlign\n /**\n * Pins the bar to the bottom of its scroll container — a canvas surface above a top\n * hairline, at `--z-sticky`. Use inside a scrolling form/dialog body so the primary\n * action stays reachable.\n */\n sticky?: boolean\n /** Space between actions. Responsive. Default `3`. */\n gap?: Responsive<Space>\n /** Render as `footer` when the bar is the form's own closing landmark. Default `div`. */\n as?: ActionBarElement\n}\n\n/**\n * A row of form/dialog actions — Cancel, Save, Submit. Composes with `Button`; doesn't\n * lay out anything else itself.\n *\n * <ActionBar sticky>\n * <Button variant=\"ghost\">Back</Button>\n * <Button>Continue</Button>\n * </ActionBar>\n */\nexport const ActionBar = forwardRef<HTMLElement, ActionBarProps>(function ActionBar(\n {\n align = 'end',\n sticky = false,\n gap,\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.bar, visibilityClass({ hideBelow, hideAbove }), className)}\n data-align={align}\n data-sticky={sticky || undefined}\n style={mergeStyles(responsiveVars('action-bar-gap', gap, space), style)}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"ActionBar.js","names":["styles","ActionBar_module_default","ActionBar","forwardRef","Comp","rest","cx","visibilityClass","className","align","sticky","mergeStyles","responsiveVars","gap","space","style"],"sources":["../../../../src/components/layout/ActionBar/ActionBar.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport styles from './ActionBar.module.css'\n\nexport type ActionBarElement = 'div' | 'footer'\nexport type ActionBarAlign = 'start' | 'end' | 'between'\n\nexport interface ActionBarProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Where the actions sit on the row. Default `end`. */\n align?: ActionBarAlign\n /**\n * Pins the bar to the bottom of its scroll container — a canvas surface above a top\n * hairline, at `--z-sticky`. Use inside a scrolling form/dialog body so the primary\n * action stays reachable.\n */\n sticky?: boolean\n /** Space between actions. Responsive. Default `3`. */\n gap?: Responsive<Space>\n /** Render as `footer` when the bar is the form's own closing landmark. Default `div`. */\n as?: ActionBarElement\n}\n\n/**\n * The row of actions at the end of a form or dialog. Cancel, save, continue.\n *\n * @remarks\n * `ActionBar` is a row of buttons with consistent spacing and alignment: `end` by default,\n * `between` to push a destructive action to the other side, `start` to follow a form's left edge.\n *\n * @privateRemarks\n * A row of form/dialog actions — Cancel, Save, Submit. Composes with `Button`; doesn't\n * lay out anything else itself.\n *\n * <ActionBar sticky>\n * <Button variant=\"ghost\">Back</Button>\n * <Button>Continue</Button>\n * </ActionBar>\n */\nexport const ActionBar = forwardRef<HTMLElement, ActionBarProps>(function ActionBar(\n {\n align = 'end',\n sticky = false,\n gap,\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.bar, visibilityClass({ hideBelow, hideAbove }), className)}\n data-align={align}\n data-sticky={sticky || undefined}\n style={mergeStyles(responsiveVars('action-bar-gap', gap, space), style)}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;;;AAyCA,IAAaE,IAAYC,EAAwC,SAC/D,EACE,WAAQ,OACR,YAAS,IACT,QACA,IAAIC,IAAO,OACX,cACA,cACA,cACA,UACA,GAAGC,KAEL,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGN,EAAO,KAAKO,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EAC9E,cAAYC;EACZ,eAAaC,KAAU,KAAA;EACvB,OAAOC,EAAYC,EAAe,kBAAkBC,GAAKC,CAAK,GAAGC,CAAK;EACtE,GAAIV;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.js","names":["styles","AppShell_module_default","ShellContext","createContext","AppShellRoot","forwardRef","rest","generated","useId","id","mainId","cx","className","navBreakpoint","VisuallyHidden","skipLinkLabel","children","Header","sticky","Main","ctx","useContext","Sidebar","side","Footer","BottomBar","AppShell"],"sources":["../../../../src/components/layout/AppShell/AppShell.tsx"],"sourcesContent":["import { createContext, forwardRef, useContext, useId, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { VisuallyHidden } from '#components/layout/VisuallyHidden'\nimport styles from './AppShell.module.css'\n\nconst ShellContext = createContext<{ mainId: string } | null>(null)\n\nexport type AppShellNavBreakpoint = 'md' | 'lg'\n\nexport interface AppShellProps extends HTMLAttributes<HTMLDivElement> {\n /** Text of the skip link rendered before everything else. Default \"Skip to content\". */\n skipLinkLabel?: string\n /**\n * Where navigation hands over from phone to desktop: from this breakpoint up the\n * `Sidebar` appears and the `BottomBar` is removed, so there is never a width with\n * neither. `md` (48em) or `lg` (64em, default). Pair it with the same value on\n * `BottomNav hideAbove` and header `NavLinks hideBelow`.\n */\n navBreakpoint?: AppShellNavBreakpoint\n /** Id given to `AppShell.Main` (the skip link target). Generated when omitted. */\n mainId?: string\n}\n\n/**\n * The frame of an app screen: header, optional sidebar, main, footer, and a\n * mobile-only bottom bar. Slots place themselves; order in JSX doesn't matter,\n * but keep DOM order = reading order (Header, Sidebar, Main, Footer, BottomBar).\n * `navBreakpoint` decides where the bottom bar gives way to the sidebar / header nav.\n *\n * <AppShell navBreakpoint=\"md\">\n * <AppShell.Header>… <NavLinks hideBelow=\"md\">…</NavLinks></AppShell.Header>\n * <AppShell.Main>…</AppShell.Main>\n * <AppShell.BottomBar><BottomNav position=\"static\" hideAbove=\"md\">…</BottomNav></AppShell.BottomBar>\n * </AppShell>\n */\nconst AppShellRoot = forwardRef<HTMLDivElement, AppShellProps>(function AppShell(\n { skipLinkLabel = 'Skip to content', mainId, navBreakpoint = 'lg', className, children, ...rest },\n ref,\n) {\n const generated = `app-main-${useId().replace(/[^\\w-]/g, '')}`\n const id = mainId ?? generated\n return (\n <ShellContext.Provider value={{ mainId: id }}>\n <div\n ref={ref}\n className={cx(styles.shell, className)}\n data-nav-breakpoint={navBreakpoint}\n {...rest}\n >\n <VisuallyHidden as=\"a\" href={`#${id}`} focusable className={styles.skip}>\n {skipLinkLabel}\n </VisuallyHidden>\n {children}\n </div>\n </ShellContext.Provider>\n )\n})\n\nexport interface AppShellHeaderProps extends HTMLAttributes<HTMLElement> {\n /** Stick to the top of the viewport while scrolling. Default `true`. */\n sticky?: boolean\n}\n\n/** The top bar: canvas-coloured, hairline below. Put a `Container` + `Inline` inside. */\nconst Header = forwardRef<HTMLElement, AppShellHeaderProps>(function AppShellHeader(\n { sticky = true, className, ...rest },\n ref,\n) {\n return (\n <header\n ref={ref}\n className={cx(styles.header, className)}\n data-sticky={sticky || undefined}\n {...rest}\n />\n )\n})\n\nexport type AppShellMainProps = Omit<HTMLAttributes<HTMLElement>, 'id'>\n\n/** The page's `<main>` landmark and the skip link's target. Its id comes from `AppShell`. */\nconst Main = forwardRef<HTMLElement, AppShellMainProps>(function AppShellMain(\n { className, ...rest },\n ref,\n) {\n const ctx = useContext(ShellContext)\n return (\n <main\n ref={ref}\n id={ctx?.mainId}\n tabIndex={-1}\n className={cx(styles.main, className)}\n {...rest}\n />\n )\n})\n\nexport interface AppShellSidebarProps extends HTMLAttributes<HTMLElement> {\n /** Which side of Main it sits on. Logical, so it follows writing direction. Default `start`. */\n side?: 'start' | 'end'\n}\n\n/** Navigation or context beside Main. Shown from the shell's `navBreakpoint` up only (default `lg`, 64em). */\nconst Sidebar = forwardRef<HTMLElement, AppShellSidebarProps>(function AppShellSidebar(\n { side = 'start', className, ...rest },\n ref,\n) {\n return <aside ref={ref} className={cx(styles.sidebar, className)} data-side={side} {...rest} />\n})\n\nexport type AppShellFooterProps = HTMLAttributes<HTMLElement>\n\n/** The page footer: a hairline above, quieter ink. */\nconst Footer = forwardRef<HTMLElement, AppShellFooterProps>(function AppShellFooter(\n { className, ...rest },\n ref,\n) {\n return <footer ref={ref} className={cx(styles.footer, className)} {...rest} />\n})\n\nexport type AppShellBottomBarProps = HTMLAttributes<HTMLDivElement>\n\n/**\n * A region pinned to the bottom of the viewport on small screens and removed from the\n * shell's `navBreakpoint` up (default `lg`, 64em). Pads for the home indicator (`safe-area-inset-bottom`). Holds a\n * `BottomNav` or a single primary action.\n */\nconst BottomBar = forwardRef<HTMLDivElement, AppShellBottomBarProps>(function AppShellBottomBar(\n { className, ...rest },\n ref,\n) {\n return <div ref={ref} className={cx(styles.bottomBar, className)} {...rest} />\n})\n\nexport const AppShell = Object.assign(AppShellRoot, { Header, Main, Sidebar, Footer, BottomBar })\n"],"mappings":";;;;;;AAKA,IAAME,IAAeC,EAAyC,IAAI,
|
|
1
|
+
{"version":3,"file":"AppShell.js","names":["styles","AppShell_module_default","ShellContext","createContext","AppShellRoot","forwardRef","rest","generated","useId","id","mainId","cx","className","navBreakpoint","VisuallyHidden","skipLinkLabel","children","Header","sticky","Main","ctx","useContext","Sidebar","side","Footer","BottomBar","AppShell"],"sources":["../../../../src/components/layout/AppShell/AppShell.tsx"],"sourcesContent":["import { createContext, forwardRef, useContext, useId, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { VisuallyHidden } from '#components/layout/VisuallyHidden'\nimport styles from './AppShell.module.css'\n\nconst ShellContext = createContext<{ mainId: string } | null>(null)\n\nexport type AppShellNavBreakpoint = 'md' | 'lg'\n\nexport interface AppShellProps extends HTMLAttributes<HTMLDivElement> {\n /** Text of the skip link rendered before everything else. Default \"Skip to content\". */\n skipLinkLabel?: string\n /**\n * Where navigation hands over from phone to desktop: from this breakpoint up the\n * `Sidebar` appears and the `BottomBar` is removed, so there is never a width with\n * neither. `md` (48em) or `lg` (64em, default). Pair it with the same value on\n * `BottomNav hideAbove` and header `NavLinks hideBelow`.\n */\n navBreakpoint?: AppShellNavBreakpoint\n /** Id given to `AppShell.Main` (the skip link target). Generated when omitted. */\n mainId?: string\n}\n\n/**\n * The frame of an app screen. A header, an optional sidebar, the main region, a footer and a\n * phone-only bottom bar.\n *\n * @remarks\n * `AppShell` is the outermost frame of an app: landmarks in the right order, a skip link, a sticky\n * header, and a hand-over between a phone's bottom bar and a desktop's sidebar or header nav.\n * These docs are an `AppShell`.\n *\n * ## Slots\n *\n * `AppShell.Header`, `AppShell.Sidebar`, `AppShell.Main`, `AppShell.Footer` and\n * `AppShell.BottomBar` place themselves, so their order in JSX doesn't affect the layout. Keep it\n * the same as the reading order anyway: header, sidebar, main, footer, bottom bar.\n *\n * - **Header** is sticky by default (`sticky={false}` to scroll it away). Put a `Container` and an\n * `Inline` inside.\n * - **Sidebar** appears from `navBreakpoint` up. It sits at the start of the main region\n * (`side=\"end\"` for the other side), scrolls on its own, and stays below the header.\n * - **Main** is the `<main>` landmark and the skip link's target.\n * - **BottomBar** is pinned to the bottom of the viewport below `navBreakpoint`, and pads for the\n * phone's home indicator.\n *\n * ## Navigation hand-over\n *\n * `navBreakpoint` (`md` or `lg`, default `lg`) is the one width where phone navigation gives way\n * to desktop navigation, so there's never a width with neither. Pair it with the same value on\n * `BottomNav hideAbove` and the header's `NavLinks hideBelow`.\n *\n * @privateRemarks\n * The frame of an app screen: header, optional sidebar, main, footer, and a\n * mobile-only bottom bar. Slots place themselves; order in JSX doesn't matter,\n * but keep DOM order = reading order (Header, Sidebar, Main, Footer, BottomBar).\n * `navBreakpoint` decides where the bottom bar gives way to the sidebar / header nav.\n *\n * <AppShell navBreakpoint=\"md\">\n * <AppShell.Header>… <NavLinks hideBelow=\"md\">…</NavLinks></AppShell.Header>\n * <AppShell.Main>…</AppShell.Main>\n * <AppShell.BottomBar><BottomNav position=\"static\" hideAbove=\"md\">…</BottomNav></AppShell.BottomBar>\n * </AppShell>\n */\nconst AppShellRoot = forwardRef<HTMLDivElement, AppShellProps>(function AppShell(\n { skipLinkLabel = 'Skip to content', mainId, navBreakpoint = 'lg', className, children, ...rest },\n ref,\n) {\n const generated = `app-main-${useId().replace(/[^\\w-]/g, '')}`\n const id = mainId ?? generated\n return (\n <ShellContext.Provider value={{ mainId: id }}>\n <div\n ref={ref}\n className={cx(styles.shell, className)}\n data-nav-breakpoint={navBreakpoint}\n {...rest}\n >\n <VisuallyHidden as=\"a\" href={`#${id}`} focusable className={styles.skip}>\n {skipLinkLabel}\n </VisuallyHidden>\n {children}\n </div>\n </ShellContext.Provider>\n )\n})\n\nexport interface AppShellHeaderProps extends HTMLAttributes<HTMLElement> {\n /** Stick to the top of the viewport while scrolling. Default `true`. */\n sticky?: boolean\n}\n\n/** The top bar: canvas-coloured, hairline below. Put a `Container` + `Inline` inside. */\nconst Header = forwardRef<HTMLElement, AppShellHeaderProps>(function AppShellHeader(\n { sticky = true, className, ...rest },\n ref,\n) {\n return (\n <header\n ref={ref}\n className={cx(styles.header, className)}\n data-sticky={sticky || undefined}\n {...rest}\n />\n )\n})\n\nexport type AppShellMainProps = Omit<HTMLAttributes<HTMLElement>, 'id'>\n\n/** The page's `<main>` landmark and the skip link's target. Its id comes from `AppShell`. */\nconst Main = forwardRef<HTMLElement, AppShellMainProps>(function AppShellMain(\n { className, ...rest },\n ref,\n) {\n const ctx = useContext(ShellContext)\n return (\n <main\n ref={ref}\n id={ctx?.mainId}\n tabIndex={-1}\n className={cx(styles.main, className)}\n {...rest}\n />\n )\n})\n\nexport interface AppShellSidebarProps extends HTMLAttributes<HTMLElement> {\n /** Which side of Main it sits on. Logical, so it follows writing direction. Default `start`. */\n side?: 'start' | 'end'\n}\n\n/** Navigation or context beside Main. Shown from the shell's `navBreakpoint` up only (default `lg`, 64em). */\nconst Sidebar = forwardRef<HTMLElement, AppShellSidebarProps>(function AppShellSidebar(\n { side = 'start', className, ...rest },\n ref,\n) {\n return <aside ref={ref} className={cx(styles.sidebar, className)} data-side={side} {...rest} />\n})\n\nexport type AppShellFooterProps = HTMLAttributes<HTMLElement>\n\n/** The page footer: a hairline above, quieter ink. */\nconst Footer = forwardRef<HTMLElement, AppShellFooterProps>(function AppShellFooter(\n { className, ...rest },\n ref,\n) {\n return <footer ref={ref} className={cx(styles.footer, className)} {...rest} />\n})\n\nexport type AppShellBottomBarProps = HTMLAttributes<HTMLDivElement>\n\n/**\n * A region pinned to the bottom of the viewport on small screens and removed from the\n * shell's `navBreakpoint` up (default `lg`, 64em). Pads for the home indicator (`safe-area-inset-bottom`). Holds a\n * `BottomNav` or a single primary action.\n */\nconst BottomBar = forwardRef<HTMLDivElement, AppShellBottomBarProps>(function AppShellBottomBar(\n { className, ...rest },\n ref,\n) {\n return <div ref={ref} className={cx(styles.bottomBar, className)} {...rest} />\n})\n\nexport const AppShell = Object.assign(AppShellRoot, { Header, Main, Sidebar, Footer, BottomBar })\n"],"mappings":";;;;;;AAKA,IAAME,IAAeC,EAAyC,IAAI,GA2D5DC,IAAeC,EAA0C,SAC7D,EAAE,mBAAgB,mBAAmB,WAAQ,mBAAgB,MAAM,cAAW,aAAU,GAAGC,KAC3F,GACA;CACA,IAAMC,IAAY,YAAYC,EAAM,CAAC,CAAC,QAAQ,WAAW,EAAE,KACrDC,IAAKC,KAAUH;CACrB,OACE,kBAACL,EAAa,UAAd;EAAuB,OAAO,EAAE,QAAQO,EAAG;EACzC,UAAA,kBAAC,OAAD;GACO;GACL,WAAWE,EAAGX,EAAO,OAAOY,CAAS;GACrC,uBAAqBC;GACrB,GAAIP;GAJN,UAAA,CAME,kBAACQ,GAAD;IAAgB,IAAG;IAAI,MAAM,IAAIL;IAAM,WAAA;IAAU,WAAWT,EAAO;IAChE,UAAAe;GACa,CAAA,GACfC,CACE;;CACgB,CAAA;AAE3B,CAAC,GAQKC,IAASZ,EAA6C,SAC1D,EAAE,YAAS,IAAM,cAAW,GAAGC,KAC/B,GACA;CACA,OACE,kBAAC,UAAD;EACO;EACL,WAAWK,EAAGX,EAAO,QAAQY,CAAS;EACtC,eAAaM,KAAU,KAAA;EACvB,GAAIZ;CACL,CAAA;AAEL,CAAC,GAKKa,IAAOd,EAA2C,SACtD,EAAE,cAAW,GAAGC,KAChB,GACA;CACA,IAAMc,IAAMC,EAAWnB,CAAY;CACnC,OACE,kBAAC,QAAD;EACO;EACL,IAAIkB,GAAK;EACT,UAAU;EACV,WAAWT,EAAGX,EAAO,MAAMY,CAAS;EACpC,GAAIN;CACL,CAAA;AAEL,CAAC,GAQKgB,IAAUjB,EAA8C,SAC5D,EAAE,UAAO,SAAS,cAAW,GAAGC,KAChC,GACA;CACA,OAAO,kBAAC,SAAD;EAAY;EAAK,WAAWK,EAAGX,EAAO,SAASY,CAAS;EAAG,aAAWW;EAAM,GAAIjB;CAAO,CAAA;AAChG,CAAC,GAKKkB,IAASnB,EAA6C,SAC1D,EAAE,cAAW,GAAGC,KAChB,GACA;CACA,OAAO,kBAAC,UAAD;EAAa;EAAK,WAAWK,EAAGX,EAAO,QAAQY,CAAS;EAAG,GAAIN;CAAO,CAAA;AAC/E,CAAC,GASKmB,IAAYpB,EAAmD,SACnE,EAAE,cAAW,GAAGC,KAChB,GACA;CACA,OAAO,kBAAC,OAAD;EAAU;EAAK,WAAWK,EAAGX,EAAO,WAAWY,CAAS;EAAG,GAAIN;CAAO,CAAA;AAC/E,CAAC,GAEYoB,IAAW,OAAO,OAAOtB,GAAc;CAAE;CAAQ;CAAM;CAAS;CAAQ;AAAU,CAAC"}
|
|
@@ -5,6 +5,14 @@ export interface AspectRatioProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
5
5
|
ratio?: AspectRatioPreset | number;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
+
* A frame that keeps its proportions, for images, video, maps and charts.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* `AspectRatio` holds a ratio as its width changes. Its child (an image, a video, an iframe, an
|
|
12
|
+
* SVG) fills it and is cropped with `object-fit: cover`. It has no edge or radius of its own: wrap
|
|
13
|
+
* it in `Media` or a `Card` for those.
|
|
14
|
+
*
|
|
15
|
+
* @privateRemarks
|
|
8
16
|
* A frame that holds its proportions; its child (image, video, iframe, SVG) fills it
|
|
9
17
|
* and is cropped with `object-fit: cover`. It has no edge or radius of its own —
|
|
10
18
|
* wrap it (Media, Card) for that.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AspectRatio.js","names":["styles","AspectRatio_module_default","toCss","ratio","AspectRatio","forwardRef","rest","cx","className","mergeStyles","style"],"sources":["../../../../src/components/layout/AspectRatio/AspectRatio.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport styles from './AspectRatio.module.css'\n\nexport type AspectRatioPreset = '1/1' | '4/3' | '3/2' | '16/9' | '21/9' | '3/4'\n\nexport interface AspectRatioProps extends HTMLAttributes<HTMLDivElement> {\n /** Width ÷ height, as a preset or a number (e.g. `1.91`). Default `16/9`. */\n ratio?: AspectRatioPreset | number\n}\n\nconst toCss = (ratio: AspectRatioPreset | number): string =>\n typeof ratio === 'number' ? String(ratio) : ratio.replace('/', ' / ')\n\n/**\n * A frame that holds its proportions; its child (image, video, iframe, SVG) fills it\n * and is cropped with `object-fit: cover`. It has no edge or radius of its own —\n * wrap it (Media, Card) for that.\n *\n * <AspectRatio ratio=\"4/3\"><img src=\"/images/dashboard.png\" alt=\"Revenue dashboard\" /></AspectRatio>\n */\nexport const AspectRatio = forwardRef<HTMLDivElement, AspectRatioProps>(function AspectRatio(\n { ratio = '16/9', className, style, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cx(styles.frame, className)}\n style={mergeStyles({ '--aspect-ratio': toCss(ratio) } as CSSProperties, style)}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;AAYA,IAAME,KAAS,MACb,OAAOC,KAAU,WAAW,OAAOA,CAAK,IAAIA,EAAM,QAAQ,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"AspectRatio.js","names":["styles","AspectRatio_module_default","toCss","ratio","AspectRatio","forwardRef","rest","cx","className","mergeStyles","style"],"sources":["../../../../src/components/layout/AspectRatio/AspectRatio.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport styles from './AspectRatio.module.css'\n\nexport type AspectRatioPreset = '1/1' | '4/3' | '3/2' | '16/9' | '21/9' | '3/4'\n\nexport interface AspectRatioProps extends HTMLAttributes<HTMLDivElement> {\n /** Width ÷ height, as a preset or a number (e.g. `1.91`). Default `16/9`. */\n ratio?: AspectRatioPreset | number\n}\n\nconst toCss = (ratio: AspectRatioPreset | number): string =>\n typeof ratio === 'number' ? String(ratio) : ratio.replace('/', ' / ')\n\n/**\n * A frame that keeps its proportions, for images, video, maps and charts.\n *\n * @remarks\n * `AspectRatio` holds a ratio as its width changes. Its child (an image, a video, an iframe, an\n * SVG) fills it and is cropped with `object-fit: cover`. It has no edge or radius of its own: wrap\n * it in `Media` or a `Card` for those.\n *\n * @privateRemarks\n * A frame that holds its proportions; its child (image, video, iframe, SVG) fills it\n * and is cropped with `object-fit: cover`. It has no edge or radius of its own —\n * wrap it (Media, Card) for that.\n *\n * <AspectRatio ratio=\"4/3\"><img src=\"/images/dashboard.png\" alt=\"Revenue dashboard\" /></AspectRatio>\n */\nexport const AspectRatio = forwardRef<HTMLDivElement, AspectRatioProps>(function AspectRatio(\n { ratio = '16/9', className, style, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cx(styles.frame, className)}\n style={mergeStyles({ '--aspect-ratio': toCss(ratio) } as CSSProperties, style)}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;AAYA,IAAME,KAAS,MACb,OAAOC,KAAU,WAAW,OAAOA,CAAK,IAAIA,EAAM,QAAQ,KAAK,KAAK,GAiBzDC,IAAcC,EAA6C,SACtE,EAAE,WAAQ,QAAQ,cAAW,UAAO,GAAGC,KACvC,GACA;CACA,OACE,kBAAC,OAAD;EACO;EACL,WAAWC,EAAGP,EAAO,OAAOQ,CAAS;EACrC,OAAOC,EAAY,EAAE,kBAAkBP,EAAMC,CAAK,EAAE,GAAoBO,CAAK;EAC7E,GAAIJ;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -21,6 +21,14 @@ export interface BoxProps extends HTMLAttributes<HTMLElement>, VisibilityProps {
|
|
|
21
21
|
as?: BoxElement;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
+
* Padding, a surface and an edge, and nothing else. The escape hatch when no other layout fits.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* `Box` adds padding, a background surface, a border and a radius to its content. Reach for
|
|
28
|
+
* `Stack`, `Inline` or `Grid` to arrange things, and for `Card` when something is a self-contained
|
|
29
|
+
* object. `Box` is for the wells and frames left over.
|
|
30
|
+
*
|
|
31
|
+
* @privateRemarks
|
|
24
32
|
* The escape hatch: padding, a surface and an edge — nothing else. Reach for
|
|
25
33
|
* Stack/Inline/Grid for arrangement and Card for self-contained objects first.
|
|
26
34
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Box.js","names":["styles","Box_module_default","Box","forwardRef","Comp","rest","cx","visibilityClass","className","surface","border","radius","mergeStyles","responsiveVars","padding","space","paddingX","paddingY","style"],"sources":["../../../../src/components/layout/Box/Box.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Box.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type BoxElement =\n 'div' | 'span' | 'section' | 'article' | 'aside' | 'header' | 'footer' | 'li'\nexport type BoxSurface = 'none' | 'canvas' | 'surface' | 'sunken' | 'raised' | 'inverse'\nexport type BoxRadius = 'none' | 'field' | 'surface' | 'media'\n\nexport interface BoxProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Padding on every side. Responsive. */\n padding?: Responsive<Space>\n /** Inline (left/right) padding; overrides `padding` on that axis. Responsive. */\n paddingX?: Responsive<Space>\n /** Block (top/bottom) padding; overrides `padding` on that axis. Responsive. */\n paddingY?: Responsive<Space>\n /** Background fill. `inverse` also re-points ink and line colours for its children. */\n surface?: BoxSurface\n /** Hairline border in `--color-line`. One edge treatment per element — a border *or* a fill. */\n border?: boolean\n /** Corner radius, by role. */\n radius?: BoxRadius\n as?: BoxElement\n}\n\n/**\n * The escape hatch: padding, a surface and an edge — nothing else. Reach for\n * Stack/Inline/Grid for arrangement and Card for self-contained objects first.\n *\n * <Box padding={{ base: 4, md: 5 }} surface=\"sunken\" radius=\"surface\">…</Box>\n */\nexport const Box = forwardRef<HTMLElement, BoxProps>(function Box(\n {\n padding,\n paddingX,\n paddingY,\n surface = 'none',\n border = false,\n radius = 'none',\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.box, visibilityClass({ hideBelow, hideAbove }), className)}\n data-surface={surface === 'none' ? undefined : surface}\n data-border={border || undefined}\n data-radius={radius === 'none' ? undefined : radius}\n style={mergeStyles(\n responsiveVars('box-p', padding, space),\n responsiveVars('box-px', paddingX, space),\n responsiveVars('box-py', paddingY, space),\n style,\n )}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"Box.js","names":["styles","Box_module_default","Box","forwardRef","Comp","rest","cx","visibilityClass","className","surface","border","radius","mergeStyles","responsiveVars","padding","space","paddingX","paddingY","style"],"sources":["../../../../src/components/layout/Box/Box.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Box.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type BoxElement =\n 'div' | 'span' | 'section' | 'article' | 'aside' | 'header' | 'footer' | 'li'\nexport type BoxSurface = 'none' | 'canvas' | 'surface' | 'sunken' | 'raised' | 'inverse'\nexport type BoxRadius = 'none' | 'field' | 'surface' | 'media'\n\nexport interface BoxProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Padding on every side. Responsive. */\n padding?: Responsive<Space>\n /** Inline (left/right) padding; overrides `padding` on that axis. Responsive. */\n paddingX?: Responsive<Space>\n /** Block (top/bottom) padding; overrides `padding` on that axis. Responsive. */\n paddingY?: Responsive<Space>\n /** Background fill. `inverse` also re-points ink and line colours for its children. */\n surface?: BoxSurface\n /** Hairline border in `--color-line`. One edge treatment per element — a border *or* a fill. */\n border?: boolean\n /** Corner radius, by role. */\n radius?: BoxRadius\n as?: BoxElement\n}\n\n/**\n * Padding, a surface and an edge, and nothing else. The escape hatch when no other layout fits.\n *\n * @remarks\n * `Box` adds padding, a background surface, a border and a radius to its content. Reach for\n * `Stack`, `Inline` or `Grid` to arrange things, and for `Card` when something is a self-contained\n * object. `Box` is for the wells and frames left over.\n *\n * @privateRemarks\n * The escape hatch: padding, a surface and an edge — nothing else. Reach for\n * Stack/Inline/Grid for arrangement and Card for self-contained objects first.\n *\n * <Box padding={{ base: 4, md: 5 }} surface=\"sunken\" radius=\"surface\">…</Box>\n */\nexport const Box = forwardRef<HTMLElement, BoxProps>(function Box(\n {\n padding,\n paddingX,\n paddingY,\n surface = 'none',\n border = false,\n radius = 'none',\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.box, visibilityClass({ hideBelow, hideAbove }), className)}\n data-surface={surface === 'none' ? undefined : surface}\n data-border={border || undefined}\n data-radius={radius === 'none' ? undefined : radius}\n style={mergeStyles(\n responsiveVars('box-p', padding, space),\n responsiveVars('box-px', paddingX, space),\n responsiveVars('box-py', paddingY, space),\n style,\n )}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;;;AA0CA,IAAaE,IAAMC,EAAkC,SACnD,EACE,YACA,aACA,aACA,aAAU,QACV,YAAS,IACT,YAAS,QACT,IAAIC,IAAO,OACX,cACA,cACA,cACA,UACA,GAAGC,KAEL,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGN,EAAO,KAAKO,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EAC9E,gBAAcC,MAAY,SAAS,KAAA,IAAYA;EAC/C,eAAaC,KAAU,KAAA;EACvB,eAAaC,MAAW,SAAS,KAAA,IAAYA;EAC7C,OAAOC,EACLC,EAAe,SAASC,GAASC,CAAK,GACtCF,EAAe,UAAUG,GAAUD,CAAK,GACxCF,EAAe,UAAUI,GAAUF,CAAK,GACxCG,CACF;EACA,GAAIb;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -13,6 +13,23 @@ export interface ContainerProps extends HTMLAttributes<HTMLElement>, VisibilityP
|
|
|
13
13
|
as?: ContainerElement;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
+
* Centres content at a readable maximum width, with a fluid gutter outside it.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* `Container` sets a maximum width and centres its content. The gutter sits _outside_ the width,
|
|
20
|
+
* so `width="text"` is a true reading measure at any screen size.
|
|
21
|
+
*
|
|
22
|
+
* | `width` | Max width | For |
|
|
23
|
+
* | --------- | --------- | ----------------------------------- |
|
|
24
|
+
* | `narrow` | 32rem | sign-in, short forms, confirmations |
|
|
25
|
+
* | `text` | 42rem | articles and long reading |
|
|
26
|
+
* | `content` | 68rem | most pages (the default) |
|
|
27
|
+
* | `wide` | 84rem | dashboards and wide tables |
|
|
28
|
+
* | `full` | 100% | edge to edge, inside the gutter |
|
|
29
|
+
*
|
|
30
|
+
* Pass `gutter={false}` when the parent already pads its content.
|
|
31
|
+
*
|
|
32
|
+
* @privateRemarks
|
|
16
33
|
* Centres content at a readable maximum width with a fluid gutter. The gutter sits
|
|
17
34
|
* outside the width, so `width="text"` is always a true prose measure.
|
|
18
35
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Container.js","names":["styles","Container_module_default","Container","forwardRef","Comp","rest","cx","visibilityClass","className","width","gutter"],"sources":["../../../../src/components/layout/Container/Container.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport type { Width } from '#utils/tokens'\nimport styles from './Container.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type ContainerElement = 'div' | 'section' | 'main' | 'header' | 'footer' | 'article' | 'nav'\n\nexport interface ContainerProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /**\n * Maximum content width: `narrow` 32rem · `text` 42rem (prose measure) ·\n * `content` 68rem (default) · `wide` 84rem · `full` (no maximum).\n */\n width?: Width\n /** Keep the fluid page gutter on either side. Default `true`. */\n gutter?: boolean\n as?: ContainerElement\n}\n\n/**\n * Centres content at a readable maximum width with a fluid gutter. The gutter sits\n * outside the width, so `width=\"text\"` is always a true prose measure.\n *\n * <Container width=\"text\">…</Container>\n */\nexport const Container = forwardRef<HTMLElement, ContainerProps>(function Container(\n { width = 'content', gutter = true, as: Comp = 'div', hideBelow, hideAbove, className, ...rest },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.container, visibilityClass({ hideBelow, hideAbove }), className)}\n data-width={width}\n data-gutter={gutter ? undefined : 'false'}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"Container.js","names":["styles","Container_module_default","Container","forwardRef","Comp","rest","cx","visibilityClass","className","width","gutter"],"sources":["../../../../src/components/layout/Container/Container.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport type { Width } from '#utils/tokens'\nimport styles from './Container.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type ContainerElement = 'div' | 'section' | 'main' | 'header' | 'footer' | 'article' | 'nav'\n\nexport interface ContainerProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /**\n * Maximum content width: `narrow` 32rem · `text` 42rem (prose measure) ·\n * `content` 68rem (default) · `wide` 84rem · `full` (no maximum).\n */\n width?: Width\n /** Keep the fluid page gutter on either side. Default `true`. */\n gutter?: boolean\n as?: ContainerElement\n}\n\n/**\n * Centres content at a readable maximum width, with a fluid gutter outside it.\n *\n * @remarks\n * `Container` sets a maximum width and centres its content. The gutter sits _outside_ the width,\n * so `width=\"text\"` is a true reading measure at any screen size.\n *\n * | `width` | Max width | For |\n * | --------- | --------- | ----------------------------------- |\n * | `narrow` | 32rem | sign-in, short forms, confirmations |\n * | `text` | 42rem | articles and long reading |\n * | `content` | 68rem | most pages (the default) |\n * | `wide` | 84rem | dashboards and wide tables |\n * | `full` | 100% | edge to edge, inside the gutter |\n *\n * Pass `gutter={false}` when the parent already pads its content.\n *\n * @privateRemarks\n * Centres content at a readable maximum width with a fluid gutter. The gutter sits\n * outside the width, so `width=\"text\"` is always a true prose measure.\n *\n * <Container width=\"text\">…</Container>\n */\nexport const Container = forwardRef<HTMLElement, ContainerProps>(function Container(\n { width = 'content', gutter = true, as: Comp = 'div', hideBelow, hideAbove, className, ...rest },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.container, visibilityClass({ hideBelow, hideAbove }), className)}\n data-width={width}\n data-gutter={gutter ? undefined : 'false'}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;AA0CA,IAAaE,IAAYC,EAAwC,SAC/D,EAAE,WAAQ,WAAW,YAAS,IAAM,IAAIC,IAAO,OAAO,cAAW,cAAW,cAAW,GAAGC,KAC1F,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGN,EAAO,WAAWO,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EACpF,cAAYC;EACZ,eAAaC,IAAS,KAAA,IAAY;EAClC,GAAIL;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -20,6 +20,13 @@ export interface DividerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chil
|
|
|
20
20
|
decorative?: boolean;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
+
* A hairline rule between groups of content, optionally labelled, horizontal or vertical.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* `Divider` separates groups. For a rule between every item of a list, use `Stack dividers`
|
|
27
|
+
* instead, which spaces the rules for you.
|
|
28
|
+
*
|
|
29
|
+
* @privateRemarks
|
|
23
30
|
* A hairline rule between groups of content. Prefer `Stack dividers` for rules
|
|
24
31
|
* between every item of a list.
|
|
25
32
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Divider.js","names":["styles","Divider_module_default","Divider","forwardRef","rest","labelId","useId","hasLabel","label","orientation","cx","visibilityClass","className","decorative","strong","labelPosition","mergeStyles","spacing","space","style"],"sources":["../../../../src/components/layout/Divider/Divider.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Divider.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type DividerOrientation = 'horizontal' | 'vertical'\n\nexport interface DividerProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, VisibilityProps {\n /** Default `horizontal`. A vertical rule stretches to its flex/grid row. */\n orientation?: DividerOrientation\n /** Heavier rule in `--color-line-strong` at `--border-width-strong`. */\n strong?: boolean\n /** Text set into a horizontal rule, e.g. \"Earlier this week\". It also names the separator. */\n label?: ReactNode\n /** Where the label sits. Default `start` (left-aligned by default); `center` for a lone break. */\n labelPosition?: 'start' | 'center'\n /** Margin on both sides of the rule (block axis if horizontal, inline if vertical). */\n spacing?: Space\n /**\n * Purely visual: hide it from assistive tech. Use when the rule only repeats a\n * boundary that headings or landmarks already convey.\n */\n decorative?: boolean\n}\n\n/**\n * A hairline rule between groups of content. Prefer `Stack dividers` for rules\n * between every item of a list.\n *\n * <Divider label=\"Earlier this week\" spacing={6} />\n */\nexport const Divider = forwardRef<HTMLDivElement, DividerProps>(function Divider(\n {\n orientation = 'horizontal',\n strong = false,\n label,\n labelPosition = 'start',\n spacing,\n decorative = false,\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n const labelId = useId()\n const hasLabel =\n label !== undefined && label !== null && label !== false && orientation === 'horizontal'\n return (\n <div\n ref={ref}\n className={cx(styles.divider, visibilityClass({ hideBelow, hideAbove }), className)}\n role={decorative ? 'none' : 'separator'}\n aria-orientation={!decorative && orientation === 'vertical' ? 'vertical' : undefined}\n aria-labelledby={!decorative && hasLabel ? labelId : undefined}\n data-orientation={orientation}\n data-strong={strong || undefined}\n data-label-position={hasLabel ? labelPosition : undefined}\n style={mergeStyles(\n spacing === undefined\n ? undefined\n : ({ '--divider-spacing': space(spacing) } as CSSProperties),\n style,\n )}\n {...rest}\n >\n {hasLabel ? (\n <span id={labelId} className={styles.label}>\n {label}\n </span>\n ) : null}\n </div>\n )\n})\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"Divider.js","names":["styles","Divider_module_default","Divider","forwardRef","rest","labelId","useId","hasLabel","label","orientation","cx","visibilityClass","className","decorative","strong","labelPosition","mergeStyles","spacing","space","style"],"sources":["../../../../src/components/layout/Divider/Divider.tsx"],"sourcesContent":["import { forwardRef, useId, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Divider.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type DividerOrientation = 'horizontal' | 'vertical'\n\nexport interface DividerProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, VisibilityProps {\n /** Default `horizontal`. A vertical rule stretches to its flex/grid row. */\n orientation?: DividerOrientation\n /** Heavier rule in `--color-line-strong` at `--border-width-strong`. */\n strong?: boolean\n /** Text set into a horizontal rule, e.g. \"Earlier this week\". It also names the separator. */\n label?: ReactNode\n /** Where the label sits. Default `start` (left-aligned by default); `center` for a lone break. */\n labelPosition?: 'start' | 'center'\n /** Margin on both sides of the rule (block axis if horizontal, inline if vertical). */\n spacing?: Space\n /**\n * Purely visual: hide it from assistive tech. Use when the rule only repeats a\n * boundary that headings or landmarks already convey.\n */\n decorative?: boolean\n}\n\n/**\n * A hairline rule between groups of content, optionally labelled, horizontal or vertical.\n *\n * @remarks\n * `Divider` separates groups. For a rule between every item of a list, use `Stack dividers`\n * instead, which spaces the rules for you.\n *\n * @privateRemarks\n * A hairline rule between groups of content. Prefer `Stack dividers` for rules\n * between every item of a list.\n *\n * <Divider label=\"Earlier this week\" spacing={6} />\n */\nexport const Divider = forwardRef<HTMLDivElement, DividerProps>(function Divider(\n {\n orientation = 'horizontal',\n strong = false,\n label,\n labelPosition = 'start',\n spacing,\n decorative = false,\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n const labelId = useId()\n const hasLabel =\n label !== undefined && label !== null && label !== false && orientation === 'horizontal'\n return (\n <div\n ref={ref}\n className={cx(styles.divider, visibilityClass({ hideBelow, hideAbove }), className)}\n role={decorative ? 'none' : 'separator'}\n aria-orientation={!decorative && orientation === 'vertical' ? 'vertical' : undefined}\n aria-labelledby={!decorative && hasLabel ? labelId : undefined}\n data-orientation={orientation}\n data-strong={strong || undefined}\n data-label-position={hasLabel ? labelPosition : undefined}\n style={mergeStyles(\n spacing === undefined\n ? undefined\n : ({ '--divider-spacing': space(spacing) } as CSSProperties),\n style,\n )}\n {...rest}\n >\n {hasLabel ? (\n <span id={labelId} className={styles.label}>\n {label}\n </span>\n ) : null}\n </div>\n )\n})\n"],"mappings":";;;;;;;;AAyCA,IAAaE,IAAUC,EAAyC,SAC9D,EACE,iBAAc,cACd,YAAS,IACT,UACA,mBAAgB,SAChB,YACA,gBAAa,IACb,cACA,cACA,cACA,UACA,GAAGC,KAEL,GACA;CACA,IAAMC,IAAUC,EAAM,GAChBC,IACJC,KAAiC,QAAQA,MAAU,MAASC,MAAgB;CAC9E,OACE,kBAAC,OAAD;EACO;EACL,WAAWC,EAAGV,EAAO,SAASW,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EAClF,MAAMC,IAAa,SAAS;EAC5B,oBAAkB,CAACA,KAAcJ,MAAgB,aAAa,aAAa,KAAA;EAC3E,mBAAiB,CAACI,KAAcN,IAAWF,IAAU,KAAA;EACrD,oBAAkBI;EAClB,eAAaK,KAAU,KAAA;EACvB,uBAAqBP,IAAWQ,IAAgB,KAAA;EAChD,OAAOC,EACLC,MAAY,KAAA,IACR,KAAA,IACC,EAAE,qBAAqBC,EAAMD,CAAO,EAAE,GAC3CE,CACF;EACA,GAAIf;EAEH,UAAAG,IACC,kBAAC,QAAD;GAAM,IAAIF;GAAS,WAAWL,EAAO;GAClC,UAAAQ;EACG,CAAA,IACJ;CACD,CAAA;AAET,CAAC"}
|
|
@@ -38,6 +38,15 @@ export interface GridItemProps extends HTMLAttributes<HTMLElement>, VisibilityPr
|
|
|
38
38
|
as?: GridItemElement;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
+
* Two-dimensional layout, either with a column count per breakpoint or as an auto-fill grid that
|
|
42
|
+
* needs no breakpoints.
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* `Grid` has two modes. With `columns`, you choose how many equal columns there are, per
|
|
46
|
+
* breakpoint. With `minItemWidth`, the grid fits as many columns as there's room for, each at
|
|
47
|
+
* least that wide, so it adapts to its container without a single breakpoint.
|
|
48
|
+
*
|
|
49
|
+
* @privateRemarks
|
|
41
50
|
* Two-dimensional layout: either a fixed column count (responsive) or an
|
|
42
51
|
* intrinsic auto-fill grid that needs no breakpoints at all.
|
|
43
52
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Grid.js","names":["styles","Grid_module_default","tracks","n","GridRoot","forwardRef","Comp","rest","cx","visibilityClass","className","minItemWidth","mergeStyles","responsiveVars","columns","gap","space","rowGap","align","alignCss","style","GridItem","span","s","start","Grid"],"sources":["../../../../src/components/layout/Grid/Grid.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { alignCss, space, type Align, type Space } from '#utils/tokens'\nimport styles from './Grid.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type GridColumns = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12\nexport type GridMinItemWidth = 'xs' | 'sm' | 'md' | 'lg'\nexport type GridElement = 'div' | 'section' | 'ul' | 'ol' | 'dl'\nexport type GridItemElement = 'div' | 'li' | 'article' | 'section' | 'aside'\n\ninterface GridBaseProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Space between cells (both axes). Responsive. */\n gap?: Responsive<Space>\n /** Row gap, if different from `gap`. Responsive. */\n rowGap?: Responsive<Space>\n /** Cell alignment on the block axis. Default `stretch`. Responsive. */\n align?: Responsive<Align>\n as?: GridElement\n}\n\ninterface GridColumnsProps extends GridBaseProps {\n /** A fixed track count, 1–12. Responsive: `{ base: 1, sm: 2, lg: 4 }`. */\n columns?: Responsive<GridColumns>\n minItemWidth?: never\n}\n\ninterface GridAutoProps extends GridBaseProps {\n columns?: never\n /**\n * Fit as many tracks as there's room for, each at least this wide\n * (xs 12rem · sm 16rem · md 20rem · lg 24rem). No breakpoints needed.\n */\n minItemWidth: GridMinItemWidth\n}\n\nexport type GridProps = GridColumnsProps | GridAutoProps\n\nconst tracks = (n: GridColumns): string => `repeat(${String(n)}, minmax(0, 1fr))`\n\nconst GridRoot = forwardRef<HTMLElement, GridProps>(function Grid(\n {\n columns,\n minItemWidth,\n gap,\n rowGap,\n align,\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.grid, visibilityClass({ hideBelow, hideAbove }), className)}\n data-min-item={minItemWidth}\n role={Comp === 'ul' || Comp === 'ol' ? 'list' : undefined}\n style={mergeStyles(\n minItemWidth ? undefined : responsiveVars('grid-columns', columns, tracks),\n responsiveVars('grid-gap', gap, space),\n responsiveVars('grid-row-gap', rowGap, space),\n responsiveVars('grid-align', align, alignCss),\n style,\n )}\n {...rest}\n />\n )\n})\n\nexport type GridSpan = GridColumns | 'full'\n\nexport interface GridItemProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Columns to span, or `full` for edge to edge. Responsive. */\n span?: Responsive<GridSpan>\n /** Column line to start on (1-based). Responsive. */\n start?: Responsive<GridColumns>\n as?: GridItemElement\n}\n\n/**\n * A cell that spans or starts somewhere specific. Plain children don't need it.\n *\n * <Grid.Item span={{ base: 'full', md: 8 }} start={{ md: 3 }}>…</Grid.Item>\n */\nconst GridItem = forwardRef<HTMLElement, GridItemProps>(function GridItem(\n { span, start, as: Comp = 'div', hideBelow, hideAbove, className, style, ...rest },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.item, visibilityClass({ hideBelow, hideAbove }), className)}\n style={mergeStyles(\n responsiveVars('grid-item-end', span, (s) => (s === 'full' ? '-1' : `span ${String(s)}`)),\n // `full` must also pin the start line to 1; an explicit `start` still wins.\n responsiveVars('grid-item-full', span, (s) => (s === 'full' ? '1' : 'auto')),\n responsiveVars('grid-item-start', start, String),\n style,\n )}\n {...rest}\n />\n )\n})\n\n/**\n * Two-dimensional layout: either a fixed column count (responsive) or an\n * intrinsic auto-fill grid that needs no breakpoints at all.\n *\n * <Grid columns={{ base: 1, sm: 2, lg: 4 }} gap={5}>…</Grid>\n * <Grid minItemWidth=\"sm\" gap={4}>…</Grid>\n */\nexport const Grid = Object.assign(GridRoot, { Item: GridItem })\n"],"mappings":";;;;;;;;AAuCA,IAAME,KAAU,MAA2B,UAAU,OAAOC,CAAC,EAAE,oBAEzDC,IAAWC,EAAmC,SAClD,EACE,YACA,iBACA,QACA,WACA,UACA,IAAIC,IAAO,OACX,cACA,cACA,cACA,UACA,GAAGC,KAEL,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGR,EAAO,MAAMS,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EAC/E,iBAAeC;EACf,MAAML,MAAS,QAAQA,MAAS,OAAO,SAAS,KAAA;EAChD,OAAOM,EACLD,IAAe,KAAA,IAAYE,EAAe,gBAAgBC,GAASZ,CAAM,GACzEW,EAAe,YAAYE,GAAKC,CAAK,GACrCH,EAAe,gBAAgBI,GAAQD,CAAK,GAC5CH,EAAe,cAAcK,GAAOC,CAAQ,GAC5CC,CACF;EACA,GAAIb;CACL,CAAA;AAEL,CAAC,GAiBKc,IAAWhB,EAAuC,SACtD,EAAE,SAAM,UAAO,IAAIC,IAAO,OAAO,cAAW,cAAW,cAAW,UAAO,GAAGC,KAC5E,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGR,EAAO,MAAMS,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EAC/E,OAAOE,EACLC,EAAe,iBAAiBS,IAAO,MAAOC,MAAM,SAAS,OAAO,QAAQ,OAAOA,CAAC,GAAI,GAExFV,EAAe,kBAAkBS,IAAO,MAAOC,MAAM,SAAS,MAAM,MAAO,GAC3EV,EAAe,mBAAmBW,GAAO,MAAM,GAC/CJ,CACF;EACA,GAAIb;CACL,CAAA;AAEL,CAAC,
|
|
1
|
+
{"version":3,"file":"Grid.js","names":["styles","Grid_module_default","tracks","n","GridRoot","forwardRef","Comp","rest","cx","visibilityClass","className","minItemWidth","mergeStyles","responsiveVars","columns","gap","space","rowGap","align","alignCss","style","GridItem","span","s","start","Grid"],"sources":["../../../../src/components/layout/Grid/Grid.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { alignCss, space, type Align, type Space } from '#utils/tokens'\nimport styles from './Grid.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type GridColumns = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12\nexport type GridMinItemWidth = 'xs' | 'sm' | 'md' | 'lg'\nexport type GridElement = 'div' | 'section' | 'ul' | 'ol' | 'dl'\nexport type GridItemElement = 'div' | 'li' | 'article' | 'section' | 'aside'\n\ninterface GridBaseProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Space between cells (both axes). Responsive. */\n gap?: Responsive<Space>\n /** Row gap, if different from `gap`. Responsive. */\n rowGap?: Responsive<Space>\n /** Cell alignment on the block axis. Default `stretch`. Responsive. */\n align?: Responsive<Align>\n as?: GridElement\n}\n\ninterface GridColumnsProps extends GridBaseProps {\n /** A fixed track count, 1–12. Responsive: `{ base: 1, sm: 2, lg: 4 }`. */\n columns?: Responsive<GridColumns>\n minItemWidth?: never\n}\n\ninterface GridAutoProps extends GridBaseProps {\n columns?: never\n /**\n * Fit as many tracks as there's room for, each at least this wide\n * (xs 12rem · sm 16rem · md 20rem · lg 24rem). No breakpoints needed.\n */\n minItemWidth: GridMinItemWidth\n}\n\nexport type GridProps = GridColumnsProps | GridAutoProps\n\nconst tracks = (n: GridColumns): string => `repeat(${String(n)}, minmax(0, 1fr))`\n\nconst GridRoot = forwardRef<HTMLElement, GridProps>(function Grid(\n {\n columns,\n minItemWidth,\n gap,\n rowGap,\n align,\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.grid, visibilityClass({ hideBelow, hideAbove }), className)}\n data-min-item={minItemWidth}\n role={Comp === 'ul' || Comp === 'ol' ? 'list' : undefined}\n style={mergeStyles(\n minItemWidth ? undefined : responsiveVars('grid-columns', columns, tracks),\n responsiveVars('grid-gap', gap, space),\n responsiveVars('grid-row-gap', rowGap, space),\n responsiveVars('grid-align', align, alignCss),\n style,\n )}\n {...rest}\n />\n )\n})\n\nexport type GridSpan = GridColumns | 'full'\n\nexport interface GridItemProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Columns to span, or `full` for edge to edge. Responsive. */\n span?: Responsive<GridSpan>\n /** Column line to start on (1-based). Responsive. */\n start?: Responsive<GridColumns>\n as?: GridItemElement\n}\n\n/**\n * A cell that spans or starts somewhere specific. Plain children don't need it.\n *\n * <Grid.Item span={{ base: 'full', md: 8 }} start={{ md: 3 }}>…</Grid.Item>\n */\nconst GridItem = forwardRef<HTMLElement, GridItemProps>(function GridItem(\n { span, start, as: Comp = 'div', hideBelow, hideAbove, className, style, ...rest },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.item, visibilityClass({ hideBelow, hideAbove }), className)}\n style={mergeStyles(\n responsiveVars('grid-item-end', span, (s) => (s === 'full' ? '-1' : `span ${String(s)}`)),\n // `full` must also pin the start line to 1; an explicit `start` still wins.\n responsiveVars('grid-item-full', span, (s) => (s === 'full' ? '1' : 'auto')),\n responsiveVars('grid-item-start', start, String),\n style,\n )}\n {...rest}\n />\n )\n})\n\n/**\n * Two-dimensional layout, either with a column count per breakpoint or as an auto-fill grid that\n * needs no breakpoints.\n *\n * @remarks\n * `Grid` has two modes. With `columns`, you choose how many equal columns there are, per\n * breakpoint. With `minItemWidth`, the grid fits as many columns as there's room for, each at\n * least that wide, so it adapts to its container without a single breakpoint.\n *\n * @privateRemarks\n * Two-dimensional layout: either a fixed column count (responsive) or an\n * intrinsic auto-fill grid that needs no breakpoints at all.\n *\n * <Grid columns={{ base: 1, sm: 2, lg: 4 }} gap={5}>…</Grid>\n * <Grid minItemWidth=\"sm\" gap={4}>…</Grid>\n */\nexport const Grid = Object.assign(GridRoot, { Item: GridItem })\n"],"mappings":";;;;;;;;AAuCA,IAAME,KAAU,MAA2B,UAAU,OAAOC,CAAC,EAAE,oBAEzDC,IAAWC,EAAmC,SAClD,EACE,YACA,iBACA,QACA,WACA,UACA,IAAIC,IAAO,OACX,cACA,cACA,cACA,UACA,GAAGC,KAEL,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGR,EAAO,MAAMS,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EAC/E,iBAAeC;EACf,MAAML,MAAS,QAAQA,MAAS,OAAO,SAAS,KAAA;EAChD,OAAOM,EACLD,IAAe,KAAA,IAAYE,EAAe,gBAAgBC,GAASZ,CAAM,GACzEW,EAAe,YAAYE,GAAKC,CAAK,GACrCH,EAAe,gBAAgBI,GAAQD,CAAK,GAC5CH,EAAe,cAAcK,GAAOC,CAAQ,GAC5CC,CACF;EACA,GAAIb;CACL,CAAA;AAEL,CAAC,GAiBKc,IAAWhB,EAAuC,SACtD,EAAE,SAAM,UAAO,IAAIC,IAAO,OAAO,cAAW,cAAW,cAAW,UAAO,GAAGC,KAC5E,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGR,EAAO,MAAMS,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EAC/E,OAAOE,EACLC,EAAe,iBAAiBS,IAAO,MAAOC,MAAM,SAAS,OAAO,QAAQ,OAAOA,CAAC,GAAI,GAExFV,EAAe,kBAAkBS,IAAO,MAAOC,MAAM,SAAS,MAAM,MAAO,GAC3EV,EAAe,mBAAmBW,GAAO,MAAM,GAC/CJ,CACF;EACA,GAAIb;CACL,CAAA;AAEL,CAAC,GAkBYkB,IAAO,OAAO,OAAOrB,GAAU,EAAE,MAAMiB,EAAS,CAAC"}
|
|
@@ -17,6 +17,14 @@ export interface InlineProps extends HTMLAttributes<HTMLElement>, VisibilityProp
|
|
|
17
17
|
as?: InlineElement;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
+
* Things in a row that wrap when they run out of room. Button rows, tag lists, toolbars and meta
|
|
21
|
+
* lines.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* `Inline` lays its children out horizontally with a gap, vertically centred, wrapping onto new
|
|
25
|
+
* lines when they don't fit.
|
|
26
|
+
*
|
|
27
|
+
* @privateRemarks
|
|
20
28
|
* Horizontal flow: button rows, tag lists, meta lines, toolbars.
|
|
21
29
|
*
|
|
22
30
|
* <Inline gap={3} justify={{ base: 'start', md: 'between' }}>…</Inline>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Inline.js","names":["styles","Inline_module_default","Inline","forwardRef","Comp","rest","cx","visibilityClass","className","wrap","mergeStyles","responsiveVars","gap","space","rowGap","align","alignCss","justify","justifyCss","style"],"sources":["../../../../src/components/layout/Inline/Inline.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { alignCss, justifyCss, space, type Align, type Justify, type Space } from '#utils/tokens'\nimport styles from './Inline.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type InlineElement = 'div' | 'span' | 'nav' | 'ul' | 'ol' | 'li' | 'header' | 'footer' | 'p'\n\nexport interface InlineProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Space between items (both axes when wrapping). Responsive. */\n gap?: Responsive<Space>\n /** Row gap when wrapped, if different from `gap`. Responsive. */\n rowGap?: Responsive<Space>\n /** Cross-axis alignment. Default `center`. Responsive. */\n align?: Responsive<Align>\n /** Main-axis distribution. Default `start`. Responsive. */\n justify?: Responsive<Justify>\n /** Wrap onto new lines. Default `true`. */\n wrap?: boolean\n as?: InlineElement\n}\n\n/**\n * Horizontal flow: button rows, tag lists, meta lines, toolbars.\n *\n * <Inline gap={3} justify={{ base: 'start', md: 'between' }}>…</Inline>\n */\nexport const Inline = forwardRef<HTMLElement, InlineProps>(function Inline(\n {\n gap,\n rowGap,\n align,\n justify,\n wrap = true,\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.inline, visibilityClass({ hideBelow, hideAbove }), className)}\n data-wrap={wrap ? undefined : 'false'}\n role={Comp === 'ul' || Comp === 'ol' ? 'list' : undefined}\n style={mergeStyles(\n responsiveVars('inline-gap', gap, space),\n responsiveVars('inline-row-gap', rowGap, space),\n responsiveVars('inline-align', align, alignCss),\n responsiveVars('inline-justify', justify, justifyCss),\n style,\n )}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"Inline.js","names":["styles","Inline_module_default","Inline","forwardRef","Comp","rest","cx","visibilityClass","className","wrap","mergeStyles","responsiveVars","gap","space","rowGap","align","alignCss","justify","justifyCss","style"],"sources":["../../../../src/components/layout/Inline/Inline.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react'\nimport { cx } from '#utils/cx'\nimport { mergeStyles, responsiveVars, type Responsive } from '#utils/responsive'\nimport { alignCss, justifyCss, space, type Align, type Justify, type Space } from '#utils/tokens'\nimport styles from './Inline.module.css'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\n\nexport type InlineElement = 'div' | 'span' | 'nav' | 'ul' | 'ol' | 'li' | 'header' | 'footer' | 'p'\n\nexport interface InlineProps extends HTMLAttributes<HTMLElement>, VisibilityProps {\n /** Space between items (both axes when wrapping). Responsive. */\n gap?: Responsive<Space>\n /** Row gap when wrapped, if different from `gap`. Responsive. */\n rowGap?: Responsive<Space>\n /** Cross-axis alignment. Default `center`. Responsive. */\n align?: Responsive<Align>\n /** Main-axis distribution. Default `start`. Responsive. */\n justify?: Responsive<Justify>\n /** Wrap onto new lines. Default `true`. */\n wrap?: boolean\n as?: InlineElement\n}\n\n/**\n * Things in a row that wrap when they run out of room. Button rows, tag lists, toolbars and meta\n * lines.\n *\n * @remarks\n * `Inline` lays its children out horizontally with a gap, vertically centred, wrapping onto new\n * lines when they don't fit.\n *\n * @privateRemarks\n * Horizontal flow: button rows, tag lists, meta lines, toolbars.\n *\n * <Inline gap={3} justify={{ base: 'start', md: 'between' }}>…</Inline>\n */\nexport const Inline = forwardRef<HTMLElement, InlineProps>(function Inline(\n {\n gap,\n rowGap,\n align,\n justify,\n wrap = true,\n as: Comp = 'div',\n hideBelow,\n hideAbove,\n className,\n style,\n ...rest\n },\n ref,\n) {\n return (\n <Comp\n // @ts-expect-error — polymorphic ref across a union of intrinsic elements is safe here\n ref={ref}\n className={cx(styles.inline, visibilityClass({ hideBelow, hideAbove }), className)}\n data-wrap={wrap ? undefined : 'false'}\n role={Comp === 'ul' || Comp === 'ol' ? 'list' : undefined}\n style={mergeStyles(\n responsiveVars('inline-gap', gap, space),\n responsiveVars('inline-row-gap', rowGap, space),\n responsiveVars('inline-align', align, alignCss),\n responsiveVars('inline-justify', justify, justifyCss),\n style,\n )}\n {...rest}\n />\n )\n})\n"],"mappings":";;;;;;;;AAoCA,IAAaE,IAASC,EAAqC,SACzD,EACE,QACA,WACA,UACA,YACA,UAAO,IACP,IAAIC,IAAO,OACX,cACA,cACA,cACA,UACA,GAAGC,KAEL,GACA;CACA,OACE,kBAACD,GAAD;EAEO;EACL,WAAWE,EAAGN,EAAO,QAAQO,EAAgB;GAAE;GAAW;EAAU,CAAC,GAAGC,CAAS;EACjF,aAAWC,IAAO,KAAA,IAAY;EAC9B,MAAML,MAAS,QAAQA,MAAS,OAAO,SAAS,KAAA;EAChD,OAAOM,EACLC,EAAe,cAAcC,GAAKC,CAAK,GACvCF,EAAe,kBAAkBG,GAAQD,CAAK,GAC9CF,EAAe,gBAAgBI,GAAOC,CAAQ,GAC9CL,EAAe,kBAAkBM,GAASC,CAAU,GACpDC,CACF;EACA,GAAId;CACL,CAAA;AAEL,CAAC"}
|