@shohojdhara/atomix 0.2.9 → 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 +4 -0
- package/dist/atomix.css +309 -105
- package/dist/atomix.min.css +3 -5
- package/dist/index.d.ts +807 -51
- package/dist/index.esm.js +16367 -16405
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +16277 -16330
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/themes/applemix.css +309 -105
- package/dist/themes/applemix.min.css +5 -7
- package/dist/themes/boomdevs.css +202 -10
- package/dist/themes/boomdevs.min.css +3 -5
- package/dist/themes/esrar.css +309 -105
- package/dist/themes/esrar.min.css +4 -6
- package/dist/themes/flashtrade.css +310 -105
- package/dist/themes/flashtrade.min.css +5 -7
- package/dist/themes/mashroom.css +300 -96
- package/dist/themes/mashroom.min.css +4 -6
- package/dist/themes/shaj-default.css +300 -96
- package/dist/themes/shaj-default.min.css +4 -6
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +21 -32
- package/src/components/AtomixGlass/AtomixGlass.tsx +55 -42
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +205 -57
- package/src/components/AtomixGlass/GlassFilter.tsx +22 -8
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +221 -0
- package/src/components/AtomixGlass/atomixGLass.old.tsx +0 -3
- package/src/components/AtomixGlass/shader-utils.ts +8 -0
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +319 -100
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +601 -105
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +30 -12
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +173 -38
- package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +18 -18
- package/src/components/AtomixGlass/stories/shared-components.tsx +27 -5
- package/src/components/Breadcrumb/Breadcrumb.tsx +8 -3
- package/src/components/Button/Button.tsx +62 -17
- package/src/components/Callout/Callout.test.tsx +8 -14
- package/src/components/Card/Card.tsx +103 -1
- package/src/components/Card/index.ts +3 -2
- package/src/components/Footer/Footer.stories.tsx +1 -2
- package/src/components/Footer/Footer.tsx +0 -5
- package/src/components/Footer/FooterLink.tsx +3 -2
- package/src/components/Footer/FooterSection.tsx +0 -7
- package/src/components/Icon/index.ts +1 -1
- package/src/components/Modal/Modal.stories.tsx +29 -38
- package/src/components/Modal/Modal.tsx +4 -4
- package/src/components/Navigation/Nav/NavItem.tsx +8 -3
- package/src/components/Navigation/SideMenu/SideMenu.tsx +49 -41
- package/src/components/Navigation/SideMenu/SideMenuItem.tsx +63 -19
- package/src/components/Popover/Popover.tsx +1 -1
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +977 -400
- package/src/components/VideoPlayer/VideoPlayer.tsx +1 -6
- package/src/lib/composables/shared-mouse-tracker.ts +133 -0
- package/src/lib/composables/useAtomixGlass.ts +303 -115
- package/src/lib/theme/ThemeManager.integration.test.ts +124 -0
- package/src/lib/theme/ThemeManager.stories.tsx +13 -13
- package/src/lib/theme/ThemeManager.test.ts +4 -0
- package/src/lib/theme/ThemeManager.ts +203 -59
- package/src/lib/theme/ThemeProvider.tsx +183 -33
- package/src/lib/theme/composeTheme.ts +375 -0
- package/src/lib/theme/createTheme.test.ts +475 -0
- package/src/lib/theme/createTheme.ts +510 -0
- package/src/lib/theme/generateCSSVariables.ts +713 -0
- package/src/lib/theme/index.ts +67 -0
- package/src/lib/theme/themeUtils.ts +333 -0
- package/src/lib/theme/types.ts +337 -8
- package/src/lib/theme/useTheme.test.tsx +2 -1
- package/src/lib/theme/useTheme.ts +6 -22
- package/src/lib/types/components.ts +152 -57
- package/src/styles/01-settings/_index.scss +2 -2
- package/src/styles/01-settings/_settings.badge.scss +2 -2
- package/src/styles/01-settings/_settings.border-radius.scss +1 -1
- package/src/styles/01-settings/{_settings.maps.scss → _settings.design-tokens.scss} +163 -49
- package/src/styles/01-settings/_settings.modal.scss +1 -1
- package/src/styles/01-settings/_settings.spacing.scss +14 -13
- package/src/styles/03-generic/_generic.root.scss +131 -50
- package/src/styles/05-objects/_objects.block.scss +1 -1
- package/src/styles/06-components/_components.atomix-glass.scss +20 -22
- package/src/styles/06-components/_components.badge.scss +2 -2
- package/src/styles/06-components/_components.button.scss +1 -1
- package/src/styles/06-components/_components.callout.scss +1 -1
- package/src/styles/06-components/_components.card.scss +74 -2
- package/src/styles/06-components/_components.chart.scss +1 -1
- package/src/styles/06-components/_components.dropdown.scss +6 -0
- package/src/styles/06-components/_components.footer.scss +1 -1
- package/src/styles/06-components/_components.list-group.scss +1 -1
- package/src/styles/06-components/_components.list.scss +1 -1
- package/src/styles/06-components/_components.menu.scss +1 -1
- package/src/styles/06-components/_components.messages.scss +1 -1
- package/src/styles/06-components/_components.modal.scss +7 -2
- package/src/styles/06-components/_components.navbar.scss +1 -1
- package/src/styles/06-components/_components.popover.scss +10 -0
- package/src/styles/06-components/_components.product-review.scss +1 -1
- package/src/styles/06-components/_components.progress.scss +1 -1
- package/src/styles/06-components/_components.rating.scss +1 -1
- package/src/styles/06-components/_components.spinner.scss +1 -1
- package/src/styles/99-utilities/_utilities.background.scss +1 -1
- package/src/styles/99-utilities/_utilities.border.scss +1 -1
- package/src/styles/99-utilities/_utilities.link.scss +1 -1
- package/src/styles/99-utilities/_utilities.text.scss +1 -1
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createTheme - Create a theme object with computed values
|
|
3
|
+
*
|
|
4
|
+
* Similar to Material-UI's createTheme, this function accepts theme configuration
|
|
5
|
+
* options and returns a complete theme object with computed values.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const theme = createTheme({
|
|
10
|
+
* palette: {
|
|
11
|
+
* primary: { main: '#7AFFD7' },
|
|
12
|
+
* secondary: { main: '#FF5733' },
|
|
13
|
+
* },
|
|
14
|
+
* typography: {
|
|
15
|
+
* fontFamily: 'Inter, sans-serif',
|
|
16
|
+
* },
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type {
|
|
22
|
+
Theme,
|
|
23
|
+
ThemeOptions,
|
|
24
|
+
PaletteColor,
|
|
25
|
+
PaletteOptions,
|
|
26
|
+
TypographyOptions,
|
|
27
|
+
SpacingOptions,
|
|
28
|
+
SpacingFunction,
|
|
29
|
+
BreakpointsOptions,
|
|
30
|
+
ShadowOptions,
|
|
31
|
+
TransitionOptions,
|
|
32
|
+
ZIndexOptions,
|
|
33
|
+
BorderRadiusOptions,
|
|
34
|
+
} from './types';
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Default Theme Values
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
const DEFAULT_PALETTE: Theme['palette'] = {
|
|
41
|
+
primary: {
|
|
42
|
+
main: '#7AFFD7',
|
|
43
|
+
light: '#A3FFE5',
|
|
44
|
+
dark: '#00E6C3',
|
|
45
|
+
contrastText: '#000000',
|
|
46
|
+
},
|
|
47
|
+
secondary: {
|
|
48
|
+
main: '#FF5733',
|
|
49
|
+
light: '#FF8A65',
|
|
50
|
+
dark: '#C62828',
|
|
51
|
+
contrastText: '#FFFFFF',
|
|
52
|
+
},
|
|
53
|
+
error: {
|
|
54
|
+
main: '#F44336',
|
|
55
|
+
light: '#E57373',
|
|
56
|
+
dark: '#D32F2F',
|
|
57
|
+
contrastText: '#FFFFFF',
|
|
58
|
+
},
|
|
59
|
+
warning: {
|
|
60
|
+
main: '#FF9800',
|
|
61
|
+
light: '#FFB74D',
|
|
62
|
+
dark: '#F57C00',
|
|
63
|
+
contrastText: '#000000',
|
|
64
|
+
},
|
|
65
|
+
info: {
|
|
66
|
+
main: '#2196F3',
|
|
67
|
+
light: '#64B5F6',
|
|
68
|
+
dark: '#1976D2',
|
|
69
|
+
contrastText: '#FFFFFF',
|
|
70
|
+
},
|
|
71
|
+
success: {
|
|
72
|
+
main: '#4CAF50',
|
|
73
|
+
light: '#81C784',
|
|
74
|
+
dark: '#388E3C',
|
|
75
|
+
contrastText: '#FFFFFF',
|
|
76
|
+
},
|
|
77
|
+
background: {
|
|
78
|
+
default: '#FFFFFF',
|
|
79
|
+
paper: '#F5F5F5',
|
|
80
|
+
subtle: '#FAFAFA',
|
|
81
|
+
},
|
|
82
|
+
text: {
|
|
83
|
+
primary: 'rgba(0, 0, 0, 0.87)',
|
|
84
|
+
secondary: 'rgba(0, 0, 0, 0.6)',
|
|
85
|
+
disabled: 'rgba(0, 0, 0, 0.38)',
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const DEFAULT_TYPOGRAPHY: Theme['typography'] = {
|
|
90
|
+
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
|
|
91
|
+
fontSize: 14,
|
|
92
|
+
fontWeightLight: 300,
|
|
93
|
+
fontWeightRegular: 400,
|
|
94
|
+
fontWeightMedium: 500,
|
|
95
|
+
fontWeightSemiBold: 600,
|
|
96
|
+
fontWeightBold: 700,
|
|
97
|
+
h1: {
|
|
98
|
+
fontSize: '2.5rem',
|
|
99
|
+
fontWeight: 700,
|
|
100
|
+
lineHeight: 1.2,
|
|
101
|
+
letterSpacing: '-0.01562em',
|
|
102
|
+
},
|
|
103
|
+
h2: {
|
|
104
|
+
fontSize: '2rem',
|
|
105
|
+
fontWeight: 700,
|
|
106
|
+
lineHeight: 1.3,
|
|
107
|
+
letterSpacing: '-0.00833em',
|
|
108
|
+
},
|
|
109
|
+
h3: {
|
|
110
|
+
fontSize: '1.75rem',
|
|
111
|
+
fontWeight: 600,
|
|
112
|
+
lineHeight: 1.4,
|
|
113
|
+
letterSpacing: '0em',
|
|
114
|
+
},
|
|
115
|
+
h4: {
|
|
116
|
+
fontSize: '1.5rem',
|
|
117
|
+
fontWeight: 600,
|
|
118
|
+
lineHeight: 1.4,
|
|
119
|
+
letterSpacing: '0.00735em',
|
|
120
|
+
},
|
|
121
|
+
h5: {
|
|
122
|
+
fontSize: '1.25rem',
|
|
123
|
+
fontWeight: 600,
|
|
124
|
+
lineHeight: 1.5,
|
|
125
|
+
letterSpacing: '0em',
|
|
126
|
+
},
|
|
127
|
+
h6: {
|
|
128
|
+
fontSize: '1rem',
|
|
129
|
+
fontWeight: 600,
|
|
130
|
+
lineHeight: 1.6,
|
|
131
|
+
letterSpacing: '0.0075em',
|
|
132
|
+
},
|
|
133
|
+
body1: {
|
|
134
|
+
fontSize: '1rem',
|
|
135
|
+
fontWeight: 400,
|
|
136
|
+
lineHeight: 1.5,
|
|
137
|
+
},
|
|
138
|
+
body2: {
|
|
139
|
+
fontSize: '0.875rem',
|
|
140
|
+
fontWeight: 400,
|
|
141
|
+
lineHeight: 1.43,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const DEFAULT_BREAKPOINTS: Theme['breakpoints'] = {
|
|
146
|
+
values: {
|
|
147
|
+
xs: 0,
|
|
148
|
+
sm: 600,
|
|
149
|
+
md: 960,
|
|
150
|
+
lg: 1280,
|
|
151
|
+
xl: 1920,
|
|
152
|
+
},
|
|
153
|
+
unit: 'px',
|
|
154
|
+
up: (key) => {
|
|
155
|
+
const value = typeof key === 'number' ? key : DEFAULT_BREAKPOINTS.values[key] || 0;
|
|
156
|
+
return `@media (min-width:${value}${DEFAULT_BREAKPOINTS.unit})`;
|
|
157
|
+
},
|
|
158
|
+
down: (key) => {
|
|
159
|
+
const value = typeof key === 'number' ? key : DEFAULT_BREAKPOINTS.values[key] || 0;
|
|
160
|
+
return `@media (max-width:${value - 0.05}${DEFAULT_BREAKPOINTS.unit})`;
|
|
161
|
+
},
|
|
162
|
+
between: (start, end) => {
|
|
163
|
+
const startValue = typeof start === 'number' ? start : DEFAULT_BREAKPOINTS.values[start] || 0;
|
|
164
|
+
const endValue = typeof end === 'number' ? end : DEFAULT_BREAKPOINTS.values[end] || 0;
|
|
165
|
+
return `@media (min-width:${startValue}${DEFAULT_BREAKPOINTS.unit}) and (max-width:${endValue - 0.05}${DEFAULT_BREAKPOINTS.unit})`;
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const DEFAULT_SHADOWS: Theme['shadows'] = {
|
|
170
|
+
xs: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
|
171
|
+
sm: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
|
|
172
|
+
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
|
|
173
|
+
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
|
174
|
+
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
|
175
|
+
inset: 'inset 0 1px 3px 0 rgba(0, 0, 0, 0.1), inset 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const DEFAULT_TRANSITIONS: Theme['transitions'] = {
|
|
179
|
+
duration: {
|
|
180
|
+
shortest: 150,
|
|
181
|
+
shorter: 200,
|
|
182
|
+
short: 250,
|
|
183
|
+
standard: 300,
|
|
184
|
+
complex: 375,
|
|
185
|
+
enteringScreen: 225,
|
|
186
|
+
leavingScreen: 195,
|
|
187
|
+
},
|
|
188
|
+
easing: {
|
|
189
|
+
easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
190
|
+
easeOut: 'cubic-bezier(0.0, 0, 0.2, 1)',
|
|
191
|
+
easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
|
|
192
|
+
sharp: 'cubic-bezier(0.4, 0, 0.6, 1)',
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const DEFAULT_ZINDEX: Theme['zIndex'] = {
|
|
197
|
+
mobileStepper: 1000,
|
|
198
|
+
speedDial: 1050,
|
|
199
|
+
appBar: 1020,
|
|
200
|
+
drawer: 1070,
|
|
201
|
+
modal: 1040,
|
|
202
|
+
snackbar: 1080,
|
|
203
|
+
tooltip: 1060,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const DEFAULT_BORDER_RADIUS: Theme['borderRadius'] = {
|
|
207
|
+
base: '0.5rem', // 8px (spacing-2)
|
|
208
|
+
sm: '0.25rem', // 4px (spacing-1)
|
|
209
|
+
md: '0.25rem', // 4px (spacing-1)
|
|
210
|
+
lg: '0.625rem', // 10px (spacing-2.5)
|
|
211
|
+
xl: '0.75rem', // 12px (spacing-3)
|
|
212
|
+
xxl: '1rem', // 16px (spacing-4)
|
|
213
|
+
'3xl': '1.5rem', // 24px (spacing-6)
|
|
214
|
+
'4xl': '2rem', // 32px (spacing-8)
|
|
215
|
+
pill: '50rem', // 800px (spacing-200)
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// Helper Functions
|
|
220
|
+
// ============================================================================
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Deep merge two objects
|
|
224
|
+
*/
|
|
225
|
+
function deepMerge<T extends Record<string, any>>(target: T, ...sources: Partial<T>[]): T {
|
|
226
|
+
if (!sources.length) return target;
|
|
227
|
+
const source = sources.shift();
|
|
228
|
+
|
|
229
|
+
if (isObject(target) && isObject(source)) {
|
|
230
|
+
for (const key in source) {
|
|
231
|
+
if (!source.hasOwnProperty(key)) continue;
|
|
232
|
+
const sourceValue = source[key];
|
|
233
|
+
if (isObject(sourceValue)) {
|
|
234
|
+
if (!target[key]) Object.assign(target, { [key]: {} });
|
|
235
|
+
deepMerge(target[key] as any, sourceValue as any);
|
|
236
|
+
} else {
|
|
237
|
+
Object.assign(target, { [key]: sourceValue });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return deepMerge(target, ...sources);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Check if value is an object
|
|
247
|
+
*/
|
|
248
|
+
function isObject(item: any): item is Record<string, any> {
|
|
249
|
+
return item && typeof item === 'object' && !Array.isArray(item);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Convert hex color to RGB
|
|
254
|
+
*/
|
|
255
|
+
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
256
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
257
|
+
if (!result || !result[1] || !result[2] || !result[3]) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
r: parseInt(result[1], 16),
|
|
262
|
+
g: parseInt(result[2], 16),
|
|
263
|
+
b: parseInt(result[3], 16),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Calculate relative luminance
|
|
269
|
+
*/
|
|
270
|
+
function getLuminance(color: string): number {
|
|
271
|
+
const rgb = hexToRgb(color);
|
|
272
|
+
if (!rgb) return 0;
|
|
273
|
+
|
|
274
|
+
const { r, g, b } = rgb;
|
|
275
|
+
const [rs, gs, bs] = [r ?? 0, g ?? 0, b ?? 0].map((c) => {
|
|
276
|
+
const val = c / 255;
|
|
277
|
+
return val <= 0.03928 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return 0.2126 * (rs ?? 0) + 0.7152 * (gs ?? 0) + 0.0722 * (bs ?? 0);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get contrast text color (black or white) based on background
|
|
285
|
+
*/
|
|
286
|
+
function getContrastText(background: string): string {
|
|
287
|
+
const luminance = getLuminance(background);
|
|
288
|
+
return luminance > 0.5 ? '#000000' : '#FFFFFF';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Lighten a color
|
|
293
|
+
*/
|
|
294
|
+
function lighten(color: string, amount: number = 0.2): string {
|
|
295
|
+
const rgb = hexToRgb(color);
|
|
296
|
+
if (!rgb) return color;
|
|
297
|
+
|
|
298
|
+
const { r, g, b } = rgb;
|
|
299
|
+
const lightenValue = (val: number) => Math.min(255, Math.round(val + (255 - val) * amount));
|
|
300
|
+
|
|
301
|
+
const newR = lightenValue(r ?? 0).toString(16).padStart(2, '0');
|
|
302
|
+
const newG = lightenValue(g ?? 0).toString(16).padStart(2, '0');
|
|
303
|
+
const newB = lightenValue(b ?? 0).toString(16).padStart(2, '0');
|
|
304
|
+
|
|
305
|
+
return `#${newR}${newG}${newB}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Darken a color
|
|
310
|
+
*/
|
|
311
|
+
function darken(color: string, amount: number = 0.2): string {
|
|
312
|
+
const rgb = hexToRgb(color);
|
|
313
|
+
if (!rgb) return color;
|
|
314
|
+
|
|
315
|
+
const { r, g, b } = rgb;
|
|
316
|
+
const darkenValue = (val: number) => Math.max(0, Math.round(val * (1 - amount)));
|
|
317
|
+
|
|
318
|
+
const newR = darkenValue(r ?? 0).toString(16).padStart(2, '0');
|
|
319
|
+
const newG = darkenValue(g ?? 0).toString(16).padStart(2, '0');
|
|
320
|
+
const newB = darkenValue(b ?? 0).toString(16).padStart(2, '0');
|
|
321
|
+
|
|
322
|
+
return `#${newR}${newG}${newB}`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Create a complete palette color from partial configuration
|
|
327
|
+
*/
|
|
328
|
+
function createPaletteColor(color: Partial<PaletteColor> | string): PaletteColor {
|
|
329
|
+
if (typeof color === 'string') {
|
|
330
|
+
return {
|
|
331
|
+
main: color,
|
|
332
|
+
light: lighten(color),
|
|
333
|
+
dark: darken(color),
|
|
334
|
+
contrastText: getContrastText(color),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
main: color.main || '#000000',
|
|
340
|
+
light: color.light || lighten(color.main || '#000000'),
|
|
341
|
+
dark: color.dark || darken(color.main || '#000000'),
|
|
342
|
+
contrastText: color.contrastText || getContrastText(color.main || '#000000'),
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Create spacing function
|
|
348
|
+
*/
|
|
349
|
+
function createSpacing(spacingInput: SpacingOptions = 4): SpacingFunction {
|
|
350
|
+
// If it's already a function, return it
|
|
351
|
+
if (typeof spacingInput === 'function') {
|
|
352
|
+
return spacingInput;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// If it's a number, create a function that multiplies by that number
|
|
356
|
+
if (typeof spacingInput === 'number') {
|
|
357
|
+
return (...values: number[]) => {
|
|
358
|
+
if (values.length === 0) return '0px';
|
|
359
|
+
return values.map((value) => `${value * spacingInput}px`).join(' ');
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// If it's an array, use it as a scale
|
|
364
|
+
if (Array.isArray(spacingInput)) {
|
|
365
|
+
return (...values: number[]) => {
|
|
366
|
+
if (values.length === 0) return '0px';
|
|
367
|
+
return values.map((value) => `${spacingInput[value] || value}px`).join(' ');
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Default to 4px base
|
|
372
|
+
return (...values: number[]) => {
|
|
373
|
+
if (values.length === 0) return '0px';
|
|
374
|
+
return values.map((value) => `${value * 4}px`).join(' ');
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Create breakpoints object
|
|
380
|
+
*/
|
|
381
|
+
function createBreakpoints(breakpointsInput?: BreakpointsOptions): Theme['breakpoints'] {
|
|
382
|
+
const values = {
|
|
383
|
+
xs: 0,
|
|
384
|
+
sm: 600,
|
|
385
|
+
md: 960,
|
|
386
|
+
lg: 1280,
|
|
387
|
+
xl: 1920,
|
|
388
|
+
...breakpointsInput?.values,
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const unit = breakpointsInput?.unit || 'px';
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
values,
|
|
395
|
+
unit,
|
|
396
|
+
up: (key) => {
|
|
397
|
+
const value = typeof key === 'number' ? key : (values[key as keyof typeof values] ?? 0);
|
|
398
|
+
return `@media (min-width:${value}${unit})`;
|
|
399
|
+
},
|
|
400
|
+
down: (key) => {
|
|
401
|
+
const value = typeof key === 'number' ? key : (values[key as keyof typeof values] ?? 0);
|
|
402
|
+
return `@media (max-width:${value - 0.05}${unit})`;
|
|
403
|
+
},
|
|
404
|
+
between: (start, end) => {
|
|
405
|
+
const startValue = typeof start === 'number' ? start : (values[start as keyof typeof values] ?? 0);
|
|
406
|
+
const endValue = typeof end === 'number' ? end : (values[end as keyof typeof values] ?? 0);
|
|
407
|
+
return `@media (min-width:${startValue}${unit}) and (max-width:${endValue - 0.05}${unit})`;
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ============================================================================
|
|
413
|
+
// Main createTheme Function
|
|
414
|
+
// ============================================================================
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Create a theme object with computed values
|
|
418
|
+
*
|
|
419
|
+
* @param options - Theme configuration options
|
|
420
|
+
* @returns Complete theme object
|
|
421
|
+
*/
|
|
422
|
+
export function createTheme(...options: ThemeOptions[]): Theme {
|
|
423
|
+
// Merge all options
|
|
424
|
+
const mergedOptions = options.reduce((acc, option) => deepMerge(acc, option), {} as ThemeOptions);
|
|
425
|
+
|
|
426
|
+
// Create palette
|
|
427
|
+
const palette: Theme['palette'] = {
|
|
428
|
+
primary: createPaletteColor(mergedOptions.palette?.primary || DEFAULT_PALETTE.primary),
|
|
429
|
+
secondary: createPaletteColor(mergedOptions.palette?.secondary || DEFAULT_PALETTE.secondary),
|
|
430
|
+
error: createPaletteColor(mergedOptions.palette?.error || DEFAULT_PALETTE.error),
|
|
431
|
+
warning: createPaletteColor(mergedOptions.palette?.warning || DEFAULT_PALETTE.warning),
|
|
432
|
+
info: createPaletteColor(mergedOptions.palette?.info || DEFAULT_PALETTE.info),
|
|
433
|
+
success: createPaletteColor(mergedOptions.palette?.success || DEFAULT_PALETTE.success),
|
|
434
|
+
background: {
|
|
435
|
+
default: mergedOptions.palette?.background?.default || DEFAULT_PALETTE.background.default,
|
|
436
|
+
paper: mergedOptions.palette?.background?.paper || DEFAULT_PALETTE.background.paper,
|
|
437
|
+
subtle: mergedOptions.palette?.background?.subtle || DEFAULT_PALETTE.background.subtle,
|
|
438
|
+
},
|
|
439
|
+
text: {
|
|
440
|
+
primary: mergedOptions.palette?.text?.primary || DEFAULT_PALETTE.text.primary,
|
|
441
|
+
secondary: mergedOptions.palette?.text?.secondary || DEFAULT_PALETTE.text.secondary,
|
|
442
|
+
disabled: mergedOptions.palette?.text?.disabled || DEFAULT_PALETTE.text.disabled,
|
|
443
|
+
},
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// Create typography
|
|
447
|
+
const typography: Theme['typography'] = deepMerge(
|
|
448
|
+
{ ...DEFAULT_TYPOGRAPHY } as any,
|
|
449
|
+
(mergedOptions.typography || {}) as any
|
|
450
|
+
) as Theme['typography'];
|
|
451
|
+
|
|
452
|
+
// Create spacing
|
|
453
|
+
const spacing = createSpacing(mergedOptions.spacing);
|
|
454
|
+
|
|
455
|
+
// Create breakpoints
|
|
456
|
+
const breakpoints = createBreakpoints(mergedOptions.breakpoints);
|
|
457
|
+
|
|
458
|
+
// Create shadows
|
|
459
|
+
const shadows: Theme['shadows'] = deepMerge({ ...DEFAULT_SHADOWS }, mergedOptions.shadows || {});
|
|
460
|
+
|
|
461
|
+
// Create transitions
|
|
462
|
+
const transitions: Theme['transitions'] = deepMerge(
|
|
463
|
+
{ ...DEFAULT_TRANSITIONS },
|
|
464
|
+
mergedOptions.transitions || {}
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
// Create z-index
|
|
468
|
+
const zIndex: Theme['zIndex'] = deepMerge({ ...DEFAULT_ZINDEX }, mergedOptions.zIndex || {});
|
|
469
|
+
|
|
470
|
+
// Create border radius
|
|
471
|
+
const borderRadius: Theme['borderRadius'] = deepMerge(
|
|
472
|
+
{ ...DEFAULT_BORDER_RADIUS },
|
|
473
|
+
mergedOptions.borderRadius || {}
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
// Create theme object
|
|
477
|
+
const theme: Theme = {
|
|
478
|
+
// Metadata
|
|
479
|
+
name: mergedOptions.name || 'Custom Theme',
|
|
480
|
+
class: mergedOptions.class,
|
|
481
|
+
description: mergedOptions.description,
|
|
482
|
+
author: mergedOptions.author,
|
|
483
|
+
version: mergedOptions.version || '1.0.0',
|
|
484
|
+
tags: mergedOptions.tags,
|
|
485
|
+
supportsDarkMode: mergedOptions.supportsDarkMode,
|
|
486
|
+
status: mergedOptions.status || 'experimental',
|
|
487
|
+
a11y: mergedOptions.a11y,
|
|
488
|
+
color: mergedOptions.color || palette.primary.main,
|
|
489
|
+
features: mergedOptions.features,
|
|
490
|
+
dependencies: mergedOptions.dependencies,
|
|
491
|
+
|
|
492
|
+
// Theme configuration
|
|
493
|
+
palette,
|
|
494
|
+
typography,
|
|
495
|
+
spacing,
|
|
496
|
+
breakpoints,
|
|
497
|
+
shadows,
|
|
498
|
+
transitions,
|
|
499
|
+
zIndex,
|
|
500
|
+
borderRadius,
|
|
501
|
+
custom: mergedOptions.custom || {},
|
|
502
|
+
|
|
503
|
+
// Mark as JS theme
|
|
504
|
+
__isJSTheme: true,
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
return theme;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export default createTheme;
|