@recursica/mantine-adapter 0.32.0 → 0.34.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/OVERSTYLING.md +56 -0
  3. package/USAGE.md +6 -4
  4. package/dist/mantine-adapter.cjs +2 -2
  5. package/dist/mantine-adapter.cjs.map +1 -1
  6. package/dist/mantine-adapter.js +1765 -1445
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/dist/src/components/Modal/Modal.d.ts +21 -8
  9. package/dist/src/components/Pagination/Pagination.d.ts +45 -25
  10. package/dist/src/components/Panel/Panel.d.ts +22 -8
  11. package/dist/src/components/Stepper/Stepper.d.ts +4 -2
  12. package/dist/src/components/Table/Table.d.ts +25 -9
  13. package/dist/src/components/Tooltip/Tooltip.d.ts +4 -2
  14. package/llms.txt +57 -2
  15. package/package.json +3 -2
  16. package/src/OverStyling.tsx +10 -226
  17. package/src/components/Accordion/USAGE.md +90 -0
  18. package/src/components/AssistiveElement/USAGE.md +36 -0
  19. package/src/components/AutoComplete/USAGE.md +66 -0
  20. package/src/components/Avatar/USAGE.md +65 -0
  21. package/src/components/Badge/USAGE.md +59 -0
  22. package/src/components/Breadcrumb/USAGE.md +59 -0
  23. package/src/components/Button/USAGE.md +100 -0
  24. package/src/components/Card/USAGE.md +71 -0
  25. package/src/components/Checkbox/USAGE.md +64 -0
  26. package/src/components/Chip/USAGE.md +77 -0
  27. package/src/components/Container/USAGE.md +47 -0
  28. package/src/components/DatePicker/USAGE.md +55 -0
  29. package/src/components/Dropdown/USAGE.md +51 -0
  30. package/src/components/FileInput/USAGE.md +36 -0
  31. package/src/components/FileUpload/USAGE.md +41 -0
  32. package/src/components/Flex/USAGE.md +48 -0
  33. package/src/components/FormControlLayout/USAGE.md +40 -0
  34. package/src/components/FormControlWrapper/USAGE.md +75 -0
  35. package/src/components/Group/USAGE.md +48 -0
  36. package/src/components/HoverCard/USAGE.md +121 -0
  37. package/src/components/Label/USAGE.md +87 -0
  38. package/src/components/Link/USAGE.md +69 -0
  39. package/src/components/Loader/USAGE.md +63 -0
  40. package/src/components/Menu/USAGE.md +124 -0
  41. package/src/components/Modal/Modal.tsx +129 -23
  42. package/src/components/Modal/USAGE.md +58 -0
  43. package/src/components/NumberInput/USAGE.md +55 -0
  44. package/src/components/Pagination/Pagination.tsx +73 -17
  45. package/src/components/Pagination/USAGE.md +55 -0
  46. package/src/components/Panel/Panel.tsx +134 -14
  47. package/src/components/Panel/USAGE.md +145 -0
  48. package/src/components/Popover/USAGE.md +121 -0
  49. package/src/components/Radio/USAGE.md +36 -0
  50. package/src/components/ReadOnlyField/USAGE.md +52 -0
  51. package/src/components/SegmentedControl/USAGE.md +56 -0
  52. package/src/components/Slider/USAGE.md +86 -0
  53. package/src/components/Stack/USAGE.md +48 -0
  54. package/src/components/Stepper/Stepper.tsx +20 -2
  55. package/src/components/Stepper/USAGE.md +41 -0
  56. package/src/components/Switch/USAGE.md +65 -0
  57. package/src/components/Table/Table.tsx +153 -16
  58. package/src/components/Table/USAGE.md +41 -0
  59. package/src/components/Tabs/USAGE.md +45 -0
  60. package/src/components/Text/USAGE.md +40 -0
  61. package/src/components/TextArea/USAGE.md +48 -0
  62. package/src/components/TextField/USAGE.md +60 -0
  63. package/src/components/TimePicker/USAGE.md +36 -0
  64. package/src/components/Timeline/USAGE.md +57 -0
  65. package/src/components/Title/USAGE.md +36 -0
  66. package/src/components/Toast/USAGE.md +80 -0
  67. package/src/components/Tooltip/Tooltip.tsx +26 -2
  68. package/src/components/Tooltip/USAGE.md +124 -0
  69. package/src/components/TransferList/USAGE.md +46 -0
  70. package/src/components/Tree/USAGE.md +46 -0
@@ -0,0 +1,87 @@
1
+ # Label - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Label` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Label } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Label } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return <Label required>Field Label</Label>;
23
+ }
24
+ ```
25
+
26
+ ---
27
+
28
+ ## 3. Design System Integration
29
+
30
+ All Recursica components in the `@recursica/mantine-adapter` 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
+ ## Architecture Overview
43
+
44
+ The `Label` component is fundamentally built as a strict, localized wrapper around Mantine's native `Input.Label`. The core goal is to preserve context, ref-forwarding, and native accessibility links, while completely overriding visual behaviors via Recursica design variables in scoped CSS.
45
+
46
+ ## Key Design Decisions
47
+
48
+ ### **Layout Architecture**
49
+
50
+ - Because Recursica dictates complex alignments between primary label text, asterisks, edit icons, and optional strings, Mantine's rigid form label flow could not be used out-of-the-box.
51
+ - Implemented `display: flex; flex-wrap: wrap;` directly on `.root`.
52
+ - Injected strict integer-based flex `order` properties (e.g. `order: 1` for text, `order: 2` for `required`, `order: 5` for `optionalText`) to physically decouple DOM rendering from markup flow.
53
+ - Because `optionalText` often acts as secondary contextual detail, it forces `flex-basis: 100%`, securely wrapping to a secondary line underneath the primary label properties and transforming the horizontal gap spacing seamlessly into a `margin-top` vector.
54
+
55
+ ### **Optional Text & Required Mutually Exclusive Parsing**
56
+
57
+ - By design standards, a component cannot logically be both "Required" and mapped as "Optional".
58
+ - To bulletproof implementations natively, the adapter forcibly suppresses rendering of the `resolvedOptionalText` strings if the parent wrapper invokes `required={true}`.
59
+ - `optionalText` can operate as dynamic data or specifically as a boolean `true`, which forces the adapter to natively render the formal `(Optional)` string map.
60
+
61
+ ### **The "Edit Icon" Replacer Logic**
62
+
63
+ - Passing `withEditIcon={true}` evaluates it as fundamentally mutually exclusive to the standard required indicator asterisk.
64
+ - `required={required && !withEditIcon}` is passed to the underlying Mantine structure so the native `*` is entirely suppressed.
65
+ - Added localized styling via `data-replaces-asterisk={required ? "true" : undefined}` onto the `.editIconWrapper` element. If an editable instance is simultaneously designated as required, the edit icon functionally assumes the indicator role natively, overriding its default icon metrics explicitly to match `--recursica_ui-kit_components_label_properties_colors_asterisk`.
66
+
67
+ ### **Bypassing `Input.Wrapper` Integrations**
68
+
69
+ - By decoupling from Mantine's standard `Input.Wrapper`, the `Label` component is exclusively mapped manually through `FormControlWrapper`. This grants us exact visual sync regarding where the label renders based on `formLayout="stacked"` or `formLayout="side-by-side"`, without fighting internal Mantine positional hooks that assume vertical stacking by default.
70
+
71
+ ### **Label Size Container Constraints**
72
+
73
+ - The `labelSize` parameter (mapping values like `"small"`) internally **does not scale typographic font metrics**. Instead, it dynamically defines the explicit horizontal bounding width limit of the block container itself.
74
+ - **Architectural Rule:** `labelSize` modifications are strictly designed to execute exclusively when `formLayout="side-by-side"` is active. It acts as an optical grid threshold ensuring left-aligned string wrappers constrain correctly uniformly down a column without bleeding into the physical input arrays alongside them.
75
+
76
+ ## Outstanding Technical Debt / Issues
77
+
78
+ ### **Description Property Omission**
79
+
80
+ - **Issue:** Currently, the adapter `Label` does not natively parse or support a structured `description` node directly attached beneath it.
81
+ - **Context:** Standard forms often attach subtext beneath inputs, but mapping a description purely within the `<Label>` (distinct from an overall form-control description) is omitted structurally until subsequent UI Kit requirements mandate dedicated `description` styles locally on the label component itself.
82
+
83
+ ### **Global Interactive Hover Mappings**
84
+
85
+ - **Issue:** Currently, the `editIconWrapper` lacks a dedicated hover background layer.
86
+ - **Context:** While the global theme abstracts interactive hover environments deeply using layered variable syntax (e.g., `--recursica_brand_layer_1_elements_interactive_hover-color`), Recursica's token generation framework natively fails to expose a universally generic placeholder token (e.g., `--recursica_elements_interactive_hover-color`) which downstream wrappers can map their `<Layer>` injections directly into safely.
87
+ - **Resolution Path:** We are explicitly ignoring the `.editIconWrapper:hover` background color assignment until the raw Figma token exports directly scaffold a generic token placeholder that we can interface cleanly with across arbitrary `<Layer>` contexts, avoiding manually mapping statically hardcoded depths (`layer-0`, `layer-1`).
@@ -0,0 +1,69 @@
1
+ # Link - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Link` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Link } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Link } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return (
23
+ <Link href="/dashboard" variant="inline">
24
+ Go to Dashboard
25
+ </Link>
26
+ );
27
+ }
28
+ ```
29
+
30
+ ---
31
+
32
+ ## 3. Design System Integration
33
+
34
+ All Recursica components in the `@recursica/mantine-adapter` package adhere strictly to design system spacing, scaling, and behavior patterns.
35
+
36
+ > [!IMPORTANT]
37
+ >
38
+ > - **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.
39
+ > - **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.
40
+ > - **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.
41
+
42
+ ---
43
+
44
+ ## 4. Key Integration Features & Constraints
45
+
46
+ ## Missing States in Design Tokens
47
+
48
+ Currently, the design tokens (`recursica_variables_scoped.css`) only provide states for:
49
+
50
+ - `default`
51
+ - `hover`
52
+ - `visited`
53
+ - `visited-hover`
54
+
55
+ There are no tokens for the `active` or `focus` states. If these are needed, they must be added to the Figma variables and exported via the token pipeline. The component relies on the browser's default focus outline for accessibility unless overriden by a global reset.
56
+
57
+ ## Overriding Mantine's underline Prop
58
+
59
+ Mantine's `Anchor` component uses `underline="hover"` by default. Because our design system specifies exact `text-decoration` styles via CSS tokens, we explicitly pass `underline="never"` to the underlying Mantine component. This prevents Mantine from injecting its own text-decoration inline or via generic classnames, ensuring our scoped CSS remains the single source of truth.
60
+
61
+ ## Base Layout
62
+
63
+ Mantine's `Anchor` renders an inline element by default and does not natively support `leftSection` like the `Button` component. To support an optional `icon` alongside the text, we enforce a baseline layout of `display: inline-flex` and `align-items: center` in `Link.module.css`.
64
+
65
+ When an icon is present, the component conditionally passes a `data-has-icon` attribute to the root element. The CSS module uses this attribute to apply the `icon-text-gap` token via the CSS `gap` property.
66
+
67
+ ## Inner Wrappers
68
+
69
+ The icon and children are wrapped in internal `<span>` tags (`.iconWrapper` and `.labelText` respectively). This follows the component development guide for structural robustness, allowing us to enforce specific sizing on the icon and intrinsic text truncation behavior if the link is placed in a bounded container.
@@ -0,0 +1,63 @@
1
+ # Loader - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Loader` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Loader } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Loader } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return <Loader size="md" />;
23
+ }
24
+ ```
25
+
26
+ ---
27
+
28
+ ## 3. Design System Integration
29
+
30
+ All Recursica components in the `@recursica/mantine-adapter` 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
+ ## Architecture & Integration
43
+
44
+ The `Loader` component acts as a strictly tokenized wrapper bridging the Recursica UI-Kit `loader` variables to the generic Mantine `@mantine/core` `Loader` primitive.
45
+
46
+ ### Key Decisions:
47
+
48
+ - **Variant Mapping:** Recursica's `variant` directly proxies to Mantine's `type` prop for `"oval" | "bars" | "dots"`.
49
+ - **Property Overrides Disabled:** Natively overriding specific structural variants directly on the JSX interface like `thickness` and `borderRadius` has been intentionally omitted. Component rendering relies entirely on variables exposed by the underlying UI Kit mappings tied to the size prop.
50
+
51
+ ### Token Mapping:
52
+
53
+ Sizes are bound through `data-size` attributes (`sm`, `md`, `lg` parsing to target `<div data-size="small">`, etc.).
54
+
55
+ Mantine natively sets sizing dynamically at the component root and parses thickness/variants differently natively (e.g., `oval` styles its geometry strictly via CSS `border`, while `bars` and `dots` utilize specific DOM inner spans `span.dot` / `span.bar`).
56
+
57
+ #### Specific CSS Targeting Hacks Used
58
+
59
+ - `border-width` and `border-radius` structurally style the `thickness` and `border-radius` configuration of the `oval` variant on the `::after` pseudo-element by resolving `--recursica_ui-kit_components_loader_variants_sizes_..._properties_thickness` and `_border-radius`.
60
+
61
+ ### Unsupported Properties
62
+
63
+ - **xs and xl Sizing:** These sizes are explicitly unsupported in the Recursica standard logic (as surfaced in `filterStylingProps` and UI Kit mappings). Attempting to use them will safely default back or fallthrough statically unless defined later. See `COMPONENT_ISSUES.md`.
@@ -0,0 +1,124 @@
1
+ # Menu - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Menu` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Menu } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Menu } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return (
23
+ <Menu>
24
+ <Menu.Target>
25
+ <Button>Toggle Menu</Button>
26
+ </Menu.Target>
27
+ <Menu.Dropdown>
28
+ <Menu.Item>Profile</Menu.Item>
29
+ <Menu.Item>Settings</Menu.Item>
30
+ <Menu.Item color="red">Logout</Menu.Item>
31
+ </Menu.Dropdown>
32
+ </Menu>
33
+ );
34
+ }
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 3. Design System Integration
40
+
41
+ All Recursica components in the `@recursica/mantine-adapter` package adhere strictly to design system spacing, scaling, and behavior patterns.
42
+
43
+ > [!IMPORTANT]
44
+ >
45
+ > - **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.
46
+ > - **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.
47
+ > - **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.
48
+
49
+ ---
50
+
51
+ ## 4. Key Integration Features & Constraints
52
+
53
+ ## 1. Composable API Preservation (1:1 Mapping)
54
+
55
+ **Decision:** We maintain the exact library composition API structure (`<Menu>`, `<Menu.Target>`, `<Menu.Dropdown>`, `<Menu.Item>`, `<Menu.Divider>`, `<Menu.Label>`, `<Menu.Sub>`, `<Menu.Sub.Target>`, `<Menu.Sub.Item>`, `<Menu.Sub.Dropdown>`) as a 1:1 React component mapping.
56
+
57
+ **Implementation:** Mantine's Menu internally manages WAI-ARIA role assignments (`role="menu"`, `role="menuitem"`, `aria-haspopup`, `aria-expanded`, `aria-controls`), keyboard navigation (arrow keys, Enter, Escape), and focus trapping natively across its composable hierarchy. By preserving the exact sub-component tree, Recursica safely adopts all of these accessibility behaviors without reimplementation.
58
+
59
+ ---
60
+
61
+ ## 2. Hover State Overlay Technique
62
+
63
+ **Decision:** We nullify Mantine's native hover background on `Menu.Item` and exclusively utilize Recursica's hover structure via a `::after` pseudo-element overlay.
64
+
65
+ **Implementation:** The `.item::after` pseudo-object dynamically pulls `hover-color` & `hover-opacity` bindings from the Recursica token system. Mantine natively changes `background-color` on item hover which would override our token-driven colors. We enforce `background-color` to remain the unselected-item background on hover, then layer our pseudo-overlay on top. This is the same technique used in the Accordion component.
66
+
67
+ ---
68
+
69
+ ## 3. Selected vs Unselected Item States
70
+
71
+ **Decision:** Menu items map to Recursica's `selected-item` and `unselected-item` color token groups.
72
+
73
+ **Implementation:** The CSS module defaults all items to `unselected-item_*` tokens (background, text, opacity). Items with `[data-selected]` switch to `selected-item_*` tokens. This mapping is natively driven by Mantine's internal state tracking without React state hooks.
74
+
75
+ ---
76
+
77
+ ## 4. `color` Prop Stripping
78
+
79
+ **Decision:** Mantine's `color` prop on `Menu.Item` (used for semantics like "danger/red") is explicitly stripped in strict mode.
80
+
81
+ **Implementation:** The `color` prop is deleted from the sanitized props when `overStyled` is `false`. This enforces strict design token adherence — all item colors come from the CSS module referencing Recursica variables. Developers requiring custom color semantics must either:
82
+
83
+ 1. Use `overStyled={true}` as an explicit escape hatch
84
+ 2. Contribute a proper Recursica variant to the token system
85
+
86
+ **Rationale:** The Recursica token set does not currently include danger-specific or semantic-color variants for menu items. Allowing arbitrary `color` values would break design system consistency.
87
+
88
+ ---
89
+
90
+ ## 5. Menu.Target Pass-Through
91
+
92
+ **Decision:** `Menu.Target` is a transparent pass-through with no styling applied.
93
+
94
+ **Implementation:** The target wrapper exists solely to manage Mantine's ref forwarding and event binding for the trigger element. No `filterStylingProps` or CSS module classes are applied — the trigger's appearance is entirely controlled by whatever component the integrator places inside it (e.g., `<Button>`).
95
+
96
+ ---
97
+
98
+ ## 6. Sub-Menu Support
99
+
100
+ **Decision:** We wrap the full Mantine sub-menu hierarchy (`Menu.Sub`, `Menu.Sub.Target`, `Menu.Sub.Item`, `Menu.Sub.Dropdown`).
101
+
102
+ **Implementation:** Sub-menu components inherit the same CSS module classes as their top-level counterparts since the Recursica token set does not distinguish sub-menu-specific styling. `Menu.Sub.Item` also strips the `color` prop in strict mode, consistent with `Menu.Item`. The sub-menu dropdown portal inherits the same `classNames` from the root Menu's `classNames` mapping.
103
+
104
+ ---
105
+
106
+ ## 7. Dropdown Container Padding
107
+
108
+ **Decision:** The dropdown container uses `divider-item-gap` as its internal padding.
109
+
110
+ **Implementation:** This ensures consistent spacing between the dropdown border and its content items. The gap token (`4px` by default) creates a subtle inset that visually separates items from the container edge, matching the Figma design specifications.
111
+
112
+ ---
113
+
114
+ ## 8. Minimal CSS Override Philosophy
115
+
116
+ **Decision:** The CSS module only overrides visual design tokens (colors, typography, spacing, borders, shadows). All structural layout properties (flex, cursor, pointer-events, box-sizing, etc.) are deferred to Mantine's native behavior.
117
+
118
+ **Implementation:** Unlike form-field components (e.g., Dropdown/TextField) which require deep structural overrides to strip Mantine's macro wrappers, the Menu is an overlay component where Mantine's native layout is already correct. We avoid setting:
119
+
120
+ - `overflow` on the dropdown (Mantine renders sub-menu dropdowns inside the parent DOM tree using Floating UI absolute positioning — setting overflow clips them)
121
+ - `display`, `align-items`, `width`, `cursor` on items (Mantine's button-based items already handle this)
122
+ - `pointer-events` on disabled items (Mantine handles disabled natively)
123
+
124
+ **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.
@@ -2,6 +2,12 @@ import React from "react";
2
2
  import {
3
3
  Modal as MantineModal,
4
4
  type ModalProps as MantineModalProps,
5
+ type ModalRootProps as MantineModalRootProps,
6
+ type ModalOverlayProps as MantineModalOverlayProps,
7
+ type ModalContentProps as MantineModalContentProps,
8
+ type ModalHeaderProps as MantineModalHeaderProps,
9
+ type ModalTitleProps as MantineModalTitleProps,
10
+ type ModalCloseButtonProps as MantineModalCloseButtonProps,
5
11
  } from "@mantine/core";
6
12
  import {
7
13
  filterStylingProps,
@@ -114,18 +120,22 @@ ModalInner.displayName = "Modal";
114
120
  * > primary action button MUST be the right-most element, with the secondary
115
121
  * > (outline variant) action placed immediately to the left of it.
116
122
  */
117
- const ModalFooter = React.forwardRef<
118
- HTMLDivElement,
123
+ export type ModalFooterProps = RecursicaOverStyled<
119
124
  React.ComponentPropsWithoutRef<"div">
120
- >(function ModalFooter({ className, ...rest }, ref) {
121
- return (
122
- <div
123
- ref={ref}
124
- className={`${styles.footer} ${className || ""}`}
125
- {...rest}
126
- />
127
- );
128
- });
125
+ >;
126
+
127
+ const ModalFooter = React.forwardRef<HTMLDivElement, ModalFooterProps>(
128
+ function ModalFooter({ overStyled = false, className, ...rest }, ref) {
129
+ const sanitizedProps = filterStylingProps(rest, overStyled);
130
+ return (
131
+ <div
132
+ ref={ref}
133
+ className={`${styles.footer} ${className || ""}`}
134
+ {...sanitizedProps}
135
+ />
136
+ );
137
+ },
138
+ );
129
139
  ModalFooter.displayName = "Modal.Footer";
130
140
 
131
141
  const ModalBody = React.forwardRef<
@@ -197,16 +207,112 @@ const ModalBody = React.forwardRef<
197
207
  });
198
208
  ModalBody.displayName = "Modal.Body";
199
209
 
210
+ // ============================================================
211
+ // MODAL SUB-COMPONENTS (wrapped so overStyled/filterStylingProps applies)
212
+ // ============================================================
213
+
214
+ export type ModalRootProps = RecursicaOverStyled<MantineModalRootProps>;
215
+
216
+ const ModalRoot = React.forwardRef<HTMLDivElement, ModalRootProps>(
217
+ function ModalRoot({ overStyled = false, ...rest }, ref) {
218
+ const sanitizedProps = filterStylingProps(rest, overStyled);
219
+ return (
220
+ <MantineModal.Root
221
+ ref={ref}
222
+ {...(sanitizedProps as unknown as MantineModalRootProps)}
223
+ />
224
+ );
225
+ },
226
+ );
227
+ ModalRoot.displayName = "Modal.Root";
228
+
229
+ export type ModalOverlayProps = RecursicaOverStyled<MantineModalOverlayProps>;
230
+
231
+ const ModalOverlay = React.forwardRef<HTMLDivElement, ModalOverlayProps>(
232
+ function ModalOverlay({ overStyled = false, ...rest }, ref) {
233
+ const sanitizedProps = filterStylingProps(rest, overStyled);
234
+ return (
235
+ <MantineModal.Overlay
236
+ ref={ref}
237
+ {...(sanitizedProps as unknown as MantineModalOverlayProps)}
238
+ />
239
+ );
240
+ },
241
+ );
242
+ ModalOverlay.displayName = "Modal.Overlay";
243
+
244
+ export type ModalContentProps = RecursicaOverStyled<MantineModalContentProps>;
245
+
246
+ const ModalContent = React.forwardRef<HTMLDivElement, ModalContentProps>(
247
+ function ModalContent({ overStyled = false, ...rest }, ref) {
248
+ const sanitizedProps = filterStylingProps(rest, overStyled);
249
+ return (
250
+ <MantineModal.Content
251
+ ref={ref}
252
+ {...(sanitizedProps as unknown as MantineModalContentProps)}
253
+ />
254
+ );
255
+ },
256
+ );
257
+ ModalContent.displayName = "Modal.Content";
258
+
259
+ export type ModalHeaderProps = RecursicaOverStyled<MantineModalHeaderProps>;
260
+
261
+ const ModalHeader = React.forwardRef<HTMLElement, ModalHeaderProps>(
262
+ function ModalHeader({ overStyled = false, ...rest }, ref) {
263
+ const sanitizedProps = filterStylingProps(rest, overStyled);
264
+ return (
265
+ <MantineModal.Header
266
+ ref={ref}
267
+ {...(sanitizedProps as unknown as MantineModalHeaderProps)}
268
+ />
269
+ );
270
+ },
271
+ );
272
+ ModalHeader.displayName = "Modal.Header";
273
+
274
+ export type ModalTitleProps = RecursicaOverStyled<MantineModalTitleProps>;
275
+
276
+ const ModalTitle = React.forwardRef<HTMLHeadingElement, ModalTitleProps>(
277
+ function ModalTitle({ overStyled = false, ...rest }, ref) {
278
+ const sanitizedProps = filterStylingProps(rest, overStyled);
279
+ return (
280
+ <MantineModal.Title
281
+ ref={ref}
282
+ {...(sanitizedProps as unknown as MantineModalTitleProps)}
283
+ />
284
+ );
285
+ },
286
+ );
287
+ ModalTitle.displayName = "Modal.Title";
288
+
289
+ export type ModalCloseButtonProps =
290
+ RecursicaOverStyled<MantineModalCloseButtonProps>;
291
+
292
+ const ModalCloseButton = React.forwardRef<
293
+ HTMLButtonElement,
294
+ ModalCloseButtonProps
295
+ >(function ModalCloseButton({ overStyled = false, ...rest }, ref) {
296
+ const sanitizedProps = filterStylingProps(rest, overStyled);
297
+ return (
298
+ <MantineModal.CloseButton
299
+ ref={ref}
300
+ {...(sanitizedProps as unknown as MantineModalCloseButtonProps)}
301
+ />
302
+ );
303
+ });
304
+ ModalCloseButton.displayName = "Modal.CloseButton";
305
+
200
306
  interface ModalComponent
201
307
  extends React.ForwardRefExoticComponent<
202
308
  ModalProps & React.RefAttributes<HTMLDivElement>
203
309
  > {
204
- Root: typeof MantineModal.Root;
205
- Overlay: typeof MantineModal.Overlay;
206
- Content: typeof MantineModal.Content;
207
- Header: typeof MantineModal.Header;
208
- Title: typeof MantineModal.Title;
209
- CloseButton: typeof MantineModal.CloseButton;
310
+ Root: typeof ModalRoot;
311
+ Overlay: typeof ModalOverlay;
312
+ Content: typeof ModalContent;
313
+ Header: typeof ModalHeader;
314
+ Title: typeof ModalTitle;
315
+ CloseButton: typeof ModalCloseButton;
210
316
  Body: typeof ModalBody;
211
317
  Footer: typeof ModalFooter;
212
318
  }
@@ -214,11 +320,11 @@ interface ModalComponent
214
320
  export const Modal = ModalInner as ModalComponent & {
215
321
  Footer: typeof ModalFooter;
216
322
  };
217
- Modal.Root = MantineModal.Root;
218
- Modal.Overlay = MantineModal.Overlay;
219
- Modal.Content = MantineModal.Content;
220
- Modal.Header = MantineModal.Header;
221
- Modal.Title = MantineModal.Title;
222
- Modal.CloseButton = MantineModal.CloseButton;
323
+ Modal.Root = ModalRoot;
324
+ Modal.Overlay = ModalOverlay;
325
+ Modal.Content = ModalContent;
326
+ Modal.Header = ModalHeader;
327
+ Modal.Title = ModalTitle;
328
+ Modal.CloseButton = ModalCloseButton;
223
329
  Modal.Body = ModalBody;
224
330
  Modal.Footer = ModalFooter;
@@ -0,0 +1,58 @@
1
+ # Modal - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Modal` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Modal } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Modal } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return (
23
+ <Modal opened={isOpen} onClose={close} title="Modal Title">
24
+ <Text>This is a styled Modal wrapping the native primitive.</Text>
25
+ </Modal>
26
+ );
27
+ }
28
+ ```
29
+
30
+ ---
31
+
32
+ ## 3. Design System Integration
33
+
34
+ All Recursica components in the `@recursica/mantine-adapter` package adhere strictly to design system spacing, scaling, and behavior patterns.
35
+
36
+ > [!IMPORTANT]
37
+ >
38
+ > - **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.
39
+ > - **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.
40
+ > - **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.
41
+
42
+ ---
43
+
44
+ ## 4. Key Integration Features & Constraints
45
+
46
+ ## Architecture
47
+
48
+ The `Modal` component strictly wraps Mantine's `<Modal>` primitive. We strip Mantine's abstract native styling props (`size`, `radius`, `shadow`) via the `overStyled` interface and strictly inject CSS variable definitions onto the internal node abstractions (`.content`, `.header`, `.body`, `.title`).
49
+
50
+ ## Limitations & Structural Decisions
51
+
52
+ ### 1. Stripped `size` Prop
53
+
54
+ Mantine natively exposes an abstract `size` prop (`"sm" | "md" | "lg" | "xl"`) that scales the Modal geometry. The Recursica UI Kit explicitly dictates strict geometric bounding boxes: `max-width: 960px` and `min-width: 304px`. To enforce absolute parity with the design system, the `size` prop has been intentionally omitted from the component's interface. The width of the Modal will scale fluidly strictly between these Figma-driven pixel limits.
55
+
56
+ ### 2. Scroll Dividers behavior
57
+
58
+ Mantine internally handles scroll state natively, dynamically showing/hiding a divider line when content overflows in `.body`. This logic is tightly coupled to React DOM measurements internally. Our component inherits this dynamic behavior rather than statically rendering a permanent divider, matching Mantine's robust overflow UX. However, we aggressively override the generated `border-bottom` via CSS modules to ensure that when it _does_ appear, it correctly utilizes the `--recursica_ui-kit_components_modal_colors_scroll-divider` variable and `--recursica_ui-kit_components_modal_properties_scroll-divider-thickness` token.
@@ -0,0 +1,55 @@
1
+ # NumberInput - Usage Guide
2
+
3
+ This document describes how to integrate and use the `NumberInput` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { NumberInput } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { NumberInput } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return <NumberInput label="Quantity" min={1} max={10} defaultValue={1} />;
23
+ }
24
+ ```
25
+
26
+ ---
27
+
28
+ ## 3. Design System Integration
29
+
30
+ All Recursica components in the `@recursica/mantine-adapter` 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
+ ## 1. Native Macro Wrapper Bypass
43
+
44
+ **Decision:** The `<NumberInput>` component explicitly bypasses Mantine's native `Input.Wrapper` DOM injections.
45
+ **Implementation:** We pass `label={undefined}`, `description={undefined}`, and `error={undefined}` directly into the primitive `<MantineNumberInput>`. All visual form control geometry is delegated exclusively to our unified `<FormControlWrapper>`, ensuring 100% token adherence for label spacing and assistive text styling without duplicate DOM rendering.
46
+
47
+ ## 2. Right Section & Controls Override
48
+
49
+ **Decision:** Passing a `rightSection` element will natively remove the increment/decrement arrow controls.
50
+ **Implementation:** Mantine inherently renders its stepper controls inside the `rightSection` DOM slot. Providing a custom right-aligned icon or text element intentionally overwrites this slot. If a layout strictly requires both a custom right-aligned element and the stepper controls simultaneously, the integrator must manually rebuild the arrows using Mantine's `handlersRef` within a custom right-section wrapper.
51
+
52
+ ## 3. Controls Styling
53
+
54
+ **Decision:** The increment/decrement control arrows rely partially on native Mantine CSS inheritance.
55
+ **Implementation:** The current Recursica design system tokens do not provide explicit UI styling parameters (`background`, `border`, `hover` states) for the inner number-input arrows. We have explicitly removed Mantine's default borders to cleanly nest them inside the unified input box, and mapped the icon colors to the generic `trailing-icon` token variable, but further visual configurations currently fall back to Mantine defaults.