@tamagui/create-theme 1.31.4 → 1.32.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/dist/cjs/index.js +9 -128
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +4 -117
- package/dist/esm/index.js.map +1 -1
- package/package.json +3 -3
- package/src/ThemeBuilder.tsx +352 -0
- package/src/createTheme.tsx +72 -0
- package/src/helpers.tsx +44 -0
- package/src/index.tsx +4 -174
- package/src/isMinusZero.tsx +3 -0
- package/src/masks.tsx +155 -0
- package/src/tests.tsx +27 -0
- package/src/themeInfo.tsx +26 -0
- package/src/types.tsx +27 -0
- package/types/ThemeBuilder.d.ts +86 -0
- package/types/createTheme.d.ts +15 -0
- package/types/helpers.d.ts +24 -0
- package/types/index.d.ts +4 -37
- package/types/index.d.ts.map +1 -1
- package/types/isMinusZero.d.ts +2 -0
- package/types/masks.d.ts +16 -0
- package/types/tests.d.ts +2 -0
- package/types/themeInfo.d.ts +10 -0
- package/types/types.d.ts +23 -0
package/dist/cjs/index.js
CHANGED
|
@@ -3,10 +3,6 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
6
|
var __copyProps = (to, from, except, desc) => {
|
|
11
7
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
8
|
for (let key of __getOwnPropNames(from))
|
|
@@ -15,134 +11,19 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
11
|
}
|
|
16
12
|
return to;
|
|
17
13
|
};
|
|
14
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
18
15
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
16
|
var src_exports = {};
|
|
20
|
-
__export(src_exports, {
|
|
21
|
-
addChildren: () => addChildren,
|
|
22
|
-
applyMask: () => applyMask,
|
|
23
|
-
createShiftMask: () => createShiftMask,
|
|
24
|
-
createStrengthenMask: () => createStrengthenMask,
|
|
25
|
-
createTheme: () => createTheme,
|
|
26
|
-
createWeakenMask: () => createWeakenMask,
|
|
27
|
-
skipMask: () => skipMask
|
|
28
|
-
});
|
|
29
17
|
module.exports = __toCommonJS(src_exports);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Object.entries(definition).map(([key, offset]) => {
|
|
35
|
-
return [key, getValue(palette, offset)];
|
|
36
|
-
})
|
|
37
|
-
),
|
|
38
|
-
...options == null ? void 0 : options.nonInheritedValues
|
|
39
|
-
};
|
|
40
|
-
THEME_INFO.set(theme, { palette, definition, cache: /* @__PURE__ */ new Map() });
|
|
41
|
-
return theme;
|
|
42
|
-
}
|
|
43
|
-
const getValue = (palette, value) => {
|
|
44
|
-
if (typeof value === "string")
|
|
45
|
-
return value;
|
|
46
|
-
const max = palette.length - 1;
|
|
47
|
-
const isPositive = value === 0 ? !isMinusZero(value) : value >= 0;
|
|
48
|
-
const next = isPositive ? value : max + value;
|
|
49
|
-
const index = Math.min(Math.max(0, next), max);
|
|
50
|
-
return palette[index];
|
|
51
|
-
};
|
|
52
|
-
function addChildren(themes, getChildren) {
|
|
53
|
-
const out = { ...themes };
|
|
54
|
-
for (const key in themes) {
|
|
55
|
-
const subThemes = getChildren(key, themes[key]);
|
|
56
|
-
for (const sKey in subThemes) {
|
|
57
|
-
out[`${key}_${sKey}`] = subThemes[sKey];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return out;
|
|
61
|
-
}
|
|
62
|
-
const skipMask = (template, { skip }) => {
|
|
63
|
-
if (!skip)
|
|
64
|
-
return template;
|
|
65
|
-
return Object.fromEntries(
|
|
66
|
-
Object.entries(template).filter(([k]) => !(k in skip))
|
|
67
|
-
);
|
|
68
|
-
};
|
|
69
|
-
const createShiftMask = ({ inverse } = {}) => {
|
|
70
|
-
return (template, opts) => {
|
|
71
|
-
const { override, max: maxIn, palette, min = 0, strength = 1 } = opts;
|
|
72
|
-
const values = Object.entries(template);
|
|
73
|
-
const max = maxIn ?? (palette ? Object.values(palette).length - 1 : Infinity);
|
|
74
|
-
const out = {};
|
|
75
|
-
for (const [key, value] of values) {
|
|
76
|
-
if (typeof value === "string")
|
|
77
|
-
continue;
|
|
78
|
-
if (typeof (override == null ? void 0 : override[key]) === "number") {
|
|
79
|
-
const overrideShift = override[key];
|
|
80
|
-
out[key] = value + overrideShift;
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
const isPositive = value === 0 ? !isMinusZero(value) : value >= 0;
|
|
84
|
-
const direction = isPositive ? 1 : -1;
|
|
85
|
-
const invert = inverse ? -1 : 1;
|
|
86
|
-
const next = value + strength * direction * invert;
|
|
87
|
-
const clamped = isPositive ? Math.max(min, Math.min(max, next)) : Math.min(-min, Math.max(-max, next));
|
|
88
|
-
out[key] = clamped;
|
|
89
|
-
}
|
|
90
|
-
return skipMask(out, opts);
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
const createWeakenMask = () => createShiftMask();
|
|
94
|
-
const createStrengthenMask = () => createShiftMask({ inverse: true });
|
|
95
|
-
function isMinusZero(value) {
|
|
96
|
-
return 1 / value === -Infinity;
|
|
97
|
-
}
|
|
98
|
-
function applyMask(theme, mask, options = {}) {
|
|
99
|
-
const info = THEME_INFO.get(theme);
|
|
100
|
-
if (!info) {
|
|
101
|
-
throw new Error(
|
|
102
|
-
process.env.NODE_ENV !== "production" ? `No info found for theme, you must pass the theme created by createThemeFromPalette directly to extendTheme` : `\u274C Err2`
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
const template = mask(info.definition, {
|
|
106
|
-
palette: info.palette,
|
|
107
|
-
...options
|
|
108
|
-
});
|
|
109
|
-
const next = createTheme(info.palette, template);
|
|
110
|
-
return next;
|
|
111
|
-
}
|
|
112
|
-
if (process.env.NODE_ENV === "development") {
|
|
113
|
-
const palette = ["0", "1", "2", "3", "-3", "-2", "-1", "-0"];
|
|
114
|
-
const template = { bg: 1, fg: -1 };
|
|
115
|
-
const stongerMask = createStrengthenMask();
|
|
116
|
-
const weakerMask = createWeakenMask();
|
|
117
|
-
const theme = createTheme(palette, template);
|
|
118
|
-
if (theme.bg !== "1")
|
|
119
|
-
throw `\u274C`;
|
|
120
|
-
if (theme.fg !== "-1")
|
|
121
|
-
throw `\u274C`;
|
|
122
|
-
const str = applyMask(theme, stongerMask);
|
|
123
|
-
if (str.bg !== "0")
|
|
124
|
-
throw `\u274C`;
|
|
125
|
-
if (str.fg !== "-0")
|
|
126
|
-
throw `\u274C`;
|
|
127
|
-
const weak = applyMask(theme, weakerMask);
|
|
128
|
-
if (weak.bg !== "2")
|
|
129
|
-
throw `\u274C`;
|
|
130
|
-
if (weak.fg !== "-2")
|
|
131
|
-
throw `\u274C`;
|
|
132
|
-
const weak2 = applyMask(theme, weakerMask, { strength: 2 });
|
|
133
|
-
if (weak2.bg !== "3")
|
|
134
|
-
throw `\u274C`;
|
|
135
|
-
if (weak2.fg !== "-3")
|
|
136
|
-
throw `\u274C`;
|
|
137
|
-
}
|
|
18
|
+
__reExport(src_exports, require("./createTheme"), module.exports);
|
|
19
|
+
__reExport(src_exports, require("./masks"), module.exports);
|
|
20
|
+
__reExport(src_exports, require("./types"), module.exports);
|
|
21
|
+
__reExport(src_exports, require("./ThemeBuilder"), module.exports);
|
|
138
22
|
// Annotate the CommonJS export names for ESM import in node:
|
|
139
23
|
0 && (module.exports = {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
createTheme,
|
|
145
|
-
createWeakenMask,
|
|
146
|
-
skipMask
|
|
24
|
+
...require("./createTheme"),
|
|
25
|
+
...require("./masks"),
|
|
26
|
+
...require("./types"),
|
|
27
|
+
...require("./ThemeBuilder")
|
|
147
28
|
});
|
|
148
29
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.tsx"],
|
|
4
|
-
"mappings": "
|
|
4
|
+
"mappings": ";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,wBAAc,0BAAd;AACA,wBAAc,oBADd;AAEA,wBAAc,oBAFd;AAGA,wBAAc,2BAHd;",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,118 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.entries(definition).map(([key, offset]) => {
|
|
6
|
-
return [key, getValue(palette, offset)];
|
|
7
|
-
})
|
|
8
|
-
),
|
|
9
|
-
...options == null ? void 0 : options.nonInheritedValues
|
|
10
|
-
};
|
|
11
|
-
THEME_INFO.set(theme, { palette, definition, cache: /* @__PURE__ */ new Map() });
|
|
12
|
-
return theme;
|
|
13
|
-
}
|
|
14
|
-
const getValue = (palette, value) => {
|
|
15
|
-
if (typeof value === "string")
|
|
16
|
-
return value;
|
|
17
|
-
const max = palette.length - 1;
|
|
18
|
-
const isPositive = value === 0 ? !isMinusZero(value) : value >= 0;
|
|
19
|
-
const next = isPositive ? value : max + value;
|
|
20
|
-
const index = Math.min(Math.max(0, next), max);
|
|
21
|
-
return palette[index];
|
|
22
|
-
};
|
|
23
|
-
function addChildren(themes, getChildren) {
|
|
24
|
-
const out = { ...themes };
|
|
25
|
-
for (const key in themes) {
|
|
26
|
-
const subThemes = getChildren(key, themes[key]);
|
|
27
|
-
for (const sKey in subThemes) {
|
|
28
|
-
out[`${key}_${sKey}`] = subThemes[sKey];
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return out;
|
|
32
|
-
}
|
|
33
|
-
const skipMask = (template, { skip }) => {
|
|
34
|
-
if (!skip)
|
|
35
|
-
return template;
|
|
36
|
-
return Object.fromEntries(
|
|
37
|
-
Object.entries(template).filter(([k]) => !(k in skip))
|
|
38
|
-
);
|
|
39
|
-
};
|
|
40
|
-
const createShiftMask = ({ inverse } = {}) => {
|
|
41
|
-
return (template, opts) => {
|
|
42
|
-
const { override, max: maxIn, palette, min = 0, strength = 1 } = opts;
|
|
43
|
-
const values = Object.entries(template);
|
|
44
|
-
const max = maxIn ?? (palette ? Object.values(palette).length - 1 : Infinity);
|
|
45
|
-
const out = {};
|
|
46
|
-
for (const [key, value] of values) {
|
|
47
|
-
if (typeof value === "string")
|
|
48
|
-
continue;
|
|
49
|
-
if (typeof (override == null ? void 0 : override[key]) === "number") {
|
|
50
|
-
const overrideShift = override[key];
|
|
51
|
-
out[key] = value + overrideShift;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
const isPositive = value === 0 ? !isMinusZero(value) : value >= 0;
|
|
55
|
-
const direction = isPositive ? 1 : -1;
|
|
56
|
-
const invert = inverse ? -1 : 1;
|
|
57
|
-
const next = value + strength * direction * invert;
|
|
58
|
-
const clamped = isPositive ? Math.max(min, Math.min(max, next)) : Math.min(-min, Math.max(-max, next));
|
|
59
|
-
out[key] = clamped;
|
|
60
|
-
}
|
|
61
|
-
return skipMask(out, opts);
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
const createWeakenMask = () => createShiftMask();
|
|
65
|
-
const createStrengthenMask = () => createShiftMask({ inverse: true });
|
|
66
|
-
function isMinusZero(value) {
|
|
67
|
-
return 1 / value === -Infinity;
|
|
68
|
-
}
|
|
69
|
-
function applyMask(theme, mask, options = {}) {
|
|
70
|
-
const info = THEME_INFO.get(theme);
|
|
71
|
-
if (!info) {
|
|
72
|
-
throw new Error(
|
|
73
|
-
process.env.NODE_ENV !== "production" ? `No info found for theme, you must pass the theme created by createThemeFromPalette directly to extendTheme` : `\u274C Err2`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
const template = mask(info.definition, {
|
|
77
|
-
palette: info.palette,
|
|
78
|
-
...options
|
|
79
|
-
});
|
|
80
|
-
const next = createTheme(info.palette, template);
|
|
81
|
-
return next;
|
|
82
|
-
}
|
|
83
|
-
if (process.env.NODE_ENV === "development") {
|
|
84
|
-
const palette = ["0", "1", "2", "3", "-3", "-2", "-1", "-0"];
|
|
85
|
-
const template = { bg: 1, fg: -1 };
|
|
86
|
-
const stongerMask = createStrengthenMask();
|
|
87
|
-
const weakerMask = createWeakenMask();
|
|
88
|
-
const theme = createTheme(palette, template);
|
|
89
|
-
if (theme.bg !== "1")
|
|
90
|
-
throw `\u274C`;
|
|
91
|
-
if (theme.fg !== "-1")
|
|
92
|
-
throw `\u274C`;
|
|
93
|
-
const str = applyMask(theme, stongerMask);
|
|
94
|
-
if (str.bg !== "0")
|
|
95
|
-
throw `\u274C`;
|
|
96
|
-
if (str.fg !== "-0")
|
|
97
|
-
throw `\u274C`;
|
|
98
|
-
const weak = applyMask(theme, weakerMask);
|
|
99
|
-
if (weak.bg !== "2")
|
|
100
|
-
throw `\u274C`;
|
|
101
|
-
if (weak.fg !== "-2")
|
|
102
|
-
throw `\u274C`;
|
|
103
|
-
const weak2 = applyMask(theme, weakerMask, { strength: 2 });
|
|
104
|
-
if (weak2.bg !== "3")
|
|
105
|
-
throw `\u274C`;
|
|
106
|
-
if (weak2.fg !== "-3")
|
|
107
|
-
throw `\u274C`;
|
|
108
|
-
}
|
|
109
|
-
export {
|
|
110
|
-
addChildren,
|
|
111
|
-
applyMask,
|
|
112
|
-
createShiftMask,
|
|
113
|
-
createStrengthenMask,
|
|
114
|
-
createTheme,
|
|
115
|
-
createWeakenMask,
|
|
116
|
-
skipMask
|
|
117
|
-
};
|
|
1
|
+
export * from "./createTheme";
|
|
2
|
+
export * from "./masks";
|
|
3
|
+
export * from "./types";
|
|
4
|
+
export * from "./ThemeBuilder";
|
|
118
5
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.tsx"],
|
|
4
|
-
"mappings": "
|
|
4
|
+
"mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamagui/create-theme",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.32.0",
|
|
4
4
|
"types": "./types/index.d.ts",
|
|
5
5
|
"main": "dist/cjs",
|
|
6
6
|
"module": "dist/esm",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@tamagui/web": "1.
|
|
30
|
+
"@tamagui/web": "1.32.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@tamagui/build": "1.
|
|
33
|
+
"@tamagui/build": "1.32.0"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { Narrow } from '@tamagui/web'
|
|
2
|
+
|
|
3
|
+
import { createTheme } from './createTheme'
|
|
4
|
+
import { objectEntries, objectFromEntries, objectKeys } from './helpers'
|
|
5
|
+
import { applyMask, createMask } from './masks'
|
|
6
|
+
import { CreateMask, CreateThemeOptions, MaskOptions } from './types'
|
|
7
|
+
|
|
8
|
+
export type Palette = string[]
|
|
9
|
+
|
|
10
|
+
type GenericTheme = {
|
|
11
|
+
[key: string]: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type Template = {
|
|
15
|
+
[key: string]: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type ThemeUsingMask<Masks = string> = MaskOptions & {
|
|
19
|
+
mask: Masks
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type ThemeUsingTemplate = CreateThemeOptions & {
|
|
23
|
+
palette: string
|
|
24
|
+
template: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type ThemePreDefined = {
|
|
28
|
+
theme: GenericTheme
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type Theme<Masks = string> =
|
|
32
|
+
| ThemePreDefined
|
|
33
|
+
| ThemeUsingTemplate
|
|
34
|
+
| ThemeUsingMask<Masks>
|
|
35
|
+
|
|
36
|
+
type ThemeWithParent<Masks = string> = Theme<Masks> & {
|
|
37
|
+
parent: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type PaletteDefinitions = {
|
|
41
|
+
[key: string]: Palette
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type ThemeDefinition<Masks extends string = string> =
|
|
45
|
+
| Theme<Masks>
|
|
46
|
+
| ThemeWithParent<Masks>[]
|
|
47
|
+
|
|
48
|
+
type UnionableString = string & {}
|
|
49
|
+
|
|
50
|
+
type ThemeDefinitions<Masks extends string = string> = {
|
|
51
|
+
[key: string]: ThemeDefinition<Masks | UnionableString>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type TemplateDefinitions = {
|
|
55
|
+
[key: string]: Template
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type MaskDefinitions = {
|
|
59
|
+
[key: string]: CreateMask | CreateMask['mask']
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type ThemeBuilderState = {
|
|
63
|
+
palettes?: PaletteDefinitions
|
|
64
|
+
templates?: TemplateDefinitions
|
|
65
|
+
themes?: ThemeDefinitions
|
|
66
|
+
masks?: MaskDefinitions
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type ObjectStringKeys<A extends Object | undefined> = A extends Object
|
|
70
|
+
? Exclude<keyof A, symbol | number>
|
|
71
|
+
: never
|
|
72
|
+
|
|
73
|
+
type GetGeneratedThemeFromTemplate<Template> = {
|
|
74
|
+
[key in keyof Template]: string
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type GetParentTheme<P, Themes extends ThemeDefinitions | undefined> = P extends string
|
|
78
|
+
? P extends keyof Themes
|
|
79
|
+
? Themes[P]
|
|
80
|
+
: GetParentName<P> extends keyof Themes
|
|
81
|
+
? Themes[GetParentName<P>]
|
|
82
|
+
: GetParentName<GetParentName<P>> extends keyof Themes
|
|
83
|
+
? Themes[GetParentName<GetParentName<P>>]
|
|
84
|
+
: GetParentName<GetParentName<GetParentName<P>>> extends keyof Themes
|
|
85
|
+
? Themes[GetParentName<GetParentName<GetParentName<P>>>]
|
|
86
|
+
: never
|
|
87
|
+
: never
|
|
88
|
+
|
|
89
|
+
type GetGeneratedTheme<TD extends any, S extends ThemeBuilderState> = TD extends {
|
|
90
|
+
theme: infer T
|
|
91
|
+
}
|
|
92
|
+
? T
|
|
93
|
+
: TD extends { parent: infer P }
|
|
94
|
+
? // ? GetGeneratedThemeFromTemplate<GetParentTheme<P, S['templates']>>
|
|
95
|
+
GetGeneratedTheme<GetParentTheme<P, S['themes']>, S>
|
|
96
|
+
: TD extends { template: infer T }
|
|
97
|
+
? T extends keyof S['templates']
|
|
98
|
+
? GetGeneratedThemeFromTemplate<S['templates'][T]>
|
|
99
|
+
: TD
|
|
100
|
+
: TD
|
|
101
|
+
|
|
102
|
+
type GetGeneratedThemes<S extends ThemeBuilderState> = {
|
|
103
|
+
[Key in keyof S['themes']]: GetGeneratedTheme<S['themes'][Key], S>
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type GetParentName<N extends string> =
|
|
107
|
+
N extends `${infer A}_${infer B}_${infer C}_${infer D}_${string}`
|
|
108
|
+
? `${A}_${B}_${C}_${D}`
|
|
109
|
+
: N extends `${infer A}_${infer B}_${infer C}_${string}`
|
|
110
|
+
? `${A}_${B}_${C}`
|
|
111
|
+
: N extends `${infer A}_${infer B}_${string}`
|
|
112
|
+
? `${A}_${B}`
|
|
113
|
+
: N extends `${infer A}_${string}`
|
|
114
|
+
? `${A}`
|
|
115
|
+
: never
|
|
116
|
+
|
|
117
|
+
class ThemeBuilder<State extends ThemeBuilderState> {
|
|
118
|
+
constructor(public state: State) {}
|
|
119
|
+
|
|
120
|
+
addPalettes<const P extends PaletteDefinitions>(palettes: P) {
|
|
121
|
+
return new ThemeBuilder({
|
|
122
|
+
...this.state,
|
|
123
|
+
palettes: {
|
|
124
|
+
// as {} prevents generic string key merge messing up types
|
|
125
|
+
...(this.state.palettes as {}),
|
|
126
|
+
...palettes,
|
|
127
|
+
},
|
|
128
|
+
} as const)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
addTemplates<const T extends TemplateDefinitions>(templates: T) {
|
|
132
|
+
return new ThemeBuilder({
|
|
133
|
+
...this.state,
|
|
134
|
+
templates: {
|
|
135
|
+
// as {} prevents generic string key merge messing up types
|
|
136
|
+
...(this.state.templates as {}),
|
|
137
|
+
...templates,
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
addMasks<const T extends MaskDefinitions>(masks: T) {
|
|
143
|
+
return new ThemeBuilder({
|
|
144
|
+
...this.state,
|
|
145
|
+
masks: {
|
|
146
|
+
// as {} prevents generic string key merge messing up types
|
|
147
|
+
...(this.state.masks as {}),
|
|
148
|
+
...(objectFromEntries(
|
|
149
|
+
objectEntries(masks).map(([key, val]) => [key, createMask(val)])
|
|
150
|
+
) as T),
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
addThemes<const T extends ThemeDefinitions<ObjectStringKeys<State['masks']>>>(
|
|
156
|
+
themes: T
|
|
157
|
+
) {
|
|
158
|
+
return new ThemeBuilder({
|
|
159
|
+
...this.state,
|
|
160
|
+
themes: {
|
|
161
|
+
// as {} prevents generic string key merge messing up types
|
|
162
|
+
...(this.state.themes as {}),
|
|
163
|
+
...themes,
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
addChildThemes<
|
|
169
|
+
CTD extends Narrow<ThemeDefinitions<ObjectStringKeys<State['masks']>>>,
|
|
170
|
+
const AvoidNestingWithin extends string[] = []
|
|
171
|
+
>(
|
|
172
|
+
childThemeDefinition: CTD,
|
|
173
|
+
options?: {
|
|
174
|
+
avoidNestingWithin?: AvoidNestingWithin
|
|
175
|
+
}
|
|
176
|
+
) {
|
|
177
|
+
const currentThemes = this.state.themes as State['themes']
|
|
178
|
+
if (!currentThemes) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`No themes defined yet, use addThemes first to set your base themes`
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// AvoidNestingWithin[number] exclude isn't working here...
|
|
185
|
+
type CurrentNames = Exclude<keyof typeof currentThemes, symbol | number>
|
|
186
|
+
type ChildNames = Exclude<keyof CTD, symbol | number>
|
|
187
|
+
|
|
188
|
+
const currentThemeNames = objectKeys(currentThemes) as CurrentNames[]
|
|
189
|
+
const incomingThemeNames = objectKeys(childThemeDefinition) as ChildNames[]
|
|
190
|
+
|
|
191
|
+
const namesWithDefinitions = currentThemeNames.flatMap((prefix) => {
|
|
192
|
+
if (options?.avoidNestingWithin) {
|
|
193
|
+
if (
|
|
194
|
+
options.avoidNestingWithin.some(
|
|
195
|
+
(avoidName) => prefix.startsWith(avoidName) || prefix.endsWith(avoidName)
|
|
196
|
+
)
|
|
197
|
+
) {
|
|
198
|
+
return []
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return incomingThemeNames.map((subName) => {
|
|
203
|
+
const fullName = `${prefix}_${subName}`
|
|
204
|
+
const definition = childThemeDefinition[subName]
|
|
205
|
+
return [fullName, definition] as const
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
const childThemes = objectFromEntries(namesWithDefinitions) as {
|
|
210
|
+
[key in `${CurrentNames}_${ChildNames}`]: CTD & {
|
|
211
|
+
parent: GetParentName<key>
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return new ThemeBuilder({
|
|
216
|
+
...this.state,
|
|
217
|
+
themes: {
|
|
218
|
+
// as {} prevents generic string key merge messing up types
|
|
219
|
+
...(this.state.themes as {}),
|
|
220
|
+
...childThemes,
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
build(): GetGeneratedThemes<State> {
|
|
226
|
+
if (!this.state.themes) {
|
|
227
|
+
return {} as any
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const out = {}
|
|
231
|
+
const maskedThemes: {
|
|
232
|
+
parentName: string
|
|
233
|
+
themeName: string
|
|
234
|
+
mask: ThemeUsingMask
|
|
235
|
+
}[] = []
|
|
236
|
+
|
|
237
|
+
for (const themeName in this.state.themes) {
|
|
238
|
+
const nameParts = themeName.split('_')
|
|
239
|
+
const parentName = nameParts.slice(0, nameParts.length - 1).join('_')
|
|
240
|
+
|
|
241
|
+
const definitions = this.state.themes[themeName]
|
|
242
|
+
const themeDefinition = Array.isArray(definitions)
|
|
243
|
+
? (() => {
|
|
244
|
+
const found = definitions.find((d) => parentName.startsWith(d.parent!))
|
|
245
|
+
if (!found) {
|
|
246
|
+
throw new Error(`No parent for ${themeName}: ${parentName}`)
|
|
247
|
+
}
|
|
248
|
+
return found
|
|
249
|
+
})()
|
|
250
|
+
: definitions
|
|
251
|
+
|
|
252
|
+
if ('theme' in themeDefinition) {
|
|
253
|
+
out[themeName] = themeDefinition.theme
|
|
254
|
+
} else if ('mask' in themeDefinition) {
|
|
255
|
+
maskedThemes.push({ parentName, themeName, mask: themeDefinition })
|
|
256
|
+
} else {
|
|
257
|
+
const {
|
|
258
|
+
palette: paletteName,
|
|
259
|
+
template: templateName,
|
|
260
|
+
...options
|
|
261
|
+
} = themeDefinition
|
|
262
|
+
|
|
263
|
+
if (!this.state.palettes) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`No palettes defined for theme with palette expected: ${themeName}`
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let palette = this.state.palettes[paletteName]
|
|
270
|
+
|
|
271
|
+
if (!palette) {
|
|
272
|
+
const fullPaletteName = `${parentName}_${paletteName}`
|
|
273
|
+
palette = this.state.palettes[fullPaletteName]
|
|
274
|
+
// try using the prefix
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!palette) {
|
|
278
|
+
throw new Error(`No palette for theme ${themeName}: ${paletteName}`)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const template = this.state.templates?.[templateName]
|
|
282
|
+
if (!template) {
|
|
283
|
+
throw new Error(`No template for theme ${themeName}: ${templateName}`)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
out[themeName] = createTheme(palette, template, options)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
for (const { mask, themeName, parentName } of maskedThemes) {
|
|
291
|
+
const parent = out[parentName]
|
|
292
|
+
|
|
293
|
+
if (!parent) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
`No parent theme found with name ${parentName} for theme ${themeName} to use as a mask target`
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const { mask: maskName, ...options } = mask
|
|
300
|
+
const maskFunction = this.state.masks?.[maskName]
|
|
301
|
+
|
|
302
|
+
if (!maskFunction) {
|
|
303
|
+
throw new Error(`No mask ${maskFunction}`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
out[themeName] = applyMask(parent, maskFunction as any, options)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return out as any
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function createThemeBuilder() {
|
|
314
|
+
return new ThemeBuilder({})
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// // test types
|
|
318
|
+
// let x = createThemeBuilder()
|
|
319
|
+
// .addMasks({
|
|
320
|
+
// test: {
|
|
321
|
+
// name: 'mask',
|
|
322
|
+
// mask: (() => {}) as any,
|
|
323
|
+
// },
|
|
324
|
+
// })
|
|
325
|
+
// .addThemes({
|
|
326
|
+
// light: {
|
|
327
|
+
// template: '',
|
|
328
|
+
// palette: '',
|
|
329
|
+
// },
|
|
330
|
+
// dark: {
|
|
331
|
+
// mask: 'test',
|
|
332
|
+
// },
|
|
333
|
+
// })
|
|
334
|
+
// .addChildThemes({
|
|
335
|
+
// List: [
|
|
336
|
+
// {
|
|
337
|
+
// parent: '',
|
|
338
|
+
// mask: 'test',
|
|
339
|
+
// },
|
|
340
|
+
// ],
|
|
341
|
+
// })
|
|
342
|
+
|
|
343
|
+
// x
|
|
344
|
+
|
|
345
|
+
// x.state.themes
|
|
346
|
+
// x.state.masks
|
|
347
|
+
|
|
348
|
+
// let y = x.addChildThemes({
|
|
349
|
+
// blue: {
|
|
350
|
+
// mask: 'ok',
|
|
351
|
+
// },
|
|
352
|
+
// })
|