@recursica/mantine-adapter 0.49.0 → 0.50.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/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.49.0",
16
+ "version": "0.50.0",
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.24.0",
97
+ "@recursica/adapter-common": "^0.25.0",
98
98
  "@recursica/official-release": "^2.8.0"
99
99
  },
100
100
  "peerDependencies": {
@@ -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
- {...(sanitizedProps as unknown as MantineMenuProps)}
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.