@idealyst/theme 1.1.7 → 1.1.9
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/package.json +30 -1
- package/src/babel/index.ts +9 -0
- package/src/babel/plugin.js +883 -0
- package/src/babel/plugin.ts +187 -0
- package/src/babel/runtime.ts +94 -0
- package/src/babel/theme-analyzer.js +357 -0
- package/src/breakpoints.ts +112 -0
- package/src/builder.ts +90 -18
- package/src/componentStyles.ts +93 -0
- package/src/config/cli.ts +95 -0
- package/src/config/generator.ts +817 -0
- package/src/config/index.ts +10 -0
- package/src/config/types.ts +112 -0
- package/src/darkTheme.ts +27 -18
- package/src/extensions.ts +110 -0
- package/src/index.ts +21 -4
- package/src/lightTheme.ts +14 -5
- package/src/responsive.ts +123 -0
- package/src/styleBuilder.ts +112 -0
- package/src/theme/breakpoint.ts +30 -0
- package/src/theme/extensions.ts +13 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/structures.ts +7 -0
- package/src/theme/surface.ts +1 -1
- package/src/unistyles.ts +11 -15
- package/src/useResponsiveStyle.ts +282 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { UnistylesRuntime } from 'react-native-unistyles';
|
|
2
|
+
import { Breakpoint, BreakpointsRecord } from './theme/breakpoint';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the current active breakpoint name.
|
|
6
|
+
*
|
|
7
|
+
* @returns The current breakpoint name, or undefined if not available
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const current = getCurrentBreakpoint();
|
|
12
|
+
* console.log(current); // 'md'
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function getCurrentBreakpoint(): Breakpoint | undefined {
|
|
16
|
+
return UnistylesRuntime.breakpoint as Breakpoint | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get all registered breakpoints and their values.
|
|
21
|
+
*
|
|
22
|
+
* @returns Object mapping breakpoint names to pixel values
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const breakpoints = getBreakpoints();
|
|
27
|
+
* console.log(breakpoints); // { xs: 0, sm: 576, md: 768, lg: 992, xl: 1200 }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function getBreakpoints(): BreakpointsRecord {
|
|
31
|
+
return UnistylesRuntime.breakpoints as BreakpointsRecord;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if the current viewport is at or above a specific breakpoint.
|
|
36
|
+
*
|
|
37
|
+
* @param breakpoint - The breakpoint to check against
|
|
38
|
+
* @returns True if the current viewport width is >= the breakpoint value
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* if (isBreakpointUp('md')) {
|
|
43
|
+
* // Tablet or larger
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function isBreakpointUp(breakpoint: Breakpoint): boolean {
|
|
48
|
+
const breakpoints = getBreakpoints();
|
|
49
|
+
const screenWidth = UnistylesRuntime.screen.width;
|
|
50
|
+
return screenWidth >= breakpoints[breakpoint];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if the current viewport is below a specific breakpoint.
|
|
55
|
+
*
|
|
56
|
+
* @param breakpoint - The breakpoint to check against
|
|
57
|
+
* @returns True if the current viewport width is < the breakpoint value
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* if (isBreakpointDown('md')) {
|
|
62
|
+
* // Mobile only (below tablet)
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function isBreakpointDown(breakpoint: Breakpoint): boolean {
|
|
67
|
+
return !isBreakpointUp(breakpoint);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a responsive value to its current breakpoint value.
|
|
72
|
+
* Falls back to the smallest defined breakpoint using CSS cascade behavior.
|
|
73
|
+
*
|
|
74
|
+
* @param value - Either a direct value or an object mapping breakpoints to values
|
|
75
|
+
* @returns The resolved value for the current breakpoint
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* // On a tablet (md breakpoint):
|
|
80
|
+
* const size = resolveResponsive({ xs: 'sm', md: 'lg' });
|
|
81
|
+
* console.log(size); // 'lg'
|
|
82
|
+
*
|
|
83
|
+
* // On a phone (xs breakpoint):
|
|
84
|
+
* const size = resolveResponsive({ xs: 'sm', md: 'lg' });
|
|
85
|
+
* console.log(size); // 'sm'
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function resolveResponsive<T>(value: T | Partial<Record<Breakpoint, T>>): T | undefined {
|
|
89
|
+
// If not an object, return directly
|
|
90
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
91
|
+
return value as T;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const responsiveValue = value as Partial<Record<Breakpoint, T>>;
|
|
95
|
+
const breakpoints = getBreakpoints();
|
|
96
|
+
const screenWidth = UnistylesRuntime.screen.width;
|
|
97
|
+
|
|
98
|
+
// Sort breakpoints by value descending
|
|
99
|
+
const sortedBps = Object.entries(breakpoints)
|
|
100
|
+
.sort(([, a], [, b]) => b - a)
|
|
101
|
+
.map(([name]) => name as Breakpoint);
|
|
102
|
+
|
|
103
|
+
// Find the largest breakpoint that matches current screen width
|
|
104
|
+
// and has a defined value (CSS cascade behavior)
|
|
105
|
+
for (const bp of sortedBps) {
|
|
106
|
+
if (screenWidth >= breakpoints[bp] && responsiveValue[bp] !== undefined) {
|
|
107
|
+
return responsiveValue[bp];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
package/src/builder.ts
CHANGED
|
@@ -42,6 +42,7 @@ export type BuiltTheme<
|
|
|
42
42
|
TText extends string,
|
|
43
43
|
TBorder extends string,
|
|
44
44
|
TSize extends string,
|
|
45
|
+
TBreakpoints extends string = never,
|
|
45
46
|
> = {
|
|
46
47
|
intents: Record<TIntents, IntentValue>;
|
|
47
48
|
radii: Record<TRadii, number>;
|
|
@@ -78,6 +79,7 @@ export type BuiltTheme<
|
|
|
78
79
|
typography: Record<Typography, TypographyValue>;
|
|
79
80
|
};
|
|
80
81
|
interaction: InteractionConfig;
|
|
82
|
+
breakpoints: Record<TBreakpoints, number>;
|
|
81
83
|
};
|
|
82
84
|
|
|
83
85
|
/**
|
|
@@ -92,7 +94,8 @@ type ThemeConfig<
|
|
|
92
94
|
TText extends string,
|
|
93
95
|
TBorder extends string,
|
|
94
96
|
TSize extends string,
|
|
95
|
-
|
|
97
|
+
TBreakpoints extends string,
|
|
98
|
+
> = BuiltTheme<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints>;
|
|
96
99
|
|
|
97
100
|
/**
|
|
98
101
|
* Fluent builder for creating themes with full TypeScript inference.
|
|
@@ -127,8 +130,9 @@ export class ThemeBuilder<
|
|
|
127
130
|
TText extends string = never,
|
|
128
131
|
TBorder extends string = never,
|
|
129
132
|
TSize extends string = never,
|
|
133
|
+
TBreakpoints extends string = never,
|
|
130
134
|
> {
|
|
131
|
-
private config: ThemeConfig<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize>;
|
|
135
|
+
private config: ThemeConfig<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints>;
|
|
132
136
|
|
|
133
137
|
constructor() {
|
|
134
138
|
this.config = {
|
|
@@ -143,6 +147,7 @@ export class ThemeBuilder<
|
|
|
143
147
|
},
|
|
144
148
|
sizes: {} as any,
|
|
145
149
|
interaction: {} as any,
|
|
150
|
+
breakpoints: {} as any,
|
|
146
151
|
};
|
|
147
152
|
}
|
|
148
153
|
|
|
@@ -152,8 +157,8 @@ export class ThemeBuilder<
|
|
|
152
157
|
addIntent<K extends string>(
|
|
153
158
|
name: K,
|
|
154
159
|
value: IntentValue
|
|
155
|
-
): ThemeBuilder<TIntents | K, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize> {
|
|
156
|
-
const newBuilder = new ThemeBuilder<TIntents | K, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize>();
|
|
160
|
+
): ThemeBuilder<TIntents | K, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints> {
|
|
161
|
+
const newBuilder = new ThemeBuilder<TIntents | K, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints>();
|
|
157
162
|
newBuilder.config = {
|
|
158
163
|
...this.config,
|
|
159
164
|
intents: {
|
|
@@ -170,8 +175,8 @@ export class ThemeBuilder<
|
|
|
170
175
|
addRadius<K extends string>(
|
|
171
176
|
name: K,
|
|
172
177
|
value: number
|
|
173
|
-
): ThemeBuilder<TIntents, TRadii | K, TShadows, TPallet, TSurface, TText, TBorder, TSize> {
|
|
174
|
-
const newBuilder = new ThemeBuilder<TIntents, TRadii | K, TShadows, TPallet, TSurface, TText, TBorder, TSize>();
|
|
178
|
+
): ThemeBuilder<TIntents, TRadii | K, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints> {
|
|
179
|
+
const newBuilder = new ThemeBuilder<TIntents, TRadii | K, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints>();
|
|
175
180
|
newBuilder.config = {
|
|
176
181
|
...this.config,
|
|
177
182
|
radii: {
|
|
@@ -188,8 +193,8 @@ export class ThemeBuilder<
|
|
|
188
193
|
addShadow<K extends string>(
|
|
189
194
|
name: K,
|
|
190
195
|
value: ShadowValue
|
|
191
|
-
): ThemeBuilder<TIntents, TRadii, TShadows | K, TPallet, TSurface, TText, TBorder, TSize> {
|
|
192
|
-
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows | K, TPallet, TSurface, TText, TBorder, TSize>();
|
|
196
|
+
): ThemeBuilder<TIntents, TRadii, TShadows | K, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints> {
|
|
197
|
+
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows | K, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints>();
|
|
193
198
|
newBuilder.config = {
|
|
194
199
|
...this.config,
|
|
195
200
|
shadows: {
|
|
@@ -205,8 +210,8 @@ export class ThemeBuilder<
|
|
|
205
210
|
*/
|
|
206
211
|
setInteraction(
|
|
207
212
|
interaction: InteractionConfig
|
|
208
|
-
): ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize> {
|
|
209
|
-
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize>();
|
|
213
|
+
): ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints> {
|
|
214
|
+
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints>();
|
|
210
215
|
newBuilder.config = {
|
|
211
216
|
...this.config,
|
|
212
217
|
interaction,
|
|
@@ -227,8 +232,8 @@ export class ThemeBuilder<
|
|
|
227
232
|
surface: Record<S, ColorValue>;
|
|
228
233
|
text: Record<T, ColorValue>;
|
|
229
234
|
border: Record<B, ColorValue>;
|
|
230
|
-
}): ThemeBuilder<TIntents, TRadii, TShadows, P, S, T, B, TSize> {
|
|
231
|
-
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, P, S, T, B, TSize>();
|
|
235
|
+
}): ThemeBuilder<TIntents, TRadii, TShadows, P, S, T, B, TSize, TBreakpoints> {
|
|
236
|
+
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, P, S, T, B, TSize, TBreakpoints>();
|
|
232
237
|
newBuilder.config = {
|
|
233
238
|
...this.config,
|
|
234
239
|
colors,
|
|
@@ -263,8 +268,8 @@ export class ThemeBuilder<
|
|
|
263
268
|
tooltip: Record<S, TooltipSizeValue>;
|
|
264
269
|
view: Record<S, ViewSizeValue>;
|
|
265
270
|
typography: Record<Typography, TypographyValue>;
|
|
266
|
-
}): ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, S> {
|
|
267
|
-
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, S>();
|
|
271
|
+
}): ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, S, TBreakpoints> {
|
|
272
|
+
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, S, TBreakpoints>();
|
|
268
273
|
newBuilder.config = {
|
|
269
274
|
...this.config,
|
|
270
275
|
sizes,
|
|
@@ -272,10 +277,75 @@ export class ThemeBuilder<
|
|
|
272
277
|
return newBuilder;
|
|
273
278
|
}
|
|
274
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Add a single breakpoint to the theme.
|
|
282
|
+
*
|
|
283
|
+
* IMPORTANT: At least one breakpoint must have value 0 (typically 'xs').
|
|
284
|
+
* This simulates CSS cascading behavior in Unistyles.
|
|
285
|
+
*
|
|
286
|
+
* @param name - The breakpoint name (e.g., 'xs', 'sm', 'md')
|
|
287
|
+
* @param value - The minimum width in pixels for this breakpoint
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* createTheme()
|
|
292
|
+
* .addBreakpoint('xs', 0)
|
|
293
|
+
* .addBreakpoint('sm', 576)
|
|
294
|
+
* .addBreakpoint('md', 768)
|
|
295
|
+
* .build();
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
addBreakpoint<K extends string>(
|
|
299
|
+
name: K,
|
|
300
|
+
value: number
|
|
301
|
+
): ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints | K> {
|
|
302
|
+
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints | K>();
|
|
303
|
+
newBuilder.config = {
|
|
304
|
+
...this.config,
|
|
305
|
+
breakpoints: {
|
|
306
|
+
...this.config.breakpoints,
|
|
307
|
+
[name]: value,
|
|
308
|
+
} as any,
|
|
309
|
+
};
|
|
310
|
+
return newBuilder;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Set all breakpoints at once.
|
|
315
|
+
*
|
|
316
|
+
* IMPORTANT: At least one breakpoint must have value 0.
|
|
317
|
+
* Breakpoints define responsive behavior based on viewport width.
|
|
318
|
+
*
|
|
319
|
+
* @param breakpoints - Object mapping breakpoint names to pixel values
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```typescript
|
|
323
|
+
* createTheme()
|
|
324
|
+
* .setBreakpoints({
|
|
325
|
+
* xs: 0, // Required: starts at 0
|
|
326
|
+
* sm: 576,
|
|
327
|
+
* md: 768,
|
|
328
|
+
* lg: 992,
|
|
329
|
+
* xl: 1200,
|
|
330
|
+
* })
|
|
331
|
+
* .build();
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
setBreakpoints<B extends Record<string, number>>(
|
|
335
|
+
breakpoints: B
|
|
336
|
+
): ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, keyof B & string> {
|
|
337
|
+
const newBuilder = new ThemeBuilder<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, keyof B & string>();
|
|
338
|
+
newBuilder.config = {
|
|
339
|
+
...this.config,
|
|
340
|
+
breakpoints,
|
|
341
|
+
} as any;
|
|
342
|
+
return newBuilder;
|
|
343
|
+
}
|
|
344
|
+
|
|
275
345
|
/**
|
|
276
346
|
* Build the final theme object.
|
|
277
347
|
*/
|
|
278
|
-
build(): BuiltTheme<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize> {
|
|
348
|
+
build(): BuiltTheme<TIntents, TRadii, TShadows, TPallet, TSurface, TText, TBorder, TSize, TBreakpoints> {
|
|
279
349
|
return this.config;
|
|
280
350
|
}
|
|
281
351
|
}
|
|
@@ -290,7 +360,7 @@ export function createTheme(): ThemeBuilder {
|
|
|
290
360
|
/**
|
|
291
361
|
* Create a builder from an existing theme to add more values.
|
|
292
362
|
*/
|
|
293
|
-
export function fromTheme<T extends BuiltTheme<any, any, any, any, any, any, any, any>>(
|
|
363
|
+
export function fromTheme<T extends BuiltTheme<any, any, any, any, any, any, any, any, any>>(
|
|
294
364
|
base: T
|
|
295
365
|
): ThemeBuilder<
|
|
296
366
|
keyof T['intents'] & string,
|
|
@@ -300,7 +370,8 @@ export function fromTheme<T extends BuiltTheme<any, any, any, any, any, any, any
|
|
|
300
370
|
keyof T['colors']['surface'] & string,
|
|
301
371
|
keyof T['colors']['text'] & string,
|
|
302
372
|
keyof T['colors']['border'] & string,
|
|
303
|
-
keyof T['sizes']['button'] & string
|
|
373
|
+
keyof T['sizes']['button'] & string,
|
|
374
|
+
keyof T['breakpoints'] & string
|
|
304
375
|
> {
|
|
305
376
|
const builder = new ThemeBuilder<
|
|
306
377
|
keyof T['intents'] & string,
|
|
@@ -310,7 +381,8 @@ export function fromTheme<T extends BuiltTheme<any, any, any, any, any, any, any
|
|
|
310
381
|
keyof T['colors']['surface'] & string,
|
|
311
382
|
keyof T['colors']['text'] & string,
|
|
312
383
|
keyof T['colors']['border'] & string,
|
|
313
|
-
keyof T['sizes']['button'] & string
|
|
384
|
+
keyof T['sizes']['button'] & string,
|
|
385
|
+
keyof T['breakpoints'] & string
|
|
314
386
|
>();
|
|
315
387
|
(builder as any).config = { ...base };
|
|
316
388
|
return builder;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Style Types Registry
|
|
3
|
+
*
|
|
4
|
+
* This module provides type definitions for component styles used with
|
|
5
|
+
* defineStyle, extendStyle, and overrideStyle.
|
|
6
|
+
*
|
|
7
|
+
* Components register their style types via module augmentation:
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // In Text.styles.tsx
|
|
12
|
+
* declare module '@idealyst/theme' {
|
|
13
|
+
* interface ComponentStyleRegistry {
|
|
14
|
+
* Text: TextStyleDef;
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { TextStyle, ViewStyle } from 'react-native';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Registry interface that components augment to register their style types.
|
|
24
|
+
* This enables type-safe extendStyle and overrideStyle calls.
|
|
25
|
+
*/
|
|
26
|
+
export interface ComponentStyleRegistry {
|
|
27
|
+
// Components augment this interface to add their style types
|
|
28
|
+
// Example: Text: { text: (params: TextStyleParams) => TextStyleObject }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the style definition type for a component.
|
|
33
|
+
* Returns the registered type or a loose Record type for unregistered components.
|
|
34
|
+
*/
|
|
35
|
+
export type ComponentStyleDef<K extends string> = K extends keyof ComponentStyleRegistry
|
|
36
|
+
? ComponentStyleRegistry[K]
|
|
37
|
+
: Record<string, any>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Deep partial type that works with functions.
|
|
41
|
+
* For style functions, preserves the function signature but makes the return type partial.
|
|
42
|
+
*/
|
|
43
|
+
export type DeepPartialStyle<T> = T extends (...args: infer A) => infer R
|
|
44
|
+
? (...args: A) => DeepPartialStyle<R>
|
|
45
|
+
: T extends object
|
|
46
|
+
? { [K in keyof T]?: DeepPartialStyle<T[K]> }
|
|
47
|
+
: T;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Style definition for extendStyle - requires functions with same params as base.
|
|
51
|
+
* All style properties must be functions to access dynamic params.
|
|
52
|
+
*/
|
|
53
|
+
export type ExtendStyleDef<K extends string> = DeepPartialStyle<ComponentStyleDef<K>>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Style definition for overrideStyle - requires full implementation with functions.
|
|
57
|
+
*/
|
|
58
|
+
export type OverrideStyleDef<K extends string> = ComponentStyleDef<K>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Helper to extract the params type from a dynamic style function.
|
|
62
|
+
* Use this to type your extension functions.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* type TextParams = StyleParams<TextStyleDef['text']>;
|
|
67
|
+
* // TextParams = { color?: TextColorVariant }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export type StyleParams<T> = T extends (params: infer P) => any ? P : never;
|
|
71
|
+
|
|
72
|
+
// =============================================================================
|
|
73
|
+
// Common Style Types
|
|
74
|
+
// =============================================================================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Base style object with optional variants and compound variants.
|
|
78
|
+
*/
|
|
79
|
+
export interface StyleWithVariants<TVariants extends Record<string, any> = Record<string, any>> {
|
|
80
|
+
variants?: {
|
|
81
|
+
[K in keyof TVariants]?: {
|
|
82
|
+
[V in TVariants[K] extends string | boolean ? TVariants[K] : string]?: ViewStyle | TextStyle;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
compoundVariants?: Array<{
|
|
86
|
+
[K in keyof TVariants]?: TVariants[K];
|
|
87
|
+
} & { styles: ViewStyle | TextStyle }>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Dynamic style function type.
|
|
92
|
+
*/
|
|
93
|
+
export type DynamicStyleFn<TParams, TStyle> = (params: TParams) => TStyle;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Idealyst Style Generator CLI
|
|
4
|
+
*
|
|
5
|
+
* Reads idealyst.config.ts and generates flat Unistyles-compatible style files.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx ts-node packages/theme/src/config/cli.ts [config-path] [output-dir]
|
|
9
|
+
*
|
|
10
|
+
* Defaults:
|
|
11
|
+
* config-path: ./idealyst.config.ts
|
|
12
|
+
* output-dir: ./generated
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
|
|
18
|
+
// Dynamic import for ESM compatibility
|
|
19
|
+
async function main() {
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
const configPath = args[0] || './idealyst.config.ts';
|
|
22
|
+
const outputDir = args[1] || './generated';
|
|
23
|
+
|
|
24
|
+
console.log('🎨 Idealyst Style Generator');
|
|
25
|
+
console.log(` Config: ${configPath}`);
|
|
26
|
+
console.log(` Output: ${outputDir}`);
|
|
27
|
+
console.log('');
|
|
28
|
+
|
|
29
|
+
// Resolve paths
|
|
30
|
+
const resolvedConfigPath = path.resolve(process.cwd(), configPath);
|
|
31
|
+
const resolvedOutputDir = path.resolve(process.cwd(), outputDir);
|
|
32
|
+
|
|
33
|
+
// Check config exists
|
|
34
|
+
if (!fs.existsSync(resolvedConfigPath)) {
|
|
35
|
+
console.error(`❌ Config file not found: ${resolvedConfigPath}`);
|
|
36
|
+
console.error('');
|
|
37
|
+
console.error('Create an idealyst.config.ts file with your theme configuration.');
|
|
38
|
+
console.error('See the documentation for examples.');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Import config dynamically
|
|
43
|
+
// Note: This requires ts-node or a pre-compiled config
|
|
44
|
+
let config;
|
|
45
|
+
try {
|
|
46
|
+
// Try to import the config
|
|
47
|
+
const configModule = await import(resolvedConfigPath);
|
|
48
|
+
config = configModule.default || configModule;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(`❌ Failed to load config: ${err}`);
|
|
51
|
+
console.error('');
|
|
52
|
+
console.error('Make sure your config file:');
|
|
53
|
+
console.error(' 1. Is a valid TypeScript/JavaScript file');
|
|
54
|
+
console.error(' 2. Uses export default or named exports');
|
|
55
|
+
console.error(' 3. Has all required theme properties');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Validate config
|
|
60
|
+
if (!config.themes || !config.themes.light || !config.themes.dark) {
|
|
61
|
+
console.error('❌ Invalid config: themes.light and themes.dark are required');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Import generator
|
|
66
|
+
const { generateStyles } = await import('./generator');
|
|
67
|
+
|
|
68
|
+
// Generate styles
|
|
69
|
+
console.log('⚙️ Generating styles...');
|
|
70
|
+
const files = generateStyles(config);
|
|
71
|
+
|
|
72
|
+
// Ensure output directory exists
|
|
73
|
+
if (!fs.existsSync(resolvedOutputDir)) {
|
|
74
|
+
fs.mkdirSync(resolvedOutputDir, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Write files
|
|
78
|
+
for (const [filename, content] of Object.entries(files)) {
|
|
79
|
+
const filePath = path.join(resolvedOutputDir, filename);
|
|
80
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
81
|
+
console.log(` ✓ ${filename}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log(`✅ Generated ${Object.keys(files).length} files in ${outputDir}`);
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log('Next steps:');
|
|
88
|
+
console.log(' 1. Import the generated unistyles.generated.ts in your app entry');
|
|
89
|
+
console.log(' 2. Update components to import from generated style files');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main().catch((err) => {
|
|
93
|
+
console.error('Fatal error:', err);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|