@recursica/mantine-adapter 0.49.0 → 0.50.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 +17 -0
- package/dist/index.d.ts +6 -0
- 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 +12 -4
- package/dist/mantine-adapter.js.map +1 -1
- package/package.json +2 -2
- package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +1 -0
- package/src/components/DatePicker/DatePicker.module.css +13 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +25 -0
- package/src/components/Menu/MENU_IMPLEMENTATION_NOTES.md +10 -0
- package/src/components/Menu/Menu.stories.tsx +42 -0
- package/src/components/Menu/Menu.tsx +24 -2
- package/src/components/Menu/USAGE.md +1 -0
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "git+https://github.com/borderux/recursica.git",
|
|
14
14
|
"directory": "packages/mantine-adapter"
|
|
15
15
|
},
|
|
16
|
-
"version": "0.
|
|
16
|
+
"version": "0.50.1",
|
|
17
17
|
"type": "module",
|
|
18
18
|
"main": "./dist/mantine-adapter.cjs",
|
|
19
19
|
"module": "./dist/mantine-adapter.js",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"vitest": "^3.2.4"
|
|
95
95
|
},
|
|
96
96
|
"dependencies": {
|
|
97
|
-
"@recursica/adapter-common": "^0.
|
|
97
|
+
"@recursica/adapter-common": "^0.25.0",
|
|
98
98
|
"@recursica/official-release": "^2.8.0"
|
|
99
99
|
},
|
|
100
100
|
"peerDependencies": {
|
|
@@ -27,3 +27,4 @@ The `DatePicker` component is a wrapper around the `@mantine/dates` `DatePickerI
|
|
|
27
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
28
|
- **Default placeholder**: `placeholder="MM / DD / YY"`, set the same way (before `sanitizedProps`, so consumers can override).
|
|
29
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.
|
|
30
|
+
- **Day focus ring (bug fix)**: Mantine's `Day` ships no built-in `:focus-visible` style of its own (unlike `.calendarHeaderControl`/`.calendarHeaderLevel`, which are styled as `Button` and inherited a focus ring from that treatment), so a focused day — e.g. the auto-focused selected/today day when the calendar opens — fell through to the browser's native blue outline instead of a Recursica token. Fixed by adding `.day:focus-visible` with the same generic `--recursica_brand_states_focus_*` box-shadow ring already used by `.input` and `.calendarHeaderControl`/`.calendarHeaderLevel`. mui-adapter's `.MuiPickerDay-root.Mui-focusVisible` already had this — only the mantine-adapter side was missing it.
|
|
@@ -544,3 +544,16 @@
|
|
|
544
544
|
);
|
|
545
545
|
opacity: 1;
|
|
546
546
|
}
|
|
547
|
+
|
|
548
|
+
/* Focus ring, same convention as calendarHeaderControl/input's own :focus-visible treatment.
|
|
549
|
+
Mantine's Day ships no explicit focus style of its own, so without this it fell through to
|
|
550
|
+
the browser's native (blue) :focus-visible outline instead of a Recursica token. */
|
|
551
|
+
.day:focus-visible {
|
|
552
|
+
outline: none;
|
|
553
|
+
box-shadow:
|
|
554
|
+
0 0 0 var(--recursica_brand_states_focus_border-size)
|
|
555
|
+
var(--recursica_brand_states_focus_color),
|
|
556
|
+
0 0 var(--recursica_brand_states_focus_blur)
|
|
557
|
+
var(--recursica_brand_states_focus_margin)
|
|
558
|
+
var(--recursica_brand_states_focus_color);
|
|
559
|
+
}
|
|
@@ -115,6 +115,31 @@ export const ErrorState: Story = {
|
|
|
115
115
|
},
|
|
116
116
|
};
|
|
117
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
|
+
|
|
118
143
|
export const StaticReadOnly: Story = {
|
|
119
144
|
args: {
|
|
120
145
|
label: "Static ReadOnly Review",
|
|
@@ -76,3 +76,13 @@ Decisions and design tweaks strictly tailored for the UI Kit's Menu wrapped agai
|
|
|
76
76
|
- `pointer-events` on disabled items (Mantine handles disabled natively)
|
|
77
77
|
|
|
78
78
|
**Rationale:** Early iterations included aggressive structural resets (like `overflow: hidden`, `box-sizing: border-box`, `margin: 0`) cargo-culted from the Dropdown component. These caused sub-menus to render clipped inside the parent dropdown with scrollbars. The lesson: default to Mantine's native behavior and only override what the Recursica token system explicitly needs to control.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 9. `maxHeight` Override
|
|
83
|
+
|
|
84
|
+
**Decision:** `<Menu maxHeight={...}>` lets a caller override the token-driven `.dropdown` max-height with an explicit pixel (or other CSS length) value.
|
|
85
|
+
|
|
86
|
+
**Implementation:** This is the one deliberate exception to "no inline design tokens in TSX" (see `COMPONENT_DEV_GUIDE.md`) — it's applied via Mantine's per-part `styles` API (`styles={{ dropdown: { maxHeight } }}`), the same mechanism already used for `classNames`, merged with any caller-supplied `styles`. It only takes effect when `maxHeight` is actually passed; otherwise the CSS module's token-driven `max-height` is untouched.
|
|
87
|
+
|
|
88
|
+
**Caveat:** Because sub-menu dropdowns inherit the root Menu's `classNames`/`styles` mapping (see §6), setting `maxHeight` on the root also caps `Menu.Sub.Dropdown`'s height, not just the top-level dropdown.
|
|
@@ -161,6 +161,11 @@ const meta: Meta = {
|
|
|
161
161
|
description:
|
|
162
162
|
"Controlled open state. Leave undefined for uncontrolled behavior.",
|
|
163
163
|
},
|
|
164
|
+
maxHeight: {
|
|
165
|
+
control: "number",
|
|
166
|
+
description:
|
|
167
|
+
"Overrides the token-driven dropdown max-height with an explicit pixel value.",
|
|
168
|
+
},
|
|
164
169
|
},
|
|
165
170
|
parameters: {
|
|
166
171
|
docs: {
|
|
@@ -303,6 +308,43 @@ export const WithSubmenus: Story = {
|
|
|
303
308
|
},
|
|
304
309
|
};
|
|
305
310
|
|
|
311
|
+
export const WithMaxHeight: Story = {
|
|
312
|
+
args: {
|
|
313
|
+
position: "bottom-start",
|
|
314
|
+
maxHeight: 160,
|
|
315
|
+
// Rendered open by default so this story is diffable against the MUI adapter
|
|
316
|
+
// without an interaction step.
|
|
317
|
+
opened: true,
|
|
318
|
+
},
|
|
319
|
+
parameters: {
|
|
320
|
+
docs: {
|
|
321
|
+
description: {
|
|
322
|
+
story:
|
|
323
|
+
"`maxHeight` overrides the token-driven dropdown max-height with an explicit pixel value, scrolling the item list once it's exceeded.",
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
328
|
+
render: ({ withLayer, layer, ...args }: MenuStoryArgs) => {
|
|
329
|
+
return (
|
|
330
|
+
<Menu {...args}>
|
|
331
|
+
<Menu.Target>
|
|
332
|
+
<Button variant="solid">Menu with maxHeight</Button>
|
|
333
|
+
</Menu.Target>
|
|
334
|
+
|
|
335
|
+
<Menu.Dropdown>
|
|
336
|
+
<Menu.Item leftSection={<SettingsIcon />}>Settings</Menu.Item>
|
|
337
|
+
<Menu.Item leftSection={<MessageIcon />}>Messages</Menu.Item>
|
|
338
|
+
<Menu.Item leftSection={<ImageIcon />}>Gallery</Menu.Item>
|
|
339
|
+
<Menu.Item leftSection={<SearchIcon />}>Search</Menu.Item>
|
|
340
|
+
<Menu.Item leftSection={<ArrowsIcon />}>Transfer my data</Menu.Item>
|
|
341
|
+
<Menu.Item leftSection={<TrashIcon />}>Delete my account</Menu.Item>
|
|
342
|
+
</Menu.Dropdown>
|
|
343
|
+
</Menu>
|
|
344
|
+
);
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
|
|
306
348
|
export const HoverTrigger: Story = {
|
|
307
349
|
args: {
|
|
308
350
|
trigger: "click-hover",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
1
|
+
import { forwardRef, type CSSProperties } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Menu as MantineMenu,
|
|
4
4
|
type MenuProps as MantineMenuProps,
|
|
@@ -49,6 +49,7 @@ export type MenuProps = RecursicaOverStyled<
|
|
|
49
49
|
const MenuBase = function Menu({ overStyled = false, ...rest }: MenuProps) {
|
|
50
50
|
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
51
51
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
52
|
+
const { maxHeight, ...menuRestRecord } = restRecord;
|
|
52
53
|
|
|
53
54
|
// Bind CSS module classes to Mantine's internal classNames API
|
|
54
55
|
const mergedClassNames = mergeClassNames(
|
|
@@ -64,10 +65,31 @@ const MenuBase = function Menu({ overStyled = false, ...rest }: MenuProps) {
|
|
|
64
65
|
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
65
66
|
);
|
|
66
67
|
|
|
68
|
+
// `maxHeight` is a caller-supplied override of the token-driven dropdown max-height, applied
|
|
69
|
+
// via Mantine's per-part `styles` API (same mechanism as `classNames` above) rather than a CSS
|
|
70
|
+
// module change, since it's an explicit per-instance escape hatch, not a design token. The CSS
|
|
71
|
+
// module deliberately leaves `overflow` unset (see MENU_IMPLEMENTATION_NOTES.md §8) so it
|
|
72
|
+
// doesn't clip sub-menus at the token default height; once a caller opts into a fixed
|
|
73
|
+
// `maxHeight`, though, items need to scroll instead of spilling out past the boundary.
|
|
74
|
+
const callerStyles = menuRestRecord.styles as
|
|
75
|
+
| Partial<Record<string, CSSProperties>>
|
|
76
|
+
| undefined;
|
|
77
|
+
const mergedStyles = maxHeight
|
|
78
|
+
? {
|
|
79
|
+
...callerStyles,
|
|
80
|
+
dropdown: {
|
|
81
|
+
...callerStyles?.dropdown,
|
|
82
|
+
maxHeight: maxHeight as CSSProperties["maxHeight"],
|
|
83
|
+
overflowY: "auto" as CSSProperties["overflowY"],
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
: callerStyles;
|
|
87
|
+
|
|
67
88
|
return (
|
|
68
89
|
<MantineMenu
|
|
69
|
-
{...(
|
|
90
|
+
{...(menuRestRecord as unknown as MantineMenuProps)}
|
|
70
91
|
classNames={mergedClassNames}
|
|
92
|
+
{...(mergedStyles ? { styles: mergedStyles } : {})}
|
|
71
93
|
/>
|
|
72
94
|
);
|
|
73
95
|
};
|
|
@@ -52,3 +52,4 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
|
|
|
52
52
|
|
|
53
53
|
- The full Mantine composition API is supported, including sub-menus: `Menu`, `Menu.Target`, `Menu.Dropdown`, `Menu.Item`, `Menu.Divider`, `Menu.Label`, `Menu.Sub`, `Menu.Sub.Target`, `Menu.Sub.Item`, and `Menu.Sub.Dropdown`.
|
|
54
54
|
- The `color` prop on `Menu.Item` (and `Menu.Sub.Item`) — used by Mantine for semantics like a "danger" item — is ignored unless `overStyled={true}` is set.
|
|
55
|
+
- `maxHeight` on the root `<Menu>` overrides the dropdown's token-driven max-height with an explicit pixel (or other CSS length) value, e.g. `<Menu maxHeight={320}>`. It's a per-instance escape hatch, not a design token — leave it unset to use the token default.
|