@fpkit/acss 2.2.0 → 3.1.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/libs/chunk-5RAWNUVD.js +8 -0
- package/libs/chunk-5RAWNUVD.js.map +1 -0
- package/libs/{chunk-UJAQVHWC.js → chunk-CWRNJA4P.js} +2 -2
- package/libs/{chunk-R7NLLZU2.cjs → chunk-DYFAUAB7.cjs} +3 -3
- package/libs/chunk-NWJDAHP6.cjs +17 -0
- package/libs/chunk-NWJDAHP6.cjs.map +1 -0
- package/libs/components/breadcrumbs/breadcrumb.cjs +5 -5
- package/libs/components/breadcrumbs/breadcrumb.d.cts +2 -2
- package/libs/components/breadcrumbs/breadcrumb.d.ts +2 -2
- package/libs/components/breadcrumbs/breadcrumb.js +2 -2
- package/libs/components/flexbox/flex.css +1 -0
- package/libs/components/flexbox/flex.css.map +1 -0
- package/libs/components/flexbox/flex.min.css +3 -0
- package/libs/components/link/link.cjs +5 -5
- package/libs/components/link/link.css +1 -1
- package/libs/components/link/link.css.map +1 -1
- package/libs/components/link/link.d.cts +2 -131
- package/libs/components/link/link.d.ts +2 -131
- package/libs/components/link/link.js +1 -1
- package/libs/components/link/link.min.css +2 -2
- package/libs/components/nav/nav.css +1 -1
- package/libs/components/nav/nav.css.map +1 -1
- package/libs/components/nav/nav.min.css +2 -2
- package/libs/hooks.cjs +3 -3
- package/libs/hooks.d.cts +1 -1
- package/libs/hooks.d.ts +1 -1
- package/libs/hooks.js +2 -2
- package/libs/index.cjs +30 -29
- package/libs/index.cjs.map +1 -1
- package/libs/index.css +1 -1
- package/libs/index.css.map +1 -1
- package/libs/index.d.cts +246 -290
- package/libs/index.d.ts +246 -290
- package/libs/index.js +15 -15
- package/libs/index.js.map +1 -1
- package/libs/link-59ad884f.d.ts +371 -0
- package/package.json +2 -2
- package/src/components/flexbox/README.mdx +996 -0
- package/src/components/flexbox/flex.scss +847 -0
- package/src/components/flexbox/flex.stories.tsx +1233 -0
- package/src/components/flexbox/flex.test.tsx +689 -0
- package/src/components/flexbox/flex.tsx +484 -0
- package/src/components/flexbox/flex.types.ts +224 -0
- package/src/components/link/link.scss +4 -10
- package/src/components/link/link.tsx +18 -3
- package/src/components/nav/nav.scss +1 -1
- package/src/index.scss +1 -0
- package/src/index.ts +19 -2
- package/src/patterns/page/page-header.tsx +1 -3
- package/src/styles/flexbox/flex.css +736 -0
- package/src/styles/flexbox/flex.css.map +1 -0
- package/src/styles/index.css +739 -9
- package/src/styles/index.css.map +1 -1
- package/src/styles/link/link.css +2 -6
- package/src/styles/link/link.css.map +1 -1
- package/src/styles/nav/nav.css +3 -3
- package/libs/chunk-5PJYLVFY.cjs +0 -17
- package/libs/chunk-5PJYLVFY.cjs.map +0 -1
- package/libs/chunk-NNTBIHSD.js +0 -8
- package/libs/chunk-NNTBIHSD.js.map +0 -1
- package/libs/components/alert/alert.min.min.css +0 -2
- package/libs/components/badge/badge.min.min.css +0 -2
- package/libs/components/breadcrumbs/breadcrumb.min.min.css +0 -2
- package/libs/components/buttons/button.min.min.css +0 -2
- package/libs/components/cards/card-style.min.min.css +0 -2
- package/libs/components/cards/card.min.min.css +0 -2
- package/libs/components/details/details.min.min.css +0 -2
- package/libs/components/dialog/dialog.min.min.css +0 -2
- package/libs/components/form/form.min.min.css +0 -2
- package/libs/components/icons/icon.min.min.css +0 -2
- package/libs/components/images/img.min.min.css +0 -2
- package/libs/components/layout/landmarks.min.min.css +0 -2
- package/libs/components/link/link.min.min.css +0 -2
- package/libs/components/list/list.min.min.css +0 -2
- package/libs/components/nav/nav.min.min.css +0 -2
- package/libs/components/progress/progress.min.min.css +0 -2
- package/libs/components/styles/index.min.min.css +0 -2
- package/libs/components/tag/tag.min.min.css +0 -2
- package/libs/components/text-to-speech/text-to-speech.min.min.css +0 -2
- /package/libs/{chunk-UJAQVHWC.js.map → chunk-CWRNJA4P.js.map} +0 -0
- /package/libs/{chunk-R7NLLZU2.cjs.map → chunk-DYFAUAB7.cjs.map} +0 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import UI from "../ui";
|
|
3
|
+
import type {
|
|
4
|
+
FlexProps,
|
|
5
|
+
FlexItemProps,
|
|
6
|
+
FlexSpacerProps,
|
|
7
|
+
FlexComponent,
|
|
8
|
+
ResponsiveFlexProps,
|
|
9
|
+
FlexContainerElement,
|
|
10
|
+
FlexItemElement,
|
|
11
|
+
} from "./flex.types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generates CSS class names from flex props
|
|
15
|
+
* Converts prop values to corresponding utility class names
|
|
16
|
+
*
|
|
17
|
+
* @param props - Flex properties to convert
|
|
18
|
+
* @param prefix - Optional breakpoint prefix (e.g., 'sm:', 'md:')
|
|
19
|
+
* @returns Array of CSS class names
|
|
20
|
+
*/
|
|
21
|
+
const generateFlexClasses = (props: ResponsiveFlexProps, prefix = ""): string[] => {
|
|
22
|
+
const classes: string[] = [];
|
|
23
|
+
|
|
24
|
+
if (props.direction) {
|
|
25
|
+
const dirMap = {
|
|
26
|
+
row: "flex-row",
|
|
27
|
+
"row-reverse": "flex-row-reverse",
|
|
28
|
+
column: "flex-col",
|
|
29
|
+
"column-reverse": "flex-col-reverse",
|
|
30
|
+
};
|
|
31
|
+
classes.push(`${prefix}${dirMap[props.direction]}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (props.wrap) {
|
|
35
|
+
const wrapMap = {
|
|
36
|
+
wrap: "flex-wrap",
|
|
37
|
+
nowrap: "flex-nowrap",
|
|
38
|
+
"wrap-reverse": "flex-wrap-reverse",
|
|
39
|
+
};
|
|
40
|
+
classes.push(`${prefix}${wrapMap[props.wrap]}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (props.gap) {
|
|
44
|
+
classes.push(`${prefix}gap-${props.gap}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (props.rowGap) {
|
|
48
|
+
classes.push(`${prefix}row-gap-${props.rowGap}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (props.colGap) {
|
|
52
|
+
classes.push(`${prefix}col-gap-${props.colGap}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (props.justify) {
|
|
56
|
+
const justifyMap = {
|
|
57
|
+
start: "justify-start",
|
|
58
|
+
end: "justify-end",
|
|
59
|
+
center: "justify-center",
|
|
60
|
+
between: "justify-between",
|
|
61
|
+
around: "justify-around",
|
|
62
|
+
evenly: "justify-evenly",
|
|
63
|
+
};
|
|
64
|
+
classes.push(`${prefix}${justifyMap[props.justify]}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (props.align) {
|
|
68
|
+
const alignMap = {
|
|
69
|
+
start: "items-start",
|
|
70
|
+
end: "items-end",
|
|
71
|
+
center: "items-center",
|
|
72
|
+
baseline: "items-baseline",
|
|
73
|
+
stretch: "items-stretch",
|
|
74
|
+
};
|
|
75
|
+
classes.push(`${prefix}${alignMap[props.align]}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (props.alignContent) {
|
|
79
|
+
const alignContentMap = {
|
|
80
|
+
start: "content-start",
|
|
81
|
+
end: "content-end",
|
|
82
|
+
center: "content-center",
|
|
83
|
+
between: "content-between",
|
|
84
|
+
around: "content-around",
|
|
85
|
+
evenly: "content-evenly",
|
|
86
|
+
stretch: "content-stretch",
|
|
87
|
+
};
|
|
88
|
+
classes.push(`${prefix}${alignContentMap[props.alignContent]}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return classes;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Flex - A flexible container component for creating flexbox layouts
|
|
96
|
+
*
|
|
97
|
+
* Provides a declarative React API for flexbox layouts with responsive props,
|
|
98
|
+
* preset variants, and full TypeScript support. Built on top of the fpkit
|
|
99
|
+
* flexbox utility system, converting props to utility classes for optimal performance.
|
|
100
|
+
*
|
|
101
|
+
* ## Features
|
|
102
|
+
* - **Compound Pattern**: Use Flex.Item and Flex.Spacer sub-components
|
|
103
|
+
* - **Responsive Props**: Different layouts at sm/md/lg/xl breakpoints
|
|
104
|
+
* - **Preset Variants**: Common patterns like 'center', 'between', 'stack'
|
|
105
|
+
* - **CSS Custom Properties**: Runtime theming via styles prop
|
|
106
|
+
* - **Polymorphic**: Render as semantic container elements via 'as' prop
|
|
107
|
+
* - **Type-Safe**: Full TypeScript support with autocomplete
|
|
108
|
+
*
|
|
109
|
+
* ## Semantic Elements Only
|
|
110
|
+
* The `as` prop is restricted to semantic container elements:
|
|
111
|
+
* - **Block containers**: div (default), section, article, aside, main, header, footer
|
|
112
|
+
* - **List containers**: ul, ol, dl, nav
|
|
113
|
+
* - **Form containers**: form, fieldset
|
|
114
|
+
*
|
|
115
|
+
* This restriction ensures proper HTML structure and prevents misuse as inline
|
|
116
|
+
* or interactive elements (e.g., span, a, button).
|
|
117
|
+
*
|
|
118
|
+
* ## Accessibility
|
|
119
|
+
* - Uses semantic HTML by default (div, but customizable via 'as' prop)
|
|
120
|
+
* - Forwards all ARIA attributes to the rendered element
|
|
121
|
+
* - No interactive behavior by default (purely layout)
|
|
122
|
+
*
|
|
123
|
+
* ## Breakpoints
|
|
124
|
+
* - **sm**: 30rem (480px)
|
|
125
|
+
* - **md**: 48rem (768px)
|
|
126
|
+
* - **lg**: 62rem (992px)
|
|
127
|
+
* - **xl**: 80rem (1280px)
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Basic flex container
|
|
131
|
+
* <Flex direction="row" gap="md" justify="between" align="center">
|
|
132
|
+
* <div>Item 1</div>
|
|
133
|
+
* <div>Item 2</div>
|
|
134
|
+
* </Flex>
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* // Responsive layout - column on mobile, row on medium+
|
|
138
|
+
* <Flex direction="column" md={{ direction: "row", gap: "lg" }}>
|
|
139
|
+
* <Flex.Item flex="1">Content 1</Flex.Item>
|
|
140
|
+
* <Flex.Item flex="1">Content 2</Flex.Item>
|
|
141
|
+
* </Flex>
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* // Preset variant
|
|
145
|
+
* <Flex variant="center">
|
|
146
|
+
* <div>Centered content</div>
|
|
147
|
+
* </Flex>
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* // Using Flex.Spacer to push items apart
|
|
151
|
+
* <Flex>
|
|
152
|
+
* <div>Left</div>
|
|
153
|
+
* <Flex.Spacer />
|
|
154
|
+
* <div>Right</div>
|
|
155
|
+
* </Flex>
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // Custom CSS properties
|
|
159
|
+
* <Flex styles={{ '--flex-gap': '2rem' }}>
|
|
160
|
+
* <div>Item</div>
|
|
161
|
+
* </Flex>
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // Semantic HTML with 'as' prop
|
|
165
|
+
* <Flex as="nav" role="navigation" aria-label="Main navigation">
|
|
166
|
+
* <a href="/">Home</a>
|
|
167
|
+
* <a href="/about">About</a>
|
|
168
|
+
* </Flex>
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* // Semantic list structure
|
|
172
|
+
* <Flex as="ul" gap="md">
|
|
173
|
+
* <Flex.Item as="li">Item 1</Flex.Item>
|
|
174
|
+
* <Flex.Item as="li">Item 2</Flex.Item>
|
|
175
|
+
* </Flex>
|
|
176
|
+
*/
|
|
177
|
+
const FlexBase = React.forwardRef<HTMLElement, FlexProps>((props, ref) => {
|
|
178
|
+
const {
|
|
179
|
+
variant,
|
|
180
|
+
inline = false,
|
|
181
|
+
as = "div",
|
|
182
|
+
className = "",
|
|
183
|
+
styles,
|
|
184
|
+
children,
|
|
185
|
+
sm,
|
|
186
|
+
md,
|
|
187
|
+
lg,
|
|
188
|
+
xl,
|
|
189
|
+
direction,
|
|
190
|
+
wrap,
|
|
191
|
+
gap,
|
|
192
|
+
rowGap,
|
|
193
|
+
colGap,
|
|
194
|
+
justify,
|
|
195
|
+
align,
|
|
196
|
+
alignContent,
|
|
197
|
+
...rest
|
|
198
|
+
} = props;
|
|
199
|
+
|
|
200
|
+
const classes: string[] = [];
|
|
201
|
+
|
|
202
|
+
// Base flex class
|
|
203
|
+
classes.push(inline ? "flex-inline" : "flex");
|
|
204
|
+
|
|
205
|
+
// Apply variant preset if specified
|
|
206
|
+
if (variant) {
|
|
207
|
+
const variantMap = {
|
|
208
|
+
center: "flex-center",
|
|
209
|
+
between: "flex-between",
|
|
210
|
+
around: "flex-around",
|
|
211
|
+
stack: "flex-stack",
|
|
212
|
+
spread: "flex-spread",
|
|
213
|
+
};
|
|
214
|
+
classes.push(variantMap[variant]);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Base responsive props
|
|
218
|
+
classes.push(
|
|
219
|
+
...generateFlexClasses({
|
|
220
|
+
direction,
|
|
221
|
+
wrap,
|
|
222
|
+
gap,
|
|
223
|
+
rowGap,
|
|
224
|
+
colGap,
|
|
225
|
+
justify,
|
|
226
|
+
align,
|
|
227
|
+
alignContent,
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// Responsive breakpoint classes
|
|
232
|
+
if (sm) {
|
|
233
|
+
classes.push(...generateFlexClasses(sm, "sm:"));
|
|
234
|
+
}
|
|
235
|
+
if (md) {
|
|
236
|
+
classes.push(...generateFlexClasses(md, "md:"));
|
|
237
|
+
}
|
|
238
|
+
if (lg) {
|
|
239
|
+
classes.push(...generateFlexClasses(lg, "lg:"));
|
|
240
|
+
}
|
|
241
|
+
if (xl) {
|
|
242
|
+
classes.push(...generateFlexClasses(xl, "xl:"));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Merge custom className
|
|
246
|
+
const allClasses = [...classes, className].filter(Boolean).join(" ");
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<UI as={as} ref={ref} classes={allClasses} styles={styles} {...rest}>
|
|
250
|
+
{children}
|
|
251
|
+
</UI>
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
FlexBase.displayName = "Flex";
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Flex.Item - Individual flex item component with sizing controls
|
|
259
|
+
*
|
|
260
|
+
* Provides fine-grained control over flex item behavior including grow,
|
|
261
|
+
* shrink, basis, alignment, and order. Supports responsive flex sizing
|
|
262
|
+
* at different breakpoints.
|
|
263
|
+
*
|
|
264
|
+
* ## Semantic Elements
|
|
265
|
+
* The `as` prop accepts container elements plus list item elements:
|
|
266
|
+
* - **Block containers**: div (default), section, article, aside, main, header, footer
|
|
267
|
+
* - **List containers**: ul, ol, dl, nav
|
|
268
|
+
* - **Form containers**: form, fieldset
|
|
269
|
+
* - **List items**: li, dt, dd
|
|
270
|
+
*
|
|
271
|
+
* List item elements (li, dt, dd) are included to support semantic list
|
|
272
|
+
* structures within flex containers.
|
|
273
|
+
*
|
|
274
|
+
* ## Props
|
|
275
|
+
* - **grow**: Flex grow factor (0 | 1)
|
|
276
|
+
* - **shrink**: Flex shrink factor (0 | 1)
|
|
277
|
+
* - **basis**: Flex basis ('auto' | '0' | 'full')
|
|
278
|
+
* - **flex**: Flex shorthand ('1' | 'auto' | 'initial' | 'none')
|
|
279
|
+
* - **alignSelf**: Override parent align-items
|
|
280
|
+
* - **order**: Visual order ('first' | 'last' | 'none')
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // Flex item that grows to fill available space
|
|
284
|
+
* <Flex.Item flex="1">
|
|
285
|
+
* Flexible content
|
|
286
|
+
* </Flex.Item>
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* // Fixed-width item that doesn't grow or shrink
|
|
290
|
+
* <Flex.Item flex="none" styles={{ width: '200px' }}>
|
|
291
|
+
* Fixed width
|
|
292
|
+
* </Flex.Item>
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* // Responsive flex - no grow on mobile, grow on medium+
|
|
296
|
+
* <Flex.Item flex="none" md={{ flex: "1" }}>
|
|
297
|
+
* Responsive item
|
|
298
|
+
* </Flex.Item>
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* // Custom alignment for individual item
|
|
302
|
+
* <Flex.Item alignSelf="end">
|
|
303
|
+
* Aligned to end
|
|
304
|
+
* </Flex.Item>
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* // Change visual order
|
|
308
|
+
* <Flex.Item order="last">
|
|
309
|
+
* Shows last visually
|
|
310
|
+
* </Flex.Item>
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* // Semantic list item
|
|
314
|
+
* <Flex as="ul" gap="md">
|
|
315
|
+
* <Flex.Item as="li">List item 1</Flex.Item>
|
|
316
|
+
* <Flex.Item as="li">List item 2</Flex.Item>
|
|
317
|
+
* </Flex>
|
|
318
|
+
*/
|
|
319
|
+
const FlexItem = React.forwardRef<HTMLElement, FlexItemProps>((props, ref) => {
|
|
320
|
+
const {
|
|
321
|
+
grow,
|
|
322
|
+
shrink,
|
|
323
|
+
basis,
|
|
324
|
+
flex,
|
|
325
|
+
alignSelf,
|
|
326
|
+
order,
|
|
327
|
+
as = "div",
|
|
328
|
+
className = "",
|
|
329
|
+
styles,
|
|
330
|
+
children,
|
|
331
|
+
sm,
|
|
332
|
+
md,
|
|
333
|
+
lg,
|
|
334
|
+
xl,
|
|
335
|
+
...rest
|
|
336
|
+
} = props;
|
|
337
|
+
|
|
338
|
+
const classes: string[] = [];
|
|
339
|
+
|
|
340
|
+
// Apply flex sizing
|
|
341
|
+
if (flex) {
|
|
342
|
+
const flexMap = {
|
|
343
|
+
"1": "flex-1",
|
|
344
|
+
auto: "flex-auto",
|
|
345
|
+
initial: "flex-initial",
|
|
346
|
+
none: "flex-none",
|
|
347
|
+
};
|
|
348
|
+
classes.push(flexMap[flex]);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Individual flex properties
|
|
352
|
+
if (grow !== undefined) {
|
|
353
|
+
classes.push(`flex-grow-${grow}`);
|
|
354
|
+
}
|
|
355
|
+
if (shrink !== undefined) {
|
|
356
|
+
classes.push(`flex-shrink-${shrink}`);
|
|
357
|
+
}
|
|
358
|
+
if (basis) {
|
|
359
|
+
const basisMap = {
|
|
360
|
+
auto: "flex-basis-auto",
|
|
361
|
+
"0": "flex-basis-0",
|
|
362
|
+
full: "flex-basis-full",
|
|
363
|
+
};
|
|
364
|
+
classes.push(basisMap[basis]);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Align self
|
|
368
|
+
if (alignSelf) {
|
|
369
|
+
const alignSelfMap = {
|
|
370
|
+
auto: "self-auto",
|
|
371
|
+
start: "self-start",
|
|
372
|
+
end: "self-end",
|
|
373
|
+
center: "self-center",
|
|
374
|
+
baseline: "self-baseline",
|
|
375
|
+
stretch: "self-stretch",
|
|
376
|
+
};
|
|
377
|
+
classes.push(alignSelfMap[alignSelf]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Order
|
|
381
|
+
if (order) {
|
|
382
|
+
const orderMap = {
|
|
383
|
+
first: "order-first",
|
|
384
|
+
last: "order-last",
|
|
385
|
+
none: "order-none",
|
|
386
|
+
};
|
|
387
|
+
classes.push(orderMap[order]);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Responsive flex sizing
|
|
391
|
+
if (sm?.flex) {
|
|
392
|
+
const flexMap = { "1": "flex-1", auto: "flex-auto", none: "flex-none" };
|
|
393
|
+
classes.push(`sm:${flexMap[sm.flex]}`);
|
|
394
|
+
}
|
|
395
|
+
if (md?.flex) {
|
|
396
|
+
const flexMap = { "1": "flex-1", auto: "flex-auto", none: "flex-none" };
|
|
397
|
+
classes.push(`md:${flexMap[md.flex]}`);
|
|
398
|
+
}
|
|
399
|
+
if (lg?.flex) {
|
|
400
|
+
const flexMap = { "1": "flex-1", auto: "flex-auto", none: "flex-none" };
|
|
401
|
+
classes.push(`lg:${flexMap[lg.flex]}`);
|
|
402
|
+
}
|
|
403
|
+
if (xl?.flex) {
|
|
404
|
+
const flexMap = { "1": "flex-1", auto: "flex-auto", none: "flex-none" };
|
|
405
|
+
classes.push(`xl:${flexMap[xl.flex]}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Merge custom className
|
|
409
|
+
const allClasses = [...classes, className].filter(Boolean).join(" ");
|
|
410
|
+
|
|
411
|
+
return (
|
|
412
|
+
<UI as={as} ref={ref} classes={allClasses} styles={styles} {...rest}>
|
|
413
|
+
{children}
|
|
414
|
+
</UI>
|
|
415
|
+
);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
FlexItem.displayName = "Flex.Item";
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Flex.Spacer - Auto-expanding spacer element for pushing items apart
|
|
422
|
+
*
|
|
423
|
+
* Creates a flex item with `flex: 1 1 0%` that automatically expands
|
|
424
|
+
* to fill available space, pushing adjacent items to the edges.
|
|
425
|
+
* Commonly used to separate groups of items in a flex container.
|
|
426
|
+
*
|
|
427
|
+
* ## Semantic Elements Only
|
|
428
|
+
* The `as` prop is restricted to container elements:
|
|
429
|
+
* - **Block containers**: div (default), section, article, aside, main, header, footer
|
|
430
|
+
* - **List containers**: ul, ol, dl, nav
|
|
431
|
+
* - **Form containers**: form, fieldset
|
|
432
|
+
*
|
|
433
|
+
* Spacers are purely presentational and should typically use non-semantic
|
|
434
|
+
* containers like div.
|
|
435
|
+
*
|
|
436
|
+
* ## Accessibility
|
|
437
|
+
* - Renders as `<div>` by default (purely presentational)
|
|
438
|
+
* - Use `aria-hidden="true"` if purely decorative
|
|
439
|
+
* - Consider semantic alternatives for critical spacing
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* // Push items to opposite edges
|
|
443
|
+
* <Flex>
|
|
444
|
+
* <div>Left side</div>
|
|
445
|
+
* <Flex.Spacer />
|
|
446
|
+
* <div>Right side</div>
|
|
447
|
+
* </Flex>
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* // Multiple items with spacers
|
|
451
|
+
* <Flex>
|
|
452
|
+
* <div>Start</div>
|
|
453
|
+
* <Flex.Spacer />
|
|
454
|
+
* <div>Middle</div>
|
|
455
|
+
* <Flex.Spacer />
|
|
456
|
+
* <div>End</div>
|
|
457
|
+
* </Flex>
|
|
458
|
+
*/
|
|
459
|
+
const FlexSpacer = React.forwardRef<HTMLElement, FlexSpacerProps>((props, ref) => {
|
|
460
|
+
const { as = "div", className = "", styles, ...rest } = props;
|
|
461
|
+
|
|
462
|
+
const classes = ["flex-1", className].filter(Boolean).join(" ");
|
|
463
|
+
|
|
464
|
+
return <UI as={as} ref={ref} classes={classes} styles={styles} {...rest} />;
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
FlexSpacer.displayName = "Flex.Spacer";
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Attach sub-components to Flex using compound component pattern
|
|
471
|
+
*/
|
|
472
|
+
const Flex = FlexBase as FlexComponent;
|
|
473
|
+
Flex.Item = FlexItem;
|
|
474
|
+
Flex.Spacer = FlexSpacer;
|
|
475
|
+
|
|
476
|
+
export default Flex;
|
|
477
|
+
export { FlexItem, FlexSpacer };
|
|
478
|
+
export type {
|
|
479
|
+
FlexProps,
|
|
480
|
+
FlexItemProps,
|
|
481
|
+
FlexSpacerProps,
|
|
482
|
+
FlexContainerElement,
|
|
483
|
+
FlexItemElement,
|
|
484
|
+
};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Flex container components
|
|
3
|
+
* Supports responsive flexbox layouts with CSS custom properties
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Valid HTML elements for Flex container
|
|
8
|
+
* Restricted to semantic container elements only
|
|
9
|
+
*/
|
|
10
|
+
export type FlexContainerElement =
|
|
11
|
+
| "div"
|
|
12
|
+
| "section"
|
|
13
|
+
| "article"
|
|
14
|
+
| "aside"
|
|
15
|
+
| "main"
|
|
16
|
+
| "header"
|
|
17
|
+
| "footer"
|
|
18
|
+
| "nav"
|
|
19
|
+
| "ul"
|
|
20
|
+
| "ol"
|
|
21
|
+
| "dl"
|
|
22
|
+
| "form"
|
|
23
|
+
| "fieldset";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Valid HTML elements for Flex.Item
|
|
27
|
+
* Includes list item elements in addition to container elements
|
|
28
|
+
*/
|
|
29
|
+
export type FlexItemElement = FlexContainerElement | "li" | "dt" | "dd";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Flex container direction
|
|
33
|
+
*/
|
|
34
|
+
export type FlexDirection = "row" | "row-reverse" | "column" | "column-reverse";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Flex wrap behavior
|
|
38
|
+
*/
|
|
39
|
+
export type FlexWrap = "wrap" | "nowrap" | "wrap-reverse";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Flex justify-content alignment
|
|
43
|
+
*/
|
|
44
|
+
export type FlexJustify = "start" | "end" | "center" | "between" | "around" | "evenly";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Flex align-items alignment
|
|
48
|
+
*/
|
|
49
|
+
export type FlexAlign = "start" | "end" | "center" | "baseline" | "stretch";
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Flex align-content alignment (multi-line)
|
|
53
|
+
*/
|
|
54
|
+
export type FlexAlignContent =
|
|
55
|
+
| "start"
|
|
56
|
+
| "end"
|
|
57
|
+
| "center"
|
|
58
|
+
| "between"
|
|
59
|
+
| "around"
|
|
60
|
+
| "evenly"
|
|
61
|
+
| "stretch";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Flex align-self alignment (individual items)
|
|
65
|
+
*/
|
|
66
|
+
export type FlexAlignSelf = "auto" | "start" | "end" | "center" | "baseline" | "stretch";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gap size options
|
|
70
|
+
*/
|
|
71
|
+
export type FlexGap = "0" | "xs" | "sm" | "md" | "lg" | "xl";
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Preset flex layout variants
|
|
75
|
+
*/
|
|
76
|
+
export type FlexVariant = "center" | "between" | "around" | "stack" | "spread";
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Responsive flex properties for breakpoints
|
|
80
|
+
*/
|
|
81
|
+
export interface ResponsiveFlexProps {
|
|
82
|
+
/** Flex direction */
|
|
83
|
+
direction?: FlexDirection;
|
|
84
|
+
/** Flex wrap behavior */
|
|
85
|
+
wrap?: FlexWrap;
|
|
86
|
+
/** Gap between flex items */
|
|
87
|
+
gap?: FlexGap;
|
|
88
|
+
/** Row gap (vertical spacing) */
|
|
89
|
+
rowGap?: FlexGap;
|
|
90
|
+
/** Column gap (horizontal spacing) */
|
|
91
|
+
colGap?: FlexGap;
|
|
92
|
+
/** Justify content (main axis alignment) */
|
|
93
|
+
justify?: FlexJustify;
|
|
94
|
+
/** Align items (cross axis alignment) */
|
|
95
|
+
align?: FlexAlign;
|
|
96
|
+
/** Align content (multi-line alignment) */
|
|
97
|
+
alignContent?: FlexAlignContent;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Base Flex component props
|
|
102
|
+
*
|
|
103
|
+
* ## Semantic Elements Only
|
|
104
|
+
* The `as` prop is restricted to semantic container elements to ensure
|
|
105
|
+
* proper HTML structure and accessibility. Use only block-level container
|
|
106
|
+
* elements like div, section, article, or form elements.
|
|
107
|
+
*
|
|
108
|
+
* **Allowed elements**: div, section, article, aside, main, header, footer,
|
|
109
|
+
* nav, ul, ol, dl, form, fieldset
|
|
110
|
+
*
|
|
111
|
+
* **Not allowed**: span, a, button, input, or other inline/interactive elements
|
|
112
|
+
*/
|
|
113
|
+
export interface FlexProps
|
|
114
|
+
extends ResponsiveFlexProps,
|
|
115
|
+
Omit<React.HTMLAttributes<HTMLElement>, "className"> {
|
|
116
|
+
/** Preset layout variant */
|
|
117
|
+
variant?: FlexVariant;
|
|
118
|
+
/** Use inline-flex instead of flex */
|
|
119
|
+
inline?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Element type to render as
|
|
122
|
+
* @remarks Restricted to semantic container elements only for proper HTML structure
|
|
123
|
+
*/
|
|
124
|
+
as?: FlexContainerElement;
|
|
125
|
+
/** Additional CSS class names */
|
|
126
|
+
className?: string;
|
|
127
|
+
/** Inline styles and CSS custom properties */
|
|
128
|
+
styles?: React.CSSProperties;
|
|
129
|
+
/** Children elements */
|
|
130
|
+
children?: React.ReactNode;
|
|
131
|
+
/** Responsive props for small screens (≥30rem / 480px) */
|
|
132
|
+
sm?: ResponsiveFlexProps;
|
|
133
|
+
/** Responsive props for medium screens (≥48rem / 768px) */
|
|
134
|
+
md?: ResponsiveFlexProps;
|
|
135
|
+
/** Responsive props for large screens (≥62rem / 992px) */
|
|
136
|
+
lg?: ResponsiveFlexProps;
|
|
137
|
+
/** Responsive props for extra large screens (≥80rem / 1280px) */
|
|
138
|
+
xl?: ResponsiveFlexProps;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Flex.Item component props
|
|
143
|
+
*
|
|
144
|
+
* ## Semantic Elements
|
|
145
|
+
* The `as` prop accepts container elements plus list item elements (li, dt, dd)
|
|
146
|
+
* to support semantic list structures within flex containers.
|
|
147
|
+
*
|
|
148
|
+
* **Allowed elements**: div, section, article, aside, main, header, footer,
|
|
149
|
+
* nav, ul, ol, dl, form, fieldset, li, dt, dd
|
|
150
|
+
*/
|
|
151
|
+
export interface FlexItemProps extends Omit<React.HTMLAttributes<HTMLElement>, "className"> {
|
|
152
|
+
/** Flex grow factor */
|
|
153
|
+
grow?: 0 | 1;
|
|
154
|
+
/** Flex shrink factor */
|
|
155
|
+
shrink?: 0 | 1;
|
|
156
|
+
/** Flex basis */
|
|
157
|
+
basis?: "auto" | "0" | "full";
|
|
158
|
+
/** Flex shorthand: '1' | 'auto' | 'initial' | 'none' */
|
|
159
|
+
flex?: "1" | "auto" | "initial" | "none";
|
|
160
|
+
/** Align self (overrides parent align-items) */
|
|
161
|
+
alignSelf?: FlexAlignSelf;
|
|
162
|
+
/** Order of the flex item */
|
|
163
|
+
order?: "first" | "last" | "none";
|
|
164
|
+
/**
|
|
165
|
+
* Element type to render as
|
|
166
|
+
* @remarks Includes list item elements (li, dt, dd) in addition to container elements
|
|
167
|
+
*/
|
|
168
|
+
as?: FlexItemElement;
|
|
169
|
+
/** Additional CSS class names */
|
|
170
|
+
className?: string;
|
|
171
|
+
/** Inline styles and CSS custom properties */
|
|
172
|
+
styles?: React.CSSProperties;
|
|
173
|
+
/** Children elements */
|
|
174
|
+
children?: React.ReactNode;
|
|
175
|
+
/** Responsive props for small screens (≥30rem / 480px) */
|
|
176
|
+
sm?: {
|
|
177
|
+
flex?: "1" | "auto" | "none";
|
|
178
|
+
};
|
|
179
|
+
/** Responsive props for medium screens (≥48rem / 768px) */
|
|
180
|
+
md?: {
|
|
181
|
+
flex?: "1" | "auto" | "none";
|
|
182
|
+
};
|
|
183
|
+
/** Responsive props for large screens (≥62rem / 992px) */
|
|
184
|
+
lg?: {
|
|
185
|
+
flex?: "1" | "auto" | "none";
|
|
186
|
+
};
|
|
187
|
+
/** Responsive props for extra large screens (≥80rem / 1280px) */
|
|
188
|
+
xl?: {
|
|
189
|
+
flex?: "1" | "auto" | "none";
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Flex.Spacer component props
|
|
195
|
+
* Creates an auto-expanding spacer element (flex: 1)
|
|
196
|
+
*
|
|
197
|
+
* ## Semantic Elements Only
|
|
198
|
+
* The `as` prop is restricted to container elements. Spacers are purely
|
|
199
|
+
* presentational and should use non-semantic containers like div.
|
|
200
|
+
*
|
|
201
|
+
* **Allowed elements**: div, section, article, aside, main, header, footer,
|
|
202
|
+
* nav, ul, ol, dl, form, fieldset
|
|
203
|
+
*/
|
|
204
|
+
export interface FlexSpacerProps extends Omit<React.HTMLAttributes<HTMLElement>, "className"> {
|
|
205
|
+
/**
|
|
206
|
+
* Element type to render as
|
|
207
|
+
* @remarks Restricted to container elements. Default is 'div'.
|
|
208
|
+
*/
|
|
209
|
+
as?: FlexContainerElement;
|
|
210
|
+
/** Additional CSS class names */
|
|
211
|
+
className?: string;
|
|
212
|
+
/** Inline styles and CSS custom properties */
|
|
213
|
+
styles?: React.CSSProperties;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Combined Flex component type with sub-components
|
|
218
|
+
*/
|
|
219
|
+
export type FlexComponent = React.ForwardRefExoticComponent<
|
|
220
|
+
FlexProps & React.RefAttributes<HTMLElement>
|
|
221
|
+
> & {
|
|
222
|
+
Item: React.ForwardRefExoticComponent<FlexItemProps & React.RefAttributes<HTMLElement>>;
|
|
223
|
+
Spacer: React.ForwardRefExoticComponent<FlexSpacerProps & React.RefAttributes<HTMLElement>>;
|
|
224
|
+
};
|