@recursica/mantine-adapter 0.41.0 → 0.42.1
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/CHANGELOG.md +24 -0
- package/dist/index.d.ts +43 -3
- package/dist/mantine-adapter.cjs +2 -2
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +2580 -2218
- package/dist/mantine-adapter.js.map +1 -1
- package/package.json +1 -1
- package/src/GlobalExemptions.modules.css +0 -6
- package/src/components/Accordion/Accordion.module.css +0 -8
- package/src/components/AutoComplete/AutoComplete.module.css +0 -7
- package/src/components/Avatar/Avatar.module.css +0 -8
- package/src/components/Checkbox/CheckboxGroup.tsx +32 -10
- package/src/components/Chip/Chip.module.css +0 -16
- package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +14 -1
- package/src/components/DatePicker/DatePicker.icons.tsx +27 -0
- package/src/components/DatePicker/DatePicker.module.css +256 -7
- package/src/components/DatePicker/DatePicker.stories.tsx +0 -7
- package/src/components/DatePicker/DatePicker.tsx +37 -1
- package/src/components/DatePicker/USAGE.md +6 -2
- package/src/components/Dropdown/Dropdown.module.css +0 -7
- package/src/components/FileInput/FileInput.module.css +0 -21
- package/src/components/FileInput/FileInput.tsx +6 -0
- package/src/components/FileUpload/FileUpload.module.css +0 -11
- package/src/components/HoverCard/HoverCard.module.css +1 -7
- package/src/components/Label/Label.module.css +0 -6
- package/src/components/Link/Link.module.css +1 -6
- package/src/components/Menu/Menu.module.css +0 -6
- package/src/components/Modal/Modal.module.css +0 -11
- package/src/components/NumberInput/NumberInput.module.css +0 -7
- package/src/components/Pagination/Pagination.module.css +0 -81
- package/src/components/SegmentedControl/SegmentedControl.module.css +0 -1
- package/src/components/Slider/IMPLEMENTATION_NOTES.md +1 -0
- package/src/components/Slider/Slider.module.css +34 -0
- package/src/components/Slider/Slider.stories.tsx +1 -1
- package/src/components/TextArea/TextArea.module.css +0 -7
- package/src/components/TextArea/TextArea.tsx +2 -6
- package/src/components/TextField/TextField.module.css +0 -7
- package/src/components/TimePicker/TimePicker.module.css +0 -16
- package/src/components/Timeline/Timeline.module.css +0 -6
- package/src/components/Toast/Toast.module.css +0 -7
- package/src/components/Tooltip/Tooltip.module.css +0 -9
- package/src/components/TransferList/TRANSFERLIST_IMPLEMENTATION_NOTES.md +111 -0
- package/src/components/TransferList/TransferList.module.css +174 -41
- package/src/components/TransferList/TransferList.stories.tsx +110 -6
- package/src/components/TransferList/TransferList.tsx +417 -7
- package/src/components/TransferList/USAGE.md +37 -6
package/package.json
CHANGED
|
@@ -5,9 +5,3 @@
|
|
|
5
5
|
* recursica_variables_scoped.css but are not used by any specific component.
|
|
6
6
|
* Isolating them here keeps individual component stylesheets clean and focused.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
/* ==== GLOBAL ICON STYLE ====
|
|
10
|
-
Global layout option specifying the overall SVG rendering style (solid/outline).
|
|
11
|
-
Note to Forge Devs: style is not a CSS property or variable type and cannot be applied in standard CSS stylesheets.
|
|
12
|
-
This variable is ignored in the UI adapters and should be reviewed by the Forge team. */
|
|
13
|
-
/* recursica-ignore: --recursica_ui-kit_globals_icon_style */
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- accordion-header open-appearance background-color/border-color are ignored because Forge does
|
|
3
|
-
not apply a visual swap on expand, even though the token schema defines "open" appearance
|
|
4
|
-
colors distinct from "closed" (see .control below, which intentionally keeps a constant
|
|
5
|
-
background/border across collapsed/expanded). */
|
|
6
|
-
/* recursica-ignore: --recursica_ui-kit_components_accordion-header_variants_appearance_open_properties_colors_background-color */
|
|
7
|
-
/* recursica-ignore: --recursica_ui-kit_components_accordion-header_variants_appearance_open_properties_colors_border-color */
|
|
8
|
-
|
|
9
1
|
/* ==== ACCORDION CONTAINER ==== */
|
|
10
2
|
.root {
|
|
11
3
|
box-sizing: border-box;
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- border-size variables are ignored because the input box uses a uniform static boundary to prevent
|
|
3
|
-
unexpected layout shifts or flickering when switching between focused, disabled, or error states. */
|
|
4
|
-
/* recursica-ignore: --recursica_ui-kit_components_autocomplete_properties_border-size */
|
|
5
|
-
/* recursica-ignore: --recursica_ui-kit_components_autocomplete_variants_states_disabled_properties_border-size */
|
|
6
|
-
/* recursica-ignore: --recursica_ui-kit_components_autocomplete_variants_states_error_properties_border-size */
|
|
7
|
-
|
|
8
1
|
/* LAYOUT SPACING OVERRIDES:
|
|
9
2
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
10
3
|
- Also sets the --autocomplete-control-{max,min}-width hooks consumed inline in AutoComplete.tsx,
|
|
@@ -7,14 +7,6 @@ HARDCODED VALUES:
|
|
|
7
7
|
- Image has no explicit size tokens other than root size, assume it scales 100%
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
/* EXEMPTIONS:
|
|
11
|
-
The generic size variables below are defined in Figma but unused.
|
|
12
|
-
We explicitly style the avatar's dimensions using the dedicated width and height tokens
|
|
13
|
-
(e.g., small_properties_width and small_properties_height) for precise component layout. */
|
|
14
|
-
/* recursica-ignore: --recursica_ui-kit_components_avatar_variants_sizes_default_properties_size */
|
|
15
|
-
/* recursica-ignore: --recursica_ui-kit_components_avatar_variants_sizes_large_properties_size */
|
|
16
|
-
/* recursica-ignore: --recursica_ui-kit_components_avatar_variants_sizes_small_properties_size */
|
|
17
|
-
|
|
18
10
|
.root {
|
|
19
11
|
box-sizing: border-box;
|
|
20
12
|
margin: 0;
|
|
@@ -68,6 +68,16 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
68
68
|
// Delete prohibited sizing hooks from bypassing the variables
|
|
69
69
|
delete restRecord["size"];
|
|
70
70
|
|
|
71
|
+
// Mantine's own `Checkbox.Group` always establishes a context whose `value` defaults to
|
|
72
|
+
// `[]` (see @mantine/core's `useUncontrolled({ finalValue: [] })`), and its `Checkbox` spreads
|
|
73
|
+
// that context-derived `checked` *after* the individually-passed `checked` prop — so any
|
|
74
|
+
// `Checkbox` nested in a real `Checkbox.Group`, even one given neither `value` nor
|
|
75
|
+
// `defaultValue`, has its own `checked` prop silently forced to `false`. Callers who only want
|
|
76
|
+
// this component for its layout (item-gap spacing), not array-tracked selection — e.g.
|
|
77
|
+
// `TransferList`'s ungrouped rows, which pass each `Checkbox` its own controlled `checked` —
|
|
78
|
+
// must skip Mantine's real group primitive entirely rather than hand it an empty value.
|
|
79
|
+
const isArrayControlled = value !== undefined || defaultValue !== undefined;
|
|
80
|
+
|
|
71
81
|
return (
|
|
72
82
|
<WithReadOnlyWrapper
|
|
73
83
|
className={className}
|
|
@@ -97,18 +107,30 @@ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
97
107
|
readOnlyValue={value !== undefined ? value : defaultValue}
|
|
98
108
|
readOnlyNativeProps={props}
|
|
99
109
|
activeComponent={
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
isArrayControlled ? (
|
|
111
|
+
<MantineCheckbox.Group
|
|
112
|
+
ref={ref}
|
|
113
|
+
/* Natively bind local disabled lock dynamically */
|
|
114
|
+
{...(sanitizedProps as unknown as MantineCheckboxGroupProps)}
|
|
115
|
+
disabled={readOnly || (restRecord as any).disabled}
|
|
116
|
+
value={value as any}
|
|
117
|
+
defaultValue={defaultValue as any}
|
|
118
|
+
>
|
|
119
|
+
<div className={styles.groupRoot} data-layout={formLayout}>
|
|
120
|
+
{children}
|
|
121
|
+
</div>
|
|
122
|
+
</MantineCheckbox.Group>
|
|
123
|
+
) : (
|
|
124
|
+
<div
|
|
125
|
+
ref={ref}
|
|
126
|
+
role="group"
|
|
127
|
+
{...(sanitizedProps as Record<string, unknown>)}
|
|
128
|
+
className={styles.groupRoot}
|
|
129
|
+
data-layout={formLayout}
|
|
130
|
+
>
|
|
109
131
|
{children}
|
|
110
132
|
</div>
|
|
111
|
-
|
|
133
|
+
)
|
|
112
134
|
}
|
|
113
135
|
/>
|
|
114
136
|
);
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- close-icon-color, leading-icon-color, and select-specific state icon variables are ignored because
|
|
3
|
-
Chip uses explicit state color definitions (e.g. unselected, selected, error, error-selected variant colors)
|
|
4
|
-
to drive pixel-perfect hover and click transitions, rather than generic single-color overrides.
|
|
5
|
-
- text-size is ignored since actual chip sizing is dynamically driven by precise vertical padding and line-heights. */
|
|
6
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_properties_close-icon-color */
|
|
7
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_properties_leading-icon-color */
|
|
8
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_properties_text-size */
|
|
9
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_variants_selection-states_unselected_variants_states_error_properties_colors_leading-icon-color */
|
|
10
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_variants_selection-states_unselected_variants_states_error_properties_colors_selected-icon-color */
|
|
11
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_variants_selection-states_unselected_variants_states_error_properties_colors_icon-color */
|
|
12
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_variants_selection-states_selected_variants_states_error_properties_colors_icon-color */
|
|
13
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_variants_selection-states_selected_variants_states_error_properties_colors_leading-icon-color */
|
|
14
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_variants_selection-states_selected_properties_colors_leading-icon-color */
|
|
15
|
-
/* recursica-ignore: --recursica_ui-kit_components_chip_variants_selection-states_unselected_properties_colors_selected-icon-color */
|
|
16
|
-
|
|
17
1
|
/* HARDCODED VALUES
|
|
18
2
|
border-style: solid; (Baseline reset)
|
|
19
3
|
box-sizing: border-box; (Baseline reset)
|
|
@@ -8,9 +8,22 @@ The `DatePicker` component is a wrapper around the `@mantine/dates` `DatePickerI
|
|
|
8
8
|
|
|
9
9
|
1. **Naked Input Usage**: We intentionally pass `label={undefined}`, `description={undefined}`, and `error={undefined}` to the Mantine `DatePickerInput` component. This suppresses Mantine's internal macro form wrapping and ensures that only our `WithReadOnlyWrapper` > `FormControlWrapper` orchestrates labels, description text, and ARIA state error boundaries.
|
|
10
10
|
2. **Read-Only Implementation**: Since the value type for `DatePickerInput` can be a date object, string, or array, the `WithReadOnlyWrapper` attempts to safely cast the output value using standard `String(value)`. For production apps utilizing heavy date formatting logic, developers can pass a custom `readOnlyComponent` explicitly to bypass this default cast.
|
|
11
|
-
3. **Calendar Portal/Dropdown (Figma Token Issue)**: The Recursica UI Kit's `date-picker` component in Figma fails to export any explicit structural or color properties for the calendar popover itself (e.g. elevation, surface background, selected day colors). To solve this organically within the framework without breaking strict token adherence, we manually
|
|
11
|
+
3. **Calendar Portal/Dropdown (Figma Token Issue)**: The Recursica UI Kit's `date-picker` component in Figma fails to export any explicit structural or color properties for the calendar popover itself (e.g. elevation, surface background, selected day colors, hover, today, in-range). To solve this organically within the framework without breaking strict token adherence, we manually map every calendar state to the closest existing Recursica token instead of leaving any of it on Mantine's own un-tokenized defaults:
|
|
12
|
+
- **Surface** (`.dropdown`) & **selected day** (`.day[data-selected]`): `--recursica_ui-kit_components_hover-card-popover` tokens for elevation/padding/surface, `--recursica_ui-kit_components_button_variants_styles_solid` tokens for the selected day fill (unchanged from before).
|
|
13
|
+
- **Header content, weekday row** (`.calendarHeader`, `.weekday`): the hover-card-popover content-color token — previously unmapped, so these leaked Mantine's own text colors.
|
|
14
|
+
- **Header nav controls & month/year label** (`.calendarHeaderControl`, `.calendarHeaderLevel`, `.calendarHeaderControlIcon`): these are functionally buttons, so they're now styled with `--recursica_ui-kit_components_button_variants_styles_text` tokens (border/background/color/elevation) plus small-size `Button` sizing tokens (height, icon-only vs. label-content border-radius/padding/min-width, icon size, typography) — the same tokens `Button`'s `data-variant="text"` uses. Disabled opacity/elevation and the focus-visible ring also mirror `Button`'s own treatment instead of the generic brand tokens used elsewhere in this file.
|
|
15
|
+
- **Hover** (any day cell, header nav, header level button): the generic `--recursica_brand_states_hover_color`/`_hover_opacity` pair, via the same `::after`-overlay technique used by `Button`/`Menu` — the standard convention for components with no dedicated hover token. Unlike `Button`, the day number is a bare text node with no wrapping span to `z-index` above the overlay, so here the overlay sits at `z-index: -1` instead of the text getting `z-index: 1` — same visual effect, opposite side of the layering. Also unlike `Button` (a plain button with no library-native hover CSS), Mantine's `Day`/`CalendarHeaderControl`/`CalendarHeaderLevel` all ship their own native `:hover { background-color: ... }` rule — an explicit `background-color: transparent` reset on `:hover` was needed first, or the overlay would just layer on top of Mantine's own gray-0/dark-5 fill instead of replacing it. (Caught in review: the first pass added the overlay without this reset, and the visible hover color was indistinguishable from Mantine's plain default — confirmed via computed-style diffing, not just eyeballing the screenshot.)
|
|
16
|
+
- **Disabled / outside-month days**: the generic `--recursica_brand_states_disabled` opacity token — the same one used repo-wide (Button, Chip, Tree, Pagination, Accordion) for elements with no dedicated disabled token. **Disabled header nav/label controls** use `Button`'s own text-variant disabled opacity/elevation tokens instead, since they're now styled as `Button`.
|
|
17
|
+
- **Today marker**: no dedicated token exists, so it reuses the hover-card-popover border-size/color tokens already used for the dropdown surface's own border (previously hardcoded `1px`). `highlightToday` is now defaulted to `true` in `DatePicker.tsx` (Mantine ships it `false`, meaning today was never marked at all before this pass).
|
|
18
|
+
- **Weekend days**: Mantine's default colors these red; the UI Kit has no such concept, so weekend text is explicitly forced back to the same color as any other day rather than left on that unrelated default.
|
|
19
|
+
- **Range selection** (`.day[data-in-range]`/`[data-first-in-range]`/`[data-last-in-range]`): styled with the generic `--recursica_brand_states_overlay_color`/`_overlay_opacity` pair — previously unused anywhere in the codebase, but its name and shape (a translucent tint token) is exactly this use case. **Not currently reachable through our public API**: `RecursicaDatePickerProps` doesn't discriminate on `type`, so `value`/`defaultValue` are typed for single-date selection only — `type="range"` fails typecheck for consumers. This CSS is forward-compatible groundwork, not a demoed feature; proper range-mode typing would be a separate, larger follow-up. Also flagged as a judgment call on the token pairing itself: first usage anywhere, no prior precedent or design-team confirmation.
|
|
12
20
|
|
|
13
21
|
## Styling Quirks
|
|
14
22
|
|
|
15
23
|
- The `DatePickerInput` mimics Mantine's `Input` structure natively (`.input`, `.wrapper`, `.section`). We attach our `styles.input` and `styles.root` classes exactly like `TextField`.
|
|
16
24
|
- The global layout margin override (`.layoutOverride`) utilizes `--form-control-margin-bottom` driven by specific stacked/side-by-side design tokens.
|
|
25
|
+
- **Popover `classNames` gotcha (bug fix)**: `.dropdown` was previously passed via the top-level `classNames` prop alongside `wrapper`/`input`/`section`/`day`/etc., which silently did nothing — the calendar surface is rendered by Mantine's internal `Popover`, whose style API is a separate namespace only reachable via `popoverProps.classNames` (`PickerInputBaseProps.popoverProps: Partial<Omit<PopoverProps, 'children'>>`). The `.dropdown` CSS (background/border/border-radius/box-shadow/padding/font-family, matching `Popover`'s own `.dropdown` rules 1:1) was never actually applied until this was fixed — confirmed via computed styles before/after (`box-shadow: none` → the real elevation token). `DatePicker.tsx` now merges any consumer-supplied `popoverProps`/`popoverProps.classNames` with our default instead of overwriting them.
|
|
26
|
+
- **Default value format**: `valueFormat="MM/DD/YY"` (a `dayjs` format string, Mantine's default is `"MMMM D, YYYY"`), set before the `sanitizedProps` spread so consumers can still override via their own `valueFormat` prop.
|
|
27
|
+
- **Default leading icon**: `leftSection={<CalendarIcon />}` (new `DatePicker.icons.tsx`, following the existing local-inline-SVG convention used by `Pagination.icons.tsx` — no icon library is a dependency of either adapter) is set the same way, overridable via `leftSection`.
|
|
28
|
+
- **Default placeholder**: `placeholder="MM / DD / YY"`, set the same way (before `sanitizedProps`, so consumers can override).
|
|
29
|
+
- **Single-line enforcement (bug fix)**: `DatePickerInput` renders its value/placeholder as a `<button>`'s text content, not a native `<input>` — it wraps like any other block text by default. A long placeholder (e.g. the original story's `"Select a deadline..."`) wrapped to two lines, inflating the field's height well past its `min-height` token and making the field visually look narrower than its actual `width` token value even though the box itself was always the right size. Fixed with `white-space: nowrap`/`text-overflow: ellipsis`/`overflow-x: clip`/`overflow-y: visible` on `.input`, the same convention `FileInput.module.css`'s `.value` uses.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default leading icon for DatePicker's left section. Consumers can override it by passing
|
|
5
|
+
* their own `leftSection` prop (see DatePicker.tsx). Sized/colored entirely via the generic
|
|
6
|
+
* `.section :global(svg)` rule in DatePicker.module.css, same as the icon in the
|
|
7
|
+
* WithLeadingIcon story — no width/height/color hardcoded here on purpose.
|
|
8
|
+
*/
|
|
9
|
+
export function CalendarIcon(props: React.SVGProps<SVGSVGElement>) {
|
|
10
|
+
return (
|
|
11
|
+
<svg
|
|
12
|
+
viewBox="0 0 24 24"
|
|
13
|
+
fill="none"
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
strokeWidth="2"
|
|
16
|
+
strokeLinecap="round"
|
|
17
|
+
strokeLinejoin="round"
|
|
18
|
+
aria-hidden
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
|
|
22
|
+
<line x1="16" y1="2" x2="16" y2="6" />
|
|
23
|
+
<line x1="8" y1="2" x2="8" y2="6" />
|
|
24
|
+
<line x1="3" y1="10" x2="21" y2="10" />
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- border-size variables are ignored because a uniform 1px border is applied globally to prevent
|
|
3
|
-
unexpected layout shift or flickering during focused, disabled, or error state transitions. */
|
|
4
|
-
/* recursica-ignore: --recursica_ui-kit_components_date-picker_properties_border-size */
|
|
5
|
-
/* recursica-ignore: --recursica_ui-kit_components_date-picker_variants_states_disabled_properties_border-size */
|
|
6
|
-
/* recursica-ignore: --recursica_ui-kit_components_date-picker_variants_states_error_properties_border-size */
|
|
7
|
-
|
|
8
1
|
/* LAYOUT SPACING OVERRIDES:
|
|
9
2
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens. */
|
|
10
3
|
.layoutOverride {
|
|
@@ -117,6 +110,14 @@
|
|
|
117
110
|
);
|
|
118
111
|
|
|
119
112
|
outline: none;
|
|
113
|
+
|
|
114
|
+
/* Single-line enforcement: DatePickerInput renders its value/placeholder as a <button>'s text
|
|
115
|
+
content (not a native <input>), so it wraps by default like any other block text — clip it to
|
|
116
|
+
one line with ellipsis instead, same convention as FileInput's .value. */
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
text-overflow: ellipsis;
|
|
119
|
+
overflow-x: clip;
|
|
120
|
+
overflow-y: visible;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
.input::placeholder {
|
|
@@ -263,10 +264,257 @@
|
|
|
263
264
|
);
|
|
264
265
|
}
|
|
265
266
|
|
|
267
|
+
/* Month label + prev/next nav controls: styled as Recursica's own "text" button variant (small
|
|
268
|
+
size) rather than bare popover text — same border/background/color/typography tokens Button
|
|
269
|
+
uses for data-variant="text", since these are functionally buttons inside the popover. */
|
|
270
|
+
.calendarHeaderControl,
|
|
271
|
+
.calendarHeaderLevel {
|
|
272
|
+
position: relative;
|
|
273
|
+
border-style: solid;
|
|
274
|
+
border-width: var(
|
|
275
|
+
--recursica_ui-kit_components_button_variants_styles_text_properties_border-size
|
|
276
|
+
);
|
|
277
|
+
border-color: var(
|
|
278
|
+
--recursica_ui-kit_components_button_variants_styles_text_properties_colors_border-color
|
|
279
|
+
);
|
|
280
|
+
background-color: var(
|
|
281
|
+
--recursica_ui-kit_components_button_variants_styles_text_properties_colors_background-color
|
|
282
|
+
);
|
|
283
|
+
box-shadow: var(
|
|
284
|
+
--recursica_ui-kit_components_button_variants_styles_text_properties_elevation
|
|
285
|
+
);
|
|
286
|
+
color: var(
|
|
287
|
+
--recursica_ui-kit_components_button_variants_styles_text_properties_colors_text-color
|
|
288
|
+
);
|
|
289
|
+
font-family: var(
|
|
290
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_text_font-family
|
|
291
|
+
);
|
|
292
|
+
font-size: var(
|
|
293
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_text_font-size
|
|
294
|
+
);
|
|
295
|
+
font-style: var(
|
|
296
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_text_font-style
|
|
297
|
+
);
|
|
298
|
+
font-weight: var(
|
|
299
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_text_font-weight
|
|
300
|
+
);
|
|
301
|
+
letter-spacing: var(
|
|
302
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_text_letter-spacing
|
|
303
|
+
);
|
|
304
|
+
text-decoration: var(
|
|
305
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_text_text-decoration
|
|
306
|
+
);
|
|
307
|
+
text-transform: var(
|
|
308
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_text_text-transform
|
|
309
|
+
);
|
|
310
|
+
transition: all 0.2s ease;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Prev/next nav: icon-only button sizing (small). */
|
|
314
|
+
.calendarHeaderControl {
|
|
315
|
+
min-width: var(
|
|
316
|
+
--recursica_ui-kit_components_button_variants_content_icon-only_variants_sizes_small_properties_min-width
|
|
317
|
+
);
|
|
318
|
+
height: var(
|
|
319
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_height
|
|
320
|
+
);
|
|
321
|
+
border-radius: var(
|
|
322
|
+
--recursica_ui-kit_components_button_variants_content_icon-only_variants_sizes_small_properties_border-radius
|
|
323
|
+
);
|
|
324
|
+
padding: 0
|
|
325
|
+
var(
|
|
326
|
+
--recursica_ui-kit_components_button_variants_content_icon-only_variants_sizes_small_properties_horizontal-padding
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* Month/year label: label-content button sizing (small). */
|
|
331
|
+
.calendarHeaderLevel {
|
|
332
|
+
height: var(
|
|
333
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_height
|
|
334
|
+
);
|
|
335
|
+
border-radius: var(
|
|
336
|
+
--recursica_ui-kit_components_button_variants_content_label_variants_sizes_small_properties_border-radius
|
|
337
|
+
);
|
|
338
|
+
padding: 0
|
|
339
|
+
var(
|
|
340
|
+
--recursica_ui-kit_components_button_variants_content_label_variants_sizes_small_properties_horizontal-padding
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* Neutralize Mantine's own native :hover background-color (gray-0/dark-5) so only our
|
|
345
|
+
token-driven overlay below is visible — same reasoning as .day above. */
|
|
346
|
+
.calendarHeaderControl:hover:not(:disabled, [data-disabled]),
|
|
347
|
+
.calendarHeaderLevel:hover:not(:disabled, [data-disabled]) {
|
|
348
|
+
background-color: transparent;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.calendarHeaderControl::after,
|
|
352
|
+
.calendarHeaderLevel::after {
|
|
353
|
+
content: "";
|
|
354
|
+
position: absolute;
|
|
355
|
+
inset: 0;
|
|
356
|
+
border-radius: inherit;
|
|
357
|
+
z-index: -1;
|
|
358
|
+
pointer-events: none;
|
|
359
|
+
background-color: var(--recursica_brand_states_hover_color);
|
|
360
|
+
opacity: 0;
|
|
361
|
+
transition: opacity 150ms ease;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.calendarHeaderControl:hover:not(:disabled, [data-disabled])::after,
|
|
365
|
+
.calendarHeaderLevel:hover:not(:disabled, [data-disabled])::after {
|
|
366
|
+
opacity: var(--recursica_brand_states_hover_opacity);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.calendarHeaderControl:disabled,
|
|
370
|
+
.calendarHeaderControl[data-disabled],
|
|
371
|
+
.calendarHeaderLevel:disabled,
|
|
372
|
+
.calendarHeaderLevel[data-disabled] {
|
|
373
|
+
opacity: var(
|
|
374
|
+
--recursica_ui-kit_components_button_variants_styles_text_variants_states_disabled_properties_opacity
|
|
375
|
+
);
|
|
376
|
+
box-shadow: var(
|
|
377
|
+
--recursica_ui-kit_components_button_variants_styles_text_variants_states_disabled_properties_elevation
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* Focus ring, same convention as Button's own :focus-visible treatment. */
|
|
382
|
+
.calendarHeaderControl:focus-visible,
|
|
383
|
+
.calendarHeaderLevel:focus-visible {
|
|
384
|
+
outline: none;
|
|
385
|
+
box-shadow:
|
|
386
|
+
0 0 0 var(--recursica_brand_states_focus_border-size)
|
|
387
|
+
var(--recursica_brand_states_focus_color),
|
|
388
|
+
0 0 var(--recursica_brand_states_focus_blur)
|
|
389
|
+
var(--recursica_brand_states_focus_margin)
|
|
390
|
+
var(--recursica_brand_states_focus_color);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.calendarHeaderControlIcon {
|
|
394
|
+
color: var(
|
|
395
|
+
--recursica_ui-kit_components_button_variants_styles_text_properties_colors_icon-color
|
|
396
|
+
);
|
|
397
|
+
width: var(
|
|
398
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_icon
|
|
399
|
+
);
|
|
400
|
+
height: var(
|
|
401
|
+
--recursica_ui-kit_components_button_variants_sizes_small_properties_icon
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* Weekday row (Mo, Tu, We...) */
|
|
406
|
+
.weekday {
|
|
407
|
+
color: var(
|
|
408
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_content
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
266
412
|
.day {
|
|
413
|
+
position: relative;
|
|
414
|
+
color: var(
|
|
415
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_content
|
|
416
|
+
);
|
|
417
|
+
border-radius: var(--recursica_brand_dimensions_border-radii_default);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Weekend days: Mantine's default colors them red; the UI Kit has no such concept, so they're
|
|
421
|
+
intentionally kept identical to any other day rather than left on Mantine's un-tokenized default. */
|
|
422
|
+
.day[data-weekend] {
|
|
267
423
|
color: var(
|
|
268
424
|
--recursica_ui-kit_components_hover-card-popover_properties_colors_content
|
|
269
425
|
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* Today marker: reuse the popover border-size/color tokens already used for the dropdown
|
|
429
|
+
surface itself, since the UI Kit exports no dedicated "today" indicator token. */
|
|
430
|
+
.day[data-today][data-highlight-today]:not([data-selected], [data-in-range]) {
|
|
431
|
+
border-style: solid;
|
|
432
|
+
border-width: var(
|
|
433
|
+
--recursica_ui-kit_components_hover-card-popover_properties_border-size
|
|
434
|
+
);
|
|
435
|
+
border-color: var(
|
|
436
|
+
--recursica_ui-kit_components_hover-card-popover_properties_colors_border-color
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* Outside-month & disabled days: dim with the generic disabled-opacity token used everywhere else
|
|
441
|
+
in the design system for elements with no dedicated disabled token. */
|
|
442
|
+
.day[data-outside],
|
|
443
|
+
.day[data-disabled] {
|
|
444
|
+
opacity: var(--recursica_brand_states_disabled);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.day[data-disabled] {
|
|
448
|
+
cursor: not-allowed;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/* Unlike Button (a plain button with no built-in library hover), Mantine's Day ships its own
|
|
452
|
+
native :hover background-color (gray-0/dark-5) — neutralize it so only our token-driven
|
|
453
|
+
overlay below is visible. Same specificity as Mantine's own selector, so this relies on our
|
|
454
|
+
CSS module loading after @mantine/dates' stylesheet to win the tie (true today; see
|
|
455
|
+
DATEPICKER_IMPLEMENTATION_NOTES.md if that load order ever changes). */
|
|
456
|
+
.day:hover:not(:disabled, [data-disabled], [data-static]) {
|
|
457
|
+
background-color: transparent;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* Hover overlay, same generic mechanism used by Button/Menu for components without a dedicated
|
|
461
|
+
hover token. Unlike Button, the day number here is a bare text node (Mantine's Day renders it
|
|
462
|
+
directly, with no wrapping span to z-index above the overlay), so this sits at z-index: -1
|
|
463
|
+
instead — still scoped to .day's own positioned box, just painted behind the digit rather than
|
|
464
|
+
in front of it. */
|
|
465
|
+
.day::after {
|
|
466
|
+
content: "";
|
|
467
|
+
position: absolute;
|
|
468
|
+
inset: 0;
|
|
469
|
+
border-radius: inherit;
|
|
470
|
+
z-index: -1;
|
|
471
|
+
pointer-events: none;
|
|
472
|
+
background-color: var(--recursica_brand_states_hover_color);
|
|
473
|
+
opacity: 0;
|
|
474
|
+
transition: opacity 150ms ease;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.day:hover:not(:disabled, [data-disabled], [data-static])::after {
|
|
478
|
+
opacity: var(--recursica_brand_states_hover_opacity);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/* Range selection (type="range"): the UI Kit exports no dedicated in-range token, so the generic
|
|
482
|
+
overlay token — meant for exactly this kind of translucent tint — fills the band between the
|
|
483
|
+
two selected endpoints. Painted via ::before (behind the day number and the ::after hover
|
|
484
|
+
overlay) so the fill's opacity never dims the text itself. */
|
|
485
|
+
.day[data-in-range] {
|
|
486
|
+
border-radius: 0;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.day[data-in-range]::before {
|
|
490
|
+
content: "";
|
|
491
|
+
position: absolute;
|
|
492
|
+
inset: 0;
|
|
493
|
+
z-index: -1;
|
|
494
|
+
pointer-events: none;
|
|
495
|
+
background-color: var(--recursica_brand_states_overlay_color);
|
|
496
|
+
opacity: var(--recursica_brand_states_overlay_opacity);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.day[data-first-in-range] {
|
|
500
|
+
border-radius: 0;
|
|
501
|
+
border-start-start-radius: var(
|
|
502
|
+
--recursica_brand_dimensions_border-radii_default
|
|
503
|
+
);
|
|
504
|
+
border-end-start-radius: var(
|
|
505
|
+
--recursica_brand_dimensions_border-radii_default
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.day[data-last-in-range] {
|
|
510
|
+
border-radius: 0;
|
|
511
|
+
border-end-end-radius: var(--recursica_brand_dimensions_border-radii_default);
|
|
512
|
+
border-start-end-radius: var(
|
|
513
|
+
--recursica_brand_dimensions_border-radii_default
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.day[data-first-in-range][data-last-in-range] {
|
|
270
518
|
border-radius: var(--recursica_brand_dimensions_border-radii_default);
|
|
271
519
|
}
|
|
272
520
|
|
|
@@ -278,4 +526,5 @@
|
|
|
278
526
|
color: var(
|
|
279
527
|
--recursica_ui-kit_components_button_variants_styles_solid_properties_colors_text-color
|
|
280
528
|
);
|
|
529
|
+
opacity: 1;
|
|
281
530
|
}
|
|
@@ -65,7 +65,6 @@ export const Default: Story = {
|
|
|
65
65
|
args: {
|
|
66
66
|
disabled: false,
|
|
67
67
|
label: "Project Deadline",
|
|
68
|
-
placeholder: "Select a deadline...",
|
|
69
68
|
assistiveText: "Specify the absolute cutoff for code submission.",
|
|
70
69
|
},
|
|
71
70
|
};
|
|
@@ -73,7 +72,6 @@ export const Default: Story = {
|
|
|
73
72
|
export const FormsSideBySide: Story = {
|
|
74
73
|
args: {
|
|
75
74
|
label: "Incident Start Date",
|
|
76
|
-
placeholder: "Pick date...",
|
|
77
75
|
assistiveText: "When did the incident originally occur?",
|
|
78
76
|
formLayout: "side-by-side",
|
|
79
77
|
},
|
|
@@ -82,7 +80,6 @@ export const FormsSideBySide: Story = {
|
|
|
82
80
|
export const WithLeadingIcon: Story = {
|
|
83
81
|
args: {
|
|
84
82
|
label: "Launch Date",
|
|
85
|
-
placeholder: "Select launch date...",
|
|
86
83
|
leftSection: (
|
|
87
84
|
<svg
|
|
88
85
|
width="24"
|
|
@@ -106,7 +103,6 @@ export const WithLeadingIcon: Story = {
|
|
|
106
103
|
export const Disabled: Story = {
|
|
107
104
|
args: {
|
|
108
105
|
label: "Disabled Date Range",
|
|
109
|
-
placeholder: "Disabled selection...",
|
|
110
106
|
disabled: true,
|
|
111
107
|
},
|
|
112
108
|
};
|
|
@@ -114,7 +110,6 @@ export const Disabled: Story = {
|
|
|
114
110
|
export const ErrorState: Story = {
|
|
115
111
|
args: {
|
|
116
112
|
label: "Execution Date",
|
|
117
|
-
placeholder: "Pick a valid date...",
|
|
118
113
|
error: "The chosen date conflicts with an existing deployment freeze.",
|
|
119
114
|
required: true,
|
|
120
115
|
},
|
|
@@ -123,7 +118,6 @@ export const ErrorState: Story = {
|
|
|
123
118
|
export const StaticReadOnly: Story = {
|
|
124
119
|
args: {
|
|
125
120
|
label: "Static ReadOnly Review",
|
|
126
|
-
placeholder: "Ignored...",
|
|
127
121
|
value: new Date("2026-05-21"),
|
|
128
122
|
readOnly: true,
|
|
129
123
|
},
|
|
@@ -132,7 +126,6 @@ export const StaticReadOnly: Story = {
|
|
|
132
126
|
export const EditableReadOnly: Story = {
|
|
133
127
|
args: {
|
|
134
128
|
label: "Editable ReadOnly Review",
|
|
135
|
-
placeholder: "Ignored until active...",
|
|
136
129
|
defaultValue: new Date("2026-06-01"),
|
|
137
130
|
readOnly: true,
|
|
138
131
|
labelWithEditIcon: true,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "../../utils/filterStylingProps";
|
|
8
8
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
9
9
|
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
10
|
+
import { CalendarIcon } from "./DatePicker.icons";
|
|
10
11
|
import styles from "./DatePicker.module.css";
|
|
11
12
|
|
|
12
13
|
import { type RecursicaDatePickerProps as BaseRecursicaDatePickerProps } from "@recursica/adapter-common";
|
|
@@ -83,9 +84,12 @@ export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
83
84
|
wrapper: styles.root, // The nested Input internal relative wrapper bounding box
|
|
84
85
|
input: styles.input,
|
|
85
86
|
section: styles.section,
|
|
86
|
-
dropdown: styles.dropdown,
|
|
87
87
|
day: styles.day,
|
|
88
|
+
weekday: styles.weekday,
|
|
88
89
|
calendarHeader: styles.calendarHeader,
|
|
90
|
+
calendarHeaderControl: styles.calendarHeaderControl,
|
|
91
|
+
calendarHeaderLevel: styles.calendarHeaderLevel,
|
|
92
|
+
calendarHeaderControlIcon: styles.calendarHeaderControlIcon,
|
|
89
93
|
};
|
|
90
94
|
|
|
91
95
|
const classNamesProp = restRecord.classNames;
|
|
@@ -106,6 +110,33 @@ export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
106
110
|
: styles.section;
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
// The calendar popover surface (.dropdown) is styled by Mantine's underlying `Popover`
|
|
114
|
+
// component, not `DatePickerInput` itself — it only accepts classNames via `popoverProps`,
|
|
115
|
+
// not the top-level `classNames` map above (which only reaches the Input/Calendar parts).
|
|
116
|
+
const popoverPropsInput = restRecord.popoverProps;
|
|
117
|
+
const consumerPopoverProps =
|
|
118
|
+
popoverPropsInput &&
|
|
119
|
+
typeof popoverPropsInput === "object" &&
|
|
120
|
+
!Array.isArray(popoverPropsInput)
|
|
121
|
+
? (popoverPropsInput as Record<string, unknown>)
|
|
122
|
+
: undefined;
|
|
123
|
+
const consumerPopoverClassNames =
|
|
124
|
+
consumerPopoverProps?.classNames &&
|
|
125
|
+
typeof consumerPopoverProps.classNames === "object" &&
|
|
126
|
+
!Array.isArray(consumerPopoverProps.classNames)
|
|
127
|
+
? (consumerPopoverProps.classNames as Partial<Record<string, string>>)
|
|
128
|
+
: undefined;
|
|
129
|
+
delete restRecord["popoverProps"];
|
|
130
|
+
const mergedPopoverProps = {
|
|
131
|
+
...consumerPopoverProps,
|
|
132
|
+
classNames: {
|
|
133
|
+
...consumerPopoverClassNames,
|
|
134
|
+
dropdown: consumerPopoverClassNames?.dropdown
|
|
135
|
+
? `${styles.dropdown} ${consumerPopoverClassNames.dropdown}`
|
|
136
|
+
: styles.dropdown,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
109
140
|
const wrapperClass = className
|
|
110
141
|
? `${styles.layoutOverride} ${className}`
|
|
111
142
|
: styles.layoutOverride;
|
|
@@ -158,6 +189,11 @@ export const DatePicker = forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
158
189
|
"data-disabled": disabled ? "true" : undefined,
|
|
159
190
|
"data-error": error ? "true" : undefined,
|
|
160
191
|
}}
|
|
192
|
+
highlightToday // Default on so today is visually marked; consumers can still override via rest
|
|
193
|
+
valueFormat="MM/DD/YY" // Default display format; consumers can still override via rest
|
|
194
|
+
placeholder="MM / DD / YY" // Default placeholder; consumers can still override via rest
|
|
195
|
+
leftSection={<CalendarIcon />} // Default leading icon; consumers can still override via rest
|
|
196
|
+
popoverProps={mergedPopoverProps}
|
|
161
197
|
{...(sanitizedProps as unknown as DatePickerInputProps)}
|
|
162
198
|
/>
|
|
163
199
|
}
|
|
@@ -19,7 +19,7 @@ import React from "react";
|
|
|
19
19
|
import { DatePicker } from "@recursica/mantine-adapter";
|
|
20
20
|
|
|
21
21
|
export default function Demo() {
|
|
22
|
-
return <DatePicker label="Select Date"
|
|
22
|
+
return <DatePicker label="Select Date" />;
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -42,4 +42,8 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
|
|
|
42
42
|
## Structural Constraints
|
|
43
43
|
|
|
44
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 calendar
|
|
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.
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/* EXEMPTIONS:
|
|
2
|
-
- border-size variables are ignored because a uniform 1px border is applied globally to prevent
|
|
3
|
-
unexpected layout shift or flickering during focus, disabled, or error state transitions. */
|
|
4
|
-
/* recursica-ignore: --recursica_ui-kit_components_dropdown_properties_border-size */
|
|
5
|
-
/* recursica-ignore: --recursica_ui-kit_components_dropdown_variants_states_disabled_properties_border-size */
|
|
6
|
-
/* recursica-ignore: --recursica_ui-kit_components_dropdown_variants_states_error_properties_border-size */
|
|
7
|
-
|
|
8
1
|
/* LAYOUT SPACING OVERRIDES:
|
|
9
2
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
10
3
|
- Also sets the --dropdown-control-{max,min}-width hooks consumed inline in Dropdown.tsx, since
|