@rakeyshgidwani/roger-ui-bank-theme-harvey 0.2.52 → 0.3.1
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 +1 -1
- package/dist/components/ui/button.d.ts +3 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.esm.js +3 -2
- package/dist/components/ui/button.js +3 -2
- package/dist/components/ui/layout/container.d.ts +57 -0
- package/dist/components/ui/layout/container.d.ts.map +1 -0
- package/dist/components/ui/layout/container.esm.js +173 -0
- package/dist/components/ui/layout/container.js +173 -0
- package/dist/components/ui/layout/index.d.ts +9 -0
- package/dist/components/ui/layout/index.d.ts.map +1 -0
- package/dist/components/ui/layout/index.esm.js +6 -0
- package/dist/components/ui/layout/index.js +6 -0
- package/dist/components/ui/layout/responsive-grid.d.ts +93 -0
- package/dist/components/ui/layout/responsive-grid.d.ts.map +1 -0
- package/dist/components/ui/layout/responsive-grid.esm.js +124 -0
- package/dist/components/ui/layout/responsive-grid.js +124 -0
- package/dist/components/ui/layouts/adaptive-layout.d.ts +1 -0
- package/dist/components/ui/layouts/adaptive-layout.d.ts.map +1 -1
- package/dist/components/ui/layouts/adaptive-layout.esm.js +2 -2
- package/dist/components/ui/layouts/adaptive-layout.js +2 -2
- package/dist/components/ui/navigation/index.d.ts +2 -1
- package/dist/components/ui/navigation/index.d.ts.map +1 -1
- package/dist/components/ui/navigation/index.esm.js +1 -0
- package/dist/components/ui/navigation/index.js +1 -0
- package/dist/components/ui/navigation/progressive-navigation.d.ts +37 -0
- package/dist/components/ui/navigation/progressive-navigation.d.ts.map +1 -0
- package/dist/components/ui/navigation/progressive-navigation.esm.js +145 -0
- package/dist/components/ui/navigation/progressive-navigation.js +145 -0
- package/dist/components/ui/navigation/types.d.ts +21 -0
- package/dist/components/ui/navigation/types.d.ts.map +1 -1
- package/dist/hooks/use-adaptive-layout.d.ts +2 -1
- package/dist/hooks/use-adaptive-layout.d.ts.map +1 -1
- package/dist/hooks/use-adaptive-layout.esm.js +13 -8
- package/dist/hooks/use-adaptive-layout.js +13 -8
- package/dist/hooks/use-device.d.ts +3 -1
- package/dist/hooks/use-device.d.ts.map +1 -1
- package/dist/hooks/use-device.esm.js +14 -7
- package/dist/hooks/use-device.js +14 -7
- package/dist/index.d.ts +19 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +9 -4
- package/dist/index.js +9 -4
- package/dist/plugins/css-purge-optimizer.d.ts +25 -0
- package/dist/plugins/css-purge-optimizer.d.ts.map +1 -0
- package/dist/plugins/css-purge-optimizer.esm.js +414 -0
- package/dist/plugins/css-purge-optimizer.js +414 -0
- package/dist/plugins/performance-monitor.d.ts +29 -0
- package/dist/plugins/performance-monitor.d.ts.map +1 -0
- package/dist/plugins/performance-monitor.esm.js +221 -0
- package/dist/plugins/performance-monitor.js +221 -0
- package/dist/plugins/progressive-css-loader.d.ts +21 -0
- package/dist/plugins/progressive-css-loader.d.ts.map +1 -0
- package/dist/plugins/progressive-css-loader.esm.js +227 -0
- package/dist/plugins/progressive-css-loader.js +227 -0
- package/dist/plugins/theme-css-generator.d.ts.map +1 -1
- package/dist/plugins/theme-css-generator.esm.js +19 -6
- package/dist/plugins/theme-css-generator.js +19 -6
- package/dist/styles.css +1027 -112
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.esm.js +4 -1
- package/dist/theme.js +4 -1
- package/dist/themes/phase1-constants.d.ts +23 -0
- package/dist/themes/phase1-constants.d.ts.map +1 -0
- package/dist/themes/phase1-constants.esm.js +180 -0
- package/dist/themes/phase1-constants.js +180 -0
- package/dist/themes/themes/default.d.ts.map +1 -1
- package/dist/themes/themes/default.esm.js +4 -1
- package/dist/themes/themes/default.js +4 -1
- package/dist/themes/themes/harvey.d.ts.map +1 -1
- package/dist/themes/themes/harvey.esm.js +4 -1
- package/dist/themes/themes/harvey.js +4 -1
- package/dist/themes/types.d.ts +62 -0
- package/dist/themes/types.d.ts.map +1 -1
- package/dist/themes/validation.d.ts +17 -0
- package/dist/themes/validation.d.ts.map +1 -1
- package/dist/themes/validation.esm.js +218 -0
- package/dist/themes/validation.js +218 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/progressive-css-injector.d.ts +80 -0
- package/dist/utils/progressive-css-injector.d.ts.map +1 -0
- package/dist/utils/progressive-css-injector.esm.js +217 -0
- package/dist/utils/progressive-css-injector.js +217 -0
- package/package.json +1 -1
- package/src/components/ui/button.tsx +9 -6
- package/src/components/ui/layout/container.tsx +312 -0
- package/src/components/ui/layout/index.ts +10 -0
- package/src/components/ui/layout/responsive-grid.tsx +286 -0
- package/src/components/ui/layouts/adaptive-layout.tsx +3 -1
- package/src/components/ui/navigation/index.ts +2 -0
- package/src/components/ui/navigation/progressive-navigation.tsx +453 -0
- package/src/components/ui/navigation/types.ts +41 -0
- package/src/hooks/use-adaptive-layout.ts +13 -9
- package/src/hooks/use-device.tsx +17 -10
- package/src/index.ts +19 -4
- package/src/plugins/css-purge-optimizer.ts +491 -0
- package/src/plugins/performance-monitor.ts +292 -0
- package/src/plugins/progressive-css-loader.ts +269 -0
- package/src/plugins/theme-css-generator.ts +22 -6
- package/src/styles/components/base/badge.css +2 -2
- package/src/styles/components/base/button.css +238 -35
- package/src/styles/components/base/card.css +2 -2
- package/src/styles/components/base/checkbox.css +3 -3
- package/src/styles/components/base/label.css +3 -3
- package/src/styles/components/feedback/skeleton.css +1 -1
- package/src/styles/components/feedback/toast.css +1 -1
- package/src/styles/components/index.css +3 -0
- package/src/styles/components/layout/container.css +466 -0
- package/src/styles/components/layout/index.css +5 -0
- package/src/styles/components/layout/responsive-grid.css +422 -0
- package/src/styles/components/navigation/breadcrumb.css +1 -1
- package/src/styles/components/navigation/index.css +1 -0
- package/src/styles/components/navigation/menu.css +2 -2
- package/src/styles/components/navigation/pagination.css +4 -4
- package/src/styles/components/navigation/progressive-navigation.css +633 -0
- package/src/styles/components/navigation/sidebar.css +4 -4
- package/src/styles/components/navigation/stepper.css +2 -2
- package/src/styles/components/navigation/tabs.css +1 -1
- package/src/styles/components/ui/theme-toggle.css +2 -2
- package/src/styles/progressive.css +17 -0
- package/src/styles/themes/harvey.css +103 -19
- package/src/styles/utilities/semantic-input-system.css +7 -13
- package/src/theme.ts +5 -1
- package/src/themes/phase1-constants.ts +189 -0
- package/src/themes/themes/default.ts +5 -1
- package/src/themes/themes/harvey.ts +5 -1
- package/src/themes/types.ts +77 -1
- package/src/themes/validation.ts +249 -0
- package/src/types.ts +77 -1
- package/src/utils/progressive-css-injector.ts +254 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container Component
|
|
3
|
+
* Implements Phase 2 container max-widths and content width system
|
|
4
|
+
*
|
|
5
|
+
* Container Max-Widths:
|
|
6
|
+
* - xs: 100%, sm: 640px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px, 3xl: 1792px
|
|
7
|
+
*
|
|
8
|
+
* Content Width Patterns:
|
|
9
|
+
* - narrow: Optimal for reading content (65ch-75ch)
|
|
10
|
+
* - standard: General content (80%-90% width)
|
|
11
|
+
* - wide: Data/dashboards (90%-100% width)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
'use client'
|
|
15
|
+
|
|
16
|
+
import * as React from 'react';
|
|
17
|
+
|
|
18
|
+
export interface ContainerProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
|
|
21
|
+
// Container type determines max-width behavior
|
|
22
|
+
type?: 'container' | 'content' | 'fluid';
|
|
23
|
+
|
|
24
|
+
// Content width patterns
|
|
25
|
+
width?: 'narrow' | 'standard' | 'wide' | 'full';
|
|
26
|
+
|
|
27
|
+
// Custom max-widths per breakpoint
|
|
28
|
+
maxWidths?: {
|
|
29
|
+
xs?: string;
|
|
30
|
+
sm?: string;
|
|
31
|
+
md?: string;
|
|
32
|
+
lg?: string;
|
|
33
|
+
xl?: string;
|
|
34
|
+
'2xl'?: string;
|
|
35
|
+
'3xl'?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Padding configuration
|
|
39
|
+
padding?: {
|
|
40
|
+
xs?: string;
|
|
41
|
+
sm?: string;
|
|
42
|
+
md?: string;
|
|
43
|
+
lg?: string;
|
|
44
|
+
xl?: string;
|
|
45
|
+
'2xl'?: string;
|
|
46
|
+
'3xl'?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Center container content
|
|
50
|
+
centered?: boolean;
|
|
51
|
+
|
|
52
|
+
// Allow content to break out of container on small screens
|
|
53
|
+
breakout?: boolean;
|
|
54
|
+
|
|
55
|
+
// Character-based width constraints (for reading content)
|
|
56
|
+
characterWidth?: {
|
|
57
|
+
min?: string; // e.g., '65ch'
|
|
58
|
+
max?: string; // e.g., '75ch'
|
|
59
|
+
optimal?: string; // e.g., '70ch'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Component polymorphism
|
|
63
|
+
as?: keyof JSX.IntrinsicElements;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Default container max-widths (from implementation plan)
|
|
67
|
+
const DEFAULT_CONTAINER_MAX_WIDTHS = {
|
|
68
|
+
xs: '100%',
|
|
69
|
+
sm: '640px',
|
|
70
|
+
md: '768px',
|
|
71
|
+
lg: '1024px',
|
|
72
|
+
xl: '1280px',
|
|
73
|
+
'2xl': '1536px',
|
|
74
|
+
'3xl': '1792px'
|
|
75
|
+
} as const;
|
|
76
|
+
|
|
77
|
+
// Default content width patterns (from implementation plan)
|
|
78
|
+
const CONTENT_WIDTH_PATTERNS = {
|
|
79
|
+
narrow: {
|
|
80
|
+
xs: '100%',
|
|
81
|
+
sm: '100%',
|
|
82
|
+
md: '65ch',
|
|
83
|
+
lg: '70ch',
|
|
84
|
+
xl: '75ch',
|
|
85
|
+
'2xl': '75ch',
|
|
86
|
+
'3xl': '75ch'
|
|
87
|
+
},
|
|
88
|
+
standard: {
|
|
89
|
+
xs: '100%',
|
|
90
|
+
sm: '100%',
|
|
91
|
+
md: '90%',
|
|
92
|
+
lg: '85%',
|
|
93
|
+
xl: '80%',
|
|
94
|
+
'2xl': '80%',
|
|
95
|
+
'3xl': '80%'
|
|
96
|
+
},
|
|
97
|
+
wide: {
|
|
98
|
+
xs: '100%',
|
|
99
|
+
sm: '100%',
|
|
100
|
+
md: '100%',
|
|
101
|
+
lg: '95%',
|
|
102
|
+
xl: '90%',
|
|
103
|
+
'2xl': '90%',
|
|
104
|
+
'3xl': '90%'
|
|
105
|
+
},
|
|
106
|
+
full: {
|
|
107
|
+
xs: '100%',
|
|
108
|
+
sm: '100%',
|
|
109
|
+
md: '100%',
|
|
110
|
+
lg: '100%',
|
|
111
|
+
xl: '100%',
|
|
112
|
+
'2xl': '100%',
|
|
113
|
+
'3xl': '100%'
|
|
114
|
+
}
|
|
115
|
+
} as const;
|
|
116
|
+
|
|
117
|
+
// Default padding configuration
|
|
118
|
+
const DEFAULT_PADDING = {
|
|
119
|
+
xs: 'var(--cs-spacing-scale-md)',
|
|
120
|
+
sm: 'var(--cs-spacing-scale-lg)',
|
|
121
|
+
md: 'var(--cs-spacing-scale-xl)',
|
|
122
|
+
lg: 'var(--cs-spacing-scale-2xl)',
|
|
123
|
+
xl: 'var(--cs-spacing-scale-2xl)',
|
|
124
|
+
'2xl': 'var(--cs-spacing-scale-3xl)',
|
|
125
|
+
'3xl': 'var(--cs-spacing-scale-3xl)'
|
|
126
|
+
} as const;
|
|
127
|
+
|
|
128
|
+
export const Container: React.FC<ContainerProps> = ({
|
|
129
|
+
children,
|
|
130
|
+
type = 'container',
|
|
131
|
+
width = 'standard',
|
|
132
|
+
maxWidths: customMaxWidths,
|
|
133
|
+
padding: customPadding,
|
|
134
|
+
centered = true,
|
|
135
|
+
breakout = false,
|
|
136
|
+
characterWidth,
|
|
137
|
+
className = '',
|
|
138
|
+
style: userStyle,
|
|
139
|
+
as: Component = 'div',
|
|
140
|
+
...restProps
|
|
141
|
+
}) => {
|
|
142
|
+
const testId = (restProps as any)['data-testid'] || 'container';
|
|
143
|
+
// Determine max-widths based on type and width
|
|
144
|
+
const maxWidths = React.useMemo(() => {
|
|
145
|
+
if (customMaxWidths) {
|
|
146
|
+
return customMaxWidths;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
switch (type) {
|
|
150
|
+
case 'container':
|
|
151
|
+
return DEFAULT_CONTAINER_MAX_WIDTHS;
|
|
152
|
+
case 'content':
|
|
153
|
+
return CONTENT_WIDTH_PATTERNS[width];
|
|
154
|
+
case 'fluid':
|
|
155
|
+
return CONTENT_WIDTH_PATTERNS.full;
|
|
156
|
+
default:
|
|
157
|
+
return DEFAULT_CONTAINER_MAX_WIDTHS;
|
|
158
|
+
}
|
|
159
|
+
}, [type, width, customMaxWidths]);
|
|
160
|
+
|
|
161
|
+
// Determine padding
|
|
162
|
+
const padding = customPadding || DEFAULT_PADDING;
|
|
163
|
+
|
|
164
|
+
// Build CSS custom properties
|
|
165
|
+
const containerStyle = React.useMemo(() => {
|
|
166
|
+
const style: React.CSSProperties & Record<string, any> = {};
|
|
167
|
+
|
|
168
|
+
// Set max-width configurations
|
|
169
|
+
Object.entries(maxWidths).forEach(([breakpoint, maxWidth]) => {
|
|
170
|
+
style[`--container-max-width-${breakpoint}`] = maxWidth;
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Set padding configurations
|
|
174
|
+
Object.entries(padding).forEach(([breakpoint, paddingValue]) => {
|
|
175
|
+
style[`--container-padding-${breakpoint}`] = paddingValue;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Set character width constraints
|
|
179
|
+
if (characterWidth) {
|
|
180
|
+
if (characterWidth.min) {
|
|
181
|
+
style['--container-character-width-min'] = characterWidth.min;
|
|
182
|
+
}
|
|
183
|
+
if (characterWidth.max) {
|
|
184
|
+
style['--container-character-width-max'] = characterWidth.max;
|
|
185
|
+
}
|
|
186
|
+
if (characterWidth.optimal) {
|
|
187
|
+
style['--container-character-width-optimal'] = characterWidth.optimal;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return style;
|
|
192
|
+
}, [maxWidths, padding, characterWidth]);
|
|
193
|
+
|
|
194
|
+
// Build CSS classes
|
|
195
|
+
const containerClasses = React.useMemo(() => {
|
|
196
|
+
const baseClass = 'container';
|
|
197
|
+
const typeClass = `container--${type}`;
|
|
198
|
+
const widthClass = width !== 'standard' ? `container--width-${width}` : '';
|
|
199
|
+
const centeredClass = centered ? 'container--centered' : '';
|
|
200
|
+
const breakoutClass = breakout ? 'container--breakout' : '';
|
|
201
|
+
const characterWidthClass = characterWidth ? 'container--character-width' : '';
|
|
202
|
+
|
|
203
|
+
return [baseClass, typeClass, widthClass, centeredClass, breakoutClass, characterWidthClass, className]
|
|
204
|
+
.filter(Boolean)
|
|
205
|
+
.join(' ');
|
|
206
|
+
}, [type, width, centered, breakout, characterWidth, className]);
|
|
207
|
+
|
|
208
|
+
// Merge user styles with container styles
|
|
209
|
+
const mergedStyle = React.useMemo(() => ({
|
|
210
|
+
...containerStyle,
|
|
211
|
+
...userStyle
|
|
212
|
+
}), [containerStyle, userStyle]);
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<Component
|
|
216
|
+
className={containerClasses}
|
|
217
|
+
style={mergedStyle}
|
|
218
|
+
data-testid={testId}
|
|
219
|
+
{...(restProps as any)}
|
|
220
|
+
>
|
|
221
|
+
{children}
|
|
222
|
+
</Component>
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// Content Container - Specialized for reading content
|
|
227
|
+
export interface ContentContainerProps extends Omit<ContainerProps, 'type' | 'width'> {
|
|
228
|
+
// Reading optimization
|
|
229
|
+
lineLength?: 'short' | 'optimal' | 'long'; // Maps to character widths
|
|
230
|
+
textAlign?: 'left' | 'center' | 'justify';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export const ContentContainer: React.FC<ContentContainerProps> = ({
|
|
234
|
+
children,
|
|
235
|
+
lineLength = 'optimal',
|
|
236
|
+
textAlign = 'left',
|
|
237
|
+
characterWidth: customCharacterWidth,
|
|
238
|
+
style: userStyle,
|
|
239
|
+
...props
|
|
240
|
+
}) => {
|
|
241
|
+
// Map line length to character widths
|
|
242
|
+
const characterWidth = React.useMemo(() => {
|
|
243
|
+
if (customCharacterWidth) {
|
|
244
|
+
return customCharacterWidth;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
switch (lineLength) {
|
|
248
|
+
case 'short':
|
|
249
|
+
return { min: '45ch', max: '65ch', optimal: '55ch' };
|
|
250
|
+
case 'optimal':
|
|
251
|
+
return { min: '65ch', max: '75ch', optimal: '70ch' };
|
|
252
|
+
case 'long':
|
|
253
|
+
return { min: '75ch', max: '85ch', optimal: '80ch' };
|
|
254
|
+
default:
|
|
255
|
+
return { min: '65ch', max: '75ch', optimal: '70ch' };
|
|
256
|
+
}
|
|
257
|
+
}, [lineLength, customCharacterWidth]);
|
|
258
|
+
|
|
259
|
+
const mergedStyle = React.useMemo(() => ({
|
|
260
|
+
textAlign: textAlign,
|
|
261
|
+
...userStyle
|
|
262
|
+
}), [textAlign, userStyle]);
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<Container
|
|
266
|
+
type="content"
|
|
267
|
+
width="narrow"
|
|
268
|
+
characterWidth={characterWidth}
|
|
269
|
+
style={mergedStyle}
|
|
270
|
+
{...props}
|
|
271
|
+
>
|
|
272
|
+
{children}
|
|
273
|
+
</Container>
|
|
274
|
+
);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// Section Container - For page sections with full-width backgrounds
|
|
278
|
+
export interface SectionContainerProps extends ContainerProps {
|
|
279
|
+
// Background behavior
|
|
280
|
+
fullWidthBackground?: boolean;
|
|
281
|
+
backgroundVariant?: 'default' | 'muted' | 'accent' | 'inverse';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export const SectionContainer: React.FC<SectionContainerProps> = ({
|
|
285
|
+
children,
|
|
286
|
+
fullWidthBackground = false,
|
|
287
|
+
backgroundVariant = 'default',
|
|
288
|
+
className = '',
|
|
289
|
+
...props
|
|
290
|
+
}) => {
|
|
291
|
+
const sectionClasses = React.useMemo(() => {
|
|
292
|
+
const baseClass = 'section-container';
|
|
293
|
+
const backgroundClass = fullWidthBackground ? 'section-container--full-width-bg' : '';
|
|
294
|
+
const variantClass = backgroundVariant !== 'default' ? `section-container--${backgroundVariant}` : '';
|
|
295
|
+
|
|
296
|
+
return [baseClass, backgroundClass, variantClass, className]
|
|
297
|
+
.filter(Boolean)
|
|
298
|
+
.join(' ');
|
|
299
|
+
}, [fullWidthBackground, backgroundVariant, className]);
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<Container
|
|
303
|
+
className={sectionClasses}
|
|
304
|
+
as="section"
|
|
305
|
+
{...props}
|
|
306
|
+
>
|
|
307
|
+
{children}
|
|
308
|
+
</Container>
|
|
309
|
+
);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
export default Container;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout Components Index
|
|
3
|
+
* Exports all layout components for Phase 2 implementation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { ResponsiveGrid, ResponsiveGridItem } from './responsive-grid';
|
|
7
|
+
export type { ResponsiveGridProps, ResponsiveGridItemProps } from './responsive-grid';
|
|
8
|
+
|
|
9
|
+
export { Container, ContentContainer, SectionContainer } from './container';
|
|
10
|
+
export type { ContainerProps, ContentContainerProps, SectionContainerProps } from './container';
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responsive Grid Component
|
|
3
|
+
* Implements Phase 2 responsive grid system as defined in the implementation plan
|
|
4
|
+
*
|
|
5
|
+
* Grid Progression:
|
|
6
|
+
* - xs: 1 column (Single column mobile)
|
|
7
|
+
* - sm: 2 columns (2 columns small tablet)
|
|
8
|
+
* - md: 2 columns (2 columns tablet)
|
|
9
|
+
* - lg: 3 columns (3 columns desktop)
|
|
10
|
+
* - xl: 4 columns (4 columns large desktop)
|
|
11
|
+
* - 2xl: 4 columns (4 columns ultra-wide)
|
|
12
|
+
* - 3xl: 5 columns (5 columns large displays)
|
|
13
|
+
*
|
|
14
|
+
* Gap Progression:
|
|
15
|
+
* - xs: 16px, sm: 20px, md: 24px, lg: 28px, xl: 32px, 2xl: 36px, 3xl: 40px
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use client'
|
|
19
|
+
|
|
20
|
+
import * as React from 'react';
|
|
21
|
+
|
|
22
|
+
export interface ResponsiveGridProps {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
|
|
25
|
+
// Column configuration per breakpoint
|
|
26
|
+
columns?: {
|
|
27
|
+
xs?: number;
|
|
28
|
+
sm?: number;
|
|
29
|
+
md?: number;
|
|
30
|
+
lg?: number;
|
|
31
|
+
xl?: number;
|
|
32
|
+
'2xl'?: number;
|
|
33
|
+
'3xl'?: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Gap configuration per breakpoint
|
|
37
|
+
gaps?: {
|
|
38
|
+
xs?: string;
|
|
39
|
+
sm?: string;
|
|
40
|
+
md?: string;
|
|
41
|
+
lg?: string;
|
|
42
|
+
xl?: string;
|
|
43
|
+
'2xl'?: string;
|
|
44
|
+
'3xl'?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Pre-defined grid patterns
|
|
48
|
+
pattern?: 'default' | 'content' | 'dashboard' | 'gallery' | 'cards';
|
|
49
|
+
|
|
50
|
+
// Layout behavior
|
|
51
|
+
autoFit?: boolean; // Use auto-fit instead of fixed columns
|
|
52
|
+
minItemWidth?: string; // Minimum width for auto-fit
|
|
53
|
+
|
|
54
|
+
// Alignment
|
|
55
|
+
alignItems?: 'start' | 'center' | 'end' | 'stretch';
|
|
56
|
+
justifyItems?: 'start' | 'center' | 'end' | 'stretch';
|
|
57
|
+
|
|
58
|
+
// Accessibility
|
|
59
|
+
'aria-label'?: string;
|
|
60
|
+
'data-testid'?: string;
|
|
61
|
+
|
|
62
|
+
// Styling
|
|
63
|
+
className?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Pre-defined grid patterns
|
|
67
|
+
const GRID_PATTERNS = {
|
|
68
|
+
default: {
|
|
69
|
+
columns: { xs: 1, sm: 2, md: 2, lg: 3, xl: 4, '2xl': 4, '3xl': 5 },
|
|
70
|
+
gaps: { xs: '16px', sm: '20px', md: '24px', lg: '28px', xl: '32px', '2xl': '36px', '3xl': '40px' }
|
|
71
|
+
},
|
|
72
|
+
content: {
|
|
73
|
+
columns: { xs: 1, sm: 1, md: 2, lg: 2, xl: 3, '2xl': 3, '3xl': 4 },
|
|
74
|
+
gaps: { xs: '16px', sm: '20px', md: '24px', lg: '28px', xl: '32px', '2xl': '36px', '3xl': '40px' }
|
|
75
|
+
},
|
|
76
|
+
dashboard: {
|
|
77
|
+
columns: { xs: 1, sm: 2, md: 3, lg: 4, xl: 5, '2xl': 6, '3xl': 7 },
|
|
78
|
+
gaps: { xs: '12px', sm: '16px', md: '20px', lg: '24px', xl: '28px', '2xl': '32px', '3xl': '36px' }
|
|
79
|
+
},
|
|
80
|
+
gallery: {
|
|
81
|
+
columns: { xs: 2, sm: 3, md: 4, lg: 5, xl: 6, '2xl': 7, '3xl': 8 },
|
|
82
|
+
gaps: { xs: '8px', sm: '12px', md: '16px', lg: '20px', xl: '24px', '2xl': '28px', '3xl': '32px' }
|
|
83
|
+
},
|
|
84
|
+
cards: {
|
|
85
|
+
columns: { xs: 1, sm: 1, md: 2, lg: 3, xl: 3, '2xl': 4, '3xl': 4 },
|
|
86
|
+
gaps: { xs: '20px', sm: '24px', md: '28px', lg: '32px', xl: '36px', '2xl': '40px', '3xl': '44px' }
|
|
87
|
+
}
|
|
88
|
+
} as const;
|
|
89
|
+
|
|
90
|
+
export const ResponsiveGrid: React.FC<ResponsiveGridProps> = ({
|
|
91
|
+
children,
|
|
92
|
+
columns: customColumns,
|
|
93
|
+
gaps: customGaps,
|
|
94
|
+
pattern = 'default',
|
|
95
|
+
autoFit = false,
|
|
96
|
+
minItemWidth = '250px',
|
|
97
|
+
alignItems = 'stretch',
|
|
98
|
+
justifyItems = 'stretch',
|
|
99
|
+
'aria-label': ariaLabel,
|
|
100
|
+
'data-testid': testId = 'responsive-grid',
|
|
101
|
+
className = '',
|
|
102
|
+
}) => {
|
|
103
|
+
// Get pattern configuration
|
|
104
|
+
const patternConfig = GRID_PATTERNS[pattern];
|
|
105
|
+
|
|
106
|
+
// Use custom configuration or fall back to pattern
|
|
107
|
+
const columns = customColumns || patternConfig.columns;
|
|
108
|
+
const gaps = customGaps || patternConfig.gaps;
|
|
109
|
+
|
|
110
|
+
// Build CSS custom properties for responsive grid
|
|
111
|
+
const gridStyle = React.useMemo(() => {
|
|
112
|
+
const style: React.CSSProperties & Record<string, any> = {};
|
|
113
|
+
|
|
114
|
+
// Set column configurations
|
|
115
|
+
Object.entries(columns).forEach(([breakpoint, columnCount]) => {
|
|
116
|
+
style[`--responsive-grid-columns-${breakpoint}`] = columnCount;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Set gap configurations
|
|
120
|
+
Object.entries(gaps).forEach(([breakpoint, gapValue]) => {
|
|
121
|
+
style[`--responsive-grid-gap-${breakpoint}`] = gapValue;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Set alignment
|
|
125
|
+
style['--responsive-grid-align-items'] = alignItems;
|
|
126
|
+
style['--responsive-grid-justify-items'] = justifyItems;
|
|
127
|
+
|
|
128
|
+
// Set auto-fit configuration
|
|
129
|
+
if (autoFit) {
|
|
130
|
+
style['--responsive-grid-min-item-width'] = minItemWidth;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return style;
|
|
134
|
+
}, [columns, gaps, alignItems, justifyItems, autoFit, minItemWidth]);
|
|
135
|
+
|
|
136
|
+
// Build CSS classes
|
|
137
|
+
const gridClasses = React.useMemo(() => {
|
|
138
|
+
const baseClass = 'responsive-grid';
|
|
139
|
+
const patternClass = `responsive-grid--${pattern}`;
|
|
140
|
+
const autoFitClass = autoFit ? 'responsive-grid--auto-fit' : '';
|
|
141
|
+
|
|
142
|
+
return [baseClass, patternClass, autoFitClass, className]
|
|
143
|
+
.filter(Boolean)
|
|
144
|
+
.join(' ');
|
|
145
|
+
}, [pattern, autoFit, className]);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div
|
|
149
|
+
className={gridClasses}
|
|
150
|
+
style={gridStyle}
|
|
151
|
+
aria-label={ariaLabel}
|
|
152
|
+
data-testid={testId}
|
|
153
|
+
>
|
|
154
|
+
{children}
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Grid Item Component for explicit control
|
|
160
|
+
export interface ResponsiveGridItemProps {
|
|
161
|
+
children: React.ReactNode;
|
|
162
|
+
|
|
163
|
+
// Spanning configuration
|
|
164
|
+
colSpan?: {
|
|
165
|
+
xs?: number;
|
|
166
|
+
sm?: number;
|
|
167
|
+
md?: number;
|
|
168
|
+
lg?: number;
|
|
169
|
+
xl?: number;
|
|
170
|
+
'2xl'?: number;
|
|
171
|
+
'3xl'?: number;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
rowSpan?: {
|
|
175
|
+
xs?: number;
|
|
176
|
+
sm?: number;
|
|
177
|
+
md?: number;
|
|
178
|
+
lg?: number;
|
|
179
|
+
xl?: number;
|
|
180
|
+
'2xl'?: number;
|
|
181
|
+
'3xl'?: number;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Position configuration
|
|
185
|
+
colStart?: {
|
|
186
|
+
xs?: number;
|
|
187
|
+
sm?: number;
|
|
188
|
+
md?: number;
|
|
189
|
+
lg?: number;
|
|
190
|
+
xl?: number;
|
|
191
|
+
'2xl'?: number;
|
|
192
|
+
'3xl'?: number;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
rowStart?: {
|
|
196
|
+
xs?: number;
|
|
197
|
+
sm?: number;
|
|
198
|
+
md?: number;
|
|
199
|
+
lg?: number;
|
|
200
|
+
xl?: number;
|
|
201
|
+
'2xl'?: number;
|
|
202
|
+
'3xl'?: number;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Alignment (overrides grid defaults)
|
|
206
|
+
alignSelf?: 'auto' | 'start' | 'center' | 'end' | 'stretch';
|
|
207
|
+
justifySelf?: 'auto' | 'start' | 'center' | 'end' | 'stretch';
|
|
208
|
+
|
|
209
|
+
// Styling
|
|
210
|
+
className?: string;
|
|
211
|
+
'data-testid'?: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const ResponsiveGridItem: React.FC<ResponsiveGridItemProps> = ({
|
|
215
|
+
children,
|
|
216
|
+
colSpan,
|
|
217
|
+
rowSpan,
|
|
218
|
+
colStart,
|
|
219
|
+
rowStart,
|
|
220
|
+
alignSelf = 'auto',
|
|
221
|
+
justifySelf = 'auto',
|
|
222
|
+
className = '',
|
|
223
|
+
'data-testid': testId = 'responsive-grid-item',
|
|
224
|
+
}) => {
|
|
225
|
+
// Build CSS custom properties for grid item
|
|
226
|
+
const itemStyle = React.useMemo(() => {
|
|
227
|
+
const style: React.CSSProperties & Record<string, any> = {};
|
|
228
|
+
|
|
229
|
+
// Set column span configurations
|
|
230
|
+
if (colSpan) {
|
|
231
|
+
Object.entries(colSpan).forEach(([breakpoint, span]) => {
|
|
232
|
+
style[`--responsive-grid-item-col-span-${breakpoint}`] = span;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Set row span configurations
|
|
237
|
+
if (rowSpan) {
|
|
238
|
+
Object.entries(rowSpan).forEach(([breakpoint, span]) => {
|
|
239
|
+
style[`--responsive-grid-item-row-span-${breakpoint}`] = span;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Set column start configurations
|
|
244
|
+
if (colStart) {
|
|
245
|
+
Object.entries(colStart).forEach(([breakpoint, start]) => {
|
|
246
|
+
style[`--responsive-grid-item-col-start-${breakpoint}`] = start;
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Set row start configurations
|
|
251
|
+
if (rowStart) {
|
|
252
|
+
Object.entries(rowStart).forEach(([breakpoint, start]) => {
|
|
253
|
+
style[`--responsive-grid-item-row-start-${breakpoint}`] = start;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Set alignment
|
|
258
|
+
style['--responsive-grid-item-align-self'] = alignSelf;
|
|
259
|
+
style['--responsive-grid-item-justify-self'] = justifySelf;
|
|
260
|
+
|
|
261
|
+
return style;
|
|
262
|
+
}, [colSpan, rowSpan, colStart, rowStart, alignSelf, justifySelf]);
|
|
263
|
+
|
|
264
|
+
// Build CSS classes
|
|
265
|
+
const itemClasses = React.useMemo(() => {
|
|
266
|
+
const baseClass = 'responsive-grid-item';
|
|
267
|
+
const spanClass = colSpan || rowSpan ? 'responsive-grid-item--has-span' : '';
|
|
268
|
+
const positionClass = colStart || rowStart ? 'responsive-grid-item--has-position' : '';
|
|
269
|
+
|
|
270
|
+
return [baseClass, spanClass, positionClass, className]
|
|
271
|
+
.filter(Boolean)
|
|
272
|
+
.join(' ');
|
|
273
|
+
}, [colSpan, rowSpan, colStart, rowStart, className]);
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<div
|
|
277
|
+
className={itemClasses}
|
|
278
|
+
style={itemStyle}
|
|
279
|
+
data-testid={testId}
|
|
280
|
+
>
|
|
281
|
+
{children}
|
|
282
|
+
</div>
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
export default ResponsiveGrid;
|
|
@@ -14,6 +14,7 @@ export interface AdaptiveLayoutProps {
|
|
|
14
14
|
description?: string
|
|
15
15
|
showHeader?: boolean
|
|
16
16
|
showSidebar?: boolean
|
|
17
|
+
showFooter?: boolean
|
|
17
18
|
enableTouchOptimization?: boolean
|
|
18
19
|
enablePerformanceMonitoring?: boolean
|
|
19
20
|
enableAdvancedFeatures?: boolean
|
|
@@ -26,6 +27,7 @@ export const AdaptiveLayout: React.FC<AdaptiveLayoutProps> = ({
|
|
|
26
27
|
description = 'Automatically adapts to different device types with optimal layouts',
|
|
27
28
|
showHeader = true,
|
|
28
29
|
showSidebar = true,
|
|
30
|
+
showFooter = false,
|
|
29
31
|
enableTouchOptimization = true,
|
|
30
32
|
enablePerformanceMonitoring = true,
|
|
31
33
|
enableAdvancedFeatures = true,
|
|
@@ -73,7 +75,7 @@ export const AdaptiveLayout: React.FC<AdaptiveLayoutProps> = ({
|
|
|
73
75
|
return (
|
|
74
76
|
<MobileLayout
|
|
75
77
|
{...commonProps}
|
|
76
|
-
showFooter={
|
|
78
|
+
showFooter={showFooter}
|
|
77
79
|
enableTouchOptimization={enableTouchOptimization}
|
|
78
80
|
>
|
|
79
81
|
{children}
|
|
@@ -9,6 +9,7 @@ export { Tabs } from './tabs';
|
|
|
9
9
|
export { Stepper } from './stepper';
|
|
10
10
|
export { Menu } from './menu';
|
|
11
11
|
export { Sidebar } from './sidebar';
|
|
12
|
+
export { ProgressiveNavigation } from './progressive-navigation';
|
|
12
13
|
|
|
13
14
|
// User Menu Components
|
|
14
15
|
export { UserMenu } from './user-menu.js';
|
|
@@ -31,6 +32,7 @@ export type {
|
|
|
31
32
|
StepAction,
|
|
32
33
|
MenuProps,
|
|
33
34
|
SidebarProps,
|
|
35
|
+
ProgressiveNavigationProps,
|
|
34
36
|
NavigationState,
|
|
35
37
|
NavigationContextValue,
|
|
36
38
|
NavigationAction,
|