@recursica/adapter-mantine-v8 1.0.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/.storybook/commonArgTypes.ts +148 -0
- package/ARCHITECTURE.md +18 -0
- package/CHANGELOG.md +688 -0
- package/LICENSE +21 -0
- package/OVERSTYLING.md +56 -0
- package/README.md +70 -0
- package/SETUP.md +93 -0
- package/USAGE.md +57 -0
- package/dist/adapter-mantine-v8.cjs +12 -0
- package/dist/adapter-mantine-v8.cjs.map +1 -0
- package/dist/adapter-mantine-v8.css +1 -0
- package/dist/adapter-mantine-v8.js +5759 -0
- package/dist/adapter-mantine-v8.js.map +1 -0
- package/dist/index.d.ts +2095 -0
- package/dist/test.html +1 -0
- package/dist/vite.svg +1 -0
- package/docs/PHILOSOPHY.md +39 -0
- package/llms.txt +74 -0
- package/package.json +131 -0
- package/recursica_brand.json +2677 -0
- package/recursica_tokens.json +799 -0
- package/recursica_ui-kit.json +14981 -0
- package/recursica_variables_scoped.css +30489 -0
- package/src/GlobalExemptions.modules.css +7 -0
- package/src/Introduction.stories.tsx +154 -0
- package/src/OverStyling.tsx +79 -0
- package/src/Version.tsx +44 -0
- package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +72 -0
- package/src/components/Accordion/Accordion.module.css +367 -0
- package/src/components/Accordion/Accordion.stories.tsx +189 -0
- package/src/components/Accordion/Accordion.test.tsx +1 -0
- package/src/components/Accordion/Accordion.tsx +220 -0
- package/src/components/Accordion/USAGE.md +51 -0
- package/src/components/Accordion/index.ts +1 -0
- package/src/components/AssistiveElement/ASSISTIVEELEMENT_IMPLEMENTATION_NOTES.md +45 -0
- package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +68 -0
- package/src/components/AssistiveElement/AssistiveElement.test.tsx +1 -0
- package/src/components/AssistiveElement/AssistiveElement.tsx +93 -0
- package/src/components/AssistiveElement/USAGE.md +36 -0
- package/src/components/AutoComplete/AUTOCOMPLETE_IMPLEMENTATION_NOTES.md +42 -0
- package/src/components/AutoComplete/AutoComplete.module.css +430 -0
- package/src/components/AutoComplete/AutoComplete.stories.tsx +336 -0
- package/src/components/AutoComplete/AutoComplete.test.tsx +1 -0
- package/src/components/AutoComplete/AutoComplete.tsx +179 -0
- package/src/components/AutoComplete/USAGE.md +80 -0
- package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +34 -0
- package/src/components/Avatar/Avatar.module.css +336 -0
- package/src/components/Avatar/Avatar.stories.tsx +92 -0
- package/src/components/Avatar/Avatar.test.tsx +1 -0
- package/src/components/Avatar/Avatar.tsx +108 -0
- package/src/components/Avatar/USAGE.md +38 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Badge/BADGE_IMPLEMENTATION_NOTES.md +18 -0
- package/src/components/Badge/Badge.module.css +127 -0
- package/src/components/Badge/Badge.stories.tsx +78 -0
- package/src/components/Badge/Badge.test.tsx +1 -0
- package/src/components/Badge/Badge.tsx +64 -0
- package/src/components/Badge/USAGE.md +56 -0
- package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +47 -0
- package/src/components/Breadcrumb/Breadcrumb.module.css +48 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
- package/src/components/Breadcrumb/Breadcrumb.test.tsx +1 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +85 -0
- package/src/components/Breadcrumb/USAGE.md +55 -0
- package/src/components/Breadcrumb/markCurrentPageItem.test.ts +49 -0
- package/src/components/Button/Button.module.css +446 -0
- package/src/components/Button/Button.stories.tsx +196 -0
- package/src/components/Button/Button.styleIsolation.dom.test.tsx +98 -0
- package/src/components/Button/Button.tsx +184 -0
- package/src/components/Button/IMPLEMENTATION_NOTES.md +109 -0
- package/src/components/Button/USAGE.md +80 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +17 -0
- package/src/components/Card/Card.module.css +119 -0
- package/src/components/Card/Card.stories.tsx +131 -0
- package/src/components/Card/Card.test.tsx +1 -0
- package/src/components/Card/Card.tsx +248 -0
- package/src/components/Card/USAGE.md +63 -0
- package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +25 -0
- package/src/components/Checkbox/Checkbox.module.css +267 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +97 -0
- package/src/components/Checkbox/Checkbox.test.tsx +1 -0
- package/src/components/Checkbox/Checkbox.tsx +151 -0
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +147 -0
- package/src/components/Checkbox/USAGE.md +48 -0
- package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +110 -0
- package/src/components/Chip/Chip.module.css +288 -0
- package/src/components/Chip/Chip.stories.tsx +128 -0
- package/src/components/Chip/Chip.test.tsx +1 -0
- package/src/components/Chip/Chip.tsx +157 -0
- package/src/components/Chip/USAGE.md +55 -0
- package/src/components/Container/CONTAINER_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Container/Container.module.css +7 -0
- package/src/components/Container/Container.stories.tsx +118 -0
- package/src/components/Container/Container.test.tsx +1 -0
- package/src/components/Container/Container.tsx +69 -0
- package/src/components/Container/USAGE.md +47 -0
- package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +31 -0
- package/src/components/DatePicker/DatePicker.icons.tsx +27 -0
- package/src/components/DatePicker/DatePicker.module.css +566 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +158 -0
- package/src/components/DatePicker/DatePicker.test.tsx +1 -0
- package/src/components/DatePicker/DatePicker.tsx +218 -0
- package/src/components/DatePicker/USAGE.md +49 -0
- package/src/components/Dropdown/BareDropdown.tsx +97 -0
- package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +12 -0
- package/src/components/Dropdown/Dropdown.module.css +477 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +252 -0
- package/src/components/Dropdown/Dropdown.test.tsx +1 -0
- package/src/components/Dropdown/Dropdown.tsx +213 -0
- package/src/components/Dropdown/USAGE.md +69 -0
- package/src/components/FileInput/FILEINPUT_IMPLEMENTATION_NOTES.md +153 -0
- package/src/components/FileInput/FileInput.module.css +280 -0
- package/src/components/FileInput/FileInput.stories.tsx +313 -0
- package/src/components/FileInput/FileInput.test.tsx +1 -0
- package/src/components/FileInput/FileInput.tsx +417 -0
- package/src/components/FileInput/USAGE.md +145 -0
- package/src/components/FileUpload/FILEUPLOAD_IMPLEMENTATION_NOTES.md +244 -0
- package/src/components/FileUpload/FileUpload.module.css +205 -0
- package/src/components/FileUpload/FileUpload.stories.tsx +362 -0
- package/src/components/FileUpload/FileUpload.test.tsx +1 -0
- package/src/components/FileUpload/FileUpload.tsx +354 -0
- package/src/components/FileUpload/USAGE.md +199 -0
- package/src/components/Flex/FLEX_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Flex/Flex.module.css +7 -0
- package/src/components/Flex/Flex.stories.tsx +128 -0
- package/src/components/Flex/Flex.test.tsx +1 -0
- package/src/components/Flex/Flex.tsx +75 -0
- package/src/components/Flex/USAGE.md +48 -0
- package/src/components/FormControlLayout/FormControlLayout.module.css +90 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +118 -0
- package/src/components/FormControlLayout/FormControlLayout.test.tsx +1 -0
- package/src/components/FormControlLayout/FormControlLayout.tsx +74 -0
- package/src/components/FormControlLayout/USAGE.md +40 -0
- package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +32 -0
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +75 -0
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +129 -0
- package/src/components/FormControlWrapper/FormControlWrapper.test.tsx +1 -0
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +151 -0
- package/src/components/FormControlWrapper/USAGE.md +47 -0
- package/src/components/Grid/GRID_IMPLEMENTATION_NOTES.md +18 -0
- package/src/components/Grid/Grid.module.css +11 -0
- package/src/components/Grid/Grid.stories.tsx +206 -0
- package/src/components/Grid/Grid.test.tsx +1 -0
- package/src/components/Grid/Grid.tsx +140 -0
- package/src/components/Grid/USAGE.md +47 -0
- package/src/components/Group/GROUP_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Group/Group.module.css +7 -0
- package/src/components/Group/Group.stories.tsx +120 -0
- package/src/components/Group/Group.test.tsx +1 -0
- package/src/components/Group/Group.tsx +60 -0
- package/src/components/Group/USAGE.md +48 -0
- package/src/components/Heading/HEADING_IMPLEMENTATION_NOTES.md +18 -0
- package/src/components/Heading/Heading.module.css +13 -0
- package/src/components/Heading/Heading.stories.tsx +50 -0
- package/src/components/Heading/Heading.test.tsx +1 -0
- package/src/components/Heading/Heading.tsx +52 -0
- package/src/components/Heading/USAGE.md +36 -0
- package/src/components/HoverCard/HOVERCARD_IMPLEMENTATION_NOTES.md +77 -0
- package/src/components/HoverCard/HoverCard.module.css +90 -0
- package/src/components/HoverCard/HoverCard.stories.tsx +172 -0
- package/src/components/HoverCard/HoverCard.test.tsx +1 -0
- package/src/components/HoverCard/HoverCard.tsx +133 -0
- package/src/components/HoverCard/USAGE.md +52 -0
- package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +48 -0
- package/src/components/Label/Label.module.css +141 -0
- package/src/components/Label/Label.stories.tsx +131 -0
- package/src/components/Label/Label.test.tsx +1 -0
- package/src/components/Label/Label.tsx +113 -0
- package/src/components/Label/USAGE.md +47 -0
- package/src/components/Label/index.ts +1 -0
- package/src/components/Layer/USAGE.md +54 -0
- package/src/components/Link/IMPLEMENTATION_NOTES.md +19 -0
- package/src/components/Link/Link.module.css +110 -0
- package/src/components/Link/Link.stories.tsx +58 -0
- package/src/components/Link/Link.test.tsx +1 -0
- package/src/components/Link/Link.tsx +67 -0
- package/src/components/Link/USAGE.md +56 -0
- package/src/components/Link/index.ts +1 -0
- package/src/components/Loader/LOADER_IMPLEMENTATION_NOTES.md +24 -0
- package/src/components/Loader/Loader.animate.dom.test.tsx +82 -0
- package/src/components/Loader/Loader.module.css +63 -0
- package/src/components/Loader/Loader.stories.tsx +123 -0
- package/src/components/Loader/Loader.test.tsx +1 -0
- package/src/components/Loader/Loader.tsx +69 -0
- package/src/components/Loader/USAGE.md +44 -0
- package/src/components/Menu/MENU_IMPLEMENTATION_NOTES.md +88 -0
- package/src/components/Menu/Menu.module.css +280 -0
- package/src/components/Menu/Menu.stories.tsx +371 -0
- package/src/components/Menu/Menu.test.tsx +1 -0
- package/src/components/Menu/Menu.tsx +373 -0
- package/src/components/Menu/USAGE.md +55 -0
- package/src/components/Modal/MODAL_IMPLEMENTATION_NOTES.md +32 -0
- package/src/components/Modal/Modal.module.css +302 -0
- package/src/components/Modal/Modal.stories.tsx +105 -0
- package/src/components/Modal/Modal.test.tsx +1 -0
- package/src/components/Modal/Modal.tsx +368 -0
- package/src/components/Modal/USAGE.md +58 -0
- package/src/components/Modal/index.ts +1 -0
- package/src/components/NumberInput/NUMBER_INPUT_IMPLEMENTATION_NOTES.md +24 -0
- package/src/components/NumberInput/NumberInput.module.css +302 -0
- package/src/components/NumberInput/NumberInput.stories.tsx +96 -0
- package/src/components/NumberInput/NumberInput.test.tsx +1 -0
- package/src/components/NumberInput/NumberInput.tsx +157 -0
- package/src/components/NumberInput/USAGE.md +52 -0
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +16 -0
- package/src/components/Pagination/Pagination.icons.tsx +66 -0
- package/src/components/Pagination/Pagination.module.css +188 -0
- package/src/components/Pagination/Pagination.stories.tsx +48 -0
- package/src/components/Pagination/Pagination.test.tsx +1 -0
- package/src/components/Pagination/Pagination.tsx +265 -0
- package/src/components/Pagination/USAGE.md +36 -0
- package/src/components/Pagination/index.ts +5 -0
- package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +116 -0
- package/src/components/Panel/Panel.module.css +209 -0
- package/src/components/Panel/Panel.stories.tsx +227 -0
- package/src/components/Panel/Panel.test.tsx +1 -0
- package/src/components/Panel/Panel.tsx +269 -0
- package/src/components/Panel/USAGE.md +56 -0
- package/src/components/Popover/IMPLEMENTATION_NOTES.md +77 -0
- package/src/components/Popover/Popover.module.css +86 -0
- package/src/components/Popover/Popover.stories.tsx +133 -0
- package/src/components/Popover/Popover.test.tsx +1 -0
- package/src/components/Popover/Popover.tsx +142 -0
- package/src/components/Popover/USAGE.md +61 -0
- package/src/components/Popover/index.ts +1 -0
- package/src/components/Radio/Radio.module.css +231 -0
- package/src/components/Radio/Radio.stories.tsx +93 -0
- package/src/components/Radio/Radio.test.tsx +1 -0
- package/src/components/Radio/Radio.tsx +168 -0
- package/src/components/Radio/RadioGroup.stories.tsx +117 -0
- package/src/components/Radio/RadioGroup.tsx +124 -0
- package/src/components/Radio/USAGE.md +36 -0
- package/src/components/ReadOnlyField/READ_ONLY_FIELD_IMPLEMENTATION_NOTES.md +15 -0
- package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +54 -0
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +163 -0
- package/src/components/ReadOnlyField/ReadOnlyField.test.tsx +1 -0
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +114 -0
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
- package/src/components/ReadOnlyField/USAGE.md +44 -0
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
- package/src/components/ReadOnlyField/index.ts +5 -0
- package/src/components/RecursicaThemeProvider/USAGE.md +60 -0
- package/src/components/SegmentedControl/IMPLEMENTATION_NOTES.md +25 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +185 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +106 -0
- package/src/components/SegmentedControl/SegmentedControl.test.tsx +1 -0
- package/src/components/SegmentedControl/SegmentedControl.tsx +110 -0
- package/src/components/SegmentedControl/USAGE.md +55 -0
- package/src/components/Slider/IMPLEMENTATION_NOTES.md +92 -0
- package/src/components/Slider/Slider.module.css +588 -0
- package/src/components/Slider/Slider.stories.tsx +282 -0
- package/src/components/Slider/Slider.test.tsx +1 -0
- package/src/components/Slider/Slider.tsx +391 -0
- package/src/components/Slider/USAGE.md +56 -0
- package/src/components/Stack/STACK_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Stack/Stack.module.css +7 -0
- package/src/components/Stack/Stack.stories.tsx +108 -0
- package/src/components/Stack/Stack.test.tsx +1 -0
- package/src/components/Stack/Stack.tsx +62 -0
- package/src/components/Stack/USAGE.md +48 -0
- package/src/components/Stepper/IMPLEMENTATION_NOTES.md +28 -0
- package/src/components/Stepper/Stepper.module.css +404 -0
- package/src/components/Stepper/Stepper.stories.tsx +148 -0
- package/src/components/Stepper/Stepper.test.tsx +1 -0
- package/src/components/Stepper/Stepper.tsx +93 -0
- package/src/components/Stepper/USAGE.md +41 -0
- package/src/components/Switch/SWITCH_IMPLEMENTATION_NOTES.md +64 -0
- package/src/components/Switch/Switch.module.css +279 -0
- package/src/components/Switch/Switch.stories.tsx +114 -0
- package/src/components/Switch/Switch.test.tsx +1 -0
- package/src/components/Switch/Switch.tsx +177 -0
- package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
- package/src/components/Switch/SwitchGroup.tsx +127 -0
- package/src/components/Switch/USAGE.md +42 -0
- package/src/components/Table/TABLE_IMPLEMENTATION_NOTES.md +78 -0
- package/src/components/Table/Table.icons.tsx +40 -0
- package/src/components/Table/Table.module.css +444 -0
- package/src/components/Table/Table.stories.tsx +140 -0
- package/src/components/Table/Table.test.tsx +1 -0
- package/src/components/Table/Table.tsx +243 -0
- package/src/components/Table/USAGE.md +78 -0
- package/src/components/Tabs/IMPLEMENTATION_NOTES.md +27 -0
- package/src/components/Tabs/Tabs.module.css +591 -0
- package/src/components/Tabs/Tabs.stories.tsx +154 -0
- package/src/components/Tabs/Tabs.test.tsx +1 -0
- package/src/components/Tabs/Tabs.tsx +114 -0
- package/src/components/Tabs/USAGE.md +45 -0
- package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +12 -0
- package/src/components/Text/Text.module.css +13 -0
- package/src/components/Text/Text.stories.tsx +75 -0
- package/src/components/Text/Text.test.tsx +1 -0
- package/src/components/Text/Text.tsx +53 -0
- package/src/components/Text/USAGE.md +40 -0
- package/src/components/TextArea/TEXTAREA_IMPLEMENTATION_NOTES.md +7 -0
- package/src/components/TextArea/TextArea.module.css +183 -0
- package/src/components/TextArea/TextArea.stories.tsx +94 -0
- package/src/components/TextArea/TextArea.test.tsx +1 -0
- package/src/components/TextArea/TextArea.tsx +137 -0
- package/src/components/TextArea/USAGE.md +48 -0
- package/src/components/TextField/TEXTFIELD_IMPLEMENTATION_NOTES.md +19 -0
- package/src/components/TextField/TextField.module.css +264 -0
- package/src/components/TextField/TextField.stories.tsx +162 -0
- package/src/components/TextField/TextField.test.tsx +1 -0
- package/src/components/TextField/TextField.tsx +139 -0
- package/src/components/TextField/USAGE.md +44 -0
- package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +119 -0
- package/src/components/TimePicker/TimePicker.module.css +291 -0
- package/src/components/TimePicker/TimePicker.stories.tsx +141 -0
- package/src/components/TimePicker/TimePicker.test.tsx +1 -0
- package/src/components/TimePicker/TimePicker.tsx +304 -0
- package/src/components/TimePicker/USAGE.md +57 -0
- package/src/components/Timeline/TIMELINE_IMPLEMENTATION_NOTES.md +13 -0
- package/src/components/Timeline/Timeline.module.css +373 -0
- package/src/components/Timeline/Timeline.stories.tsx +128 -0
- package/src/components/Timeline/Timeline.test.tsx +1 -0
- package/src/components/Timeline/Timeline.tsx +73 -0
- package/src/components/Timeline/TimelineItem.tsx +79 -0
- package/src/components/Timeline/USAGE.md +49 -0
- package/src/components/Toast/TOAST_IMPLEMENTATION_NOTES.md +39 -0
- package/src/components/Toast/Toast.module.css +184 -0
- package/src/components/Toast/Toast.stories.tsx +67 -0
- package/src/components/Toast/Toast.test.tsx +1 -0
- package/src/components/Toast/Toast.tsx +82 -0
- package/src/components/Toast/USAGE.md +50 -0
- package/src/components/Toast/index.ts +1 -0
- package/src/components/Tooltip/TOOLTIP_IMPLEMENTATION_NOTES.md +85 -0
- package/src/components/Tooltip/Tooltip.module.css +91 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +165 -0
- package/src/components/Tooltip/Tooltip.test.tsx +1 -0
- package/src/components/Tooltip/Tooltip.tsx +117 -0
- package/src/components/Tooltip/USAGE.md +80 -0
- package/src/components/TransferList/TRANSFERLIST_IMPLEMENTATION_NOTES.md +111 -0
- package/src/components/TransferList/TransferList.module.css +182 -0
- package/src/components/TransferList/TransferList.stories.tsx +121 -0
- package/src/components/TransferList/TransferList.test.tsx +1 -0
- package/src/components/TransferList/TransferList.tsx +419 -0
- package/src/components/TransferList/USAGE.md +77 -0
- package/src/components/Tree/IMPLEMENTATION_NOTES.md +48 -0
- package/src/components/Tree/Tree.module.css +232 -0
- package/src/components/Tree/Tree.stories.tsx +104 -0
- package/src/components/Tree/Tree.test.tsx +1 -0
- package/src/components/Tree/Tree.tsx +220 -0
- package/src/components/Tree/USAGE.md +96 -0
- package/src/components/Tree/index.ts +1 -0
- package/src/components/index.ts +52 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +179 -0
- package/src/types/index.ts +9 -0
- package/src/types/mantine.d.ts +7 -0
- package/src/types/scss-modules.d.ts +9 -0
- package/src/utils/ColorSchemeWrapper.tsx +27 -0
- package/src/utils/RequireAccessibleLabel.ts +1 -0
- package/src/utils/copyToClipboard.ts +36 -0
- package/src/utils/filterStylingProps.test.ts +85 -0
- package/src/utils/filterStylingProps.ts +150 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/renderRichOption.tsx +65 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# TimePicker Implementation Notes
|
|
2
|
+
|
|
3
|
+
## Architecture Overview
|
|
4
|
+
|
|
5
|
+
`TimePicker` is a composite of two independently-styled controls sitting side by side in a flex row:
|
|
6
|
+
|
|
7
|
+
1. **The time field** — Mantine's own `TimePicker` component (not `TimeInput` — a separate, more advanced component in the same package with real segmented hour/minute/second masking), always rendered with `format="12h"`.
|
|
8
|
+
2. **The AM/PM selector** — `BareDropdown`, a headless variant of this adapter's own `Dropdown` component (see below), not a native `<select>`.
|
|
9
|
+
|
|
10
|
+
This shape is deliberate and **not configurable** — there is no prop to get a plain 24-hour input or to hide the AM/PM control. Every consumer gets the same 12-hour + Dropdown-styled-AM/PM composite.
|
|
11
|
+
|
|
12
|
+
## Why not just Mantine's `TimePicker` alone
|
|
13
|
+
|
|
14
|
+
Mantine's `TimePicker` with `format="12h"` already renders hour/minute masking **and** an AM/PM control together — but that control is a native HTML `<select>` (`AmPmInput`, rendered via `format === "12h" && <AmPmInput />` in Mantine's own source, unconditionally bundled with 12h format — there's no prop to get one without the other). A native `<select>`'s styling is limited, especially its open option list (browser/OS-rendered), so it doesn't visually match Recursica's `Dropdown` component. (Matt Massey, 2026-08-07, after reviewing the initial native-select version in Storybook.)
|
|
15
|
+
|
|
16
|
+
Resolution: keep Mantine's `TimePicker` for what it does well (real, accessible 1-12 hour + minute + second masking with proper keyboard navigation — not something worth rebuilding from scratch), **CSS-hide its bundled native AM/PM `<select>`** (`.timeWrapper :global([data-am-pm]) { display: none; }`), and drive AM/PM entirely through our own `BareDropdown` next to it.
|
|
17
|
+
|
|
18
|
+
## `BareDropdown`
|
|
19
|
+
|
|
20
|
+
A new, **internal-only** component in `../Dropdown/BareDropdown.tsx` — not exported from this folder's `index.ts` (there isn't one; the barrel export is at the package level and only references `Dropdown`, not `BareDropdown`). It's the same `MantineSelect` primitive `Dropdown` wraps, styled via the **same** `Dropdown.module.css` classes (`wrapper`/`input`/`section`/`dropdown`/`option`), but with no `FormControlWrapper`/`WithReadOnlyWrapper` and no label/assistiveText/error/required props.
|
|
21
|
+
|
|
22
|
+
**Why not the public `Dropdown` component directly**: `Dropdown` wraps its own `FormControlWrapper`/`FormControlLayout` internally (it's meant to be used standalone). Nesting the full `Dropdown` inside `TimePicker` — which is already `FormControlWrapper`-wrapped — would double up `FormControl`/`FormControlLayout` structure. `BareDropdown` is the headless escape hatch for exactly this situation: reuse `Dropdown`'s visual styling without its structural wrapping. (Matt Massey, 2026-08-07.)
|
|
23
|
+
|
|
24
|
+
## Internal state, unlike every other component here
|
|
25
|
+
|
|
26
|
+
Every other input in this adapter is a thin pass-through — the wrapped library component owns all interaction state. `TimePicker` is the exception: the time field and the AM/PM `BareDropdown` both mutate the _same_ conceptual value, so something has to reconcile them. `TimePicker.tsx` keeps a `useState<string | undefined>` for the full 24-hour `"HH:mm[:ss]"` value, seeded from `value`/`defaultValue`, synced when `value` changes (controlled usage), and updated by both controls (the field's own `onChange`, and the `BareDropdown`'s `onChange` — which just adds or subtracts 12 from the current hour). The public `value`/`onChange` API is unaffected — still the same plain string convention as always.
|
|
27
|
+
|
|
28
|
+
## Hidden/omitted native props
|
|
29
|
+
|
|
30
|
+
Because the AM/PM behavior is fixed, several of Mantine's `TimePicker` props are **omitted from the public API** rather than exposed and silently ignored — a consumer shouldn't be able to pass a prop that looks like it should do something (change format, relabel the native AM/PM control) when it can't:
|
|
31
|
+
|
|
32
|
+
- `format`, `min`, `max` — hardcoded/remapped internally (`format` always `"12h"`; `min`/`max` come from `minTime`/`maxTime` instead, matching the shared `RecursicaTimePickerProps` contract).
|
|
33
|
+
- `amPmInputLabel`, `amPmLabels`, `amPmSelectProps`, `amPmRef` — all control the native AM/PM `<select>`, which is now CSS-hidden and non-interactive. Exposing these would be misleading.
|
|
34
|
+
- `withDropdown`, `presets`, `maxDropdownContentHeight`, `scrollAreaProps`, `reverseTimeControlsList`, `popoverProps` — the optional time-presets popover feature isn't wired up; kept out of the public API to keep it minimal.
|
|
35
|
+
|
|
36
|
+
## Design tokens
|
|
37
|
+
|
|
38
|
+
- No dedicated `min-height` token exists for `time-picker` (unlike `text-field`/`date-picker`) — `.fieldsGroup`'s explicit `height` is derived from `text_line-height` instead, which also fixes a real bug: Mantine's own field CSS sets `height: 100%` on every field to fill `.fieldsGroup`, and a percentage height only resolves against a _concrete_ parent height. Before this, `.fieldsGroup` was auto-height (padding only), so `100%` resolved to `0` — this is why the AM/PM control was invisible in early builds (verified via real headless-browser inspection, not just markup checks — a raw server-render test had missed this entirely since it doesn't compute layout).
|
|
39
|
+
- `icon-size`/`icon-color`/`icon-text-gap`/`placeholder-opacity` (plus the disabled/error `icon-color` variants) are wired via `leftSection` — see "Leading icon" below. Not exempted: despite an earlier note here claiming otherwise, `SpinInput` (`@mantine/dates`) renders a real `<input placeholder="--">` per hour/minute/second segment, so `::placeholder` targets it like any other input.
|
|
40
|
+
- The AM/PM `BareDropdown` draws its own border/background/padding from `Dropdown`'s own tokens via `Dropdown.module.css` — it does not reuse any `time-picker` tokens.
|
|
41
|
+
|
|
42
|
+
## Read-Only Implementation
|
|
43
|
+
|
|
44
|
+
`readOnlyType="text"`, matching `DatePicker`'s convention.
|
|
45
|
+
|
|
46
|
+
## Visual review fixes (Matt Massey, 2026-08-07)
|
|
47
|
+
|
|
48
|
+
Follow-up fixes after the first Storybook review of the `BareDropdown`-based rebuild:
|
|
49
|
+
|
|
50
|
+
- **Outer box was ~62px tall instead of matching `TextField`'s 48px**: Mantine's `TimePicker` renders its `classNames.input` slot as a real, separately-styled box (its own border, background, and `min-height: var(--input-height)` = 36px by default) nested _inside_ `classNames.wrapper` — not a plain content container. Left alone, that gave every field a second, competing Mantine-default border/background stacked underneath our own `.timeWrapper` chrome. Fixed by resetting `.timeInput` (mapped to `classNames.input`) to a bare, unstyled flex shell (`border: none; background-color: transparent; padding: 0; height: auto; min-height: 0;`) — every visual property now lives on `.timeWrapper` alone. Confirmed by reading Mantine's `Input.mjs`/`use-input-props.mjs` source directly, not just trial-and-error CSS.
|
|
51
|
+
- **No dedicated `min-height` token for `time-picker`** (unlike `text-field`): `.timeWrapper` now sets `min-height: var(--recursica_ui-kit_globals_form_field_size_single-line-input-height)` — the same global `text-field`'s own `min-height` token itself resolves to (confirmed via `recursica_variables_scoped.css`), so both land on exactly 48px without binding to another component's own namespace.
|
|
52
|
+
- **No dedicated gap token between the time field and the AM/PM dropdown**: checked `ui-kit.globals.form.properties` — the closest candidates (`label-field-gap-horizontal`, `vertical-item-gap`) are for label-to-field and vertical stacking, not a horizontal gap between two sibling controls. `.root`'s flex `gap` continues to reuse `time-picker`'s own `horizontal-padding` token as a stand-in until a real token exists.
|
|
53
|
+
- **AM/PM `BareDropdown` was as wide as the time field**: two stacked causes, both now fixed:
|
|
54
|
+
1. A plain `style={{ width: "fit-content" }}` prop doesn't reach the bordered box at all — Mantine's `useInputProps` routes a top-level `style` prop to the _label_ `InputWrapper`, not the input box. Fixed by using the styles-api `styles={{ wrapper: { width: "fit-content" } }}` prop instead, which targets that box directly.
|
|
55
|
+
2. Even with that fixed, the box stayed wide: `Dropdown.module.css`'s `.input` sets `width: 100%`, and a native `<input>`'s own intrinsic/`auto` width is a fixed browser default (~20 characters), not based on its actual "AM"/"PM" value text, so resetting to `auto` alone didn't shrink it either. Fixed with an explicit small width (`.amPmSelect :global(.mantine-Select-input) { width: 2.5rem; }`, targeting Mantine's stable global class since this module can't reference `Dropdown.module.css`'s own hashed class name) — the wrapper's rendered width just follows this input, since `Dropdown.module.css`'s right-section icon is positioned absolutely and doesn't add to flex flow.
|
|
56
|
+
|
|
57
|
+
## Visual review round 2 (Matt Massey, 2026-08-08)
|
|
58
|
+
|
|
59
|
+
- **AM/PM box was too tight — 2.5rem left no room for the "AM"/"PM" text or breathing room around the chevron**: `2.5rem` (40px) is a `box-sizing: border-box` width, so it has to fit Dropdown's own left padding (16px) _and_ its right padding reserved for the chevron section (48px, `horizontal-padding + icon-size + icon-text-gap`) inside it — that's 64px of padding alone, more than the 40px box, leaving a _negative_ content area (hence the invisible value and the chevron crowding the border). Widened to `6rem` (96px), leaving a real ~32px for the text.
|
|
60
|
+
|
|
61
|
+
## Visual review round 3 (Matt Massey, 2026-08-08) — AM/PM value now defaults and persists
|
|
62
|
+
|
|
63
|
+
Previously flagged as a known limitation, now fixed: Mantine's own `TimePicker` only reports a valid `onChange` value once its internal `amPm` state is non-`null` (see `getTimeString` in `@mantine/dates`) — that state is normally set by interacting with the native AM/PM `<select>`, which this component CSS-hides entirely. In practice, typing hour/minute digits alone never fired a valid `onChange`, so the AM/PM `BareDropdown` stayed blank forever and selecting a value there did nothing (its own `handleMeridiemChange` logic bailed out early, since `hour` was always `undefined`).
|
|
64
|
+
|
|
65
|
+
**Fix**: `convertTimeTo12HourFormat` in `@mantine/dates` derives `amPm` from whatever `hours` value it's given — `null` only when `hours` itself is `null`. And `onAmPmChange` (Mantine's internal handler for the — hidden — native select) calls `setAmPm(value)` _unconditionally_, before any validity check. So simulating one real interaction with that native `<select>` — defaulting it to "AM" — permanently seeds Mantine's internal `amPm` state, after which typing hour/minute correctly resolves and reports a real value, and our own `BareDropdown` (which drives the same native select the same way) correctly displays and changes it.
|
|
66
|
+
|
|
67
|
+
Implementation: a `useEffect` on mount, gated to only run when there's no real initial `value`/`defaultValue` already (Mantine already derives the correct AM/PM from a real value on its own — this only fixes the genuinely-empty-start case). It sets the native select's `.value` via `Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, "value").set` (required to make a React-controlled element pick up a value set outside of React) and dispatches a `change` event — access to the native element comes via `amPmRef`, a prop omitted from this component's own public API but still usable internally when calling Mantine's `<TimePicker>` directly.
|
|
68
|
+
|
|
69
|
+
## Visual review round 6 (Matt Massey, 2026-08-17) — AM/PM dropdown blank until an hour is typed
|
|
70
|
+
|
|
71
|
+
Round 3 fixed Mantine's internal `amPm` state never becoming valid (the deadlock preventing `onChange` from ever firing). It did not fix a separate problem: the visible `BareDropdown`'s own `value` prop was computed as `hour === undefined ? null : isPM ? "PM" : "AM"` — explicitly `null` whenever no hour had been typed yet, so the control displayed empty on mount even though round 3's mount effect had already seeded Mantine's hidden native `<select>` to "AM". Those are two different pieces of state: the hidden native select (round 3's fix target) and `internalValue`/`hour` (what `BareDropdown` actually renders), and writing to one never touched the other.
|
|
72
|
+
|
|
73
|
+
The MUI adapter's equivalent (`mui-adapter/src/components/TimePicker/TimePicker.tsx`) never had this problem — its `isPM` is a plain boolean (`internalValue ? internalValue.hour() >= 12 : false`) with no `null`/"unset" branch, so its dropdown always renders a concrete "AM"/"PM".
|
|
74
|
+
|
|
75
|
+
**Fix**: dropped the `null` branch — `BareDropdown`'s `value` is now just `isPM ? "PM" : "AM"`, matching MUI. `isPM` already evaluates `false` when `hour` is `undefined`, so this alone makes the dropdown default to "AM" with no other changes.
|
|
76
|
+
|
|
77
|
+
Note: selecting AM/PM before any hour is typed is still a no-op in both adapters (`handleMeridiemChange` bails out when `hour`/`internalValue` is undefined) — that's pre-existing, shared behavior in both adapters, not something this fix touches.
|
|
78
|
+
|
|
79
|
+
## Leading icon (Matt Massey, 2026-08-30)
|
|
80
|
+
|
|
81
|
+
Added `leftSection` (`RecursicaTimePickerProps`), matching `TextField`/`AutoComplete`'s naming and convention: purely decorative, consumer-supplied, no default. Unlike `DatePicker`'s fixed `CalendarIcon`, there's no single icon that makes sense for every `TimePicker` use, so this is opt-in only.
|
|
82
|
+
|
|
83
|
+
Mantine's `TimePicker` already accepts `leftSection` (inherited from `@mantine/core`'s `__InputProps` via `InputBase`, which it renders through internally with `component: "div"` — confirmed by reading `TimePicker.mjs`/`Input.mjs` directly) — it was already passing through unstyled before this change (not blocked by `filterStylingProps`), just never mapped to a `classNames.section`/CSS, so an icon would render with no size or color. Added `section: styles.section` to the `classNames` map and the token-driven `.section`/`.section svg` rules (icon-size, icon-color, disabled/error icon-color) to actually style it.
|
|
84
|
+
|
|
85
|
+
## Leading icon overlapped the field text (bug fix, Matt Massey, 2026-08-30)
|
|
86
|
+
|
|
87
|
+
Follow-up to the section above, found during a mantine-vs-mui source-of-truth comparison: `.section`
|
|
88
|
+
(the leading icon) is absolutely positioned — Mantine's own default for `Input.Section`, same as
|
|
89
|
+
`Dropdown`/`AutoComplete`'s equivalent — so it never occupies real flex-flow space. `.timeWrapper`'s
|
|
90
|
+
own `gap` (added in the round above, intended to space the icon from the field) has **no effect** on
|
|
91
|
+
it for exactly that reason: `gap` only spaces in-flow flex siblings, and an absolutely-positioned
|
|
92
|
+
child isn't one. Nothing else reserved room for the icon either, so `.fieldsGroup`'s hour/minute/
|
|
93
|
+
second inputs rendered right where `.timeWrapper`'s own base `padding-left` put them — directly
|
|
94
|
+
underneath the icon (visually, the icon and the leading "-- : --" overlapped).
|
|
95
|
+
|
|
96
|
+
Separately, `.section[data-position="left"]` had no explicit `left`/`padding-left` of its own,
|
|
97
|
+
so it fell back to Mantine's un-tokenized default: centered in a fixed-width box flush against the
|
|
98
|
+
border, not inset by `horizontal-padding` the way `Dropdown`/`AutoComplete`'s own left sections are.
|
|
99
|
+
|
|
100
|
+
**Fix** (mirrors `Dropdown.module.css`'s established pattern exactly):
|
|
101
|
+
|
|
102
|
+
- `.section[data-position="left"] { position: absolute; left: 0; padding-left: horizontal-padding; }`
|
|
103
|
+
— explicit instead of relying on Mantine's un-tokenized default.
|
|
104
|
+
- `.timeWrapper[data-with-left-section] .fieldsGroup { padding-left: calc(icon-size + icon-text-gap); }`
|
|
105
|
+
— reserves the room the (now out-of-flow) icon needs, on top of `.timeWrapper`'s own existing
|
|
106
|
+
base `padding-left` (which already covers the initial `horizontal-padding` inset).
|
|
107
|
+
|
|
108
|
+
**Why mui-adapter, once it fixes its own "icon too far right" bug for the same story, should match
|
|
109
|
+
this corrected rendering and not the previous (overlapping) one**: the previous rendering was a
|
|
110
|
+
straightforward layout bug, not an intentional design — an icon and adjacent text overlapping is
|
|
111
|
+
never a valid state for any other component in either adapter (`Dropdown`, `AutoComplete`,
|
|
112
|
+
`TextField`, `DatePicker` all reserve real space for a leading icon before rendering the field's
|
|
113
|
+
own text/placeholder). This fix brings `TimePicker` in line with that same, already-established
|
|
114
|
+
convention.
|
|
115
|
+
|
|
116
|
+
## Visual review round 5 (Matt Massey, 2026-08-08)
|
|
117
|
+
|
|
118
|
+
- **AM/PM error-state border wasn't changing**: `BareDropdown` set `data-error`/`data-disabled` via Mantine's `wrapperProps` — which targets the _outer_ `Input.Wrapper` (the label/description/error stacking element), a different, ancestor element from the "wrapper" styles-api slot that actually carries `styles.root`'s border. `Dropdown.module.css`'s `.root[data-error]`/`[data-disabled]` rules never matched as a result. This is the exact same "two different things both called 'wrapper'" trap as the earlier `style` vs `styles.wrapper` bug. Fixed by using `attributes={{ wrapper: {...} }}` instead — the styles-api hook that actually targets the same slot as `classNames.wrapper`. **This is a shared, pre-existing bug** — the real `Dropdown.tsx` had the identical mistake, so its error/disabled states never applied a border color either; fixed there too (low-risk, purely-additive, same reasoning as the `data-selected` fix above).
|
|
119
|
+
- **Static/Editable ReadOnly showed the raw 24-hour value with no AM/PM** (e.g. "14:30" instead of "2:30 PM"): `readOnlyValue` was passed the raw internal string as-is. Added `formatReadOnlyTime`, converting to a 12-hour + AM/PM display string before handing it to `WithReadOnlyWrapper`.
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/* EXEMPTIONS:
|
|
10
|
+
- border-size is constant across disabled/error states — only border-color changes; the
|
|
11
|
+
disabled/error border-size tokens are never applied by design. */
|
|
12
|
+
/* recursica-ignore: --recursica_ui-kit_components_time-picker_variants_states_disabled_properties_border-size */
|
|
13
|
+
/* recursica-ignore: --recursica_ui-kit_components_time-picker_variants_states_error_properties_border-size */
|
|
14
|
+
|
|
15
|
+
/* LAYOUT SPACING OVERRIDES:
|
|
16
|
+
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens. */
|
|
17
|
+
.layoutOverride {
|
|
18
|
+
--form-control-margin-bottom: var(
|
|
19
|
+
--recursica_ui-kit_components_time-picker_variants_layouts_stacked_properties_top-bottom-margin
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.layoutOverride[data-form-layout="side-by-side"] {
|
|
24
|
+
--form-control-margin-bottom: var(
|
|
25
|
+
--recursica_ui-kit_components_time-picker_variants_layouts_side-by-side_properties_top-bottom-margin
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* HARDCODED VALUES:
|
|
30
|
+
- border-style: solid. Native structural rendering rule.
|
|
31
|
+
- display: flex on .root — the time field and the AM/PM BareDropdown are two visually separate
|
|
32
|
+
controls sitting side by side, not merged into one shared box. See
|
|
33
|
+
TIMEPICKER_IMPLEMENTATION_NOTES.md for why this composite exists.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
.root {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: flex-start;
|
|
39
|
+
gap: var(
|
|
40
|
+
--recursica_ui-kit_components_time-picker_properties_horizontal-padding
|
|
41
|
+
);
|
|
42
|
+
width: fit-content;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Mantine's own native AM/PM <select> is unconditionally bundled with format="12h" (no prop to
|
|
46
|
+
omit it) — hide it; our own BareDropdown next to the field is the only AM/PM control. */
|
|
47
|
+
.timeWrapper :global([data-am-pm]) {
|
|
48
|
+
display: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.timeWrapper {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
/* This `gap` is a no-op: the leading-icon `.section` is absolutely positioned (Mantine's own
|
|
55
|
+
default for Input.Section, same as Dropdown/AutoComplete), so it never participates in flex
|
|
56
|
+
gap spacing as a normal flow sibling — see `.fieldsGroup` below for the fix that actually
|
|
57
|
+
reserves room for it. Left in place only because it's harmless with a single in-flow child. */
|
|
58
|
+
gap: var(--recursica_ui-kit_components_time-picker_properties_icon-text-gap);
|
|
59
|
+
position: relative;
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
/* min-width, not width: the `width` token was authored for a single plain input (confirmed against
|
|
62
|
+
text-field's identical token shape); this is now only the time-entry half of the composite. */
|
|
63
|
+
min-width: var(--recursica_ui-kit_components_time-picker_properties_width);
|
|
64
|
+
width: fit-content;
|
|
65
|
+
/* time-picker has no dedicated min-height token (unlike text-field/date-picker); this global is
|
|
66
|
+
the one text-field's own min-height token resolves to, so both controls land on the same 48px
|
|
67
|
+
height without binding to another component's own namespace. See
|
|
68
|
+
TIMEPICKER_IMPLEMENTATION_NOTES.md. */
|
|
69
|
+
min-height: var(
|
|
70
|
+
--recursica_ui-kit_globals_form_field_size_single-line-input-height
|
|
71
|
+
);
|
|
72
|
+
border-radius: var(
|
|
73
|
+
--recursica_ui-kit_components_time-picker_properties_border-radius
|
|
74
|
+
);
|
|
75
|
+
border-width: var(
|
|
76
|
+
--recursica_ui-kit_components_time-picker_properties_border-size
|
|
77
|
+
);
|
|
78
|
+
border-style: solid;
|
|
79
|
+
border-color: var(
|
|
80
|
+
--recursica_ui-kit_components_time-picker_properties_colors_border-color
|
|
81
|
+
);
|
|
82
|
+
background-color: var(
|
|
83
|
+
--recursica_ui-kit_components_time-picker_properties_colors_background-color
|
|
84
|
+
);
|
|
85
|
+
color: var(
|
|
86
|
+
--recursica_ui-kit_components_time-picker_properties_colors_text-color
|
|
87
|
+
);
|
|
88
|
+
padding-top: var(
|
|
89
|
+
--recursica_ui-kit_components_time-picker_properties_vertical-padding
|
|
90
|
+
);
|
|
91
|
+
padding-bottom: var(
|
|
92
|
+
--recursica_ui-kit_components_time-picker_properties_vertical-padding
|
|
93
|
+
);
|
|
94
|
+
padding-left: var(
|
|
95
|
+
--recursica_ui-kit_components_time-picker_properties_horizontal-padding
|
|
96
|
+
);
|
|
97
|
+
padding-right: var(
|
|
98
|
+
--recursica_ui-kit_components_time-picker_properties_horizontal-padding
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Mantine's TimePicker renders its own "input" slot as a real, separately-styled box (border,
|
|
103
|
+
background, height/min-height, padding-inline) nested *inside* .timeWrapper — not just a plain
|
|
104
|
+
content container. Left alone, that gave every field its own competing Mantine-default border/
|
|
105
|
+
background/36px-min-height stacked underneath our own .timeWrapper chrome, which is what made
|
|
106
|
+
the outer box look far taller than intended. Reset it to a plain, unstyled flex shell; every
|
|
107
|
+
visual property (border, background, padding, height) is owned by .timeWrapper alone. */
|
|
108
|
+
.timeInput {
|
|
109
|
+
height: auto;
|
|
110
|
+
min-height: 0;
|
|
111
|
+
border: none;
|
|
112
|
+
background-color: transparent;
|
|
113
|
+
padding: 0;
|
|
114
|
+
display: block;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Padding lives on .timeWrapper, not .fieldsGroup: Mantine's own field styles set `height: 100%` on
|
|
118
|
+
every field (hour/minute/second SpinInputs) to fill their direct parent (.fieldsGroup). A
|
|
119
|
+
percentage height only resolves against a parent with a *concrete* height — giving .fieldsGroup
|
|
120
|
+
an explicit height (derived from time-picker's own text_line-height token, since there's no
|
|
121
|
+
dedicated min-height token for this component, unlike text-field/date-picker) avoids that
|
|
122
|
+
collapsing to zero, and doubles as the "min-height" this component doesn't have a token for. */
|
|
123
|
+
.fieldsGroup {
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
height: var(
|
|
127
|
+
--recursica_ui-kit_components_time-picker_properties_text_line-height
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Leading icon (leftSection). Only one icon-color token exists for time-picker (unlike
|
|
132
|
+
text-field's separate leading/trailing) — this field has no right-side icon slot.
|
|
133
|
+
Positioned explicitly (left:0 + padding-left) rather than left on Mantine's own absolute-
|
|
134
|
+
positioning default alone (bug fix, see `.fieldsGroup` below): Mantine centers it in a
|
|
135
|
+
fixed-width box flush against the border with no Recursica padding token applied at all,
|
|
136
|
+
same technique Dropdown/AutoComplete's own `.section[data-position="left"]` already use. */
|
|
137
|
+
.section {
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.section[data-position="left"] {
|
|
143
|
+
position: absolute;
|
|
144
|
+
left: 0;
|
|
145
|
+
padding-left: var(
|
|
146
|
+
--recursica_ui-kit_components_time-picker_properties_horizontal-padding
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.section :global(svg) {
|
|
151
|
+
width: var(--recursica_ui-kit_components_time-picker_properties_icon-size);
|
|
152
|
+
height: var(--recursica_ui-kit_components_time-picker_properties_icon-size);
|
|
153
|
+
color: var(
|
|
154
|
+
--recursica_ui-kit_components_time-picker_properties_colors_icon-color
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Reserves room for the (absolutely positioned, see `.section` above) leading icon in the actual
|
|
159
|
+
text flow — without this, `.fieldsGroup`'s hour/minute/second inputs start right where
|
|
160
|
+
`.timeWrapper`'s own base `padding-left` (horizontal-padding) puts them, which is directly
|
|
161
|
+
underneath the icon (bug: the icon and the field's leading ":" visibly overlapped). Only the
|
|
162
|
+
icon-size/icon-text-gap on top of that base padding is needed here, since `.timeWrapper`'s own
|
|
163
|
+
padding-left already reserves the horizontal-padding portion. */
|
|
164
|
+
.timeWrapper[data-with-left-section] .fieldsGroup {
|
|
165
|
+
padding-left: calc(
|
|
166
|
+
var(--recursica_ui-kit_components_time-picker_properties_icon-size) +
|
|
167
|
+
var(--recursica_ui-kit_components_time-picker_properties_icon-text-gap)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* SpinInput renders a real <input placeholder="--">, one per hour/minute/second segment. */
|
|
172
|
+
.timeField::placeholder {
|
|
173
|
+
opacity: var(
|
|
174
|
+
--recursica_ui-kit_components_time-picker_properties_placeholder-opacity
|
|
175
|
+
);
|
|
176
|
+
color: inherit;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.timeField {
|
|
180
|
+
font-family: var(
|
|
181
|
+
--recursica_ui-kit_components_time-picker_properties_text_font-family
|
|
182
|
+
);
|
|
183
|
+
font-size: var(
|
|
184
|
+
--recursica_ui-kit_components_time-picker_properties_text_font-size
|
|
185
|
+
);
|
|
186
|
+
font-style: var(
|
|
187
|
+
--recursica_ui-kit_components_time-picker_properties_text_font-style
|
|
188
|
+
);
|
|
189
|
+
font-weight: var(
|
|
190
|
+
--recursica_ui-kit_components_time-picker_properties_text_font-weight
|
|
191
|
+
);
|
|
192
|
+
letter-spacing: var(
|
|
193
|
+
--recursica_ui-kit_components_time-picker_properties_text_letter-spacing
|
|
194
|
+
);
|
|
195
|
+
line-height: var(
|
|
196
|
+
--recursica_ui-kit_components_time-picker_properties_text_line-height
|
|
197
|
+
);
|
|
198
|
+
text-decoration: var(
|
|
199
|
+
--recursica_ui-kit_components_time-picker_properties_text_text-decoration
|
|
200
|
+
);
|
|
201
|
+
text-transform: var(
|
|
202
|
+
--recursica_ui-kit_components_time-picker_properties_text_text-transform
|
|
203
|
+
);
|
|
204
|
+
color: inherit;
|
|
205
|
+
outline: none;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* The AM/PM BareDropdown reuses Dropdown.module.css's own border/background/padding entirely —
|
|
209
|
+
this only controls its size/alignment within the flex row. */
|
|
210
|
+
.amPmSelect {
|
|
211
|
+
flex: 0 0 auto;
|
|
212
|
+
width: auto;
|
|
213
|
+
min-width: 5.5rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Dropdown.module.css's own .input sets width: 100% (correct for a standalone Dropdown filling
|
|
217
|
+
its own column) — resetting that to `auto` still isn't enough here: a native <input>'s intrinsic
|
|
218
|
+
("auto"/"fit-content") width is a fixed UA default (~20 characters wide), not based on its actual
|
|
219
|
+
value text, so it doesn't naturally shrink to "AM"/"PM" either way. Give it an explicit small
|
|
220
|
+
width sized for a couple of characters instead — the wrapper (.amPmSelect, whose own rendered
|
|
221
|
+
width just follows this input, since Dropdown.module.css's .section icon is positioned
|
|
222
|
+
absolutely and doesn't add to flex flow) ends up hugging the content as a result. Target
|
|
223
|
+
Mantine's own stable global class (not the Dropdown.module.css hash, which this module can't
|
|
224
|
+
reference directly). See TIMEPICKER_IMPLEMENTATION_NOTES.md.
|
|
225
|
+
Width math (box-sizing: border-box, so padding eats into this number): Dropdown's own
|
|
226
|
+
horizontal-padding (16px) on the left + the chevron's reserved right-section padding
|
|
227
|
+
(horizontal-padding + icon-size + icon-text-gap = 16+24+8 = 48px) on the right leaves very
|
|
228
|
+
little room for the "AM"/"PM" text itself — 2.5rem (40px) put the padding in a negative content
|
|
229
|
+
box, which is why the value was invisible and the chevron looked jammed against the border. 6rem
|
|
230
|
+
leaves a real ~32px content area. */
|
|
231
|
+
.amPmSelect :global(.mantine-Select-input) {
|
|
232
|
+
width: 6rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* -------------------------------------
|
|
236
|
+
STATE CASCADE ARCHITECTURE
|
|
237
|
+
-------------------------------------- */
|
|
238
|
+
|
|
239
|
+
/* Focus State Mapping — applied to the time field only (the BareDropdown has its own via Dropdown's
|
|
240
|
+
own CSS module). */
|
|
241
|
+
.timeField:focus-within,
|
|
242
|
+
.timeWrapper:focus-within {
|
|
243
|
+
box-shadow:
|
|
244
|
+
0 0 0 var(--recursica_brand_states_focus_border-size)
|
|
245
|
+
var(--recursica_brand_states_focus_color),
|
|
246
|
+
0 0 var(--recursica_brand_states_focus_blur)
|
|
247
|
+
var(--recursica_brand_states_focus_margin)
|
|
248
|
+
var(--recursica_brand_states_focus_color);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Error State Mapping (Propagated strictly down from the wrapper DOM context) */
|
|
252
|
+
.root[data-error="true"] .timeWrapper {
|
|
253
|
+
border-color: var(
|
|
254
|
+
--recursica_ui-kit_components_time-picker_variants_states_error_properties_colors_border-color
|
|
255
|
+
) !important;
|
|
256
|
+
background-color: var(
|
|
257
|
+
--recursica_ui-kit_components_time-picker_variants_states_error_properties_colors_background-color
|
|
258
|
+
) !important;
|
|
259
|
+
color: var(
|
|
260
|
+
--recursica_ui-kit_components_time-picker_variants_states_error_properties_colors_text-color
|
|
261
|
+
) !important;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.root[data-error="true"] .section :global(svg) {
|
|
265
|
+
color: var(
|
|
266
|
+
--recursica_ui-kit_components_time-picker_variants_states_error_properties_colors_icon-color
|
|
267
|
+
) !important;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* Disabled State Mapping (Propagated strictly down from the wrapper DOM context) */
|
|
271
|
+
.root[data-disabled="true"] .timeWrapper {
|
|
272
|
+
border-color: var(
|
|
273
|
+
--recursica_ui-kit_components_time-picker_variants_states_disabled_properties_colors_border-color
|
|
274
|
+
) !important;
|
|
275
|
+
background-color: var(
|
|
276
|
+
--recursica_ui-kit_components_time-picker_variants_states_disabled_properties_colors_background-color
|
|
277
|
+
) !important;
|
|
278
|
+
color: var(
|
|
279
|
+
--recursica_ui-kit_components_time-picker_variants_states_disabled_properties_colors_text-color
|
|
280
|
+
) !important;
|
|
281
|
+
opacity: var(
|
|
282
|
+
--recursica_ui-kit_components_time-picker_variants_states_disabled_properties_opacity
|
|
283
|
+
) !important;
|
|
284
|
+
cursor: not-allowed;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.root[data-disabled="true"] .section :global(svg) {
|
|
288
|
+
color: var(
|
|
289
|
+
--recursica_ui-kit_components_time-picker_variants_states_disabled_properties_colors_icon-color
|
|
290
|
+
) !important;
|
|
291
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { TimePicker } from "./TimePicker";
|
|
3
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof TimePicker> = {
|
|
6
|
+
title: "UI-Kit/TimePicker",
|
|
7
|
+
component: TimePicker,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
The \`TimePicker\` primitive provides a segmented hour/minute (optionally seconds) time entry input, paired with a dedicated AM/PM \`Dropdown\`-style selector, integrated directly into the \`FormControlWrapper\` architecture. This 12-hour + AM/PM composite is the only way this component operates — a Recursica-specific design, not a user-configurable option.
|
|
14
|
+
|
|
15
|
+
### Examples
|
|
16
|
+
Always structure horizontal architectures via the generic \`formLayout\` parameter.
|
|
17
|
+
\`\`\`tsx
|
|
18
|
+
<TimePicker
|
|
19
|
+
label="Start Time"
|
|
20
|
+
assistiveText="Select the deployment kick-off time."
|
|
21
|
+
formLayout="stacked"
|
|
22
|
+
/>
|
|
23
|
+
\`\`\`
|
|
24
|
+
`,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
argTypes: {
|
|
29
|
+
...formControlArgTypes,
|
|
30
|
+
disabled: {
|
|
31
|
+
control: "boolean",
|
|
32
|
+
description:
|
|
33
|
+
"Maps the formal disabled variable states structurally to the input core.",
|
|
34
|
+
},
|
|
35
|
+
error: {
|
|
36
|
+
control: "text",
|
|
37
|
+
description:
|
|
38
|
+
"Applies the strict error string boundary rendering invalid structures seamlessly.",
|
|
39
|
+
},
|
|
40
|
+
required: {
|
|
41
|
+
control: "boolean",
|
|
42
|
+
},
|
|
43
|
+
label: {
|
|
44
|
+
control: "text",
|
|
45
|
+
},
|
|
46
|
+
assistiveText: {
|
|
47
|
+
control: "text",
|
|
48
|
+
},
|
|
49
|
+
readOnly: {
|
|
50
|
+
control: "boolean",
|
|
51
|
+
description:
|
|
52
|
+
"Toggles structural read-only data presentation explicitly blocking standard component bindings.",
|
|
53
|
+
},
|
|
54
|
+
withSeconds: {
|
|
55
|
+
control: "boolean",
|
|
56
|
+
description: "Shows and allows editing the seconds segment.",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default meta;
|
|
62
|
+
|
|
63
|
+
type Story = StoryObj<typeof TimePicker>;
|
|
64
|
+
|
|
65
|
+
export const Default: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
disabled: false,
|
|
68
|
+
label: "Meeting Time",
|
|
69
|
+
assistiveText: "Choose the start time in your local timezone.",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const FormsSideBySide: Story = {
|
|
74
|
+
args: {
|
|
75
|
+
label: "Incident Start Time",
|
|
76
|
+
assistiveText: "When did the incident originally occur?",
|
|
77
|
+
formLayout: "side-by-side",
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const WithSeconds: Story = {
|
|
82
|
+
args: {
|
|
83
|
+
label: "Precise Execution Time",
|
|
84
|
+
assistiveText: "Includes a seconds segment for exact scheduling.",
|
|
85
|
+
withSeconds: true,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const Disabled: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
label: "Disabled Time Slot",
|
|
92
|
+
disabled: true,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const ErrorState: Story = {
|
|
97
|
+
args: {
|
|
98
|
+
label: "Deployment Window",
|
|
99
|
+
error: "The chosen time falls outside the allowed deployment window.",
|
|
100
|
+
required: true,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const WithLeadingIcon: Story = {
|
|
105
|
+
args: {
|
|
106
|
+
label: "Meeting Time",
|
|
107
|
+
assistiveText: "Choose the start time in your local timezone.",
|
|
108
|
+
leftSection: (
|
|
109
|
+
<svg
|
|
110
|
+
width="24"
|
|
111
|
+
height="24"
|
|
112
|
+
viewBox="0 0 24 24"
|
|
113
|
+
fill="none"
|
|
114
|
+
stroke="currentColor"
|
|
115
|
+
strokeWidth="2"
|
|
116
|
+
strokeLinecap="round"
|
|
117
|
+
strokeLinejoin="round"
|
|
118
|
+
>
|
|
119
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
120
|
+
<polyline points="12 6 12 12 16 14"></polyline>
|
|
121
|
+
</svg>
|
|
122
|
+
),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const StaticReadOnly: Story = {
|
|
127
|
+
args: {
|
|
128
|
+
label: "Static ReadOnly Review",
|
|
129
|
+
value: "14:30",
|
|
130
|
+
readOnly: true,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const EditableReadOnly: Story = {
|
|
135
|
+
args: {
|
|
136
|
+
label: "Editable ReadOnly Review",
|
|
137
|
+
defaultValue: "09:00",
|
|
138
|
+
readOnly: true,
|
|
139
|
+
labelWithEditIcon: true,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Placeholder for future TimePicker component tests. Add tests here if/when needed.
|