@recursica/mantine-adapter 0.33.0 → 0.35.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 +17 -0
- package/OVERSTYLING.md +56 -0
- package/USAGE.md +6 -4
- package/dist/mantine-adapter.cjs +2 -2
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.js +2085 -1707
- package/dist/mantine-adapter.js.map +1 -1
- package/dist/src/components/Grid/Grid.d.ts +91 -0
- package/dist/src/components/Modal/Modal.d.ts +21 -8
- package/dist/src/components/Pagination/Pagination.d.ts +45 -25
- package/dist/src/components/Panel/Panel.d.ts +22 -8
- package/dist/src/components/Stepper/Stepper.d.ts +4 -2
- package/dist/src/components/Table/Table.d.ts +25 -9
- package/dist/src/components/Tooltip/Tooltip.d.ts +4 -2
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/index.d.ts +64 -0
- package/llms.txt +58 -2
- package/package.json +3 -2
- package/src/OverStyling.tsx +10 -226
- package/src/components/Grid/GRID_IMPLEMENTATION_NOTES.md +9 -0
- package/src/components/Grid/Grid.module.css +11 -0
- package/src/components/Grid/Grid.stories.tsx +182 -0
- package/src/components/Grid/Grid.tsx +145 -0
- package/src/components/Grid/USAGE.md +47 -0
- package/src/components/Modal/Modal.tsx +129 -23
- package/src/components/Pagination/Pagination.tsx +73 -17
- package/src/components/Panel/Panel.tsx +134 -14
- package/src/components/Stepper/Stepper.tsx +20 -2
- package/src/components/Table/Table.tsx +153 -16
- package/src/components/Table/USAGE.md +0 -10
- package/src/components/Tooltip/Tooltip.tsx +26 -2
- package/src/components/index.ts +1 -0
- package/src/index.ts +2 -0
package/src/OverStyling.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import ReactMarkdown from "react-markdown";
|
|
2
|
+
import { TypographyStylesProvider } from "@mantine/core";
|
|
1
3
|
import {
|
|
2
4
|
Container,
|
|
3
5
|
Card,
|
|
@@ -13,6 +15,7 @@ import {
|
|
|
13
15
|
toggleGlobalOverStyled,
|
|
14
16
|
wrapComponent,
|
|
15
17
|
} from "@recursica/adapter-common";
|
|
18
|
+
import overStylingDoc from "../OVERSTYLING.md?raw";
|
|
16
19
|
|
|
17
20
|
const Button = wrapComponent(RawButton);
|
|
18
21
|
const Text = wrapComponent(RawText);
|
|
@@ -24,246 +27,27 @@ export const OverStylingInfo = () => {
|
|
|
24
27
|
<Container size="md" style={{ padding: "32px 0" }}>
|
|
25
28
|
<Card>
|
|
26
29
|
<Card.Content>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
</
|
|
30
|
-
<Text mb="rec-md">
|
|
31
|
-
By default, all Recursica components are strictly sandboxed. This
|
|
32
|
-
means they are protected against arbitrary styling configurations
|
|
33
|
-
(like passing generic React <code>style</code> objects, custom{" "}
|
|
34
|
-
<code>classNames</code> injections, or using deep Mantine layout
|
|
35
|
-
hooks like <code>bg</code> and <code>c</code>). This strict
|
|
36
|
-
compile-time and run-time enforcement guarantees that your design
|
|
37
|
-
system tokens remain true across your application.
|
|
38
|
-
</Text>
|
|
39
|
-
<Text mb="rec-md">
|
|
40
|
-
However, there may be edge cases where a developer absolutely must
|
|
41
|
-
modify a component beyond what the design tokens natively allow. For
|
|
42
|
-
this, we provide the <strong>escape hatch</strong> property:{" "}
|
|
43
|
-
<code>overStyled={`{true}`}</code>.
|
|
44
|
-
</Text>
|
|
45
|
-
|
|
46
|
-
<Title order={3} mb="rec-sm">
|
|
47
|
-
The Core Philosophy
|
|
48
|
-
</Title>
|
|
49
|
-
<Text mb="rec-sm">
|
|
50
|
-
**You should not over-style components.** Using{" "}
|
|
51
|
-
<code>overStyled</code> explicitly signifies that you are breaking
|
|
52
|
-
design system rules.
|
|
53
|
-
</Text>
|
|
54
|
-
<Stack
|
|
55
|
-
component="ol"
|
|
56
|
-
style={{ paddingLeft: "24px" }}
|
|
57
|
-
mb="rec-xl"
|
|
58
|
-
gap="rec-sm"
|
|
59
|
-
>
|
|
60
|
-
<li>
|
|
61
|
-
<Text>
|
|
62
|
-
<strong>Technical Debt:</strong> If over-styling is required, it
|
|
63
|
-
should be treated as a short-term workaround. Ideally, the
|
|
64
|
-
component will be refactored once the required layouts or
|
|
65
|
-
variants are officially integrated into the core Recursica
|
|
66
|
-
component library.
|
|
67
|
-
</Text>
|
|
68
|
-
</li>
|
|
69
|
-
<li>
|
|
70
|
-
<Text>
|
|
71
|
-
<strong>Auditing & Searching:</strong> Because this pattern
|
|
72
|
-
creates technical debt, we enforce the explicit{" "}
|
|
73
|
-
<code>overStyled</code> boolean. This provides a highly
|
|
74
|
-
auditable, easily searchable string. Product managers and
|
|
75
|
-
engineers can quickly grep the codebase for{" "}
|
|
76
|
-
<code>overStyled</code> (or the <code>RecursicaOverStyled</code>{" "}
|
|
77
|
-
typings) to hunt down components that don't match standard
|
|
78
|
-
patterns.
|
|
79
|
-
</Text>
|
|
80
|
-
</li>
|
|
81
|
-
<li>
|
|
82
|
-
<Text>
|
|
83
|
-
<strong>Highly Custom Components:</strong> If your application
|
|
84
|
-
genuinely requires massive custom layouts that the UI kit cannot
|
|
85
|
-
support, <strong>do not hack the Recursica component</strong>.
|
|
86
|
-
Instead, it is highly encouraged that you import the underlying
|
|
87
|
-
primitive component directly from <code>@mantine/core</code> and
|
|
88
|
-
construct your independent feature there. While you can utilize
|
|
89
|
-
raw Recursica CSS variables on these custom components, note
|
|
90
|
-
that they are not guaranteed to be accurately maintained as
|
|
91
|
-
Recursica evolves. Keep strict components strict!
|
|
92
|
-
</Text>
|
|
93
|
-
</li>
|
|
94
|
-
</Stack>
|
|
30
|
+
<TypographyStylesProvider>
|
|
31
|
+
<ReactMarkdown>{overStylingDoc}</ReactMarkdown>
|
|
32
|
+
</TypographyStylesProvider>
|
|
95
33
|
|
|
96
34
|
<Stack
|
|
97
35
|
style={{ height: 1, backgroundColor: "#eaeaea" }}
|
|
98
|
-
|
|
36
|
+
my="rec-xl"
|
|
99
37
|
/>
|
|
100
38
|
|
|
101
|
-
<Title order={3} mb="rec-
|
|
102
|
-
|
|
103
|
-
</Title>
|
|
104
|
-
<Text mb="rec-sm">
|
|
105
|
-
Unlike deep styling bounds (colors, typography, padding,
|
|
106
|
-
dimensions), external <strong>layout spacing properties</strong>{" "}
|
|
107
|
-
like Margins (<code>m</code>, <code>mt</code>, <code>mb</code>,{" "}
|
|
108
|
-
<code>mx</code>) are safely <strong>permitted by default</strong>.
|
|
109
|
-
This allows integrators to structurally compose components alongside
|
|
110
|
-
siblings without breaching internal token boundaries.
|
|
111
|
-
</Text>
|
|
112
|
-
<Text mb="rec-xl">
|
|
113
|
-
When using layout properties, you have the flexibility to use either
|
|
114
|
-
ecosystem seamlessly:
|
|
115
|
-
</Text>
|
|
116
|
-
<Stack
|
|
117
|
-
component="ol"
|
|
118
|
-
style={{ paddingLeft: "24px" }}
|
|
119
|
-
mb="rec-md"
|
|
120
|
-
gap="rec-sm"
|
|
121
|
-
>
|
|
122
|
-
<li>
|
|
123
|
-
<Text>
|
|
124
|
-
<strong>Mantine Core Values:</strong> Passing standard Mantine
|
|
125
|
-
sizes (like <code>mt="md"</code>) passes straight through to
|
|
126
|
-
Mantine natively, allowing you to interface completely normally
|
|
127
|
-
with a parent application's existing Mantine Theme setup that
|
|
128
|
-
might fall outside Recursica's scope.
|
|
129
|
-
</Text>
|
|
130
|
-
</li>
|
|
131
|
-
<li>
|
|
132
|
-
<Text>
|
|
133
|
-
<strong>Recursica Strict Tokens:</strong> Passing our custom
|
|
134
|
-
prefixed tokens (like <code>mt="rec-md"</code>) signals our
|
|
135
|
-
internal layout interceptor to securely translate the value
|
|
136
|
-
directly to our native <code>recursica_brand_dimensions</code>{" "}
|
|
137
|
-
CSS variables. This ensures strict design token measurements
|
|
138
|
-
while sharing the exact same prop interface!
|
|
139
|
-
</Text>
|
|
140
|
-
</li>
|
|
141
|
-
</Stack>
|
|
142
|
-
<Text mb="rec-sm" overStyled fw={500}>
|
|
143
|
-
Available Recursica Layout Tokens:
|
|
144
|
-
</Text>
|
|
145
|
-
<Stack
|
|
146
|
-
component="ul"
|
|
147
|
-
style={{ paddingLeft: "24px" }}
|
|
148
|
-
mb="rec-xl"
|
|
149
|
-
gap="rec-none"
|
|
150
|
-
>
|
|
151
|
-
<li>
|
|
152
|
-
<Text>
|
|
153
|
-
<code>rec-none</code> (0px limit)
|
|
154
|
-
</Text>
|
|
155
|
-
</li>
|
|
156
|
-
<li>
|
|
157
|
-
<Text>
|
|
158
|
-
<code>rec-sm</code> (0.5x scaling)
|
|
159
|
-
</Text>
|
|
160
|
-
</li>
|
|
161
|
-
<li>
|
|
162
|
-
<Text>
|
|
163
|
-
<code>rec-default</code> (1.0x scaling)
|
|
164
|
-
</Text>
|
|
165
|
-
</li>
|
|
166
|
-
<li>
|
|
167
|
-
<Text>
|
|
168
|
-
<code>rec-md</code> (1.5x scaling)
|
|
169
|
-
</Text>
|
|
170
|
-
</li>
|
|
171
|
-
<li>
|
|
172
|
-
<Text>
|
|
173
|
-
<code>rec-lg</code> (2.0x scaling)
|
|
174
|
-
</Text>
|
|
175
|
-
</li>
|
|
176
|
-
<li>
|
|
177
|
-
<Text>
|
|
178
|
-
<code>rec-xl</code> (3.0x scaling)
|
|
179
|
-
</Text>
|
|
180
|
-
</li>
|
|
181
|
-
<li>
|
|
182
|
-
<Text>
|
|
183
|
-
<code>rec-2xl</code> (4.0x scaling)
|
|
184
|
-
</Text>
|
|
185
|
-
</li>
|
|
186
|
-
</Stack>
|
|
187
|
-
|
|
188
|
-
<Stack
|
|
189
|
-
style={{ height: 1, backgroundColor: "#eaeaea" }}
|
|
190
|
-
mb="rec-xl"
|
|
191
|
-
/>
|
|
192
|
-
|
|
193
|
-
<Title order={3} mb="rec-sm">
|
|
194
|
-
Primitive Layout Components Exemption
|
|
39
|
+
<Title order={3} mb="rec-md">
|
|
40
|
+
Try It Yourself
|
|
195
41
|
</Title>
|
|
196
|
-
<Text mb="rec-sm">
|
|
197
|
-
Unlike complex UI components (Buttons, Tabs, Inputs) which are
|
|
198
|
-
strictly protected, <strong>Primitive Layout Components</strong> (
|
|
199
|
-
<code>Flex</code>, <code>Stack</code>, <code>Group</code>,{" "}
|
|
200
|
-
<code>Container</code>) are entirely exempt from the{" "}
|
|
201
|
-
<code>RecursicaOverStyled</code> gatekeeper.
|
|
202
|
-
</Text>
|
|
203
|
-
<Text mb="rec-xl">
|
|
204
|
-
Because the entire functional purpose of these components is
|
|
205
|
-
structural layout composition, developers are free to pass any
|
|
206
|
-
standard Mantine width, height, padding, margin, gap, and alignment
|
|
207
|
-
property directly to them without needing to flag{" "}
|
|
208
|
-
<code>overStyled={`{true}`}</code>. The internal custom token mapper
|
|
209
|
-
(such as converting <code>gap="rec-md"</code>) is still active
|
|
210
|
-
natively on these wrappers.
|
|
211
|
-
</Text>
|
|
212
|
-
|
|
213
|
-
<Stack
|
|
214
|
-
style={{ height: 1, backgroundColor: "#eaeaea" }}
|
|
215
|
-
mb="rec-xl"
|
|
216
|
-
/>
|
|
217
42
|
|
|
218
|
-
<
|
|
219
|
-
Visual Auditing & Highlights (Development Only)
|
|
220
|
-
</Title>
|
|
221
|
-
<Text mb="rec-sm">
|
|
222
|
-
To make it easy to spot technical debt and design system violations,
|
|
223
|
-
Recursica automatically tracks any component that uses the{" "}
|
|
224
|
-
<code>overStyled={`{true}`}</code> prop.
|
|
225
|
-
</Text>
|
|
226
|
-
<Text mb="rec-sm">
|
|
227
|
-
In <strong>development builds</strong>, you can highlight all
|
|
228
|
-
over-styled components on the page. Open your browser's developer
|
|
229
|
-
console and run:
|
|
230
|
-
</Text>
|
|
231
|
-
<Stack
|
|
232
|
-
style={{
|
|
233
|
-
padding: 12,
|
|
234
|
-
backgroundColor: "#f5f5f5",
|
|
235
|
-
borderRadius: 6,
|
|
236
|
-
fontSize: 13,
|
|
237
|
-
marginTop: 8,
|
|
238
|
-
marginBottom: 16,
|
|
239
|
-
}}
|
|
240
|
-
>
|
|
241
|
-
<code>recursica.toggleOverStyled()</code>
|
|
242
|
-
</Stack>
|
|
243
|
-
<Group mb="rec-md">
|
|
43
|
+
<Group mb="rec-xl">
|
|
244
44
|
<Switch
|
|
245
45
|
label="Highlight Over-Styled Components"
|
|
246
46
|
checked={isHighlightActive}
|
|
247
47
|
onChange={() => toggleGlobalOverStyled()}
|
|
248
48
|
/>
|
|
249
49
|
</Group>
|
|
250
|
-
<Text mb="rec-xl">
|
|
251
|
-
This toggles a <strong>cyan 2px box shadow</strong> outline around
|
|
252
|
-
the children of all over-styled components. The wrapping elements
|
|
253
|
-
use <code>display: contents</code> under the hood to ensure they
|
|
254
|
-
occupy zero DOM space and do not affect flex, grid, or absolute
|
|
255
|
-
positioning flow. In production builds, this debugging helper is
|
|
256
|
-
completely disabled and stripped with zero performance overhead.
|
|
257
|
-
</Text>
|
|
258
50
|
|
|
259
|
-
<Stack
|
|
260
|
-
style={{ height: 1, backgroundColor: "#eaeaea" }}
|
|
261
|
-
mb="rec-xl"
|
|
262
|
-
/>
|
|
263
|
-
|
|
264
|
-
<Title order={3} mb="rec-md">
|
|
265
|
-
Live Example
|
|
266
|
-
</Title>
|
|
267
51
|
<Text mb="rec-xl">
|
|
268
52
|
Below is a side-by-side comparison. The first is a standard
|
|
269
53
|
Recursica Button protected by the design tokens mapping. The second
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Grid Implementation Notes
|
|
2
|
+
|
|
3
|
+
The `Grid` component is a generic 12-column (by default) layout wrapper mapped directly to Mantine's `Grid`/`Grid.Col`. It requires no custom logical layouts or CSS workarounds — Mantine handles column geometry, breakpoints, and wrapping natively.
|
|
4
|
+
|
|
5
|
+
The one deliberate divergence from Mantine's native API: the public `gap` prop replaces Mantine's `gutter`, for naming consistency with the other primitive layout components (`Flex`, `Stack`, `Group`). `gap` is translated to Mantine's `gutter` prop internally via `mapLayoutProps`, the same utility used to resolve `rec-*` spacing tokens elsewhere.
|
|
6
|
+
|
|
7
|
+
`Grid.Col`'s `span`, `offset`, `order`, `visibleFrom`, and `hiddenFrom` props are passed straight through to Mantine's `Grid.Col` unchanged — Mantine already uses generic, portable naming for these.
|
|
8
|
+
|
|
9
|
+
Like the other primitive layout components, `Grid` and `Grid.Col` use `WithRecursicaSpacing<T>` rather than `RecursicaOverStyled<T>` and are listed in the "Primitive Layout Components Exemption" section of `OVERSTYLING.md`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* HARDCODED VALUES:
|
|
2
|
+
* None. This is a generic grid layout wrapper.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
.root {
|
|
6
|
+
/* Mantine handles the grid geometry (columns, gutter, spans) natively. No intrinsic design-system styles required for pure layout wrappers. */
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.col {
|
|
10
|
+
/* Mantine handles column sizing (span, offset, order, visibleFrom/hiddenFrom) natively. */
|
|
11
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
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 12-column (by default) responsive grid layout that maps directly to Mantine's Grid/Grid.Col, providing column spans, offsets, ordering, and breakpoint-based visibility.",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
gap: "rec-default",
|
|
23
|
+
columns: 12,
|
|
24
|
+
grow: false,
|
|
25
|
+
},
|
|
26
|
+
argTypes: {
|
|
27
|
+
gap: {
|
|
28
|
+
control: "select",
|
|
29
|
+
options: [
|
|
30
|
+
"rec-none",
|
|
31
|
+
"rec-sm",
|
|
32
|
+
"rec-default",
|
|
33
|
+
"rec-md",
|
|
34
|
+
"rec-lg",
|
|
35
|
+
"rec-xl",
|
|
36
|
+
"rec-2xl",
|
|
37
|
+
],
|
|
38
|
+
description: "Gap between columns",
|
|
39
|
+
},
|
|
40
|
+
columns: {
|
|
41
|
+
control: "number",
|
|
42
|
+
description: "Number of columns in each row",
|
|
43
|
+
},
|
|
44
|
+
grow: {
|
|
45
|
+
control: "boolean",
|
|
46
|
+
description: "Columns in the last row expand to fill available space",
|
|
47
|
+
},
|
|
48
|
+
justify: {
|
|
49
|
+
control: "select",
|
|
50
|
+
options: [
|
|
51
|
+
"flex-start",
|
|
52
|
+
"center",
|
|
53
|
+
"flex-end",
|
|
54
|
+
"space-between",
|
|
55
|
+
"space-around",
|
|
56
|
+
],
|
|
57
|
+
description: "Justify-content property",
|
|
58
|
+
},
|
|
59
|
+
align: {
|
|
60
|
+
control: "select",
|
|
61
|
+
options: ["flex-start", "center", "flex-end", "stretch"],
|
|
62
|
+
description: "Align-items property",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default meta;
|
|
68
|
+
|
|
69
|
+
type Story = StoryObj<GridStoryProps>;
|
|
70
|
+
|
|
71
|
+
const Swatch = ({ children }: { children: React.ReactNode }) => (
|
|
72
|
+
<Card>
|
|
73
|
+
<Card.Content>
|
|
74
|
+
<Text>{children}</Text>
|
|
75
|
+
</Card.Content>
|
|
76
|
+
</Card>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
export const Default: Story = {
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
81
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
82
|
+
<Grid {...args}>
|
|
83
|
+
<Grid.Col span={4}>
|
|
84
|
+
<Swatch>span 4</Swatch>
|
|
85
|
+
</Grid.Col>
|
|
86
|
+
<Grid.Col span={4}>
|
|
87
|
+
<Swatch>span 4</Swatch>
|
|
88
|
+
</Grid.Col>
|
|
89
|
+
<Grid.Col span={4}>
|
|
90
|
+
<Swatch>span 4</Swatch>
|
|
91
|
+
</Grid.Col>
|
|
92
|
+
</Grid>
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const ResponsiveSpans: Story = {
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
98
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
99
|
+
<Grid {...args}>
|
|
100
|
+
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
101
|
+
<Swatch>base 12 / sm 6 / md 3</Swatch>
|
|
102
|
+
</Grid.Col>
|
|
103
|
+
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
104
|
+
<Swatch>base 12 / sm 6 / md 3</Swatch>
|
|
105
|
+
</Grid.Col>
|
|
106
|
+
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
107
|
+
<Swatch>base 12 / sm 6 / md 3</Swatch>
|
|
108
|
+
</Grid.Col>
|
|
109
|
+
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>
|
|
110
|
+
<Swatch>base 12 / sm 6 / md 3</Swatch>
|
|
111
|
+
</Grid.Col>
|
|
112
|
+
</Grid>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const Offset: Story = {
|
|
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={4} offset={4}>
|
|
121
|
+
<Swatch>span 4, offset 4</Swatch>
|
|
122
|
+
</Grid.Col>
|
|
123
|
+
<Grid.Col span={4}>
|
|
124
|
+
<Swatch>span 4</Swatch>
|
|
125
|
+
</Grid.Col>
|
|
126
|
+
</Grid>
|
|
127
|
+
),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const Grow: Story = {
|
|
131
|
+
args: {
|
|
132
|
+
grow: true,
|
|
133
|
+
},
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
135
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
136
|
+
<Grid {...args}>
|
|
137
|
+
<Grid.Col span={3}>
|
|
138
|
+
<Swatch>span 3</Swatch>
|
|
139
|
+
</Grid.Col>
|
|
140
|
+
<Grid.Col span={3}>
|
|
141
|
+
<Swatch>span 3</Swatch>
|
|
142
|
+
</Grid.Col>
|
|
143
|
+
<Grid.Col span={3}>
|
|
144
|
+
<Swatch>span 3 (grows to fill row)</Swatch>
|
|
145
|
+
</Grid.Col>
|
|
146
|
+
</Grid>
|
|
147
|
+
),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const CustomColumnCount: Story = {
|
|
151
|
+
args: {
|
|
152
|
+
columns: 6,
|
|
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={2}>
|
|
158
|
+
<Swatch>span 2 of 6</Swatch>
|
|
159
|
+
</Grid.Col>
|
|
160
|
+
<Grid.Col span={2}>
|
|
161
|
+
<Swatch>span 2 of 6</Swatch>
|
|
162
|
+
</Grid.Col>
|
|
163
|
+
<Grid.Col span={2}>
|
|
164
|
+
<Swatch>span 2 of 6</Swatch>
|
|
165
|
+
</Grid.Col>
|
|
166
|
+
</Grid>
|
|
167
|
+
),
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const VisibleHiddenFrom: Story = {
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
172
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
173
|
+
<Grid {...args}>
|
|
174
|
+
<Grid.Col span={6} hiddenFrom="sm">
|
|
175
|
+
<Swatch>hidden from sm and up</Swatch>
|
|
176
|
+
</Grid.Col>
|
|
177
|
+
<Grid.Col span={6} visibleFrom="sm">
|
|
178
|
+
<Swatch>visible from sm and up</Swatch>
|
|
179
|
+
</Grid.Col>
|
|
180
|
+
</Grid>
|
|
181
|
+
),
|
|
182
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
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 styles from "./Grid.module.css";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
type RecursicaGridProps,
|
|
16
|
+
type RecursicaGridColProps,
|
|
17
|
+
} from "@recursica/adapter-common";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Grid layout wrapper.
|
|
21
|
+
*
|
|
22
|
+
* Note: Unlike complex UI components, primitive layout components (Flex, Stack, Group, Container, Grid)
|
|
23
|
+
* DO NOT use the `RecursicaOverStyled` gatekeeper. Developers must be able to freely pass
|
|
24
|
+
* width, height, padding, margins, and flexbox alignment props to construct structural layouts.
|
|
25
|
+
*/
|
|
26
|
+
export type GridProps = WithRecursicaSpacing<
|
|
27
|
+
Omit<MantineGridProps, "gutter"> & RecursicaGridProps
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
const _Grid = forwardRef<HTMLDivElement, GridProps>(function Grid(
|
|
31
|
+
{ children, gap = "rec-default", ...rest },
|
|
32
|
+
ref,
|
|
33
|
+
) {
|
|
34
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
35
|
+
root: styles.root,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const classNamesProp = rest.classNames;
|
|
39
|
+
if (
|
|
40
|
+
classNamesProp &&
|
|
41
|
+
typeof classNamesProp === "object" &&
|
|
42
|
+
!Array.isArray(classNamesProp)
|
|
43
|
+
) {
|
|
44
|
+
const o = classNamesProp as Partial<Record<string, string>>;
|
|
45
|
+
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const classNameProp = rest.className as string | undefined;
|
|
49
|
+
const finalClass = classNameProp
|
|
50
|
+
? `${styles.root} ${classNameProp}`
|
|
51
|
+
: styles.root;
|
|
52
|
+
|
|
53
|
+
const { gap: gutter, ...mappedRest } = mapLayoutProps({
|
|
54
|
+
gap,
|
|
55
|
+
...rest,
|
|
56
|
+
} as Record<string, unknown>);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<MantineGrid
|
|
60
|
+
ref={ref}
|
|
61
|
+
gutter={gutter as MantineGridProps["gutter"]}
|
|
62
|
+
className={finalClass}
|
|
63
|
+
classNames={mergedClassNames}
|
|
64
|
+
{...(mappedRest as unknown as Omit<MantineGridProps, "gutter">)}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</MantineGrid>
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
_Grid.displayName = "Grid";
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Recursica Grid layout wrapper.
|
|
74
|
+
*
|
|
75
|
+
* Supports polymorphism via the `component` prop for custom element rendering.
|
|
76
|
+
* @example
|
|
77
|
+
* ```tsx
|
|
78
|
+
* <Grid gap="rec-default">
|
|
79
|
+
* <Grid.Col span={6}>Half width</Grid.Col>
|
|
80
|
+
* <Grid.Col span={6}>Half width</Grid.Col>
|
|
81
|
+
* </Grid>
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
const GridBase = createPolymorphicComponent<"div", GridProps>(_Grid);
|
|
85
|
+
|
|
86
|
+
// ============================================================
|
|
87
|
+
// GRID.COL
|
|
88
|
+
// ============================================================
|
|
89
|
+
|
|
90
|
+
export type GridColProps = WithRecursicaSpacing<
|
|
91
|
+
MantineGridColProps & RecursicaGridColProps
|
|
92
|
+
>;
|
|
93
|
+
|
|
94
|
+
const _GridCol = forwardRef<HTMLDivElement, GridColProps>(function GridCol(
|
|
95
|
+
{ children, ...rest },
|
|
96
|
+
ref,
|
|
97
|
+
) {
|
|
98
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
99
|
+
col: styles.col,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const classNamesProp = rest.classNames;
|
|
103
|
+
if (
|
|
104
|
+
classNamesProp &&
|
|
105
|
+
typeof classNamesProp === "object" &&
|
|
106
|
+
!Array.isArray(classNamesProp)
|
|
107
|
+
) {
|
|
108
|
+
const o = classNamesProp as Partial<Record<string, string>>;
|
|
109
|
+
mergedClassNames.col = o.col ? `${styles.col} ${o.col}` : styles.col;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const classNameProp = rest.className as string | undefined;
|
|
113
|
+
const finalClass = classNameProp
|
|
114
|
+
? `${styles.col} ${classNameProp}`
|
|
115
|
+
: styles.col;
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<MantineGrid.Col
|
|
119
|
+
ref={ref}
|
|
120
|
+
className={finalClass}
|
|
121
|
+
classNames={mergedClassNames}
|
|
122
|
+
{...(mapLayoutProps(
|
|
123
|
+
rest as Record<string, unknown>,
|
|
124
|
+
) as unknown as MantineGridColProps)}
|
|
125
|
+
>
|
|
126
|
+
{children}
|
|
127
|
+
</MantineGrid.Col>
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
_GridCol.displayName = "GridCol";
|
|
131
|
+
|
|
132
|
+
export const GridCol = createPolymorphicComponent<"div", GridColProps>(
|
|
133
|
+
_GridCol,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// ============================================================
|
|
137
|
+
// DOT NOTATION EXPORT
|
|
138
|
+
// ============================================================
|
|
139
|
+
|
|
140
|
+
type GridComponent = typeof GridBase & {
|
|
141
|
+
Col: typeof GridCol;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const Grid = GridBase as GridComponent;
|
|
145
|
+
Grid.Col = GridCol;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Grid - Usage Guide
|
|
2
|
+
|
|
3
|
+
This document describes how to integrate and use the `Grid` component in your projects using `@recursica/mantine-adapter`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Import Reference
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Grid } from "@recursica/mantine-adapter";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Basic Example
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import React from "react";
|
|
19
|
+
import { Grid } from "@recursica/mantine-adapter";
|
|
20
|
+
|
|
21
|
+
export default function Demo() {
|
|
22
|
+
return (
|
|
23
|
+
<Grid gap="rec-default">
|
|
24
|
+
<Grid.Col span={6}>Half width</Grid.Col>
|
|
25
|
+
<Grid.Col span={{ base: 12, sm: 6, md: 3 }}>Responsive width</Grid.Col>
|
|
26
|
+
</Grid>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Design System Integration
|
|
34
|
+
|
|
35
|
+
All Recursica components in the `@recursica/mantine-adapter` package adhere strictly to design system spacing, scaling, and behavior patterns.
|
|
36
|
+
|
|
37
|
+
> [!IMPORTANT]
|
|
38
|
+
>
|
|
39
|
+
> - **Anti-override protection**: `Grid` is a primitive layout component (see [OVERSTYLING.md](../../../OVERSTYLING.md)) and is exempt from the `RecursicaOverStyled` gatekeeper, so standard Mantine layout props pass through freely.
|
|
40
|
+
> - **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.
|
|
41
|
+
> - **Variables and Theming**: Spacing is entirely determined by the `rec-*` token scale, mapped transparently to standard Mantine gutter values.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 4. Key Integration Features & Constraints
|
|
46
|
+
|
|
47
|
+
The `Grid` component maps directly to Mantine's `Grid`/`Grid.Col`. The one deviation from Mantine's native API: the prop for spacing between columns is named `gap` (not Mantine's `gutter`), for consistency with `Flex`, `Stack`, and `Group`. Everything else — `columns`, `grow`, `justify`, `align` on `Grid`, and `span`, `offset`, `order`, `visibleFrom`, `hiddenFrom` on `Grid.Col` — matches Mantine's own naming and accepts the same shapes, including per-breakpoint objects (`{ base, xs, sm, md, lg, xl }`).
|