@recursica/adapter-mantine-v8 1.2.1 → 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.
- package/CHANGELOG.md +18 -0
- package/OVERSTYLING.md +1 -1
- package/SETUP.md +36 -5
- package/dist/adapter-mantine-v8.cjs +2 -2
- package/dist/adapter-mantine-v8.cjs.map +1 -1
- package/dist/adapter-mantine-v8.css +1 -1
- package/dist/adapter-mantine-v8.js +3157 -3002
- package/dist/adapter-mantine-v8.js.map +1 -1
- package/dist/index.d.ts +191 -164
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/recursica_brand.json +2 -2
- package/recursica_tokens.json +2 -2
- package/recursica_ui-kit.json +2 -2
- package/recursica_variables_scoped.css +7 -3
- package/src/components/Container/USAGE.md +0 -1
- package/src/components/Flex/USAGE.md +2 -1
- package/src/components/Group/USAGE.md +2 -1
- package/src/components/LayoutGrid/LAYOUT_GRID_IMPLEMENTATION_NOTES.md +88 -0
- package/src/components/LayoutGrid/LayoutGrid.columns.dom.test.tsx +66 -0
- package/src/components/LayoutGrid/LayoutGrid.module.css +48 -0
- package/src/components/LayoutGrid/LayoutGrid.stories.tsx +61 -0
- package/src/components/LayoutGrid/LayoutGrid.test.tsx +1 -0
- package/src/components/LayoutGrid/LayoutGrid.tsx +309 -0
- package/src/components/LayoutGrid/USAGE.md +76 -0
- package/src/components/Pagination/IMPLEMENTATION_NOTES.md +29 -8
- package/src/components/Pagination/Pagination.icons.tsx +0 -32
- package/src/components/Pagination/Pagination.manifest.dom.test.tsx +108 -0
- package/src/components/Pagination/Pagination.module.css +25 -155
- package/src/components/Pagination/Pagination.tsx +328 -197
- package/src/components/Pagination/USAGE.md +1 -0
- package/src/components/Stack/USAGE.md +2 -1
- package/src/components/index.ts +1 -1
- package/src/index.ts +1 -0
- package/src/utils/breakpointsFromRecManifest.test.ts +40 -0
- package/src/utils/breakpointsFromRecManifest.ts +75 -0
- package/src/utils/index.ts +1 -0
- package/src/components/Grid/GRID_IMPLEMENTATION_NOTES.md +0 -85
- package/src/components/Grid/Grid.module.css +0 -41
- package/src/components/Grid/Grid.stories.tsx +0 -200
- package/src/components/Grid/Grid.test.tsx +0 -1
- package/src/components/Grid/Grid.tsx +0 -179
- 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
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -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.
|