@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,158 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { DatePicker } from "./DatePicker";
|
|
3
|
+
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof DatePicker> = {
|
|
6
|
+
title: "UI-Kit/DatePicker",
|
|
7
|
+
component: DatePicker,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
The \`DatePicker\` primitive provides a unified calendar date selection input integrated directly into the \`FormControlWrapper\` architecture.
|
|
14
|
+
|
|
15
|
+
### Architectural Decoupling
|
|
16
|
+
Recursica overrides the internal Mantine \`DatePickerInput\` wrapper defaults, safely injecting the date picker into our rigid structural layout systems. State modifiers (e.g. Focus, Errors, ReadOnly) hook seamlessly back onto our native CSS mapping architecture.
|
|
17
|
+
|
|
18
|
+
### Examples
|
|
19
|
+
Always structure horizontal architectures via the generic \`formLayout\` parameter.
|
|
20
|
+
\`\`\`tsx
|
|
21
|
+
<DatePicker
|
|
22
|
+
label="Start Date"
|
|
23
|
+
assistiveText="Select the deployment kick-off date."
|
|
24
|
+
formLayout="stacked"
|
|
25
|
+
/>
|
|
26
|
+
\`\`\`
|
|
27
|
+
`,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
argTypes: {
|
|
32
|
+
...formControlArgTypes,
|
|
33
|
+
disabled: {
|
|
34
|
+
control: "boolean",
|
|
35
|
+
description:
|
|
36
|
+
"Maps the formal disabled variable states structurally to the input core.",
|
|
37
|
+
},
|
|
38
|
+
error: {
|
|
39
|
+
control: "text",
|
|
40
|
+
description:
|
|
41
|
+
"Applies the strict error string boundary rendering invalid structures seamlessly.",
|
|
42
|
+
},
|
|
43
|
+
required: {
|
|
44
|
+
control: "boolean",
|
|
45
|
+
},
|
|
46
|
+
label: {
|
|
47
|
+
control: "text",
|
|
48
|
+
},
|
|
49
|
+
assistiveText: {
|
|
50
|
+
control: "text",
|
|
51
|
+
},
|
|
52
|
+
readOnly: {
|
|
53
|
+
control: "boolean",
|
|
54
|
+
description:
|
|
55
|
+
"Toggles structural read-only data presentation explicitly blocking standard component bindings.",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default meta;
|
|
61
|
+
|
|
62
|
+
type Story = StoryObj<typeof DatePicker>;
|
|
63
|
+
|
|
64
|
+
export const Default: Story = {
|
|
65
|
+
args: {
|
|
66
|
+
disabled: false,
|
|
67
|
+
label: "Project Deadline",
|
|
68
|
+
assistiveText: "Specify the absolute cutoff for code submission.",
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const FormsSideBySide: Story = {
|
|
73
|
+
args: {
|
|
74
|
+
label: "Incident Start Date",
|
|
75
|
+
assistiveText: "When did the incident originally occur?",
|
|
76
|
+
formLayout: "side-by-side",
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const WithLeadingIcon: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
label: "Launch Date",
|
|
83
|
+
leftSection: (
|
|
84
|
+
<svg
|
|
85
|
+
width="24"
|
|
86
|
+
height="24"
|
|
87
|
+
viewBox="0 0 24 24"
|
|
88
|
+
fill="none"
|
|
89
|
+
stroke="currentColor"
|
|
90
|
+
strokeWidth="2"
|
|
91
|
+
strokeLinecap="round"
|
|
92
|
+
strokeLinejoin="round"
|
|
93
|
+
>
|
|
94
|
+
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
|
|
95
|
+
<line x1="16" y1="2" x2="16" y2="6"></line>
|
|
96
|
+
<line x1="8" y1="2" x2="8" y2="6"></line>
|
|
97
|
+
<line x1="3" y1="10" x2="21" y2="10"></line>
|
|
98
|
+
</svg>
|
|
99
|
+
),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const Disabled: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
label: "Disabled Date Range",
|
|
106
|
+
disabled: true,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const ErrorState: Story = {
|
|
111
|
+
args: {
|
|
112
|
+
label: "Execution Date",
|
|
113
|
+
error: "The chosen date conflicts with an existing deployment freeze.",
|
|
114
|
+
required: true,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const OpenedCalendar: Story = {
|
|
119
|
+
args: {
|
|
120
|
+
label: "Meeting Date",
|
|
121
|
+
assistiveText: "Calendar rendered open by default for styling review.",
|
|
122
|
+
// `popoverProps.opened` overrides Mantine's own internal disclosure state (see
|
|
123
|
+
// PickerInputBase's `opened: dropdownOpened, ...popoverProps` spread order), so the
|
|
124
|
+
// dropdown stays open with no click interaction needed — same convention as Menu's
|
|
125
|
+
// `opened: true` stories.
|
|
126
|
+
popoverProps: { opened: true },
|
|
127
|
+
// Fixed (not computed) so the selected-day fill is visible on load, alongside the
|
|
128
|
+
// today marker, for styling review. Local-component constructor, not an ISO date
|
|
129
|
+
// string — `new Date("2026-08-26")` parses as UTC midnight, which renders as the
|
|
130
|
+
// 25th in any timezone behind UTC.
|
|
131
|
+
defaultValue: new Date(2026, 7, 26),
|
|
132
|
+
},
|
|
133
|
+
parameters: {
|
|
134
|
+
docs: {
|
|
135
|
+
description: {
|
|
136
|
+
story:
|
|
137
|
+
"The calendar dropdown renders open by default so its styling can be reviewed without a click interaction.",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const StaticReadOnly: Story = {
|
|
144
|
+
args: {
|
|
145
|
+
label: "Static ReadOnly Review",
|
|
146
|
+
value: new Date("2026-05-21"),
|
|
147
|
+
readOnly: true,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export const EditableReadOnly: Story = {
|
|
152
|
+
args: {
|
|
153
|
+
label: "Editable ReadOnly Review",
|
|
154
|
+
defaultValue: new Date("2026-06-01"),
|
|
155
|
+
readOnly: true,
|
|
156
|
+
labelWithEditIcon: true,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Placeholder for future DatePicker component tests. Add tests here if/when needed.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
DatePickerInput,
|
|
4
|
+
type DatePickerInputProps,
|
|
5
|
+
getFormattedDate,
|
|
6
|
+
} from "@mantine/dates";
|
|
7
|
+
import { type ReadOnlyControlProps } from "@recursica/adapter-common";
|
|
8
|
+
import {
|
|
9
|
+
filterStylingProps,
|
|
10
|
+
omitUnsupportedProps,
|
|
11
|
+
withCallerOverride,
|
|
12
|
+
mergeClassNames,
|
|
13
|
+
type RecursicaOverStyled,
|
|
14
|
+
} from "../../utils/filterStylingProps";
|
|
15
|
+
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
16
|
+
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
17
|
+
import { CalendarIcon } from "./DatePicker.icons";
|
|
18
|
+
import styles from "./DatePicker.module.css";
|
|
19
|
+
|
|
20
|
+
import { type RecursicaDatePickerProps as BaseRecursicaDatePickerProps } from "@recursica/adapter-common";
|
|
21
|
+
|
|
22
|
+
export interface RecursicaDatePickerProps
|
|
23
|
+
extends Omit<
|
|
24
|
+
DatePickerInputProps,
|
|
25
|
+
| "size"
|
|
26
|
+
| "variant"
|
|
27
|
+
| "radius"
|
|
28
|
+
| "wrapperProps"
|
|
29
|
+
| "label"
|
|
30
|
+
| "error"
|
|
31
|
+
| "required"
|
|
32
|
+
| "withAsterisk"
|
|
33
|
+
| "id"
|
|
34
|
+
| "description"
|
|
35
|
+
>,
|
|
36
|
+
Pick<
|
|
37
|
+
DatePickerInputProps,
|
|
38
|
+
"label" | "error" | "required" | "withAsterisk" | "id"
|
|
39
|
+
>,
|
|
40
|
+
Omit<
|
|
41
|
+
RecursicaFormControlWrapperProps,
|
|
42
|
+
"controlMaxWidth" | "controlMinWidth"
|
|
43
|
+
>,
|
|
44
|
+
ReadOnlyControlProps,
|
|
45
|
+
BaseRecursicaDatePickerProps {}
|
|
46
|
+
|
|
47
|
+
export type DatePickerProps = RecursicaOverStyled<RecursicaDatePickerProps>;
|
|
48
|
+
|
|
49
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
50
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
51
|
+
const UNSUPPORTED_PROPS = [
|
|
52
|
+
"size", // Recursica controls sizing via the `size` variant + design tokens, not raw dimensions.
|
|
53
|
+
"variant", // Colors/variants are token-driven; the library's native palette isn't exposed.
|
|
54
|
+
"radius", // Recursica does not expose the library's native corner-radius system.
|
|
55
|
+
"description", // Managed by FormControlWrapper via assistiveText.
|
|
56
|
+
] as const satisfies readonly (keyof DatePickerInputProps)[];
|
|
57
|
+
|
|
58
|
+
export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
59
|
+
function DatePicker(props, ref) {
|
|
60
|
+
const {
|
|
61
|
+
overStyled = false,
|
|
62
|
+
formLayout = "stacked",
|
|
63
|
+
|
|
64
|
+
// Label & Wrapper Maps
|
|
65
|
+
labelSize,
|
|
66
|
+
labelAlignment,
|
|
67
|
+
labelOptionalText,
|
|
68
|
+
labelWithEditIcon,
|
|
69
|
+
onLabelEditClick,
|
|
70
|
+
|
|
71
|
+
label,
|
|
72
|
+
assistiveText,
|
|
73
|
+
assistiveWithIcon,
|
|
74
|
+
error,
|
|
75
|
+
required,
|
|
76
|
+
withAsterisk,
|
|
77
|
+
id,
|
|
78
|
+
className,
|
|
79
|
+
style,
|
|
80
|
+
disabled,
|
|
81
|
+
readOnly,
|
|
82
|
+
readOnlyComponent,
|
|
83
|
+
emptyValueComponent,
|
|
84
|
+
value,
|
|
85
|
+
defaultValue,
|
|
86
|
+
highlightToday = true, // Default on so today is visually marked; consumers can still override.
|
|
87
|
+
valueFormat = "MM/DD/YY", // Default display format; consumers can still override.
|
|
88
|
+
placeholder = "MM / DD / YY", // Default placeholder; consumers can still override.
|
|
89
|
+
leftSection, // Default leading icon computed below; consumers can still override.
|
|
90
|
+
...rest
|
|
91
|
+
} = props;
|
|
92
|
+
|
|
93
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
94
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
95
|
+
UNSUPPORTED_PROPS,
|
|
96
|
+
);
|
|
97
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
98
|
+
|
|
99
|
+
// Securely map core native blocks down ensuring nested CSS modules map precisely
|
|
100
|
+
const mergedClassNames = mergeClassNames(
|
|
101
|
+
{
|
|
102
|
+
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
103
|
+
input: styles.input,
|
|
104
|
+
section: styles.section,
|
|
105
|
+
day: styles.day,
|
|
106
|
+
weekday: styles.weekday,
|
|
107
|
+
calendarHeader: styles.calendarHeader,
|
|
108
|
+
calendarHeaderControl: styles.calendarHeaderControl,
|
|
109
|
+
calendarHeaderLevel: styles.calendarHeaderLevel,
|
|
110
|
+
calendarHeaderControlIcon: styles.calendarHeaderControlIcon,
|
|
111
|
+
},
|
|
112
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// The calendar popover surface (.dropdown) is styled by Mantine's underlying `Popover`
|
|
116
|
+
// component, not `DatePickerInput` itself — it only accepts classNames via `popoverProps`,
|
|
117
|
+
// not the top-level `classNames` map above (which only reaches the Input/Calendar parts).
|
|
118
|
+
const popoverPropsInput = restRecord.popoverProps;
|
|
119
|
+
const consumerPopoverProps =
|
|
120
|
+
popoverPropsInput &&
|
|
121
|
+
typeof popoverPropsInput === "object" &&
|
|
122
|
+
!Array.isArray(popoverPropsInput)
|
|
123
|
+
? (popoverPropsInput as Record<string, unknown>)
|
|
124
|
+
: undefined;
|
|
125
|
+
const consumerPopoverClassNames =
|
|
126
|
+
consumerPopoverProps?.classNames &&
|
|
127
|
+
typeof consumerPopoverProps.classNames === "object" &&
|
|
128
|
+
!Array.isArray(consumerPopoverProps.classNames)
|
|
129
|
+
? (consumerPopoverProps.classNames as Partial<Record<string, string>>)
|
|
130
|
+
: undefined;
|
|
131
|
+
delete restRecord["popoverProps"];
|
|
132
|
+
const mergedPopoverProps = {
|
|
133
|
+
...consumerPopoverProps,
|
|
134
|
+
classNames: mergeClassNames(
|
|
135
|
+
{ dropdown: styles.dropdown },
|
|
136
|
+
consumerPopoverClassNames,
|
|
137
|
+
),
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const wrapperClass = className
|
|
141
|
+
? `${styles.layoutOverride} ${className}`
|
|
142
|
+
: styles.layoutOverride;
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<WithReadOnlyWrapper
|
|
146
|
+
className={wrapperClass}
|
|
147
|
+
style={style as React.CSSProperties}
|
|
148
|
+
controlMaxWidth={undefined}
|
|
149
|
+
controlMinWidth={undefined}
|
|
150
|
+
overStyled={overStyled as true}
|
|
151
|
+
formLayout={formLayout}
|
|
152
|
+
labelSize={labelSize}
|
|
153
|
+
labelAlignment={labelAlignment}
|
|
154
|
+
labelOptionalText={labelOptionalText}
|
|
155
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
156
|
+
onLabelEditClick={onLabelEditClick}
|
|
157
|
+
label={label}
|
|
158
|
+
assistiveText={assistiveText}
|
|
159
|
+
assistiveWithIcon={assistiveWithIcon}
|
|
160
|
+
error={error}
|
|
161
|
+
required={required}
|
|
162
|
+
withAsterisk={withAsterisk}
|
|
163
|
+
id={id}
|
|
164
|
+
readOnly={readOnly}
|
|
165
|
+
readOnlyComponent={readOnlyComponent}
|
|
166
|
+
emptyValueComponent={emptyValueComponent}
|
|
167
|
+
readOnlyType="text"
|
|
168
|
+
readOnlyValue={(() => {
|
|
169
|
+
const readOnlyDate = value !== undefined ? value : defaultValue;
|
|
170
|
+
// `DatePickerInput` formats its own displayed value via `valueFormat` internally
|
|
171
|
+
// (using this same `getFormattedDate` helper) — the read-only text swap-in bypassed
|
|
172
|
+
// that entirely and rendered `String(date)` instead (a raw JS `Date.toString()`, e.g.
|
|
173
|
+
// "Wed May 20 2026 17:00:00 GMT-0700 (Pacific Daylight Time)"), so a read-only
|
|
174
|
+
// DatePicker never matched what the same value looks like while editable.
|
|
175
|
+
return readOnlyDate
|
|
176
|
+
? getFormattedDate({
|
|
177
|
+
type: "default",
|
|
178
|
+
date: readOnlyDate,
|
|
179
|
+
locale: "en",
|
|
180
|
+
format: valueFormat,
|
|
181
|
+
labelSeparator: "",
|
|
182
|
+
})
|
|
183
|
+
: undefined;
|
|
184
|
+
})()}
|
|
185
|
+
readOnlyNativeProps={props}
|
|
186
|
+
activeComponent={
|
|
187
|
+
/* Naked Input execution safely decoupled from Mantine's macro Input.Wrapper DOM hooks */
|
|
188
|
+
<DatePickerInput
|
|
189
|
+
ref={ref}
|
|
190
|
+
{...(sanitizedProps as unknown as DatePickerInputProps)}
|
|
191
|
+
classNames={mergedClassNames}
|
|
192
|
+
disabled={disabled}
|
|
193
|
+
value={value}
|
|
194
|
+
defaultValue={defaultValue}
|
|
195
|
+
label={undefined} // Disable Mantine's native label
|
|
196
|
+
description={undefined} // Disable Mantine's native description
|
|
197
|
+
error={undefined} // Disable Mantine's native error text (handled by wrapper)
|
|
198
|
+
withAsterisk={false} // Handled by wrapper
|
|
199
|
+
highlightToday={highlightToday}
|
|
200
|
+
valueFormat={valueFormat}
|
|
201
|
+
placeholder={placeholder}
|
|
202
|
+
leftSection={withCallerOverride<React.ReactNode>(
|
|
203
|
+
<CalendarIcon />,
|
|
204
|
+
leftSection,
|
|
205
|
+
)}
|
|
206
|
+
wrapperProps={{
|
|
207
|
+
"data-disabled": disabled ? "true" : undefined,
|
|
208
|
+
"data-error": error ? "true" : undefined,
|
|
209
|
+
}}
|
|
210
|
+
popoverProps={mergedPopoverProps}
|
|
211
|
+
/>
|
|
212
|
+
}
|
|
213
|
+
/>
|
|
214
|
+
);
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
DatePicker.displayName = "DatePicker";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# DatePicker - Usage Guide
|
|
2
|
+
|
|
3
|
+
This document describes how to integrate and use the `DatePicker` component in your projects using `@recursica/adapter-mantine-v8`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Import Reference
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { DatePicker } from "@recursica/adapter-mantine-v8";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Basic Example
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import React from "react";
|
|
19
|
+
import { DatePicker } from "@recursica/adapter-mantine-v8";
|
|
20
|
+
|
|
21
|
+
export default function Demo() {
|
|
22
|
+
return <DatePicker label="Select Date" />;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 3. Design System Integration
|
|
29
|
+
|
|
30
|
+
All Recursica components in the `@recursica/adapter-mantine-v8` package adhere strictly to design system spacing, scaling, and behavior patterns.
|
|
31
|
+
|
|
32
|
+
> [!IMPORTANT]
|
|
33
|
+
>
|
|
34
|
+
> - **Anti-override protection**: Rogues style injections (like inline `style` or arbitrary `className`) are automatically blocked by our prop layer unless `overStyled={true}` is explicitly provided.
|
|
35
|
+
> - **No Direct Layers**: Do not pass a `layer` prop to this component. To place it on a specific visual layer, wrap it in a `<Layer layer={0|1|2|3}>` component natively.
|
|
36
|
+
> - **Variables and Theming**: Styling is entirely determined by local CSS variables defined in `recursica_variables_scoped.css` and mapped in the component's CSS module.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 4. Key Integration Features & Constraints
|
|
41
|
+
|
|
42
|
+
## Structural Constraints
|
|
43
|
+
|
|
44
|
+
1. **Read-Only Rendering**: When rendered in read-only mode, the selected date is displayed via a plain string conversion of the value. If you need custom date formatting, pass a `readOnlyComponent` prop to control how the value is rendered.
|
|
45
|
+
2. **Calendar Popover Styling**: The UI Kit exports no calendar-specific tokens (surface, selected day, hover, today, weekend, in-range), so the calendar reuses the closest existing tokens — see `DATEPICKER_IMPLEMENTATION_NOTES.md` for the exact mapping. All calendar states are token-driven; none are left on Mantine's un-tokenized defaults. The header's prev/next arrows and month/year label are styled as Recursica `text`-variant buttons.
|
|
46
|
+
3. **Today Highlighting**: `highlightToday` defaults to `true` so the current day is always visually marked; pass `highlightToday={false}` to turn it off.
|
|
47
|
+
4. **Default Value Format**: Displayed values default to `MM/DD/YY` (e.g. `08/19/26`); pass your own `valueFormat` (a `dayjs` format string) to override.
|
|
48
|
+
5. **Default Leading Icon**: A calendar icon is shown in the left section by default; pass your own `leftSection` to override it.
|
|
49
|
+
6. **Default Placeholder**: Defaults to `"MM / DD / YY"` when not set; pass your own `placeholder` to override it.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Select as MantineSelect,
|
|
4
|
+
type SelectProps as MantineSelectProps,
|
|
5
|
+
} from "@mantine/core";
|
|
6
|
+
import {
|
|
7
|
+
filterStylingProps,
|
|
8
|
+
omitUnsupportedProps,
|
|
9
|
+
mergeClassNames,
|
|
10
|
+
type RecursicaOverStyled,
|
|
11
|
+
} from "../../utils/filterStylingProps";
|
|
12
|
+
import styles from "./Dropdown.module.css";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Bare, unwrapped Select — no `FormControlWrapper`/`WithReadOnlyWrapper`, no label/assistiveText/
|
|
16
|
+
* error/required. Tied to the same `Dropdown.module.css` variables/classes as the public
|
|
17
|
+
* `Dropdown` component, so it looks identical, but is meant to be embedded inside another
|
|
18
|
+
* component that already owns its own `FormControlWrapper` (e.g. `TimePicker`'s AM/PM control) —
|
|
19
|
+
* nesting the full `Dropdown` there would double up `FormControl`/`FormControlLayout` wrapping.
|
|
20
|
+
*
|
|
21
|
+
* Not exported from this folder's `index.ts` — internal use only. Import it directly:
|
|
22
|
+
* `import { BareDropdown } from "../Dropdown/BareDropdown"`.
|
|
23
|
+
*/
|
|
24
|
+
export interface BareDropdownProps
|
|
25
|
+
extends Omit<
|
|
26
|
+
MantineSelectProps,
|
|
27
|
+
| "size"
|
|
28
|
+
| "variant"
|
|
29
|
+
| "radius"
|
|
30
|
+
| "wrapperProps"
|
|
31
|
+
| "label"
|
|
32
|
+
| "description"
|
|
33
|
+
| "error"
|
|
34
|
+
> {
|
|
35
|
+
data: MantineSelectProps["data"];
|
|
36
|
+
/** Applies the error visual state (via `data-error`) — no error message is rendered here. */
|
|
37
|
+
error?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type BareDropdownComponentProps = RecursicaOverStyled<BareDropdownProps>;
|
|
41
|
+
|
|
42
|
+
// Props this component intentionally doesn't support — deleted at runtime so they can't leak
|
|
43
|
+
// through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
|
|
44
|
+
const UNSUPPORTED_PROPS = [
|
|
45
|
+
"size", // Recursica controls sizing via the `size` variant + design tokens, not raw dimensions.
|
|
46
|
+
"variant", // Colors/variants are token-driven; the library's native palette isn't exposed.
|
|
47
|
+
"radius", // Recursica does not expose the library's native corner-radius system.
|
|
48
|
+
] as const satisfies readonly (keyof MantineSelectProps)[];
|
|
49
|
+
|
|
50
|
+
export const BareDropdown = forwardRef<
|
|
51
|
+
HTMLInputElement,
|
|
52
|
+
BareDropdownComponentProps
|
|
53
|
+
>(function BareDropdown(props, ref) {
|
|
54
|
+
const { overStyled = false, className, disabled, error, ...rest } = props;
|
|
55
|
+
const sanitizedProps = omitUnsupportedProps(
|
|
56
|
+
filterStylingProps(rest, overStyled) as Record<string, unknown>,
|
|
57
|
+
UNSUPPORTED_PROPS,
|
|
58
|
+
);
|
|
59
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
60
|
+
|
|
61
|
+
const mergedClassNames = mergeClassNames(
|
|
62
|
+
{
|
|
63
|
+
wrapper: className ? `${styles.root} ${className}` : styles.root,
|
|
64
|
+
input: styles.input,
|
|
65
|
+
section: styles.section,
|
|
66
|
+
dropdown: styles.dropdown,
|
|
67
|
+
option: styles.option,
|
|
68
|
+
},
|
|
69
|
+
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<MantineSelect
|
|
74
|
+
ref={ref}
|
|
75
|
+
{...(sanitizedProps as unknown as MantineSelectProps)}
|
|
76
|
+
classNames={mergedClassNames}
|
|
77
|
+
disabled={disabled}
|
|
78
|
+
label={undefined}
|
|
79
|
+
description={undefined}
|
|
80
|
+
error={undefined}
|
|
81
|
+
// `wrapperProps` targets Mantine's *outer* `Input.Wrapper` (the label/description/error
|
|
82
|
+
// stacking element) — a different, ancestor element from the "wrapper" styles-api slot that
|
|
83
|
+
// actually carries `styles.root`'s border/background. Dropdown.module.css's error/disabled
|
|
84
|
+
// rules (`.root[data-error]`/`[data-disabled]`) need the attribute on that inner element, so
|
|
85
|
+
// `attributes.wrapper` (which targets the same slot as `classNames.wrapper`/`styles.wrapper`)
|
|
86
|
+
// is the correct hook here, not `wrapperProps`. See TIMEPICKER_IMPLEMENTATION_NOTES.md.
|
|
87
|
+
attributes={{
|
|
88
|
+
wrapper: {
|
|
89
|
+
"data-disabled": disabled ? "true" : undefined,
|
|
90
|
+
"data-error": error ? "true" : undefined,
|
|
91
|
+
},
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
BareDropdown.displayName = "BareDropdown";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Dropdown Implementation Notes
|
|
2
|
+
|
|
3
|
+
The `Dropdown` component is mapped explicitly to Mantine's `<Select>` following the exact same strict encapsulation rules as `TextField`.
|
|
4
|
+
|
|
5
|
+
1. **Naked Primitive Mapping:** Mantine's `Select` natively executes macro-label generation. To decouple it, we explicitly disable internal labels (`label={undefined}`) and inject it purely inside our generic `FormControlWrapper`.
|
|
6
|
+
2. **Strict Dropdown Design Tokens:** The adapter implements strictly sandboxed styling utilizing only `--recursica_ui-kit_components_dropdown_...` variables. It explicitly does NOT inherit general `text-field` tokens despite geometric similarities, ensuring dropdown menus can be themed independently.
|
|
7
|
+
3. **Dropdown Appendages:** To correctly map Mantine's detached Popover `.dropdown` and list `.option` items, we targeted focus and geometric bindings appending standard padding structures matched to the dropdown height overrides dynamically into our `Dropdown.module.css`.
|
|
8
|
+
4. **Popup Vertical Padding:** The gap above/below the option list inside `.dropdown` is explicitly set from the same `--recursica_..._dropdown_properties_vertical-padding` token the closed control uses — it was previously left to Mantine's own untokenized `--combobox-padding` (4px) default.
|
|
9
|
+
5. **Clear Button Styling:** Mantine's native clear button (`clearButtonProps`, shown when `clearable` + a value are both present) is a bare `CloseButton` with its own hardcoded gray icon/hover styling by default. `Dropdown.tsx` merges in a `.clearButton` class (Dropdown.module.css) that overrides it with the same icon-size/trailing-icon-color/focus-ring tokens as the rest of the right section, using the double-class-selector specificity trick (same as Chip.module.css's `.root.root`) to beat Mantine's own CSS module rule without `!important`.
|
|
10
|
+
6. **Rich Option Content (`leadingIcon`/`supportingText`):** `data` items now accept optional `leadingIcon`/`supportingText` fields, via the shared `RecursicaComboboxItem` type in `@recursica/adapter-common` (see `MANTINE_ADAPTER_RICH_OPTION_DATA.md` at the repo root) — the same type `AutoComplete` and both `mui-adapter` components use, so it's declared once and not redeclared per component/adapter. `label` on that shared type is optional, falling back to `value` (matches Mantine's own runtime default for both `Select` and `Autocomplete`). `Dropdown.tsx` installs a default `renderOption` (`../../utils/renderRichOption.tsx`, shared with `AutoComplete`) that reads `leadingIcon`/`supportingText` straight off Mantine's own parsed `option` — no separate lookup-by-value map is needed, since Mantine's `getParsedComboboxData` passes extra fields on an item through untouched whenever the item already has both `value` and `label`. Because `label` is now optional, `data` is first run through adapter-common's `normalizeComboboxData` (backfilling `label` from `value`) before being handed to Mantine, so items missing `label` don't have their `leadingIcon`/`supportingText` silently dropped by that same parser (see `AutoComplete.tsx`'s identical, longer-standing use of this — `Dropdown`'s `label` used to be required, which is why it didn't need this before). A caller-supplied `renderOption` always wins over the default. New CSS classes (`.optionContent`/`.optionIcon`/`.optionText`/`.optionSupportingText`) reuse the menu-item component's icon/supporting-text tokens — no dedicated dropdown-option tokens exist for either, same reasoning as the `.option[data-selected]` reuse above. The icon is only rendered when `leadingIcon` is present (a conditional child, not a hidden reserved slot), so label/supportingText shift left when there's no icon; `.optionContent`'s `align-items: center` keeps the label vertically centered when there's no `supportingText` to stack under it.
|
|
11
|
+
7. **`wrapItemText`:** `label`/`supportingText` default to single-line truncation with an ellipsis (`.optionText > *` — `overflow: clip; overflow-clip-margin: 0.35em; text-overflow: ellipsis; white-space: nowrap`). Passing `wrapItemText` adds `.optionTextWrap` alongside `.optionText`, re-enabling wrapping (`white-space: normal; overflow-wrap: anywhere`) for both children — later-cascade-wins, equal specificity, same mechanism as this file's other CSS overrides. `renderRichOption`/`renderRichOptionContent` take `wrapItemText` as a third parameter and combine the two class names when it's true. `.optionText > *` used plain `overflow: hidden` until 2026-08-28 (Matt Massey) — it clipped descenders (e.g. "g") whenever `text_line-height` is tighter than the font's natural ascent+descent; `overflow-clip-margin` gives ink a small bleed allowance while still clipping genuinely overflowing text, same project-wide fix as Chip's `CHIP_IMPLEMENTATION_NOTES.md`.
|
|
12
|
+
8. **Selected Option Highlight (bug fix):** The `.option[data-selected="true"], .option[data-combobox-active="true"]` background/text-color rule (and the matching `.optionIcon`/`.optionSupportingText` color rules) used to key off `data-combobox-selected` instead of `data-combobox-active`. Despite the name, `data-combobox-active` is what Mantine sets when an option's value matches the current field value (`OptionsDropdown.mjs`: `active: checked`, consumed by `ComboboxOption.mjs`'s `mod` map); `data-combobox-selected` is an unrelated, transient keyboard-navigation highlight that Mantine sets/clears imperatively via `element.setAttribute` outside React (`use-combobox.mjs`'s `selectOption`/`clearSelectedItem`), not tied to the chosen value at all. The bug: the real selected option only showed the brand background while being arrow-key-navigated, and lost it entirely on a normal open/close (matches Playwright verification — `data-combobox-active="true"` is present and gets the token background after a plain click-select-reopen, with no keyboard involved).
|