@recursica/adapter-mantine-v8 1.2.2 → 1.3.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 (43) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/OVERSTYLING.md +1 -1
  3. package/SETUP.md +34 -3
  4. package/dist/adapter-mantine-v8.cjs +2 -2
  5. package/dist/adapter-mantine-v8.cjs.map +1 -1
  6. package/dist/adapter-mantine-v8.css +1 -1
  7. package/dist/adapter-mantine-v8.js +3157 -3002
  8. package/dist/adapter-mantine-v8.js.map +1 -1
  9. package/dist/index.d.ts +191 -164
  10. package/llms.txt +1 -1
  11. package/package.json +3 -3
  12. package/recursica_brand.json +2 -2
  13. package/recursica_tokens.json +2 -2
  14. package/recursica_ui-kit.json +2 -2
  15. package/recursica_variables_scoped.css +7 -3
  16. package/src/components/Container/USAGE.md +0 -1
  17. package/src/components/Flex/USAGE.md +2 -1
  18. package/src/components/Group/USAGE.md +2 -1
  19. package/src/components/LayoutGrid/LAYOUT_GRID_IMPLEMENTATION_NOTES.md +88 -0
  20. package/src/components/LayoutGrid/LayoutGrid.columns.dom.test.tsx +66 -0
  21. package/src/components/LayoutGrid/LayoutGrid.module.css +48 -0
  22. package/src/components/LayoutGrid/LayoutGrid.stories.tsx +61 -0
  23. package/src/components/LayoutGrid/LayoutGrid.test.tsx +1 -0
  24. package/src/components/LayoutGrid/LayoutGrid.tsx +309 -0
  25. package/src/components/LayoutGrid/USAGE.md +76 -0
  26. package/src/components/Pagination/IMPLEMENTATION_NOTES.md +29 -8
  27. package/src/components/Pagination/Pagination.icons.tsx +0 -32
  28. package/src/components/Pagination/Pagination.manifest.dom.test.tsx +108 -0
  29. package/src/components/Pagination/Pagination.module.css +25 -155
  30. package/src/components/Pagination/Pagination.tsx +328 -197
  31. package/src/components/Pagination/USAGE.md +1 -0
  32. package/src/components/Stack/USAGE.md +2 -1
  33. package/src/components/index.ts +1 -1
  34. package/src/index.ts +1 -0
  35. package/src/utils/breakpointsFromRecManifest.test.ts +40 -0
  36. package/src/utils/breakpointsFromRecManifest.ts +75 -0
  37. package/src/utils/index.ts +1 -0
  38. package/src/components/Grid/GRID_IMPLEMENTATION_NOTES.md +0 -85
  39. package/src/components/Grid/Grid.module.css +0 -41
  40. package/src/components/Grid/Grid.stories.tsx +0 -200
  41. package/src/components/Grid/Grid.test.tsx +0 -1
  42. package/src/components/Grid/Grid.tsx +0 -179
  43. package/src/components/Grid/USAGE.md +0 -61
@@ -0,0 +1,75 @@
1
+ interface ManifestNumber {
2
+ $value?: unknown;
3
+ }
4
+
5
+ interface ManifestLayoutGrid {
6
+ "min-width"?: ManifestNumber;
7
+ "max-width"?: ManifestNumber;
8
+ }
9
+
10
+ interface ManifestWithLayoutGrids {
11
+ brand?: { "layout-grids"?: Record<string, unknown> };
12
+ }
13
+
14
+ function width(grid: ManifestLayoutGrid, key: "min-width" | "max-width") {
15
+ const value = grid[key]?.$value;
16
+ if (value === undefined) return undefined;
17
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
18
+ throw new Error(
19
+ `layout-grid ${key} must be a non-negative number, got ${String(value)}`,
20
+ );
21
+ }
22
+ return value;
23
+ }
24
+
25
+ /**
26
+ * Builds a `{ name: "<start>px" }` breakpoints object from the manifest's layout grids, ready to
27
+ * pass to Mantine's `createTheme({ breakpoints })`. Opt-in: nothing in the
28
+ * adapters calls this for you, and the result is a plain object you can edit.
29
+ *
30
+ * Mantine breakpoints are mobile-first start widths, so each non-default grid maps to its
31
+ * `min-width`; a grid with only a `max-width` (e.g. `mobile`, up to 480px) starts at `0px`. The
32
+ * `default` grid has no width of its own: when other grids exist it is included, named `default`,
33
+ * starting one pixel past the widest `max-width`. A manifest with only the `default` grid (the
34
+ * stock Forge export) returns `{}`, so the theme is left untouched.
35
+ *
36
+ * Throws for a non-default grid with neither width, for non-numeric widths, and when no other grid
37
+ * has a `max-width` for `default` to start after.
38
+ */
39
+ export function breakpointsFromRecManifest(
40
+ manifest: ManifestWithLayoutGrids,
41
+ ): Record<string, string> {
42
+ const grids = (manifest.brand?.["layout-grids"] ?? {}) as Record<
43
+ string,
44
+ ManifestLayoutGrid
45
+ >;
46
+ const entries: [string, number][] = [];
47
+ let widestMax: number | undefined;
48
+ for (const [name, grid] of Object.entries(grids)) {
49
+ if (name === "default") continue;
50
+ const min = width(grid, "min-width");
51
+ const max = width(grid, "max-width");
52
+ if (min === undefined && max === undefined) {
53
+ throw new Error(
54
+ `layout-grid "${name}" has neither min-width nor max-width`,
55
+ );
56
+ }
57
+ if (max !== undefined && (widestMax === undefined || max > widestMax)) {
58
+ widestMax = max;
59
+ }
60
+ entries.push([name, min ?? 0]);
61
+ }
62
+ if (entries.length > 0) {
63
+ if (widestMax === undefined) {
64
+ throw new Error(
65
+ 'Cannot place the "default" layout-grid: no other grid has a max-width to start after',
66
+ );
67
+ }
68
+ entries.push(["default", widestMax + 1]);
69
+ }
70
+ return Object.fromEntries(
71
+ entries
72
+ .sort((a, b) => a[1] - b[1])
73
+ .map(([name, start]) => [name, `${start}px`]),
74
+ );
75
+ }
@@ -1 +1,2 @@
1
1
  export * from "./copyToClipboard";
2
+ export * from "./breakpointsFromRecManifest";
@@ -1,85 +0,0 @@
1
- # Grid Implementation Notes
2
-
3
- The `Grid` component wraps Mantine's `Grid`/`Grid.Col`, but — unlike the other primitive layout
4
- components (`Flex`, `Stack`, `Group`, `Container`) — it now has a formal Recursica prop contract
5
- (`RecursicaGridProps` in `adapter-common`) wiring the design system's `layout-grids` tokens.
6
- Earlier versions of this file documented the opposite policy ("no intrinsic design-system styles
7
- required"); that was revisited on 2026-09-21 once Forge's export added `layout-grids` tokens
8
- (`default_columns`, `default_column-gutter`, `default_row-gutter`, `default_margin` — see
9
- `docs/migration/2026-09-21-forge-token-export.md`) with no equivalent anywhere else to consume
10
- them.
11
-
12
- ## Only `columns` is an integrator-facing prop
13
-
14
- Column-gutter, row-gutter, and margin are design-system-managed values (Forge-controlled, applied
15
- via CSS variables), not per-instance settings — the integrator does not configure them. Only
16
- `columns` is exposed as an override, the same pattern `Container.size` uses for its own
17
- token-backed default. An earlier draft of this component exposed `columnGutter`/`rowGutter`/
18
- `margin` as props too; that was reverted after review — see git history on this file for that
19
- approach if it's ever needed again.
20
-
21
- ## How each token actually gets applied
22
-
23
- - **`columns`** has no CSS-variable equivalent in Mantine — it computes each column's flex-basis
24
- from a plain number. The design system default (6) is baked into `Grid.tsx` as a JS default
25
- (`columns ?? 6`), not wired live through CSS. If the token's value changes, this line needs a
26
- manual update. Callers may still override it (see `CustomColumnCount` story).
27
- - **`columnGutter`** (the design token, not a prop) is passed straight to Mantine's own `gutter`
28
- prop as `var(--recursica_brand_layout-grids_default_column-gutter)` — always, not
29
- caller-configurable. This is the only axis Mantine's Grid natively parameterizes, so it drives
30
- Mantine's own internal width/margin-inline math unmodified.
31
- - **`rowGutter`/`margin`** (the design tokens) have no Mantine equivalent at all. `Grid.module.css`
32
- reads them directly, always:
33
- - `.root.root` applies `margin: var(--recursica_brand_layout-grids_default_margin)` (Mantine has
34
- no root-level margin concept to conflict with).
35
- - `.inner.inner` overrides only `margin-block` (not the `margin` shorthand) with
36
- `var(--recursica_brand_layout-grids_default_row-gutter)` so it doesn't disturb Mantine's own
37
- horizontal `margin-inline` math, which stays driven by `columnGutter`.
38
- - `.col.col` overrides only `padding-block` for the same reason (`padding-inline` stays Mantine's
39
- own, driven by `gutter`).
40
- - All three use the doubled-class-selector trick (`.root.root`, not `.root`) to reliably beat
41
- Mantine's own compiled single-class specificity regardless of stylesheet load order — same
42
- idiom used elsewhere in this codebase (e.g. Chip).
43
-
44
- ## `Grid.Col`'s formal contract is scaffolded, not filled in yet
45
-
46
- `RecursicaGridColProps` exists in `adapter-common`, and `Grid.tsx` here intersects `GridColProps`
47
- with it — but as of 2026-09-22 it only contributes `children`. `span`, `order`, `visibleFrom`, and
48
- `hiddenFrom` were all drafted as real contract fields during this work and then commented out
49
- (Matt: get Grid merged first, come back to this later), so `Grid.Col` still types all four
50
- straight off `MantineGridColProps`, same as before any of this started. No runtime change — this
51
- is purely about where the types come from, not behavior.
52
-
53
- What's queued up in `adapter-common/src/components/Grid/RecursicaGridColProps.ts` (commented,
54
- with a TODO) for whenever this gets picked back up:
55
-
56
- - `span`/`order`: both mantine-v8 and mui-v7 independently built matching versions of these with
57
- no shared contract driving either — worth formalizing. `span` also resolves a naming mismatch:
58
- Mantine calls the column-width prop `span`, mui-v7 calls it `size` — Matt already decided on
59
- Mantine's naming (2026-09-22), so mui-v7 will need to rename `size` to `span` when this lands.
60
- - `visibleFrom`/`hiddenFrom`: same "both adapters independently agree" signal, but blocked on a
61
- real Recursica breakpoint naming convention — their `xs`/`sm`/`md`/`lg`/`xl` values are
62
- Mantine's own naming, not Recursica's, and Recursica doesn't have its own yet. Don't just copy
63
- Mantine's keys back in.
64
- - `offset`: never added — still undiffed between the two adapters.
65
- - Also note: `span`/`order`'s own per-breakpoint map variants (`{ xs: 6, sm: 3 }`) have the same
66
- `xs`/`sm`/`md`/`lg`/`xl` naming gap as `visibleFrom`/`hiddenFrom` — worth resolving together.
67
-
68
- ## Brand-layer exemption
69
-
70
- `layout-grids` tokens have no `ui-kit`-layer representation in Forge's model — a grid is a
71
- page-layout primitive, not a "component," so there was never going to be a
72
- `ui-kit_components_grid_*` indirection layer for it to go through. `Grid.module.css` reads
73
- `brand_layout-grids_default_*` directly, with `recursica-allow-brand:` exemption headers
74
- documenting why (see the analyzer's own exemption-system docs,
75
- `packages/recursica-token-analyzer/README.md` in the monorepo).
76
-
77
- ## Stories
78
-
79
- `ResponsiveSpans` and `ResponsiveSizes` explicitly override `columns={12}` — they test Mantine's
80
- own breakpoint/span system at standard 12-column proportions and aren't meant to exercise the
81
- design system's 6-column default. `ResponsiveSizes` exists purely so mui-adapter's
82
- `ui-kit-grid--responsive-sizes` story id has a source-of-truth counterpart to diff against (Mantine's
83
- `Grid.Col` has no `size` prop, only `span` — `ResponsiveSpans` already covers that). `Default`,
84
- `Offset`, `Grow`, and `CustomColumnCount` all exercise the real 6-column default (or an explicit
85
- override of it) and were resized from the old 12-column assumptions accordingly.
@@ -1,41 +0,0 @@
1
- /* HARDCODED VALUES:
2
- * None.
3
- */
4
-
5
- /* Brand-layer exemptions (recursica-allow-brand) — layout-grid tokens have no ui-kit-layer
6
- * representation in Forge's model (a grid is a page-layout primitive, not a "component"), so
7
- * Grid is the one component that reads brand_layout-grids_* directly. See
8
- * GRID_IMPLEMENTATION_NOTES.md.
9
- * recursica-allow-brand: --recursica_brand_layout-grids_default_column-gutter
10
- * recursica-allow-brand: --recursica_brand_layout-grids_default_row-gutter
11
- * recursica-allow-brand: --recursica_brand_layout-grids_default_margin
12
- */
13
-
14
- .root.root {
15
- /* Mantine handles column geometry (span, gutter width/margin-inline math) natively — the
16
- column-gutter design token is applied via Grid.tsx's `gutter` prop, not here. `margin` has no
17
- Mantine equivalent at all, so it's applied directly on the root element, always from the
18
- design token — margin is design-system-managed, not an integrator-facing override. Doubled
19
- selector beats Mantine's own single-class specificity regardless of stylesheet load order
20
- (same idiom as Chip/other components). */
21
- margin: var(--recursica_brand_layout-grids_default_margin);
22
- }
23
-
24
- .inner.inner {
25
- /* Mantine's own `.inner` margin is uniform on both axes, computed from `gutter`. Overriding just
26
- the block (vertical) component here lets row spacing differ from column spacing without
27
- touching Mantine's horizontal width/margin-inline math, which stays driven by `gutter`. Always
28
- the design token — row-gutter is design-system-managed, not an integrator-facing override. */
29
- margin-block: calc(
30
- var(--recursica_brand_layout-grids_default_row-gutter) / -2
31
- );
32
- }
33
-
34
- .col.col {
35
- /* Mantine handles column sizing (span, offset, order, visibleFrom/hiddenFrom) and inline padding
36
- (from `gutter`) natively. Overriding just the block (vertical) padding here applies the
37
- row-gutter design token independently of column-gutter, always from the design token. */
38
- padding-block: calc(
39
- var(--recursica_brand_layout-grids_default_row-gutter) / 2
40
- );
41
- }
@@ -1,200 +0,0 @@
1
- import React from "react";
2
- import type { Meta, StoryObj } from "@storybook/react";
3
- import { Grid } from "./Grid";
4
- import { Card } from "../Card/Card";
5
- import { Text } from "../Text/Text";
6
-
7
- type GridStoryProps = React.ComponentProps<typeof Grid>;
8
-
9
- const meta: Meta<GridStoryProps> = {
10
- title: "UI-Kit/Grid",
11
- component: Grid,
12
- tags: ["autodocs"],
13
- parameters: {
14
- docs: {
15
- description: {
16
- component:
17
- "Grid is a responsive grid layout mapped to Mantine's Grid/Grid.Col, providing column spans, offsets, ordering, and breakpoint-based visibility. Defaults to the design system's own layout-grid tokens: 6 columns, with design-system-managed column-gutter/row-gutter/margin values applied automatically (not integrator-configurable).",
18
- },
19
- },
20
- },
21
- args: {
22
- grow: false,
23
- },
24
- argTypes: {
25
- columns: {
26
- control: "number",
27
- description:
28
- "Number of columns in each row. Defaults to the design system's default column count (6).",
29
- },
30
- grow: {
31
- control: "boolean",
32
- description: "Columns in the last row expand to fill available space",
33
- },
34
- justify: {
35
- control: "select",
36
- options: [
37
- "flex-start",
38
- "center",
39
- "flex-end",
40
- "space-between",
41
- "space-around",
42
- ],
43
- description: "Justify-content property",
44
- },
45
- align: {
46
- control: "select",
47
- options: ["flex-start", "center", "flex-end", "stretch"],
48
- description: "Align-items property",
49
- },
50
- },
51
- };
52
-
53
- export default meta;
54
-
55
- type Story = StoryObj<GridStoryProps>;
56
-
57
- const Swatch = ({ children }: { children: React.ReactNode }) => (
58
- <Card>
59
- <Card.Content>
60
- <Text>{children}</Text>
61
- </Card.Content>
62
- </Card>
63
- );
64
-
65
- // Uses the design system default (6 columns) with no explicit `columns` override.
66
- export const Default: Story = {
67
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
68
- render: ({ withLayer, layer, ...args }: any) => (
69
- <Grid {...args}>
70
- <Grid.Col span={2}>
71
- <Swatch>span 2 of 6 (default)</Swatch>
72
- </Grid.Col>
73
- <Grid.Col span={2}>
74
- <Swatch>span 2 of 6 (default)</Swatch>
75
- </Grid.Col>
76
- <Grid.Col span={2}>
77
- <Swatch>span 2 of 6 (default)</Swatch>
78
- </Grid.Col>
79
- </Grid>
80
- ),
81
- };
82
-
83
- // Explicitly overrides `columns` to 12 — this story exercises Mantine's own breakpoint/span
84
- // system at standard 12-column proportions, independent of the design system's 6-column default.
85
- export const ResponsiveSpans: Story = {
86
- args: {
87
- columns: 12,
88
- },
89
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
90
- render: ({ withLayer, layer, ...args }: any) => (
91
- <Grid {...args}>
92
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
93
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
94
- </Grid.Col>
95
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
96
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
97
- </Grid.Col>
98
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
99
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
100
- </Grid.Col>
101
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
102
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
103
- </Grid.Col>
104
- </Grid>
105
- ),
106
- };
107
-
108
- // Mirrors mui-adapter's `ResponsiveSizes` story (same breakpoints/content) under mantine's own
109
- // prop name — Mantine's Grid.Col has no `size` prop, only `span`, which is what `ResponsiveSpans`
110
- // above already exercises. This one exists purely so mui-adapter's `responsive-sizes` story id
111
- // has a source-of-truth counterpart to diff against (see GRID_IMPLEMENTATION_NOTES.md). Same
112
- // explicit 12-column override as `ResponsiveSpans`, for the same reason.
113
- export const ResponsiveSizes: Story = {
114
- args: {
115
- columns: 12,
116
- },
117
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
118
- render: ({ withLayer, layer, ...args }: any) => (
119
- <Grid {...args}>
120
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
121
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
122
- </Grid.Col>
123
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
124
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
125
- </Grid.Col>
126
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
127
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
128
- </Grid.Col>
129
- <Grid.Col span={{ xs: 12, sm: 6, md: 3 }}>
130
- <Swatch>xs 12 / sm 6 / md 3</Swatch>
131
- </Grid.Col>
132
- </Grid>
133
- ),
134
- };
135
-
136
- export const Offset: Story = {
137
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
138
- render: ({ withLayer, layer, ...args }: any) => (
139
- <Grid {...args}>
140
- <Grid.Col span={2} offset={2}>
141
- <Swatch>span 2, offset 2 (of 6)</Swatch>
142
- </Grid.Col>
143
- <Grid.Col span={2}>
144
- <Swatch>span 2 (of 6)</Swatch>
145
- </Grid.Col>
146
- </Grid>
147
- ),
148
- };
149
-
150
- export const Grow: Story = {
151
- args: {
152
- grow: true,
153
- },
154
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
155
- render: ({ withLayer, layer, ...args }: any) => (
156
- <Grid {...args}>
157
- <Grid.Col span={3}>
158
- <Swatch>span 3 (of 6)</Swatch>
159
- </Grid.Col>
160
- <Grid.Col span={3}>
161
- <Swatch>span 3 (of 6)</Swatch>
162
- </Grid.Col>
163
- <Grid.Col span={3}>
164
- <Swatch>span 3 (grows to fill row)</Swatch>
165
- </Grid.Col>
166
- </Grid>
167
- ),
168
- };
169
-
170
- // Overrides the design system's default column count (6) to demonstrate a genuinely custom value.
171
- export const CustomColumnCount: Story = {
172
- args: {
173
- columns: 4,
174
- },
175
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
176
- render: ({ withLayer, layer, ...args }: any) => (
177
- <Grid {...args}>
178
- <Grid.Col span={2}>
179
- <Swatch>span 2 of 4</Swatch>
180
- </Grid.Col>
181
- <Grid.Col span={2}>
182
- <Swatch>span 2 of 4</Swatch>
183
- </Grid.Col>
184
- </Grid>
185
- ),
186
- };
187
-
188
- export const VisibleHiddenFrom: Story = {
189
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
190
- render: ({ withLayer, layer, ...args }: any) => (
191
- <Grid {...args}>
192
- <Grid.Col span={3} hiddenFrom="sm">
193
- <Swatch>hidden from sm and up</Swatch>
194
- </Grid.Col>
195
- <Grid.Col span={3} visibleFrom="sm">
196
- <Swatch>visible from sm and up</Swatch>
197
- </Grid.Col>
198
- </Grid>
199
- ),
200
- };
@@ -1 +0,0 @@
1
- // Placeholder for future Grid component tests. Add tests here if/when needed.
@@ -1,179 +0,0 @@
1
- import { forwardRef } from "react";
2
- import {
3
- Grid as MantineGrid,
4
- type GridProps as MantineGridProps,
5
- type GridColProps as MantineGridColProps,
6
- createPolymorphicComponent,
7
- } from "@mantine/core";
8
- import {
9
- mapLayoutProps,
10
- type WithRecursicaSpacing,
11
- } from "../../utils/filterStylingProps";
12
- import {
13
- type RecursicaGridColProps,
14
- type RecursicaGridProps,
15
- } from "@recursica/adapter-common";
16
- import styles from "./Grid.module.css";
17
-
18
- /**
19
- * Grid layout wrapper.
20
- *
21
- * Note: Unlike complex UI components, primitive layout components (Flex, Stack, Group, Container, Grid)
22
- * DO NOT use the `RecursicaOverStyled` gatekeeper. Developers must be able to freely pass
23
- * width, height, padding, margins, and flexbox alignment props to construct structural layouts.
24
- *
25
- * Recursica's `layout-grids` tokens (column-gutter, row-gutter, margin) are design-system-managed
26
- * values, not integrator-facing settings — Grid applies them itself via CSS variables. Mantine's
27
- * own `gutter` prop is not accepted; only `columns` (from `RecursicaGridProps` in
28
- * `adapter-common`) is exposed as an override, matching how `Container.size` overrides its own
29
- * token-backed default. See `GRID_IMPLEMENTATION_NOTES.md`.
30
- */
31
- export type GridProps = WithRecursicaSpacing<
32
- Omit<MantineGridProps, "gutter" | "columns"> & RecursicaGridProps
33
- >;
34
-
35
- const _Grid = forwardRef<HTMLDivElement, GridProps>(function Grid(
36
- { children, columns, ...rest },
37
- ref,
38
- ) {
39
- const mergedClassNames: Partial<Record<string, string>> = {
40
- root: styles.root,
41
- inner: styles.inner,
42
- col: styles.col,
43
- };
44
-
45
- const classNamesProp = rest.classNames;
46
- if (
47
- classNamesProp &&
48
- typeof classNamesProp === "object" &&
49
- !Array.isArray(classNamesProp)
50
- ) {
51
- const o = classNamesProp as Partial<Record<string, string>>;
52
- mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
53
- mergedClassNames.inner = o.inner
54
- ? `${styles.inner} ${o.inner}`
55
- : styles.inner;
56
- mergedClassNames.col = o.col ? `${styles.col} ${o.col}` : styles.col;
57
- }
58
-
59
- const classNameProp = rest.className as string | undefined;
60
- const finalClass = classNameProp
61
- ? `${styles.root} ${classNameProp}`
62
- : styles.root;
63
-
64
- // `gutter` is no longer a supported prop — column-gutter is design-system-managed (see below),
65
- // dropped defensively here so a caller still passing the old prop name at runtime can't shadow
66
- // the token-driven value passed to Mantine below via the `{...mappedRest}` spread.
67
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
68
- const { gutter: _legacyGutter, ...restWithoutGutter } = rest as Record<
69
- string,
70
- unknown
71
- >;
72
-
73
- const mappedRest = mapLayoutProps(
74
- restWithoutGutter as Record<string, unknown>,
75
- );
76
-
77
- // `columns` has no CSS-variable equivalent in Mantine — it computes each column's flex-basis
78
- // from a plain number, so the design system default is baked in here as a JS default rather
79
- // than wired live through CSS. Sourced from --recursica_brand_layout-grids_default_columns.
80
- const resolvedColumns = columns ?? 6;
81
-
82
- return (
83
- <MantineGrid
84
- ref={ref}
85
- columns={resolvedColumns}
86
- gutter="var(--recursica_brand_layout-grids_default_column-gutter)"
87
- className={finalClass}
88
- classNames={mergedClassNames}
89
- {...(mappedRest as unknown as Omit<
90
- MantineGridProps,
91
- "gutter" | "columns"
92
- >)}
93
- >
94
- {children}
95
- </MantineGrid>
96
- );
97
- });
98
- _Grid.displayName = "Grid";
99
-
100
- /**
101
- * Recursica Grid layout wrapper.
102
- *
103
- * Supports polymorphism via the `component` prop for custom element rendering.
104
- * @example
105
- * ```tsx
106
- * <Grid>
107
- * <Grid.Col span={3}>Quarter width (defaults to 6 columns)</Grid.Col>
108
- * <Grid.Col span={3}>Quarter width</Grid.Col>
109
- * </Grid>
110
- * ```
111
- */
112
- const GridBase = createPolymorphicComponent<"div", GridProps>(_Grid);
113
-
114
- // ============================================================
115
- // GRID.COL
116
- // ============================================================
117
-
118
- // TODO(grid-col-contract): `RecursicaGridColProps` currently only contributes `children` here —
119
- // `span`, `order`, `visibleFrom`, and `hiddenFrom` are all drafted in that type (adapter-common)
120
- // but commented out for now (2026-09-22, Matt — paused to get Grid merged). Until they're
121
- // restored there, this stays a plain pass-through of Mantine's own `GridColProps`; no Omit needed
122
- // since nothing here is contract-backed yet. Re-add the intersection/Omit once
123
- // `RecursicaGridColProps` picks these back up. See `GRID_IMPLEMENTATION_NOTES.md`.
124
- export type GridColProps = WithRecursicaSpacing<
125
- MantineGridColProps & RecursicaGridColProps
126
- >;
127
-
128
- const _GridCol = forwardRef<HTMLDivElement, GridColProps>(function GridCol(
129
- { children, ...rest },
130
- ref,
131
- ) {
132
- const mergedClassNames: Partial<Record<string, string>> = {
133
- col: styles.col,
134
- };
135
-
136
- const classNamesProp = rest.classNames;
137
- if (
138
- classNamesProp &&
139
- typeof classNamesProp === "object" &&
140
- !Array.isArray(classNamesProp)
141
- ) {
142
- const o = classNamesProp as Partial<Record<string, string>>;
143
- mergedClassNames.col = o.col ? `${styles.col} ${o.col}` : styles.col;
144
- }
145
-
146
- const classNameProp = rest.className as string | undefined;
147
- const finalClass = classNameProp
148
- ? `${styles.col} ${classNameProp}`
149
- : styles.col;
150
-
151
- return (
152
- <MantineGrid.Col
153
- ref={ref}
154
- className={finalClass}
155
- classNames={mergedClassNames}
156
- {...(mapLayoutProps(
157
- rest as Record<string, unknown>,
158
- ) as unknown as MantineGridColProps)}
159
- >
160
- {children}
161
- </MantineGrid.Col>
162
- );
163
- });
164
- _GridCol.displayName = "GridCol";
165
-
166
- export const GridCol = createPolymorphicComponent<"div", GridColProps>(
167
- _GridCol,
168
- );
169
-
170
- // ============================================================
171
- // DOT NOTATION EXPORT
172
- // ============================================================
173
-
174
- type GridComponent = typeof GridBase & {
175
- Col: typeof GridCol;
176
- };
177
-
178
- export const Grid = GridBase as GridComponent;
179
- Grid.Col = GridCol;
@@ -1,61 +0,0 @@
1
- # Grid - Usage Guide
2
-
3
- This document describes how to integrate and use the `Grid` component in your projects using `@recursica/adapter-mantine-v8`.
4
-
5
- ---
6
-
7
- ## 1. Import Reference
8
-
9
- ```tsx
10
- import { Grid } from "@recursica/adapter-mantine-v8";
11
- ```
12
-
13
- ---
14
-
15
- ## 2. Basic Example
16
-
17
- ```tsx
18
- import React from "react";
19
- import { Grid } from "@recursica/adapter-mantine-v8";
20
-
21
- export default function Demo() {
22
- return (
23
- // No props needed for the design system default: 6 columns, with the design system's own
24
- // column-gutter/row-gutter/margin values.
25
- <Grid>
26
- <Grid.Col span={3}>Half width (of 6)</Grid.Col>
27
- <Grid.Col span={{ xs: 6, sm: 3, md: 2 }}>Responsive width</Grid.Col>
28
- </Grid>
29
- );
30
- }
31
- ```
32
-
33
- ---
34
-
35
- ## 3. Design System Integration
36
-
37
- All Recursica components in the `@recursica/adapter-mantine-v8` package adhere strictly to design system spacing, scaling, and behavior patterns.
38
-
39
- > [!IMPORTANT]
40
- >
41
- > - **Anti-override protection**: `Grid` is a primitive layout component (see [OVERSTYLING.md](../../../OVERSTYLING.md)), so standard Mantine layout props pass through freely without needing `overStyled`.
42
- > - **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.
43
- > - **Variables and Theming**: Grid follows the design system's own `layout-grids` tokens — 6 columns, with column-gutter/row-gutter/margin applied automatically. Only `columns` is an integrator-facing override; the gutter/margin values are design-system-managed and not configurable via props.
44
-
45
- ---
46
-
47
- ## 4. Key Integration Features & Constraints
48
-
49
- `Grid.Col`'s `span`, `offset`, `order`, `visibleFrom`, `hiddenFrom` match Mantine's own naming and
50
- accept the same shapes, including per-breakpoint objects (`{ xs, sm, md, lg, xl }`) — all still a
51
- straight Mantine pass-through for now. A formal `RecursicaGridColProps` contract in
52
- `adapter-common` is scaffolded but not filled in yet (TODO, tracked in
53
- `GRID_IMPLEMENTATION_NOTES.md`).
54
-
55
- `Grid` no longer accepts Mantine's own `gutter` prop — column-gutter, row-gutter, and margin are
56
- design-system-managed (Forge-controlled, applied via CSS variables) rather than integrator
57
- settings:
58
-
59
- - `columns?: number` — the only design-token-backed override on Grid; defaults to 6.
60
-
61
- `grow`, `justify`, `align` are unchanged, matching Mantine's own naming.