@hanzogui/theme-builder 2.0.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/LICENSE +21 -0
- package/dist/cjs/ThemeBuilder.cjs +184 -0
- package/dist/cjs/ThemeBuilder.native.js +262 -0
- package/dist/cjs/ThemeBuilder.native.js.map +1 -0
- package/dist/cjs/createStudioThemes.cjs +40 -0
- package/dist/cjs/createStudioThemes.native.js +43 -0
- package/dist/cjs/createStudioThemes.native.js.map +1 -0
- package/dist/cjs/createThemes.cjs +345 -0
- package/dist/cjs/createThemes.native.js +366 -0
- package/dist/cjs/createThemes.native.js.map +1 -0
- package/dist/cjs/defaultComponentThemes.cjs +84 -0
- package/dist/cjs/defaultComponentThemes.native.js +87 -0
- package/dist/cjs/defaultComponentThemes.native.js.map +1 -0
- package/dist/cjs/defaultTemplates.cjs +149 -0
- package/dist/cjs/defaultTemplates.native.js +160 -0
- package/dist/cjs/defaultTemplates.native.js.map +1 -0
- package/dist/cjs/defaultTemplatesStronger.cjs +147 -0
- package/dist/cjs/defaultTemplatesStronger.native.js +158 -0
- package/dist/cjs/defaultTemplatesStronger.native.js.map +1 -0
- package/dist/cjs/defaultTemplatesStrongest.cjs +147 -0
- package/dist/cjs/defaultTemplatesStrongest.native.js +158 -0
- package/dist/cjs/defaultTemplatesStrongest.native.js.map +1 -0
- package/dist/cjs/getThemeSuitePalettes.cjs +81 -0
- package/dist/cjs/getThemeSuitePalettes.native.js +93 -0
- package/dist/cjs/getThemeSuitePalettes.native.js.map +1 -0
- package/dist/cjs/helpers.cjs +34 -0
- package/dist/cjs/helpers.native.js +39 -0
- package/dist/cjs/helpers.native.js.map +1 -0
- package/dist/cjs/index.cjs +43 -0
- package/dist/cjs/index.native.js +46 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/masks.cjs +114 -0
- package/dist/cjs/masks.native.js +118 -0
- package/dist/cjs/masks.native.js.map +1 -0
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.native.js +19 -0
- package/dist/cjs/types.native.js.map +1 -0
- package/dist/esm/ThemeBuilder.mjs +160 -0
- package/dist/esm/ThemeBuilder.mjs.map +1 -0
- package/dist/esm/ThemeBuilder.native.js +235 -0
- package/dist/esm/ThemeBuilder.native.js.map +1 -0
- package/dist/esm/createStudioThemes.mjs +17 -0
- package/dist/esm/createStudioThemes.mjs.map +1 -0
- package/dist/esm/createStudioThemes.native.js +17 -0
- package/dist/esm/createStudioThemes.native.js.map +1 -0
- package/dist/esm/createThemes.mjs +316 -0
- package/dist/esm/createThemes.mjs.map +1 -0
- package/dist/esm/createThemes.native.js +334 -0
- package/dist/esm/createThemes.native.js.map +1 -0
- package/dist/esm/defaultComponentThemes.mjs +61 -0
- package/dist/esm/defaultComponentThemes.mjs.map +1 -0
- package/dist/esm/defaultComponentThemes.native.js +61 -0
- package/dist/esm/defaultComponentThemes.native.js.map +1 -0
- package/dist/esm/defaultTemplates.mjs +126 -0
- package/dist/esm/defaultTemplates.mjs.map +1 -0
- package/dist/esm/defaultTemplates.native.js +134 -0
- package/dist/esm/defaultTemplates.native.js.map +1 -0
- package/dist/esm/defaultTemplatesStronger.mjs +124 -0
- package/dist/esm/defaultTemplatesStronger.mjs.map +1 -0
- package/dist/esm/defaultTemplatesStronger.native.js +132 -0
- package/dist/esm/defaultTemplatesStronger.native.js.map +1 -0
- package/dist/esm/defaultTemplatesStrongest.mjs +124 -0
- package/dist/esm/defaultTemplatesStrongest.mjs.map +1 -0
- package/dist/esm/defaultTemplatesStrongest.native.js +132 -0
- package/dist/esm/defaultTemplatesStrongest.native.js.map +1 -0
- package/dist/esm/getThemeSuitePalettes.mjs +57 -0
- package/dist/esm/getThemeSuitePalettes.mjs.map +1 -0
- package/dist/esm/getThemeSuitePalettes.native.js +66 -0
- package/dist/esm/getThemeSuitePalettes.native.js.map +1 -0
- package/dist/esm/helpers.mjs +9 -0
- package/dist/esm/helpers.mjs.map +1 -0
- package/dist/esm/helpers.native.js +11 -0
- package/dist/esm/helpers.native.js.map +1 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/index.mjs +10 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +10 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/masks.mjs +91 -0
- package/dist/esm/masks.mjs.map +1 -0
- package/dist/esm/masks.native.js +92 -0
- package/dist/esm/masks.native.js.map +1 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +2 -0
- package/dist/esm/types.native.js.map +1 -0
- package/package.json +48 -0
- package/src/ThemeBuilder.ts +514 -0
- package/src/createStudioThemes.ts +28 -0
- package/src/createThemes.ts +794 -0
- package/src/defaultComponentThemes.ts +26 -0
- package/src/defaultTemplates.ts +157 -0
- package/src/defaultTemplatesStronger.ts +152 -0
- package/src/defaultTemplatesStrongest.ts +152 -0
- package/src/getThemeSuitePalettes.ts +101 -0
- package/src/helpers.ts +44 -0
- package/src/index.ts +22 -0
- package/src/masks.tsx +83 -0
- package/src/types.ts +97 -0
- package/types/ThemeBuilder.d.ts +80 -0
- package/types/ThemeBuilder.d.ts.map +1 -0
- package/types/createStudioThemes.d.ts +8 -0
- package/types/createStudioThemes.d.ts.map +1 -0
- package/types/createThemes.d.ts +141 -0
- package/types/createThemes.d.ts.map +1 -0
- package/types/defaultComponentThemes.d.ts +63 -0
- package/types/defaultComponentThemes.d.ts.map +1 -0
- package/types/defaultTemplates.d.ts +42 -0
- package/types/defaultTemplates.d.ts.map +1 -0
- package/types/defaultTemplatesStronger.d.ts +42 -0
- package/types/defaultTemplatesStronger.d.ts.map +1 -0
- package/types/defaultTemplatesStrongest.d.ts +42 -0
- package/types/defaultTemplatesStrongest.d.ts.map +1 -0
- package/types/getThemeSuitePalettes.d.ts +7 -0
- package/types/getThemeSuitePalettes.d.ts.map +1 -0
- package/types/helpers.d.ts +24 -0
- package/types/helpers.d.ts.map +1 -0
- package/types/index.d.ts +11 -0
- package/types/index.d.ts.map +1 -0
- package/types/masks.d.ts +18 -0
- package/types/masks.d.ts.map +1 -0
- package/types/types.d.ts +76 -0
- package/types/types.d.ts.map +1 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MaskDefinitions,
|
|
3
|
+
PaletteDefinitions,
|
|
4
|
+
Template,
|
|
5
|
+
TemplateDefinitions,
|
|
6
|
+
ThemeDefinitions,
|
|
7
|
+
ThemeUsingMask,
|
|
8
|
+
} from '@hanzogui/create-theme'
|
|
9
|
+
import {
|
|
10
|
+
applyMask,
|
|
11
|
+
createMask,
|
|
12
|
+
createThemeWithPalettes,
|
|
13
|
+
objectEntries,
|
|
14
|
+
objectFromEntries,
|
|
15
|
+
} from '@hanzogui/create-theme'
|
|
16
|
+
import type { Narrow } from '@hanzogui/web'
|
|
17
|
+
import type { GetThemeFn } from './types'
|
|
18
|
+
|
|
19
|
+
export type ThemeBuilderInternalState = {
|
|
20
|
+
palettes?: PaletteDefinitions
|
|
21
|
+
templates?: TemplateDefinitions
|
|
22
|
+
themes?: ThemeDefinitions
|
|
23
|
+
masks?: MaskDefinitions
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type ObjectStringKeys<A extends object | undefined> = A extends object
|
|
27
|
+
? Exclude<keyof A, symbol | number>
|
|
28
|
+
: never
|
|
29
|
+
|
|
30
|
+
type GetGeneratedThemeFromTemplate<Template, TD> = {
|
|
31
|
+
[key in keyof Template]: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type GetParentTheme<P, Themes extends ThemeDefinitions | undefined> = P extends string
|
|
35
|
+
? P extends keyof Themes
|
|
36
|
+
? Themes[P]
|
|
37
|
+
: GetParentName<P> extends keyof Themes
|
|
38
|
+
? Themes[GetParentName<P>]
|
|
39
|
+
: GetParentName<GetParentName<P>> extends keyof Themes
|
|
40
|
+
? Themes[GetParentName<GetParentName<P>>]
|
|
41
|
+
: GetParentName<GetParentName<GetParentName<P>>> extends keyof Themes
|
|
42
|
+
? Themes[GetParentName<GetParentName<GetParentName<P>>>]
|
|
43
|
+
: never
|
|
44
|
+
: never
|
|
45
|
+
|
|
46
|
+
type GetGeneratedTheme<TD, S extends ThemeBuilderInternalState> = TD extends {
|
|
47
|
+
theme: infer T
|
|
48
|
+
}
|
|
49
|
+
? T
|
|
50
|
+
: TD extends { parent: infer P }
|
|
51
|
+
? GetGeneratedTheme<GetParentTheme<P, S['themes']>, S>
|
|
52
|
+
: TD extends { template: infer T }
|
|
53
|
+
? T extends keyof S['templates']
|
|
54
|
+
? GetGeneratedThemeFromTemplate<S['templates'][T], TD>
|
|
55
|
+
: TD
|
|
56
|
+
: TD
|
|
57
|
+
|
|
58
|
+
type ThemeBuilderBuildResult<
|
|
59
|
+
S extends ThemeBuilderInternalState,
|
|
60
|
+
FinalTheme extends Record<string, string | number> = Record<string, string>,
|
|
61
|
+
> =
|
|
62
|
+
Record<string, string> extends FinalTheme
|
|
63
|
+
? FinalTheme extends Record<string, string>
|
|
64
|
+
? {
|
|
65
|
+
[Key in keyof S['themes']]: GetGeneratedTheme<S['themes'][Key], S>
|
|
66
|
+
}
|
|
67
|
+
: {
|
|
68
|
+
[Key in keyof S['themes']]: FinalTheme
|
|
69
|
+
}
|
|
70
|
+
: {
|
|
71
|
+
[Key in keyof S['themes']]: FinalTheme
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type GetParentName<N extends string> =
|
|
75
|
+
N extends `${infer A}_${infer B}_${infer C}_${infer D}_${string}`
|
|
76
|
+
? `${A}_${B}_${C}_${D}`
|
|
77
|
+
: N extends `${infer A}_${infer B}_${infer C}_${string}`
|
|
78
|
+
? `${A}_${B}_${C}`
|
|
79
|
+
: N extends `${infer A}_${infer B}_${string}`
|
|
80
|
+
? `${A}_${B}`
|
|
81
|
+
: N extends `${infer A}_${string}`
|
|
82
|
+
? `${A}`
|
|
83
|
+
: never
|
|
84
|
+
|
|
85
|
+
// Flatten union types into a single object with optional properties
|
|
86
|
+
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
87
|
+
k: infer I
|
|
88
|
+
) => void
|
|
89
|
+
? I
|
|
90
|
+
: never
|
|
91
|
+
|
|
92
|
+
type Prettify<T> = {
|
|
93
|
+
[K in keyof T]: T[K]
|
|
94
|
+
} & {}
|
|
95
|
+
|
|
96
|
+
type FlattenUnion<T extends Record<string, string | number>> =
|
|
97
|
+
Prettify<{
|
|
98
|
+
[K in keyof UnionToIntersection<T>]: UnionToIntersection<T>[K]
|
|
99
|
+
}> extends infer R extends Record<string, string | number>
|
|
100
|
+
? R
|
|
101
|
+
: never
|
|
102
|
+
|
|
103
|
+
export class ThemeBuilder<
|
|
104
|
+
State extends ThemeBuilderInternalState = ThemeBuilderInternalState,
|
|
105
|
+
FinalTheme extends Record<string, string | number> = Record<string, string>,
|
|
106
|
+
> {
|
|
107
|
+
private _getThemeFn?: GetThemeFn<FinalTheme>
|
|
108
|
+
|
|
109
|
+
constructor(public state: State) {}
|
|
110
|
+
|
|
111
|
+
addPalettes<const P extends PaletteDefinitions>(palettes: P) {
|
|
112
|
+
this.state.palettes = {
|
|
113
|
+
// as {} prevents generic string key merge messing up types
|
|
114
|
+
...(this.state.palettes as {}),
|
|
115
|
+
...palettes,
|
|
116
|
+
}
|
|
117
|
+
return this as any as ThemeBuilder<
|
|
118
|
+
State & {
|
|
119
|
+
palettes: P
|
|
120
|
+
},
|
|
121
|
+
FinalTheme
|
|
122
|
+
>
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
addTemplates<const T extends TemplateDefinitions>(templates: T) {
|
|
126
|
+
this.state.templates = {
|
|
127
|
+
// as {} prevents generic string key merge messing up types
|
|
128
|
+
...(this.state.templates as {}),
|
|
129
|
+
...templates,
|
|
130
|
+
}
|
|
131
|
+
return this as any as ThemeBuilder<
|
|
132
|
+
State & {
|
|
133
|
+
templates: T
|
|
134
|
+
},
|
|
135
|
+
FinalTheme
|
|
136
|
+
>
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
addMasks<const M extends MaskDefinitions>(masks: M) {
|
|
140
|
+
this.state.masks = {
|
|
141
|
+
// as {} prevents generic string key merge messing up types
|
|
142
|
+
...(this.state.masks as {}),
|
|
143
|
+
...(objectFromEntries(
|
|
144
|
+
objectEntries(masks).map(([key, val]) => [key, createMask(val)])
|
|
145
|
+
) as M),
|
|
146
|
+
}
|
|
147
|
+
return this as any as ThemeBuilder<
|
|
148
|
+
State & {
|
|
149
|
+
masks: M
|
|
150
|
+
},
|
|
151
|
+
FinalTheme
|
|
152
|
+
>
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// for dev mode only really
|
|
156
|
+
_addedThemes: { type: 'themes' | 'childThemes'; args: any }[] = []
|
|
157
|
+
|
|
158
|
+
addThemes<const T extends ThemeDefinitions<ObjectStringKeys<State['masks']>>>(
|
|
159
|
+
themes: T
|
|
160
|
+
) {
|
|
161
|
+
this._addedThemes.push({ type: 'themes', args: [themes] })
|
|
162
|
+
|
|
163
|
+
this.state.themes = {
|
|
164
|
+
// as {} prevents generic string key merge messing up types
|
|
165
|
+
...(this.state.themes as {}),
|
|
166
|
+
...themes,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// type TemplateToTheme<X> = State['templates'] extends {}
|
|
170
|
+
// ? X extends { template: infer Y; nonInheritedValues: infer Z }
|
|
171
|
+
// ? Y extends keyof State['templates']
|
|
172
|
+
// ? { theme: Record<keyof State['templates'][Y] | keyof Z, string> }
|
|
173
|
+
// : X
|
|
174
|
+
// : X
|
|
175
|
+
// : X
|
|
176
|
+
|
|
177
|
+
return this as any as ThemeBuilder<
|
|
178
|
+
Omit<State, 'themes'> & {
|
|
179
|
+
// lets infer template themes directly onto here to avoid some type nesting issues later one
|
|
180
|
+
// themes: {
|
|
181
|
+
// [Key in keyof T]: TemplateToTheme<T[Key]>
|
|
182
|
+
// } & State['themes']
|
|
183
|
+
themes: T
|
|
184
|
+
},
|
|
185
|
+
FinalTheme
|
|
186
|
+
>
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// these wont be typed to save some complexity and because they don't need to be typed!
|
|
190
|
+
addComponentThemes<
|
|
191
|
+
CTD extends Narrow<ThemeDefinitions<ObjectStringKeys<State['masks']>>>,
|
|
192
|
+
>(
|
|
193
|
+
childThemeDefinition: CTD,
|
|
194
|
+
options?: {
|
|
195
|
+
avoidNestingWithin?: string[]
|
|
196
|
+
}
|
|
197
|
+
) {
|
|
198
|
+
void this.addChildThemes(childThemeDefinition, options)
|
|
199
|
+
return this
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
addChildThemes<
|
|
203
|
+
CTD extends Narrow<ThemeDefinitions<ObjectStringKeys<State['masks']>>>,
|
|
204
|
+
const AvoidNestingWithin extends string[] = [],
|
|
205
|
+
>(
|
|
206
|
+
childThemeDefinition: CTD,
|
|
207
|
+
options?: {
|
|
208
|
+
avoidNestingWithin?: AvoidNestingWithin
|
|
209
|
+
}
|
|
210
|
+
) {
|
|
211
|
+
const currentThemes = this.state.themes as State['themes']
|
|
212
|
+
if (!currentThemes) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`No themes defined yet, use addThemes first to set your base themes`
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this._addedThemes.push({ type: 'childThemes', args: [childThemeDefinition, options] })
|
|
219
|
+
|
|
220
|
+
// AvoidNestingWithin[number] exclude isn't working here...
|
|
221
|
+
type CurrentNames = Exclude<keyof typeof currentThemes, symbol | number>
|
|
222
|
+
type ChildNames = Exclude<keyof CTD, symbol | number>
|
|
223
|
+
|
|
224
|
+
const currentThemeNames = Object.keys(currentThemes) as CurrentNames[]
|
|
225
|
+
const incomingThemeNames = Object.keys(childThemeDefinition) as ChildNames[]
|
|
226
|
+
|
|
227
|
+
const namesWithDefinitions = currentThemeNames.flatMap((prefix) => {
|
|
228
|
+
const avoidNestingWithin = options?.avoidNestingWithin
|
|
229
|
+
if (avoidNestingWithin) {
|
|
230
|
+
if (
|
|
231
|
+
avoidNestingWithin.some(
|
|
232
|
+
(avoidName) => prefix.startsWith(avoidName) || prefix.endsWith(avoidName)
|
|
233
|
+
)
|
|
234
|
+
) {
|
|
235
|
+
return []
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return incomingThemeNames
|
|
240
|
+
.map((subName) => {
|
|
241
|
+
const fullName = `${prefix}_${subName}`
|
|
242
|
+
const definition = childThemeDefinition[subName]
|
|
243
|
+
|
|
244
|
+
if ('avoidNestingWithin' in definition) {
|
|
245
|
+
const avoidNest = definition.avoidNestingWithin as string[]
|
|
246
|
+
if (
|
|
247
|
+
avoidNest.some((name) => {
|
|
248
|
+
// For base scheme names (light/dark), use exact match to avoid matching derivatives
|
|
249
|
+
if ((name === 'light' || name === 'dark') && prefix.includes('_')) {
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
return prefix.startsWith(name) || prefix.endsWith(name)
|
|
253
|
+
})
|
|
254
|
+
) {
|
|
255
|
+
return null as never
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Avoid double-nesting: don't add a child theme to a parent that already ends with that child
|
|
260
|
+
// e.g., don't create light_blue_accent_accent from light_blue_accent + accent
|
|
261
|
+
if (prefix.endsWith(`_${subName}`)) {
|
|
262
|
+
return null as never
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Don't overwrite existing themes (e.g., a grandchild "accent" template
|
|
266
|
+
// should not overwrite a dedicated "accent" child theme with its own palette)
|
|
267
|
+
if (fullName in currentThemes) {
|
|
268
|
+
return null as never
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return [fullName, definition] as const
|
|
272
|
+
})
|
|
273
|
+
.filter(Boolean)
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
type ChildThemes = {
|
|
277
|
+
[key in `${CurrentNames}_${ChildNames}`]: CTD & {
|
|
278
|
+
parent: GetParentName<key>
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const childThemes = Object.fromEntries(namesWithDefinitions) as any as ChildThemes
|
|
283
|
+
|
|
284
|
+
const next = {
|
|
285
|
+
// as {} prevents generic string key merge messing up types
|
|
286
|
+
...(this.state.themes as {}),
|
|
287
|
+
...childThemes,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// @ts-ignore
|
|
291
|
+
this.state.themes = next
|
|
292
|
+
|
|
293
|
+
return this as any as ThemeBuilder<
|
|
294
|
+
State & {
|
|
295
|
+
themes: ChildThemes
|
|
296
|
+
},
|
|
297
|
+
FinalTheme
|
|
298
|
+
>
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
getTheme<NewTheme extends Record<string, string | number>>(
|
|
302
|
+
fn: (props: {
|
|
303
|
+
name: string
|
|
304
|
+
theme: GetGeneratedTheme<State['themes'][keyof State['themes']], State>
|
|
305
|
+
scheme?: 'light' | 'dark'
|
|
306
|
+
parentName: string
|
|
307
|
+
parentNames: string[]
|
|
308
|
+
level: number
|
|
309
|
+
palette?: string[]
|
|
310
|
+
template?: Template
|
|
311
|
+
}) => NewTheme
|
|
312
|
+
) {
|
|
313
|
+
this._getThemeFn = fn as any
|
|
314
|
+
return this as any as ThemeBuilder<State, FlattenUnion<NewTheme>>
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
build(): ThemeBuilderBuildResult<State, FinalTheme> {
|
|
318
|
+
if (!this.state.themes) {
|
|
319
|
+
return {} as any
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const out = {}
|
|
323
|
+
const maskedThemes: {
|
|
324
|
+
parentName: string
|
|
325
|
+
themeName: string
|
|
326
|
+
mask: ThemeUsingMask
|
|
327
|
+
}[] = []
|
|
328
|
+
|
|
329
|
+
for (const themeName in this.state.themes) {
|
|
330
|
+
const nameParts = themeName.split('_')
|
|
331
|
+
const parentName = nameParts.slice(0, nameParts.length - 1).join('_')
|
|
332
|
+
|
|
333
|
+
const definitions = this.state.themes[themeName]
|
|
334
|
+
const themeDefinition = Array.isArray(definitions)
|
|
335
|
+
? (() => {
|
|
336
|
+
const found = definitions.find(
|
|
337
|
+
// endWith match stronger than startsWith
|
|
338
|
+
(d) =>
|
|
339
|
+
d.parent
|
|
340
|
+
? parentName.endsWith(d.parent!) || parentName.startsWith(d.parent!)
|
|
341
|
+
: true
|
|
342
|
+
)
|
|
343
|
+
if (!found) {
|
|
344
|
+
return null
|
|
345
|
+
}
|
|
346
|
+
return found
|
|
347
|
+
})()
|
|
348
|
+
: definitions
|
|
349
|
+
|
|
350
|
+
if (!themeDefinition) {
|
|
351
|
+
// `No parent for ${themeName}: ${parentName} - Continuing...`
|
|
352
|
+
continue
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if ('theme' in themeDefinition) {
|
|
356
|
+
out[themeName] = themeDefinition.theme
|
|
357
|
+
} else if ('mask' in themeDefinition) {
|
|
358
|
+
maskedThemes.push({ parentName, themeName, mask: themeDefinition })
|
|
359
|
+
} else {
|
|
360
|
+
let {
|
|
361
|
+
palette: paletteName = '',
|
|
362
|
+
template: templateName,
|
|
363
|
+
...options
|
|
364
|
+
} = themeDefinition
|
|
365
|
+
|
|
366
|
+
const parentDefinition = this.state.themes[parentName]
|
|
367
|
+
|
|
368
|
+
if (!this.state.palettes) {
|
|
369
|
+
throw new Error(
|
|
370
|
+
`No palettes defined for theme with palette expected: ${themeName}`
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
let palette = this.state.palettes[paletteName || '']
|
|
375
|
+
let attemptParentName = `${parentName}_${paletteName}`
|
|
376
|
+
|
|
377
|
+
while (!palette && attemptParentName) {
|
|
378
|
+
if (attemptParentName in this.state.palettes) {
|
|
379
|
+
palette = this.state.palettes[attemptParentName]
|
|
380
|
+
paletteName = attemptParentName
|
|
381
|
+
} else {
|
|
382
|
+
attemptParentName = attemptParentName.split('_').slice(0, -1).join('_')
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (!palette) {
|
|
387
|
+
const msg =
|
|
388
|
+
process.env.NODE_ENV !== 'production'
|
|
389
|
+
? `: ${themeName}: ${paletteName}
|
|
390
|
+
Definition: ${JSON.stringify(themeDefinition)}
|
|
391
|
+
Parent: ${JSON.stringify(parentDefinition)}
|
|
392
|
+
Potential: (${Object.keys(this.state.palettes).join(', ')})`
|
|
393
|
+
: ``
|
|
394
|
+
throw new Error(`No palette for theme${msg}`)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const template =
|
|
398
|
+
this.state.templates?.[templateName] ??
|
|
399
|
+
// fall back to finding the scheme specific on if it exists
|
|
400
|
+
this.state.templates?.[`${nameParts[0]}_${templateName}`]
|
|
401
|
+
|
|
402
|
+
if (!template) {
|
|
403
|
+
throw new Error(
|
|
404
|
+
`No template for theme ${themeName}: ${templateName} in templates:\n- ${Object.keys(this.state.templates || {}).join('\n - ')}`
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const theme = createThemeWithPalettes(
|
|
409
|
+
this.state.palettes,
|
|
410
|
+
paletteName,
|
|
411
|
+
template,
|
|
412
|
+
options,
|
|
413
|
+
themeName,
|
|
414
|
+
true
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
out[themeName] = this._getThemeFn
|
|
418
|
+
? {
|
|
419
|
+
...theme,
|
|
420
|
+
...this._getThemeFn({
|
|
421
|
+
theme,
|
|
422
|
+
name: themeName,
|
|
423
|
+
level: nameParts.length,
|
|
424
|
+
parentName,
|
|
425
|
+
scheme: /^(light|dark)$/.test(nameParts[0])
|
|
426
|
+
? (nameParts[0] as 'light' | 'dark')
|
|
427
|
+
: undefined,
|
|
428
|
+
parentNames: nameParts.slice(0, -1),
|
|
429
|
+
palette,
|
|
430
|
+
template,
|
|
431
|
+
}),
|
|
432
|
+
}
|
|
433
|
+
: theme
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
for (const { mask, themeName, parentName } of maskedThemes) {
|
|
438
|
+
const parent = out[parentName]
|
|
439
|
+
|
|
440
|
+
if (!parent) {
|
|
441
|
+
// `No parent theme found with name ${parentName} for theme ${themeName} to use as a mask target - Continuing...`
|
|
442
|
+
continue
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const { mask: maskName, ...options } = mask
|
|
446
|
+
let maskFunction = this.state.masks?.[maskName]
|
|
447
|
+
|
|
448
|
+
if (!maskFunction) {
|
|
449
|
+
throw new Error(`No mask ${maskName}`)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const parentTheme = this.state.themes[parentName]
|
|
453
|
+
|
|
454
|
+
if (parentTheme && 'childOptions' in parentTheme) {
|
|
455
|
+
const { mask, ...childOpts } = parentTheme.childOptions as any
|
|
456
|
+
if (mask) {
|
|
457
|
+
maskFunction = this.state.masks?.[mask]
|
|
458
|
+
}
|
|
459
|
+
Object.assign(options, childOpts)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
out[themeName] = applyMask(
|
|
463
|
+
parent,
|
|
464
|
+
maskFunction as any,
|
|
465
|
+
options,
|
|
466
|
+
parentName,
|
|
467
|
+
themeName
|
|
468
|
+
)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return out as any
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export function createThemeBuilder() {
|
|
476
|
+
return new ThemeBuilder({})
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// // test types
|
|
480
|
+
// let x = createThemeBuilder()
|
|
481
|
+
// .addMasks({
|
|
482
|
+
// test: {
|
|
483
|
+
// name: 'mask',
|
|
484
|
+
// mask: (() => {}) as any,
|
|
485
|
+
// },
|
|
486
|
+
// })
|
|
487
|
+
// .addThemes({
|
|
488
|
+
// light: {
|
|
489
|
+
// template: '',
|
|
490
|
+
// palette: '',
|
|
491
|
+
// },
|
|
492
|
+
// dark: {
|
|
493
|
+
// mask: 'test',
|
|
494
|
+
// },
|
|
495
|
+
// })
|
|
496
|
+
// .addChildThemes({
|
|
497
|
+
// List: [
|
|
498
|
+
// {
|
|
499
|
+
// parent: '',
|
|
500
|
+
// mask: 'test',
|
|
501
|
+
// },
|
|
502
|
+
// ],
|
|
503
|
+
// })
|
|
504
|
+
|
|
505
|
+
// x
|
|
506
|
+
|
|
507
|
+
// x.state.themes
|
|
508
|
+
// x.state.masks
|
|
509
|
+
|
|
510
|
+
// let y = x.addChildThemes({
|
|
511
|
+
// blue: {
|
|
512
|
+
// mask: 'ok',
|
|
513
|
+
// },
|
|
514
|
+
// })
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createPalettes, createSimpleThemeBuilder } from './createThemes'
|
|
2
|
+
import { defaultComponentThemes } from './defaultComponentThemes'
|
|
3
|
+
import { defaultTemplates } from './defaultTemplates'
|
|
4
|
+
import { defaultTemplatesStronger } from './defaultTemplatesStronger'
|
|
5
|
+
import { defaultTemplatesStrongest } from './defaultTemplatesStrongest'
|
|
6
|
+
import type { BuildThemeSuiteProps } from './types'
|
|
7
|
+
|
|
8
|
+
// for studio
|
|
9
|
+
// allows more detailed configuration, used by studio
|
|
10
|
+
// eventually we should merge this down into simple and have it handle what we need
|
|
11
|
+
|
|
12
|
+
export function createStudioThemes(props: BuildThemeSuiteProps) {
|
|
13
|
+
const palettes = createPalettes(props.palettes)
|
|
14
|
+
|
|
15
|
+
const templates =
|
|
16
|
+
props.templateStrategy === 'stronger'
|
|
17
|
+
? defaultTemplatesStronger
|
|
18
|
+
: props.templateStrategy === 'strongest'
|
|
19
|
+
? defaultTemplatesStrongest
|
|
20
|
+
: defaultTemplates
|
|
21
|
+
|
|
22
|
+
return createSimpleThemeBuilder({
|
|
23
|
+
palettes,
|
|
24
|
+
templates,
|
|
25
|
+
componentThemes: defaultComponentThemes,
|
|
26
|
+
accentTheme: !!props.palettes.accent,
|
|
27
|
+
})
|
|
28
|
+
}
|