@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
@@ -4,6 +4,8 @@ import {
4
4
  Pagination as MantinePagination,
5
5
  type PaginationProps as MantinePaginationProps,
6
6
  type PaginationRootProps as MantinePaginationRootProps,
7
+ type PaginationControlProps as MantinePaginationControlProps,
8
+ type PaginationDotsProps as MantinePaginationDotsProps,
7
9
  } from "@mantine/core";
8
10
  import {
9
11
  filterStylingProps,
@@ -28,11 +30,13 @@ export type PaginationRootProps =
28
30
  RecursicaOverStyled<MantinePaginationRootProps>;
29
31
 
30
32
  export type PaginationEdgeProps<T extends React.ElementType> =
31
- React.ComponentProps<T> & {
32
- /** If set to true, displays text labels alongside the icon. */
33
- withLabel?: boolean;
34
- icon?: any;
35
- };
33
+ RecursicaOverStyled<
34
+ React.ComponentProps<T> & {
35
+ /** If set to true, displays text labels alongside the icon. */
36
+ withLabel?: boolean;
37
+ icon?: any;
38
+ }
39
+ >;
36
40
 
37
41
  function usePaginationClassNames(restRecord: Record<string, unknown>): {
38
42
  className: string;
@@ -88,14 +92,18 @@ _PaginationRoot.displayName = "Pagination.Root";
88
92
  const _PaginationNext = forwardRef<
89
93
  HTMLButtonElement,
90
94
  PaginationEdgeProps<typeof MantinePagination.Next>
91
- >(function PaginationNext({ withLabel, icon, ...props }, ref) {
95
+ >(function PaginationNext(
96
+ { overStyled = false, withLabel, icon, ...rest },
97
+ ref,
98
+ ) {
99
+ const sanitizedProps = filterStylingProps(rest, overStyled);
92
100
  const renderIcon = icon || (withLabel ? NextWithLabel : undefined);
93
101
  return (
94
102
  <MantinePagination.Next
95
103
  ref={ref}
96
104
  data-variant="text"
97
105
  icon={renderIcon as any}
98
- {...props}
106
+ {...sanitizedProps}
99
107
  />
100
108
  );
101
109
  });
@@ -104,14 +112,18 @@ _PaginationNext.displayName = "Pagination.Next";
104
112
  const _PaginationPrevious = forwardRef<
105
113
  HTMLButtonElement,
106
114
  PaginationEdgeProps<typeof MantinePagination.Previous>
107
- >(function PaginationPrevious({ withLabel, icon, ...props }, ref) {
115
+ >(function PaginationPrevious(
116
+ { overStyled = false, withLabel, icon, ...rest },
117
+ ref,
118
+ ) {
119
+ const sanitizedProps = filterStylingProps(rest, overStyled);
108
120
  const renderIcon = icon || (withLabel ? PrevWithLabel : undefined);
109
121
  return (
110
122
  <MantinePagination.Previous
111
123
  ref={ref}
112
124
  data-variant="text"
113
125
  icon={renderIcon as any}
114
- {...props}
126
+ {...sanitizedProps}
115
127
  />
116
128
  );
117
129
  });
@@ -120,14 +132,18 @@ _PaginationPrevious.displayName = "Pagination.Previous";
120
132
  const _PaginationFirst = forwardRef<
121
133
  HTMLButtonElement,
122
134
  PaginationEdgeProps<typeof MantinePagination.First>
123
- >(function PaginationFirst({ withLabel, icon, ...props }, ref) {
135
+ >(function PaginationFirst(
136
+ { overStyled = false, withLabel, icon, ...rest },
137
+ ref,
138
+ ) {
139
+ const sanitizedProps = filterStylingProps(rest, overStyled);
124
140
  const renderIcon = icon || (withLabel ? FirstWithLabel : undefined);
125
141
  return (
126
142
  <MantinePagination.First
127
143
  ref={ref}
128
144
  data-variant="text"
129
145
  icon={renderIcon as any}
130
- {...props}
146
+ {...sanitizedProps}
131
147
  />
132
148
  );
133
149
  });
@@ -136,14 +152,18 @@ _PaginationFirst.displayName = "Pagination.First";
136
152
  const _PaginationLast = forwardRef<
137
153
  HTMLButtonElement,
138
154
  PaginationEdgeProps<typeof MantinePagination.Last>
139
- >(function PaginationLast({ withLabel, icon, ...props }, ref) {
155
+ >(function PaginationLast(
156
+ { overStyled = false, withLabel, icon, ...rest },
157
+ ref,
158
+ ) {
159
+ const sanitizedProps = filterStylingProps(rest, overStyled);
140
160
  const renderIcon = icon || (withLabel ? LastWithLabel : undefined);
141
161
  return (
142
162
  <MantinePagination.Last
143
163
  ref={ref}
144
164
  data-variant="text"
145
165
  icon={renderIcon as any}
146
- {...props}
166
+ {...sanitizedProps}
147
167
  />
148
168
  );
149
169
  });
@@ -192,14 +212,47 @@ const _Pagination = forwardRef<HTMLDivElement, PaginationProps>(
192
212
  );
193
213
  _Pagination.displayName = "Pagination";
194
214
 
215
+ export type PaginationControlProps =
216
+ RecursicaOverStyled<MantinePaginationControlProps>;
217
+
218
+ const _PaginationControl = forwardRef<
219
+ HTMLButtonElement,
220
+ PaginationControlProps
221
+ >(function PaginationControl({ overStyled = false, ...rest }, ref) {
222
+ const sanitizedProps = filterStylingProps(rest, overStyled);
223
+ return (
224
+ <MantinePagination.Control
225
+ ref={ref}
226
+ {...(sanitizedProps as unknown as MantinePaginationControlProps)}
227
+ />
228
+ );
229
+ });
230
+ _PaginationControl.displayName = "Pagination.Control";
231
+
232
+ export type PaginationDotsProps =
233
+ RecursicaOverStyled<MantinePaginationDotsProps>;
234
+
235
+ const _PaginationDots = forwardRef<HTMLDivElement, PaginationDotsProps>(
236
+ function PaginationDots({ overStyled = false, ...rest }, ref) {
237
+ const sanitizedProps = filterStylingProps(rest, overStyled);
238
+ return (
239
+ <MantinePagination.Dots
240
+ ref={ref}
241
+ {...(sanitizedProps as unknown as MantinePaginationDotsProps)}
242
+ />
243
+ );
244
+ },
245
+ );
246
+ _PaginationDots.displayName = "Pagination.Dots";
247
+
195
248
  /**
196
249
  * Recursica Pagination component wrapping Mantine's Pagination.
197
250
  */
198
251
  export const Pagination = _Pagination as typeof _Pagination & {
199
252
  Root: typeof _PaginationRoot;
200
253
  Items: typeof MantinePagination.Items;
201
- Control: typeof MantinePagination.Control;
202
- Dots: typeof MantinePagination.Dots;
254
+ Control: typeof _PaginationControl;
255
+ Dots: typeof _PaginationDots;
203
256
  Next: typeof _PaginationNext;
204
257
  Previous: typeof _PaginationPrevious;
205
258
  First: typeof _PaginationFirst;
@@ -208,9 +261,12 @@ export const Pagination = _Pagination as typeof _Pagination & {
208
261
  };
209
262
 
210
263
  Pagination.Root = _PaginationRoot;
264
+ // Pagination.Items has no styling props of its own (just a `dotsIcon` prop, no
265
+ // `style`/`className`/BoxProps), so re-exporting Mantine's implementation
266
+ // directly is not a styling-gate gap.
211
267
  Pagination.Items = MantinePagination.Items;
212
- Pagination.Control = MantinePagination.Control;
213
- Pagination.Dots = MantinePagination.Dots;
268
+ Pagination.Control = _PaginationControl;
269
+ Pagination.Dots = _PaginationDots;
214
270
  Pagination.Next = _PaginationNext;
215
271
  Pagination.Previous = _PaginationPrevious;
216
272
  Pagination.First = _PaginationFirst;
@@ -0,0 +1,55 @@
1
+ # Pagination - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Pagination` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Pagination } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Pagination } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return <Pagination total={10} value={activePage} onChange={setPage} />;
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 Decision: CSS Inheritance vs. Composition
43
+
44
+ The Figma design tokens for the `Pagination` component dictate that pagination pages perfectly mimic `Button` variants (e.g. `active-pages_style: "solid"`, `inactive-pages_style: "outline"`, `navigation-controls_style: "text"`).
45
+
46
+ As of the `1.2.0` scoped CSS update, the Figma exporter automatically handles these variants. It flattens and aliases the referenced Button properties directly into the `Pagination` component's variable namespace.
47
+
48
+ We opted to use Mantine's native `PaginationControl` components to ensure all DOM structure, focus management, and accessibility attributes are preserved natively without us needing to carefully rebuild `Pagination.Items` mappings.
49
+
50
+ To honor the Figma design intents while using Mantine's raw `<button>` elements:
51
+
52
+ 1. We inherit and map all natively scoped `Pagination` variant styles (small typography, outline/solid/text colors, and hover overlays) directly into `.control` and `.control[data-active]` within `Pagination.module.css`. We do not need to manually reference `Button` variables cross-component.
53
+ 2. We inject `data-variant="text"` via `getControlProps` onto the navigation buttons so that they inherit the explicitly aliased text style variables (`navigation-controls`) defined by the UI Kit for pagination.
54
+
55
+ This achieves exact optical alignment with the tokens while maintaining Mantine's robust internal event handling for pagination.
@@ -2,6 +2,13 @@ import { forwardRef } from "react";
2
2
  import {
3
3
  Drawer as MantineDrawer,
4
4
  type DrawerProps as MantineDrawerProps,
5
+ type DrawerRootProps as MantineDrawerRootProps,
6
+ type DrawerOverlayProps as MantineDrawerOverlayProps,
7
+ type DrawerContentProps as MantineDrawerContentProps,
8
+ type DrawerHeaderProps as MantineDrawerHeaderProps,
9
+ type DrawerTitleProps as MantineDrawerTitleProps,
10
+ type DrawerCloseButtonProps as MantineDrawerCloseButtonProps,
11
+ type DrawerBodyProps as MantineDrawerBodyProps,
5
12
  } from "@mantine/core";
6
13
  import {
7
14
  filterStylingProps,
@@ -128,29 +135,142 @@ export const PanelFooter = forwardRef<HTMLDivElement, PanelFooterProps>(
128
135
  );
129
136
  PanelFooter.displayName = "PanelFooter";
130
137
 
138
+ // ============================================================
139
+ // PANEL SUB-COMPONENTS (wrapped so overStyled/filterStylingProps applies)
140
+ // ============================================================
141
+
142
+ export type PanelRootProps = RecursicaOverStyled<MantineDrawerRootProps>;
143
+
144
+ export const PanelRoot = forwardRef<HTMLDivElement, PanelRootProps>(
145
+ function PanelRoot({ overStyled = false, ...rest }, ref) {
146
+ const sanitizedProps = filterStylingProps(rest, overStyled);
147
+ return (
148
+ <MantineDrawer.Root
149
+ ref={ref}
150
+ {...(sanitizedProps as unknown as MantineDrawerRootProps)}
151
+ />
152
+ );
153
+ },
154
+ );
155
+ PanelRoot.displayName = "PanelRoot";
156
+
157
+ export type PanelOverlayProps = RecursicaOverStyled<MantineDrawerOverlayProps>;
158
+
159
+ export const PanelOverlay = forwardRef<HTMLDivElement, PanelOverlayProps>(
160
+ function PanelOverlay({ overStyled = false, ...rest }, ref) {
161
+ const sanitizedProps = filterStylingProps(rest, overStyled);
162
+ return (
163
+ <MantineDrawer.Overlay
164
+ ref={ref}
165
+ {...(sanitizedProps as unknown as MantineDrawerOverlayProps)}
166
+ />
167
+ );
168
+ },
169
+ );
170
+ PanelOverlay.displayName = "PanelOverlay";
171
+
172
+ export type PanelContentProps = RecursicaOverStyled<MantineDrawerContentProps>;
173
+
174
+ export const PanelContent = forwardRef<HTMLDivElement, PanelContentProps>(
175
+ function PanelContent({ overStyled = false, ...rest }, ref) {
176
+ const sanitizedProps = filterStylingProps(rest, overStyled);
177
+ return (
178
+ <MantineDrawer.Content
179
+ ref={ref}
180
+ {...(sanitizedProps as unknown as MantineDrawerContentProps)}
181
+ />
182
+ );
183
+ },
184
+ );
185
+ PanelContent.displayName = "PanelContent";
186
+
187
+ export type PanelHeaderProps = RecursicaOverStyled<MantineDrawerHeaderProps>;
188
+
189
+ export const PanelHeader = forwardRef<HTMLElement, PanelHeaderProps>(
190
+ function PanelHeader({ overStyled = false, ...rest }, ref) {
191
+ const sanitizedProps = filterStylingProps(rest, overStyled);
192
+ return (
193
+ <MantineDrawer.Header
194
+ ref={ref}
195
+ {...(sanitizedProps as unknown as MantineDrawerHeaderProps)}
196
+ />
197
+ );
198
+ },
199
+ );
200
+ PanelHeader.displayName = "PanelHeader";
201
+
202
+ export type PanelTitleProps = RecursicaOverStyled<MantineDrawerTitleProps>;
203
+
204
+ export const PanelTitle = forwardRef<HTMLHeadingElement, PanelTitleProps>(
205
+ function PanelTitle({ overStyled = false, ...rest }, ref) {
206
+ const sanitizedProps = filterStylingProps(rest, overStyled);
207
+ return (
208
+ <MantineDrawer.Title
209
+ ref={ref}
210
+ {...(sanitizedProps as unknown as MantineDrawerTitleProps)}
211
+ />
212
+ );
213
+ },
214
+ );
215
+ PanelTitle.displayName = "PanelTitle";
216
+
217
+ export type PanelCloseButtonProps =
218
+ RecursicaOverStyled<MantineDrawerCloseButtonProps>;
219
+
220
+ export const PanelCloseButton = forwardRef<
221
+ HTMLButtonElement,
222
+ PanelCloseButtonProps
223
+ >(function PanelCloseButton({ overStyled = false, ...rest }, ref) {
224
+ const sanitizedProps = filterStylingProps(rest, overStyled);
225
+ return (
226
+ <MantineDrawer.CloseButton
227
+ ref={ref}
228
+ {...(sanitizedProps as unknown as MantineDrawerCloseButtonProps)}
229
+ />
230
+ );
231
+ });
232
+ PanelCloseButton.displayName = "PanelCloseButton";
233
+
234
+ export type PanelBodyProps = RecursicaOverStyled<MantineDrawerBodyProps>;
235
+
236
+ export const PanelBody = forwardRef<HTMLDivElement, PanelBodyProps>(
237
+ function PanelBody({ overStyled = false, ...rest }, ref) {
238
+ const sanitizedProps = filterStylingProps(rest, overStyled);
239
+ return (
240
+ <MantineDrawer.Body
241
+ ref={ref}
242
+ {...(sanitizedProps as unknown as MantineDrawerBodyProps)}
243
+ />
244
+ );
245
+ },
246
+ );
247
+ PanelBody.displayName = "PanelBody";
248
+
131
249
  // ============================================================
132
250
  // DOT NOTATION EXPORT
133
251
  // ============================================================
134
252
 
135
253
  type PanelComponent = typeof PanelBase & {
136
- Root: typeof MantineDrawer.Root;
137
- Overlay: typeof MantineDrawer.Overlay;
138
- Content: typeof MantineDrawer.Content;
139
- Header: typeof MantineDrawer.Header;
140
- Title: typeof MantineDrawer.Title;
141
- CloseButton: typeof MantineDrawer.CloseButton;
142
- Body: typeof MantineDrawer.Body;
254
+ Root: typeof PanelRoot;
255
+ Overlay: typeof PanelOverlay;
256
+ Content: typeof PanelContent;
257
+ Header: typeof PanelHeader;
258
+ Title: typeof PanelTitle;
259
+ CloseButton: typeof PanelCloseButton;
260
+ Body: typeof PanelBody;
143
261
  Stack: typeof MantineDrawer.Stack;
144
262
  Footer: typeof PanelFooter;
145
263
  };
146
264
 
147
265
  export const Panel = PanelBase as PanelComponent;
148
- Panel.Root = MantineDrawer.Root;
149
- Panel.Overlay = MantineDrawer.Overlay;
150
- Panel.Content = MantineDrawer.Content;
151
- Panel.Header = MantineDrawer.Header;
152
- Panel.Title = MantineDrawer.Title;
153
- Panel.CloseButton = MantineDrawer.CloseButton;
154
- Panel.Body = MantineDrawer.Body;
266
+ Panel.Root = PanelRoot;
267
+ Panel.Overlay = PanelOverlay;
268
+ Panel.Content = PanelContent;
269
+ Panel.Header = PanelHeader;
270
+ Panel.Title = PanelTitle;
271
+ Panel.CloseButton = PanelCloseButton;
272
+ Panel.Body = PanelBody;
273
+ // Panel.Stack (DrawerStack) only accepts `children` — no styling props to
274
+ // strip, so re-exporting Mantine's implementation directly is not a gap.
155
275
  Panel.Stack = MantineDrawer.Stack;
156
276
  Panel.Footer = PanelFooter;
@@ -0,0 +1,145 @@
1
+ # Panel - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Panel` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Panel } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Panel } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return (
23
+ <Panel>
24
+ <Text>This is a clean, structured visual container.</Text>
25
+ </Panel>
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
+ ## 1. Mapping to Mantine Drawer
47
+
48
+ **Decision:** Panel maps to Mantine's `Drawer` component, not `Paper` or `Card`.
49
+
50
+ **Implementation:** Per the Recursica design system specification, "Panels slide in or expand from the edge of the screen to reveal additional content or functionality." This is the exact behavior of Mantine's `Drawer` component, which provides:
51
+
52
+ - Slide-in animation from any screen edge
53
+ - Overlay/backdrop
54
+ - Portal rendering
55
+ - Focus trapping
56
+ - Scroll locking
57
+ - Close button and title in header
58
+
59
+ Paper and Card are static containers; Drawer is an overlay that matches Panel's defined behavior.
60
+
61
+ ---
62
+
63
+ ## 2. Token Namespace: `panel`
64
+
65
+ **Decision:** The CSS module exclusively uses variables from the `--recursica_ui-kit_components_panel_*` namespace.
66
+
67
+ **Implementation:** The Recursica token system defines the `panel` namespace covering:
68
+
69
+ - Geometry: border-radius, border-size, min-width (200px), max-width (960px)
70
+ - Content padding: content-horizontal-padding (xl), content-vertical-padding (lg)
71
+ - Header/Footer padding: header-footer-horizontal-padding (xl), header-footer-vertical-padding (md)
72
+ - Spacing: header-close-gap (md), footer-button-gap (md)
73
+ - Divider: divider-size (1px), divider-color
74
+ - Elevation: elevation-3
75
+ - Colors (layer-aware): background, border-color, content, divider-color, header-footer-background, title
76
+ - Non-CSS: header-style ("h3") — see §7
77
+
78
+ No tokens from other component namespaces are referenced.
79
+
80
+ ---
81
+
82
+ ## 3. ClassNames Mapping to Drawer
83
+
84
+ **Decision:** Panel maps CSS module classes to Mantine Drawer's stylesNames.
85
+
86
+ **Implementation:** The Drawer stylesNames used are:
87
+
88
+ - `content` — Outer container (background, border, elevation)
89
+ - `header` — Title bar with close button (padding, divider, gap)
90
+ - `title` — Title text color
91
+ - `body` — Scrollable content area (padding)
92
+
93
+ Other stylesNames (`overlay`, `root`, `inner`, `close`) are left to Mantine defaults.
94
+
95
+ ---
96
+
97
+ ## 4. Default Placement Override
98
+
99
+ **Decision:** Use `placement` instead of `position` for configuring slide-out direction, and default it to `"right"`.
100
+
101
+ **Implementation:** The prop was renamed from `position` to `placement` to prevent collision with the CSS `position` keyword, which is strictly blocked by the styling gatekeeper (`BLOCKED_STYLING_KEYS`). This allows configuring the drawer direction natively while maintaining strict design-system boundaries. The `placement="right"` default is mapped internally to Mantine Drawer's `position` prop before any other sanitized props are applied. Right-side panels are the most common pattern for supplementary content, settings, and detail views.
102
+
103
+ ---
104
+
105
+ ## 5. Custom Panel.Footer
106
+
107
+ **Decision:** A custom `Panel.Footer` sub-component is provided. Mantine's Drawer does not have a native footer.
108
+
109
+ **Implementation:** `Panel.Footer` is a `<div>` with inline styles referencing Recursica CSS variables for:
110
+
111
+ - `header-footer-background` and `header-footer-padding` tokens
112
+ - Top divider using `divider-size` and `divider-color`
113
+ - `footer-button-gap` for action button spacing
114
+ - `margin-top: auto` to push the footer to the bottom
115
+
116
+ Inline styles are used instead of a CSS module class because the footer is rendered inside the Drawer's `<body>` element, and the CSS variables are applied to the body's parent container. The inline styles ensure the footer correctly references the panel tokens regardless of DOM position.
117
+
118
+ ---
119
+
120
+ ## 6. Hardcoded Values
121
+
122
+ ### `border-style: solid` (CSS module, `.content`)
123
+
124
+ Mantine's Drawer content does not set `border-style` natively. Without this, the border-width and border-color tokens have no visible effect. Same pattern as Card, Menu, HoverCard, Tooltip.
125
+
126
+ ---
127
+
128
+ ## 7. `header-style` Typography Utility Class
129
+
130
+ **Decision:** The `header-style` token exports as the string `"h3"`, so we use the generated global utility class `.recursica_brand_typography_h3`.
131
+
132
+ **Implementation:** The PostCSS compiler generates `.recursica_brand_typography_<typeName>` classes at the bottom of the scoped variables file to apply full typography definitions without assigning variables inline. We apply this to the `.title` class via `composes: recursica_brand_typography_h3 from global;`.
133
+
134
+ ---
135
+
136
+ ## 8. Panel Types: Standard vs Scrollable
137
+
138
+ **Decision:** Both standard and scrollable types are supported natively.
139
+
140
+ **Implementation:** Per the Recursica specification:
141
+
142
+ - **Standard** — All content visible without scrolling. The default behavior when content fits.
143
+ - **Scrollable** — Internal scrollbar enabled when content exceeds the panel height. Header and footer CTAs remain pinned.
144
+
145
+ Mantine's Drawer handles this automatically — the `body` section scrolls when content overflows, while the `header` remains fixed. The `Panel.Footer` uses `margin-top: auto` to stay at the bottom.
@@ -0,0 +1,121 @@
1
+ # Popover - Usage Guide
2
+
3
+ This document describes how to integrate and use the `Popover` component in your projects using `@recursica/mantine-adapter`.
4
+
5
+ ---
6
+
7
+ ## 1. Import Reference
8
+
9
+ ```tsx
10
+ import { Popover } from "@recursica/mantine-adapter";
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 2. Basic Example
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { Popover } from "@recursica/mantine-adapter";
20
+
21
+ export default function Demo() {
22
+ return (
23
+ <Popover position="bottom" withArrow>
24
+ <Popover.Target>
25
+ <Button>Open Popover</Button>
26
+ </Popover.Target>
27
+ <Popover.Dropdown>
28
+ <Text size="sm">This is the popover content.</Text>
29
+ </Popover.Dropdown>
30
+ </Popover>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ---
36
+
37
+ ## 3. Design System Integration
38
+
39
+ All Recursica components in the `@recursica/mantine-adapter` package adhere strictly to design system spacing, scaling, and behavior patterns.
40
+
41
+ > [!IMPORTANT]
42
+ >
43
+ > - **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.
44
+ > - **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.
45
+ > - **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.
46
+
47
+ ---
48
+
49
+ ## 4. Key Integration Features & Constraints
50
+
51
+ ## 1. Composable API Preservation (1:1 Mapping)
52
+
53
+ **Decision:** We maintain the exact library composition API structure (`<Popover>`, `<Popover.Target>`, `<Popover.Dropdown>`) as a 1:1 React component mapping.
54
+
55
+ **Implementation:** Mantine's Popover internally manages click detection, open/close state, Floating UI positioning, and portal rendering. By preserving the exact sub-component tree, Recursica safely inherits all of these behaviors without reimplementation.
56
+
57
+ ---
58
+
59
+ ## 2. Popover.Target Pass-Through
60
+
61
+ **Decision:** `Popover.Target` is a transparent pass-through with no styling applied.
62
+
63
+ **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>`). This is identical to the `Menu.Target` and `HoverCard.Target` pattern.
64
+
65
+ ---
66
+
67
+ ## 3. Token Namespace: `hover-card-popover`
68
+
69
+ **Decision:** The CSS module exclusively uses variables from the `--recursica_ui-kit_components_hover-card-popover_*` namespace.
70
+
71
+ **Implementation:** The Recursica token system defines a single shared namespace (`hover-card-popover`) for this component and HoverCard. It covers geometry (border-radius, border-size, padding, min/max-width), typography (content-text\_\*), elevation, and layer-aware colors (background, border-color, content).
72
+
73
+ ---
74
+
75
+ ## 4. Hardcoded Values
76
+
77
+ **Decision:** Two hardcoded values exist in the component.
78
+
79
+ ### `border-style: solid` (CSS module)
80
+
81
+ Mantine renders the dropdown using its `Paper` component, which does not set `border-style` natively. Without this hardcoded value, the border-width and border-color tokens would have no visible effect. This is the same pattern used in the Menu component's dropdown.
82
+
83
+ ### `arrowSize` defaulted to `16` (Popover.tsx)
84
+
85
+ Mantine's `arrowSize` prop is a JavaScript number used for inline style calculations: it sets `width`, `height`, and a positioning offset (`-arrowSize/2`) directly on the arrow `<div>` element. These inline styles **cannot** be overridden via CSS without `!important`, and the positioning offset has no CSS equivalent. This means the beak size cannot be fully CSS-driven — it is one of the rare cases where a design token value must be mirrored as a JS prop.
86
+
87
+ The default value `16` matches the Recursica `beak-size` token (`--recursica_ui-kit_components_hover-card-popover_properties_beak-size: 16px`). Developers can override `arrowSize` if needed, but should be aware this is a design system concern. If the token value changes, the default in `Popover.tsx` must also be updated.
88
+
89
+ This is documented as an open issue in `docs/COMPONENT_ISSUES.md`.
90
+
91
+ **Note:** Mantine calls this the "arrow"; Recursica calls it the "beak". The Recursica prop `withBeak` (defaulting to `true`) maps to Mantine's `withArrow`. Both `withBeak` and `withArrow` are accepted; `withBeak` takes precedence.
92
+
93
+ ---
94
+
95
+ ## 5. Minimal CSS Override Philosophy
96
+
97
+ **Decision:** The CSS module only overrides visual design tokens (colors, typography, spacing, borders, shadows). All structural layout properties are deferred to Mantine's native behavior.
98
+
99
+ **Implementation:** Popover is an overlay component where Mantine's native Floating UI positioning and Paper layout are already correct. We avoid setting:
100
+
101
+ - `overflow` on the dropdown (Mantine handles scroll behavior natively)
102
+ - `display`, `position`, `z-index` (Floating UI controls these)
103
+ - `pointer-events` (Mantine manages hover detection across target and dropdown)
104
+
105
+ **Rationale:** Default to Mantine's native behavior and only override what the Recursica token system explicitly needs to control.
106
+
107
+ ---
108
+
109
+ ## 6. ClassNames Merging on Root
110
+
111
+ **Decision:** The root `Popover` component binds CSS module classes via `classNames` on the Mantine root.
112
+
113
+ **Implementation:** The root component receives `classNames={{ dropdown: styles.dropdown, arrow: styles.arrow }}` and merges any consumer-provided `classNames` when `overStyled` is true. This ensures our token-driven styles are applied to the dropdown panel without wrapper divs, and the consumer's classes are additive.
114
+
115
+ ---
116
+
117
+ ## 7. Default Position Override
118
+
119
+ **Decision:** Recursica defaults `position` to `"top"`. Mantine defaults to `"bottom"`.
120
+
121
+ **Implementation:** The `position="top"` default is set on the Mantine root element before the prop spread, so developer-provided `position` values still take precedence. This aligns with Recursica's design intent for overlay components to appear above their trigger by default.