@animus-ui/system 0.1.0-next.v7 → 0.1.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/README.md +148 -0
- package/dist/Animus.d.ts +36 -30
- package/dist/Animus.d.ts.map +1 -1
- package/dist/AnimusExtended.d.ts +32 -27
- package/dist/AnimusExtended.d.ts.map +1 -1
- package/dist/SystemBuilder.d.ts +38 -32
- package/dist/SystemBuilder.d.ts.map +1 -1
- package/dist/compose.d.ts +24 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +36 -0
- package/dist/composeWithContext.d.ts +30 -0
- package/dist/composeWithContext.d.ts.map +1 -0
- package/dist/composeWithContext.js +79 -0
- package/dist/createComposedFamily-BsyI6rBc.js +230 -0
- package/dist/groups/index.d.ts +455 -359
- package/dist/groups/index.d.ts.map +1 -1
- package/dist/groups/index.js +139 -55
- package/dist/index.d.ts +14 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +708 -102
- package/dist/keyframes.d.ts +45 -0
- package/dist/keyframes.d.ts.map +1 -0
- package/dist/runtime/createClassResolver.d.ts +10 -0
- package/dist/runtime/createClassResolver.d.ts.map +1 -0
- package/dist/runtime/createComposedFamily.d.ts +16 -0
- package/dist/runtime/createComposedFamily.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +26 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/resolveClasses.d.ts +59 -0
- package/dist/runtime/resolveClasses.d.ts.map +1 -0
- package/dist/runtime-entry.d.ts +10 -0
- package/dist/runtime-entry.d.ts.map +1 -0
- package/dist/runtime-entry.js +2 -0
- package/dist/selectors.d.ts +40 -0
- package/dist/selectors.d.ts.map +1 -0
- package/dist/size-CusLCguT.js +97 -0
- package/dist/theme/createTheme.d.ts +59 -0
- package/dist/theme/createTheme.d.ts.map +1 -0
- package/dist/theme/flattenScale.d.ts +21 -0
- package/dist/theme/flattenScale.d.ts.map +1 -0
- package/dist/theme/index.d.ts +5 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/serializeTokens.d.ts +8 -0
- package/dist/theme/serializeTokens.d.ts.map +1 -0
- package/dist/theme/types.d.ts +42 -0
- package/dist/theme/types.d.ts.map +1 -0
- package/dist/theme/utils.d.ts +28 -0
- package/dist/theme/utils.d.ts.map +1 -0
- package/dist/transforms/border.d.ts +4 -0
- package/dist/transforms/border.d.ts.map +1 -1
- package/dist/transforms/createTransform.d.ts +3 -1
- package/dist/transforms/createTransform.d.ts.map +1 -1
- package/dist/transforms/grid.d.ts +12 -2
- package/dist/transforms/grid.d.ts.map +1 -1
- package/dist/transforms/index.d.ts +0 -1
- package/dist/transforms/index.d.ts.map +1 -1
- package/dist/transforms/size.d.ts +8 -0
- package/dist/transforms/size.d.ts.map +1 -1
- package/dist/types/component.d.ts +148 -24
- package/dist/types/component.d.ts.map +1 -1
- package/dist/types/config.d.ts +54 -7
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/properties.d.ts +5 -7
- package/dist/types/properties.d.ts.map +1 -1
- package/dist/types/props.d.ts +13 -25
- package/dist/types/props.d.ts.map +1 -1
- package/dist/types/shared.d.ts +0 -1
- package/dist/types/shared.d.ts.map +1 -1
- package/dist/types/theme.d.ts +77 -11
- package/dist/types/theme.d.ts.map +1 -1
- package/dist/utils/deepMerge.d.ts +5 -0
- package/dist/utils/deepMerge.d.ts.map +1 -0
- package/package.json +46 -17
- package/dist/PropertyBuilder.d.ts +0 -11
- package/dist/PropertyBuilder.d.ts.map +0 -1
- package/dist/size-Dge_rsuz.js +0 -70
- package/dist/transforms/utils.d.ts +0 -3
- package/dist/transforms/utils.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
//#region src/
|
|
5
|
-
|
|
1
|
+
import { n as createClassResolver, r as createComponent, t as createComposedFamily } from "./createComposedFamily-BsyI6rBc.js";
|
|
2
|
+
import { compose } from "./compose.js";
|
|
3
|
+
import { a as borderShorthand, c as numericOrStringScale, i as gridItemRatio, l as numericScale, n as size, o as createTransform, r as gridItem, s as createScale, t as percentageOrAbsolute, u as stringScale } from "./size-CusLCguT.js";
|
|
4
|
+
//#region src/utils/deepMerge.ts
|
|
5
|
+
/**
|
|
6
|
+
* Deep merge utility — replaces lodash.merge for variant accumulation.
|
|
7
|
+
*/
|
|
8
|
+
function deepMerge(target, source) {
|
|
6
9
|
const result = { ...target };
|
|
7
|
-
for (const key of Object.keys(source)) if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) result[key] = deepMerge
|
|
10
|
+
for (const key of Object.keys(source)) if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) result[key] = deepMerge(target[key], source[key]);
|
|
8
11
|
else result[key] = source[key];
|
|
9
12
|
return result;
|
|
10
13
|
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/AnimusExtended.ts
|
|
11
16
|
var AnimusExtendedWithAll = class {
|
|
12
17
|
propRegistry = {};
|
|
13
18
|
groupRegistry = {};
|
|
@@ -16,7 +21,8 @@ var AnimusExtendedWithAll = class {
|
|
|
16
21
|
variants = {};
|
|
17
22
|
activeGroups = {};
|
|
18
23
|
custom = {};
|
|
19
|
-
|
|
24
|
+
compounds = [];
|
|
25
|
+
constructor(props, groups, base, variants, states, activeGroups, custom, compounds = []) {
|
|
20
26
|
this.propRegistry = props;
|
|
21
27
|
this.groupRegistry = groups;
|
|
22
28
|
this.baseStyles = base;
|
|
@@ -24,9 +30,10 @@ var AnimusExtendedWithAll = class {
|
|
|
24
30
|
this.statesConfig = states;
|
|
25
31
|
this.activeGroups = activeGroups;
|
|
26
32
|
this.custom = custom;
|
|
33
|
+
this.compounds = compounds;
|
|
27
34
|
}
|
|
28
35
|
extend() {
|
|
29
|
-
return new AnimusExtended(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, this.custom);
|
|
36
|
+
return new AnimusExtended(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, this.custom, this.compounds);
|
|
30
37
|
}
|
|
31
38
|
asElement(component) {
|
|
32
39
|
const Component = createComponent(component, "", this._buildComponentConfig());
|
|
@@ -38,8 +45,8 @@ var AnimusExtendedWithAll = class {
|
|
|
38
45
|
const extendFn = this.extend.bind(this);
|
|
39
46
|
return Object.assign(Component, { extend: extendFn });
|
|
40
47
|
}
|
|
41
|
-
|
|
42
|
-
return
|
|
48
|
+
asClass() {
|
|
49
|
+
return createClassResolver("", this._buildComponentConfig());
|
|
43
50
|
}
|
|
44
51
|
_buildComponentConfig() {
|
|
45
52
|
const variantConfig = {};
|
|
@@ -61,45 +68,50 @@ var AnimusExtendedWithAll = class {
|
|
|
61
68
|
};
|
|
62
69
|
var AnimusExtendedWithSystem = class extends AnimusExtendedWithAll {
|
|
63
70
|
props(config) {
|
|
64
|
-
return new AnimusExtendedWithAll(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, deepMerge
|
|
71
|
+
return new AnimusExtendedWithAll(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, deepMerge({}, config), this.compounds);
|
|
65
72
|
}
|
|
66
73
|
};
|
|
67
74
|
var AnimusExtendedWithStates = class extends AnimusExtendedWithSystem {
|
|
68
|
-
|
|
69
|
-
return new AnimusExtendedWithSystem(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, deepMerge
|
|
75
|
+
system(config) {
|
|
76
|
+
return new AnimusExtendedWithSystem(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, deepMerge(this.activeGroups, config), this.custom, this.compounds);
|
|
70
77
|
}
|
|
71
78
|
};
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
var AnimusExtendedWithCompounds = class AnimusExtendedWithCompounds extends AnimusExtendedWithStates {
|
|
80
|
+
compound(condition, styles) {
|
|
81
|
+
return new AnimusExtendedWithCompounds(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, this.custom, [...this.compounds, {
|
|
82
|
+
condition,
|
|
83
|
+
styles
|
|
84
|
+
}]);
|
|
76
85
|
}
|
|
77
86
|
states(config) {
|
|
78
|
-
return new AnimusExtendedWithStates(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, deepMerge
|
|
87
|
+
return new AnimusExtendedWithStates(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, deepMerge(this.statesConfig, config), this.activeGroups, this.custom, this.compounds);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var AnimusExtendedWithVariants = class AnimusExtendedWithVariants extends AnimusExtendedWithCompounds {
|
|
91
|
+
compound(condition, styles) {
|
|
92
|
+
return new AnimusExtendedWithCompounds(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, this.custom, [...this.compounds, {
|
|
93
|
+
condition,
|
|
94
|
+
styles
|
|
95
|
+
}]);
|
|
96
|
+
}
|
|
97
|
+
variant(options) {
|
|
98
|
+
const prop = options.prop || "variant";
|
|
99
|
+
return new AnimusExtendedWithVariants(this.propRegistry, this.groupRegistry, this.baseStyles, deepMerge(this.variants, { [prop]: options }), this.statesConfig, this.activeGroups, this.custom, this.compounds);
|
|
79
100
|
}
|
|
80
101
|
};
|
|
81
102
|
var AnimusExtendedWithBase = class extends AnimusExtendedWithVariants {
|
|
82
103
|
variant(options) {
|
|
83
104
|
const prop = options.prop || "variant";
|
|
84
|
-
return new AnimusExtendedWithVariants(this.propRegistry, this.groupRegistry, this.baseStyles, deepMerge
|
|
105
|
+
return new AnimusExtendedWithVariants(this.propRegistry, this.groupRegistry, this.baseStyles, deepMerge(this.variants, { [prop]: options }), this.statesConfig, this.activeGroups, this.custom, this.compounds);
|
|
85
106
|
}
|
|
86
107
|
};
|
|
87
108
|
var AnimusExtended = class extends AnimusExtendedWithBase {
|
|
88
109
|
styles(config) {
|
|
89
|
-
return new AnimusExtendedWithBase(this.propRegistry, this.groupRegistry, deepMerge
|
|
110
|
+
return new AnimusExtendedWithBase(this.propRegistry, this.groupRegistry, deepMerge(this.baseStyles, config), this.variants, this.statesConfig, this.activeGroups, this.custom, this.compounds);
|
|
90
111
|
}
|
|
91
112
|
};
|
|
92
113
|
//#endregion
|
|
93
114
|
//#region src/Animus.ts
|
|
94
|
-
/**
|
|
95
|
-
* Deep merge utility — replaces lodash.merge for variant accumulation.
|
|
96
|
-
*/
|
|
97
|
-
function deepMerge(target, source) {
|
|
98
|
-
const result = { ...target };
|
|
99
|
-
for (const key of Object.keys(source)) if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) result[key] = deepMerge(target[key], source[key]);
|
|
100
|
-
else result[key] = source[key];
|
|
101
|
-
return result;
|
|
102
|
-
}
|
|
103
115
|
var AnimusWithAll = class {
|
|
104
116
|
propRegistry = {};
|
|
105
117
|
groupRegistry = {};
|
|
@@ -108,7 +120,8 @@ var AnimusWithAll = class {
|
|
|
108
120
|
variants = {};
|
|
109
121
|
activeGroups = {};
|
|
110
122
|
custom = {};
|
|
111
|
-
|
|
123
|
+
compounds = [];
|
|
124
|
+
constructor(props, groups, base, variants, states, activeGroups, custom, compounds = []) {
|
|
112
125
|
this.propRegistry = props;
|
|
113
126
|
this.groupRegistry = groups;
|
|
114
127
|
this.baseStyles = base;
|
|
@@ -116,9 +129,10 @@ var AnimusWithAll = class {
|
|
|
116
129
|
this.statesConfig = states;
|
|
117
130
|
this.activeGroups = activeGroups;
|
|
118
131
|
this.custom = custom;
|
|
132
|
+
this.compounds = compounds;
|
|
119
133
|
}
|
|
120
134
|
extend() {
|
|
121
|
-
return new AnimusExtended(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, this.custom);
|
|
135
|
+
return new AnimusExtended(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, this.custom, this.compounds);
|
|
122
136
|
}
|
|
123
137
|
asElement(component) {
|
|
124
138
|
const Component = createComponent(component, "", this._buildComponentConfig());
|
|
@@ -130,8 +144,8 @@ var AnimusWithAll = class {
|
|
|
130
144
|
const extendFn = this.extend.bind(this);
|
|
131
145
|
return Object.assign(Component, { extend: extendFn });
|
|
132
146
|
}
|
|
133
|
-
|
|
134
|
-
return
|
|
147
|
+
asClass() {
|
|
148
|
+
return createClassResolver("", this._buildComponentConfig());
|
|
135
149
|
}
|
|
136
150
|
_buildComponentConfig() {
|
|
137
151
|
const variantConfig = {};
|
|
@@ -152,31 +166,48 @@ var AnimusWithAll = class {
|
|
|
152
166
|
}
|
|
153
167
|
};
|
|
154
168
|
var AnimusWithSystem = class extends AnimusWithAll {
|
|
155
|
-
constructor(props, groups, base, variants, states, activeGroups) {
|
|
156
|
-
super(props, groups, base, variants, states, activeGroups, {});
|
|
169
|
+
constructor(props, groups, base, variants, states, activeGroups, compounds = []) {
|
|
170
|
+
super(props, groups, base, variants, states, activeGroups, {}, compounds);
|
|
157
171
|
}
|
|
158
172
|
props(config) {
|
|
159
|
-
return new AnimusWithAll(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, config);
|
|
173
|
+
return new AnimusWithAll(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, this.activeGroups, config, this.compounds);
|
|
160
174
|
}
|
|
161
175
|
};
|
|
162
176
|
var AnimusWithStates = class extends AnimusWithSystem {
|
|
163
|
-
constructor(props, groups, base, variants, states) {
|
|
164
|
-
super(props, groups, base, variants, states, {});
|
|
177
|
+
constructor(props, groups, base, variants, states, compounds = []) {
|
|
178
|
+
super(props, groups, base, variants, states, {}, compounds);
|
|
165
179
|
}
|
|
166
|
-
|
|
167
|
-
return new AnimusWithSystem(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, config);
|
|
180
|
+
system(config) {
|
|
181
|
+
return new AnimusWithSystem(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, this.statesConfig, config, this.compounds);
|
|
168
182
|
}
|
|
169
183
|
};
|
|
170
|
-
var
|
|
171
|
-
constructor(props, groups, base, variants) {
|
|
172
|
-
super(props, groups, base, variants, {});
|
|
184
|
+
var AnimusWithCompounds = class AnimusWithCompounds extends AnimusWithStates {
|
|
185
|
+
constructor(props, groups, base, variants, compounds = []) {
|
|
186
|
+
super(props, groups, base, variants, {}, compounds);
|
|
187
|
+
}
|
|
188
|
+
compound(condition, styles) {
|
|
189
|
+
return new AnimusWithCompounds(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, [...this.compounds, {
|
|
190
|
+
condition,
|
|
191
|
+
styles
|
|
192
|
+
}]);
|
|
173
193
|
}
|
|
174
194
|
states(config) {
|
|
175
|
-
return new AnimusWithStates(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, config);
|
|
195
|
+
return new AnimusWithStates(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, config, this.compounds);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
var AnimusWithVariants = class AnimusWithVariants extends AnimusWithCompounds {
|
|
199
|
+
constructor(props, groups, base, variants, compounds = []) {
|
|
200
|
+
super(props, groups, base, variants, compounds);
|
|
201
|
+
}
|
|
202
|
+
compound(condition, styles) {
|
|
203
|
+
return new AnimusWithCompounds(this.propRegistry, this.groupRegistry, this.baseStyles, this.variants, [...this.compounds, {
|
|
204
|
+
condition,
|
|
205
|
+
styles
|
|
206
|
+
}]);
|
|
176
207
|
}
|
|
177
208
|
variant(options) {
|
|
178
209
|
const prop = options.prop || "variant";
|
|
179
|
-
return new AnimusWithVariants(this.propRegistry, this.groupRegistry, this.baseStyles, deepMerge(this.variants, { [prop]: options }));
|
|
210
|
+
return new AnimusWithVariants(this.propRegistry, this.groupRegistry, this.baseStyles, deepMerge(this.variants, { [prop]: options }), this.compounds);
|
|
180
211
|
}
|
|
181
212
|
};
|
|
182
213
|
var AnimusWithBase = class extends AnimusWithVariants {
|
|
@@ -185,7 +216,7 @@ var AnimusWithBase = class extends AnimusWithVariants {
|
|
|
185
216
|
}
|
|
186
217
|
variant(options) {
|
|
187
218
|
const prop = options.prop || "variant";
|
|
188
|
-
return new AnimusWithVariants(this.propRegistry, this.groupRegistry, this.baseStyles, deepMerge(this.variants, { [prop]: options }));
|
|
219
|
+
return new AnimusWithVariants(this.propRegistry, this.groupRegistry, this.baseStyles, deepMerge(this.variants, { [prop]: options }), this.compounds);
|
|
189
220
|
}
|
|
190
221
|
};
|
|
191
222
|
var Animus = class extends AnimusWithBase {
|
|
@@ -197,78 +228,275 @@ var Animus = class extends AnimusWithBase {
|
|
|
197
228
|
}
|
|
198
229
|
};
|
|
199
230
|
//#endregion
|
|
200
|
-
//#region src/
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
231
|
+
//#region src/keyframes.ts
|
|
232
|
+
const fnv1a = (input) => {
|
|
233
|
+
let hash = 2166136261;
|
|
234
|
+
for (let i = 0; i < input.length; i++) {
|
|
235
|
+
hash ^= input.charCodeAt(i);
|
|
236
|
+
hash = hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24)) >>> 0;
|
|
237
|
+
}
|
|
238
|
+
return hash.toString(36);
|
|
239
|
+
};
|
|
240
|
+
const serializeFrames = (frames) => {
|
|
241
|
+
return Object.keys(frames).sort().map((stop) => {
|
|
242
|
+
const frame = frames[stop] ?? {};
|
|
243
|
+
return `${stop}{${Object.keys(frame).sort().map((p) => `${p}:${String(frame[p])}`).join(";")}}`;
|
|
244
|
+
}).join("|");
|
|
245
|
+
};
|
|
246
|
+
const generateName = (frames) => `animus-kf-${fnv1a(serializeFrames(frames))}`;
|
|
247
|
+
const createRef = (name, resolvedName) => ({
|
|
248
|
+
__brand: "KeyframeRef",
|
|
249
|
+
__name: name,
|
|
250
|
+
toString() {
|
|
251
|
+
return resolvedName;
|
|
252
|
+
},
|
|
253
|
+
valueOf() {
|
|
254
|
+
return resolvedName;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
function keyframes(map) {
|
|
258
|
+
const frameData = {};
|
|
259
|
+
const refs = {};
|
|
260
|
+
for (const key of Object.keys(map)) {
|
|
261
|
+
const frames = map[key];
|
|
262
|
+
const resolvedName = generateName(frames);
|
|
263
|
+
frameData[key] = {
|
|
264
|
+
name: resolvedName,
|
|
265
|
+
frames
|
|
266
|
+
};
|
|
267
|
+
refs[key] = createRef(key, resolvedName);
|
|
207
268
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
269
|
+
return {
|
|
270
|
+
__brand: "Keyframes",
|
|
271
|
+
__frames: frameData,
|
|
272
|
+
...refs
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/selectors.ts
|
|
277
|
+
/**
|
|
278
|
+
* Built-in selector aliases.
|
|
279
|
+
*
|
|
280
|
+
* Compound selectors (e.g. `_disabled`) target multiple CSS selectors
|
|
281
|
+
* via comma-separation to cover native, ARIA, and data attribute conventions.
|
|
282
|
+
*/
|
|
283
|
+
const BUILT_IN_SELECTORS = {
|
|
284
|
+
_link: {
|
|
285
|
+
selector: "&:link",
|
|
286
|
+
order: 10
|
|
287
|
+
},
|
|
288
|
+
_visited: {
|
|
289
|
+
selector: "&:visited",
|
|
290
|
+
order: 20
|
|
291
|
+
},
|
|
292
|
+
_hover: {
|
|
293
|
+
selector: "&:hover",
|
|
294
|
+
order: 30
|
|
295
|
+
},
|
|
296
|
+
_focusWithin: {
|
|
297
|
+
selector: "&:focus-within",
|
|
298
|
+
order: 40
|
|
299
|
+
},
|
|
300
|
+
_focus: {
|
|
301
|
+
selector: "&:focus",
|
|
302
|
+
order: 50
|
|
303
|
+
},
|
|
304
|
+
_focusVisible: {
|
|
305
|
+
selector: "&:focus-visible",
|
|
306
|
+
order: 60
|
|
307
|
+
},
|
|
308
|
+
_active: {
|
|
309
|
+
selector: "&:active",
|
|
310
|
+
order: 70
|
|
311
|
+
},
|
|
312
|
+
_target: {
|
|
313
|
+
selector: "&:target",
|
|
314
|
+
order: 80
|
|
315
|
+
},
|
|
316
|
+
_checked: {
|
|
317
|
+
selector: "&:checked, &[aria-checked=\"true\"], &[data-checked]",
|
|
318
|
+
order: 100
|
|
319
|
+
},
|
|
320
|
+
_invalid: {
|
|
321
|
+
selector: "&:invalid, &[aria-invalid=\"true\"], &[data-invalid]",
|
|
322
|
+
order: 110
|
|
323
|
+
},
|
|
324
|
+
_required: {
|
|
325
|
+
selector: "&:required, &[aria-required=\"true\"]",
|
|
326
|
+
order: 120
|
|
327
|
+
},
|
|
328
|
+
_readOnly: {
|
|
329
|
+
selector: "&:read-only, &[aria-readonly=\"true\"], &[data-readonly]",
|
|
330
|
+
order: 130
|
|
331
|
+
},
|
|
332
|
+
_expanded: {
|
|
333
|
+
selector: "&[aria-expanded=\"true\"], &[data-expanded]",
|
|
334
|
+
order: 140
|
|
335
|
+
},
|
|
336
|
+
_selected: {
|
|
337
|
+
selector: "&[aria-selected=\"true\"], &[data-selected]",
|
|
338
|
+
order: 150
|
|
339
|
+
},
|
|
340
|
+
_pressed: {
|
|
341
|
+
selector: "&[aria-pressed=\"true\"], &[data-pressed]",
|
|
342
|
+
order: 160
|
|
343
|
+
},
|
|
344
|
+
_disabled: {
|
|
345
|
+
selector: "&:disabled, &[disabled], &[aria-disabled=\"true\"], &[data-disabled]",
|
|
346
|
+
order: 200
|
|
347
|
+
},
|
|
348
|
+
_before: {
|
|
349
|
+
selector: "&::before",
|
|
350
|
+
order: 300
|
|
351
|
+
},
|
|
352
|
+
_after: {
|
|
353
|
+
selector: "&::after",
|
|
354
|
+
order: 310
|
|
355
|
+
},
|
|
356
|
+
_placeholder: {
|
|
357
|
+
selector: "&::placeholder",
|
|
358
|
+
order: 320
|
|
359
|
+
},
|
|
360
|
+
_selection: {
|
|
361
|
+
selector: "&::selection",
|
|
362
|
+
order: 330
|
|
363
|
+
},
|
|
364
|
+
_first: {
|
|
365
|
+
selector: "&:first-child",
|
|
366
|
+
order: 400
|
|
367
|
+
},
|
|
368
|
+
_last: {
|
|
369
|
+
selector: "&:last-child",
|
|
370
|
+
order: 410
|
|
371
|
+
},
|
|
372
|
+
_even: {
|
|
373
|
+
selector: "&:nth-child(even)",
|
|
374
|
+
order: 420
|
|
375
|
+
},
|
|
376
|
+
_odd: {
|
|
377
|
+
selector: "&:nth-child(odd)",
|
|
378
|
+
order: 430
|
|
379
|
+
},
|
|
380
|
+
_empty: {
|
|
381
|
+
selector: "&:empty",
|
|
382
|
+
order: 440
|
|
217
383
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* Merge user-provided selectors with built-in defaults.
|
|
387
|
+
* User selectors override built-in aliases of the same name.
|
|
388
|
+
* New aliases get an order value based on their position (500+).
|
|
389
|
+
*/
|
|
390
|
+
function mergeSelectors(base, custom) {
|
|
391
|
+
const merged = { ...base };
|
|
392
|
+
let nextOrder = 500;
|
|
393
|
+
for (const [key, selector] of Object.entries(custom)) if (key in merged) merged[key] = {
|
|
394
|
+
selector,
|
|
395
|
+
order: merged[key].order
|
|
396
|
+
};
|
|
397
|
+
else {
|
|
398
|
+
merged[key] = {
|
|
399
|
+
selector,
|
|
400
|
+
order: nextOrder
|
|
222
401
|
};
|
|
402
|
+
nextOrder += 10;
|
|
223
403
|
}
|
|
224
|
-
|
|
404
|
+
return merged;
|
|
405
|
+
}
|
|
406
|
+
/** Get the sorted alias keys for deterministic cascade ordering. */
|
|
407
|
+
function getSortedAliasKeys(map) {
|
|
408
|
+
return Object.keys(map).sort((a, b) => map[a].order - map[b].order);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Serialize the selector map for the extraction pipeline.
|
|
412
|
+
* Emits a flat `Record<string, string>` (alias → selector) plus
|
|
413
|
+
* the ordered key list for cascade determinism.
|
|
414
|
+
*/
|
|
415
|
+
function serializeSelectorMap(map) {
|
|
416
|
+
const selectors = {};
|
|
417
|
+
const order = getSortedAliasKeys(map);
|
|
418
|
+
for (const key of order) selectors[key] = map[key].selector;
|
|
419
|
+
return {
|
|
420
|
+
selectors,
|
|
421
|
+
order
|
|
422
|
+
};
|
|
423
|
+
}
|
|
225
424
|
//#endregion
|
|
226
425
|
//#region src/SystemBuilder.ts
|
|
227
426
|
var SystemBuilder = class SystemBuilder {
|
|
228
|
-
#tokens;
|
|
229
427
|
#propRegistry;
|
|
230
428
|
#groupRegistry;
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
429
|
+
#selectorRegistry;
|
|
430
|
+
#includesRegistry;
|
|
431
|
+
constructor(propRegistry, groupRegistry, selectorRegistry, includesRegistry) {
|
|
234
432
|
this.#propRegistry = propRegistry || {};
|
|
235
433
|
this.#groupRegistry = groupRegistry || {};
|
|
236
|
-
this.#
|
|
434
|
+
this.#selectorRegistry = selectorRegistry || { ...BUILT_IN_SELECTORS };
|
|
435
|
+
this.#includesRegistry = includesRegistry || [];
|
|
237
436
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
sm: 0,
|
|
242
|
-
md: 0,
|
|
243
|
-
lg: 0,
|
|
244
|
-
xl: 0
|
|
245
|
-
} })), this.#propRegistry, this.#groupRegistry, this.#globalStyles);
|
|
437
|
+
addSelectors(selectors) {
|
|
438
|
+
const merged = mergeSelectors(this.#selectorRegistry, selectors);
|
|
439
|
+
return new SystemBuilder(this.#propRegistry, this.#groupRegistry, merged, this.#includesRegistry);
|
|
246
440
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
441
|
+
addGroup(name, config) {
|
|
442
|
+
if (name in this.#propRegistry) throw new Error(`Group name "${name}" collides with an existing prop name. Group names and prop names must be disjoint.`);
|
|
443
|
+
for (const key of Object.keys(config)) if (key in this.#propRegistry) {
|
|
444
|
+
const existing = this.#propRegistry[key];
|
|
445
|
+
const incoming = config[key];
|
|
446
|
+
if (existing.property !== incoming.property || existing.scale !== incoming.scale || existing.transform !== incoming.transform || existing.negative !== incoming.negative) throw new Error(`Prop "${key}" already registered with a different definition. Existing: property="${existing.property}", scale="${String(existing.scale)}". Incoming: property="${incoming.property}", scale="${String(incoming.scale)}".`);
|
|
447
|
+
}
|
|
448
|
+
const nextProps = {
|
|
449
|
+
...this.#propRegistry,
|
|
450
|
+
...config
|
|
451
|
+
};
|
|
452
|
+
const newGroup = { [name]: Object.keys(config) };
|
|
453
|
+
return new SystemBuilder(nextProps, {
|
|
454
|
+
...this.#groupRegistry,
|
|
455
|
+
...newGroup
|
|
456
|
+
}, this.#selectorRegistry, this.#includesRegistry);
|
|
457
|
+
}
|
|
458
|
+
addProps(config) {
|
|
459
|
+
for (const key of Object.keys(config)) if (key in this.#groupRegistry) throw new Error(`Prop name "${key}" collides with an existing group name. Group names and prop names must be disjoint.`);
|
|
460
|
+
for (const key of Object.keys(config)) if (key in this.#propRegistry) {
|
|
461
|
+
const existing = this.#propRegistry[key];
|
|
462
|
+
const incoming = config[key];
|
|
463
|
+
if (existing.property !== incoming.property || existing.scale !== incoming.scale || existing.transform !== incoming.transform || existing.negative !== incoming.negative) throw new Error(`Prop "${key}" already registered with a different definition.`);
|
|
464
|
+
}
|
|
465
|
+
return new SystemBuilder({
|
|
466
|
+
...this.#propRegistry,
|
|
467
|
+
...config
|
|
468
|
+
}, this.#groupRegistry, this.#selectorRegistry, this.#includesRegistry);
|
|
253
469
|
}
|
|
254
470
|
build() {
|
|
255
471
|
const animus = new Animus(this.#propRegistry, this.#groupRegistry);
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
472
|
+
const propRegistry = this.#propRegistry;
|
|
473
|
+
const groupRegistry = this.#groupRegistry;
|
|
474
|
+
const selectorRegistry = this.#selectorRegistry;
|
|
475
|
+
const system = Object.assign(animus, { toConfig: () => {
|
|
476
|
+
return serializeInstance(propRegistry, groupRegistry, selectorRegistry);
|
|
477
|
+
} });
|
|
478
|
+
const createGlobalStyles = ((styles) => ({
|
|
479
|
+
__brand: "GlobalStyleBlock",
|
|
480
|
+
styles
|
|
481
|
+
}));
|
|
482
|
+
const createKeyframes = ((frames) => keyframes(frames));
|
|
483
|
+
return {
|
|
484
|
+
system,
|
|
485
|
+
createGlobalStyles,
|
|
486
|
+
createKeyframes
|
|
487
|
+
};
|
|
263
488
|
}
|
|
264
489
|
};
|
|
265
|
-
function serializeInstance(
|
|
490
|
+
function serializeInstance(propRegistry, groupRegistry, selectorRegistry) {
|
|
266
491
|
const serialized = {};
|
|
267
492
|
const transforms = {};
|
|
268
493
|
for (const [propName, entry] of Object.entries(propRegistry)) {
|
|
269
494
|
const s = { property: entry.property };
|
|
270
495
|
if (entry.properties && entry.properties.length > 0) s.properties = [...entry.properties];
|
|
271
|
-
|
|
496
|
+
const scale = entry.scale;
|
|
497
|
+
if (typeof scale === "string") s.scale = scale;
|
|
498
|
+
else if (scale && typeof scale === "object") s.scale = scale;
|
|
499
|
+
if (entry.negative) s.negative = true;
|
|
272
500
|
if (entry.transform) {
|
|
273
501
|
const fn = entry.transform;
|
|
274
502
|
const name = fn.transformName ?? fn.name;
|
|
@@ -277,19 +505,397 @@ function serializeInstance(tokens, propRegistry, groupRegistry, globalStyles) {
|
|
|
277
505
|
transforms[name] = fn;
|
|
278
506
|
}
|
|
279
507
|
}
|
|
508
|
+
if (entry.currentVar) s.currentVar = entry.currentVar;
|
|
280
509
|
serialized[propName] = s;
|
|
281
510
|
}
|
|
282
|
-
const
|
|
283
|
-
|
|
511
|
+
const { selectors, order } = serializeSelectorMap(selectorRegistry);
|
|
512
|
+
return {
|
|
284
513
|
propConfig: JSON.stringify(serialized),
|
|
285
514
|
groupRegistry: JSON.stringify(groupRegistry),
|
|
286
|
-
transforms
|
|
515
|
+
transforms,
|
|
516
|
+
selectorAliases: JSON.stringify(selectors),
|
|
517
|
+
selectorOrder: JSON.stringify(order)
|
|
287
518
|
};
|
|
288
|
-
|
|
519
|
+
}
|
|
520
|
+
function createSystem(config) {
|
|
521
|
+
return new SystemBuilder(void 0, void 0, void 0, config?.includes ?? []);
|
|
522
|
+
}
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/theme/utils.ts
|
|
525
|
+
/** Type guard: true for non-null, non-array objects */
|
|
526
|
+
function isObject(value) {
|
|
527
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
528
|
+
}
|
|
529
|
+
function merge(target, ...sources) {
|
|
530
|
+
for (const source of sources) {
|
|
531
|
+
if (!source) continue;
|
|
532
|
+
for (const key of Object.keys(source)) {
|
|
533
|
+
const targetVal = target[key];
|
|
534
|
+
const sourceVal = source[key];
|
|
535
|
+
if (isObject(targetVal) && isObject(sourceVal)) target[key] = merge(targetVal, sourceVal);
|
|
536
|
+
else target[key] = sourceVal;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return target;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Resolve a dot-path string against a nested object.
|
|
543
|
+
* walkDotPath({ gray: { 50: '#fafafa' } }, 'gray.50') → '#fafafa'
|
|
544
|
+
* The `_` identity key is handled: 'primary' resolves to obj.primary._ if obj.primary is an object with _.
|
|
545
|
+
*/
|
|
546
|
+
function walkDotPath(obj, path) {
|
|
547
|
+
const parts = path.split(".");
|
|
548
|
+
let current = obj;
|
|
549
|
+
for (const part of parts) {
|
|
550
|
+
if (!isObject(current)) return void 0;
|
|
551
|
+
current = current[part];
|
|
552
|
+
}
|
|
553
|
+
return current;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Flatten a nested object into a flat Record with dot-path keys.
|
|
557
|
+
* The `_` key is an identity marker — it produces the parent key without suffix.
|
|
558
|
+
* { gray: { 50: '#fafafa' } } → { 'gray.50': '#fafafa' }
|
|
559
|
+
* { primary: { _: 'ember', hover: 'x' } } → { 'primary': 'ember', 'primary.hover': 'x' }
|
|
560
|
+
* CSS variable names use dash-join, computed at the serialization boundary (not here).
|
|
561
|
+
*/
|
|
562
|
+
function flattenToDotPaths(object, path) {
|
|
563
|
+
const result = {};
|
|
564
|
+
for (const key of Object.keys(object)) {
|
|
565
|
+
const nextKey = path ? key === "_" ? path : `${path}.${key}` : key;
|
|
566
|
+
const current = object[key];
|
|
567
|
+
if (isObject(current)) Object.assign(result, flattenToDotPaths(current, nextKey));
|
|
568
|
+
else result[nextKey] = current;
|
|
569
|
+
}
|
|
289
570
|
return result;
|
|
290
571
|
}
|
|
291
|
-
|
|
292
|
-
|
|
572
|
+
/**
|
|
573
|
+
* Convert a dot-path key to a dash-join key for CSS variable naming.
|
|
574
|
+
* 'gray.50' → 'gray-50'
|
|
575
|
+
* 'primary.hover' → 'primary-hover'
|
|
576
|
+
*/
|
|
577
|
+
function dotToDash(dotPath) {
|
|
578
|
+
return dotPath.replace(/\./g, "-");
|
|
579
|
+
}
|
|
580
|
+
/** Map over object values — matches lodash.mapValues overload signatures */
|
|
581
|
+
//#endregion
|
|
582
|
+
//#region src/theme/createTheme.ts
|
|
583
|
+
const COLOR_FUNCTION_PREFIXES = [
|
|
584
|
+
"rgb(",
|
|
585
|
+
"rgba(",
|
|
586
|
+
"hsl(",
|
|
587
|
+
"hsla(",
|
|
588
|
+
"oklch(",
|
|
589
|
+
"oklab(",
|
|
590
|
+
"lch(",
|
|
591
|
+
"lab(",
|
|
592
|
+
"color(",
|
|
593
|
+
"color-mix("
|
|
594
|
+
];
|
|
595
|
+
/** Validate that a value is a valid CSS <color>. */
|
|
596
|
+
function isValidCSSColor(value) {
|
|
597
|
+
if (typeof value !== "string") return false;
|
|
598
|
+
const v = value.trim();
|
|
599
|
+
if (v === "") return false;
|
|
600
|
+
if (v === "transparent" || v === "currentColor" || v === "currentcolor") return true;
|
|
601
|
+
if (v.startsWith("#") && /^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(v)) return true;
|
|
602
|
+
for (const prefix of COLOR_FUNCTION_PREFIXES) if (v.startsWith(prefix) && v.endsWith(")")) return true;
|
|
603
|
+
if (/^[a-zA-Z]+$/.test(v)) return true;
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Validate that mode aliases reference existing color keys via dot-path traversal.
|
|
608
|
+
* Aliases are dot-path strings like 'gray.50' that must resolve in the nested color structure.
|
|
609
|
+
*/
|
|
610
|
+
function validateModeAliases(modeName, aliases, nestedColors, flatColorKeys, prefix) {
|
|
611
|
+
for (const [key, value] of Object.entries(aliases)) {
|
|
612
|
+
const aliasPath = prefix ? `${prefix}.${key}` : key;
|
|
613
|
+
if (key === "_") {
|
|
614
|
+
if (typeof value === "string") {
|
|
615
|
+
if (walkDotPath(nestedColors, value) === void 0) throw new Error(`addColorModes: mode '${modeName}' references unknown color '${value}' for alias '${prefix || key}'. Available colors: ${flatColorKeys.slice(0, 10).join(", ")}${flatColorKeys.length > 10 ? ", ..." : ""}`);
|
|
616
|
+
} else if (isObject(value)) validateModeAliases(modeName, value, nestedColors, flatColorKeys, prefix);
|
|
617
|
+
} else if (typeof value === "string") {
|
|
618
|
+
if (walkDotPath(nestedColors, value) === void 0) throw new Error(`addColorModes: mode '${modeName}' references unknown color '${value}' for alias '${aliasPath}'. Available colors: ${flatColorKeys.slice(0, 10).join(", ")}${flatColorKeys.length > 10 ? ", ..." : ""}`);
|
|
619
|
+
} else if (isObject(value)) validateModeAliases(modeName, value, nestedColors, flatColorKeys, aliasPath);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
/** Validate all color entries, throwing on invalid values. */
|
|
623
|
+
function validateColors(colors) {
|
|
624
|
+
for (const [key, value] of Object.entries(colors)) if (isObject(value)) validateColors(value);
|
|
625
|
+
else if (!isValidCSSColor(value)) throw new Error(`addColors: '${String(value)}' is not a valid CSS <color> value for key '${key}'. Expected hex (#fff), rgb(), hsl(), oklch(), named color, transparent, or currentColor.`);
|
|
626
|
+
}
|
|
627
|
+
function createState(theme) {
|
|
628
|
+
return {
|
|
629
|
+
theme: theme || { breakpoints: {} },
|
|
630
|
+
emittedScales: /* @__PURE__ */ new Set(),
|
|
631
|
+
contextualVars: /* @__PURE__ */ new Map()
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function copyState(state, nextTheme) {
|
|
635
|
+
const next = {
|
|
636
|
+
theme: nextTheme,
|
|
637
|
+
emittedScales: new Set(state.emittedScales),
|
|
638
|
+
contextualVars: /* @__PURE__ */ new Map()
|
|
639
|
+
};
|
|
640
|
+
for (const [scale, vars] of state.contextualVars) next.contextualVars.set(scale, [...vars]);
|
|
641
|
+
return next;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* ThemeScales — the final phase. Has addScale, extendScale, declareContextualVars, build.
|
|
645
|
+
* Also allows addColors and addColorModes for augmentation.
|
|
646
|
+
*/
|
|
647
|
+
var ThemeBuilder = class ThemeBuilder {
|
|
648
|
+
/** @internal */ _state;
|
|
649
|
+
constructor(state) {
|
|
650
|
+
this._state = state;
|
|
651
|
+
}
|
|
652
|
+
addBreakpoints(breakpoints) {
|
|
653
|
+
for (const [key, value] of Object.entries(breakpoints)) if (typeof value !== "number" || value < 0) throw new Error(`addBreakpoints: breakpoint '${key}' must be a non-negative number, got ${JSON.stringify(value)}`);
|
|
654
|
+
const nextTheme = merge({}, this._state.theme, { breakpoints });
|
|
655
|
+
return new ThemeBuilder(copyState(this._state, nextTheme));
|
|
656
|
+
}
|
|
657
|
+
from(builtTheme) {
|
|
658
|
+
const raw = {};
|
|
659
|
+
for (const key of Object.keys(builtTheme)) {
|
|
660
|
+
const val = builtTheme[key];
|
|
661
|
+
if (typeof val !== "function") raw[key] = val;
|
|
662
|
+
}
|
|
663
|
+
const nextTheme = merge({}, this._state.theme, raw);
|
|
664
|
+
const next = new ThemeBuilder(copyState(this._state, nextTheme));
|
|
665
|
+
const manifest = builtTheme.manifest;
|
|
666
|
+
if (manifest?.variableMap) for (const tokenPath of Object.keys(manifest.variableMap)) {
|
|
667
|
+
const scale = tokenPath.split(".")[0];
|
|
668
|
+
next._state.emittedScales.add(scale === "colors" ? "colors" : scale);
|
|
669
|
+
}
|
|
670
|
+
if (manifest?.contextualVars) for (const [scale, vars] of Object.entries(manifest.contextualVars)) next._state.contextualVars.set(scale, [...vars]);
|
|
671
|
+
return next;
|
|
672
|
+
}
|
|
673
|
+
addColors(colors) {
|
|
674
|
+
validateColors(colors);
|
|
675
|
+
const nextTheme = merge({}, this._state.theme, { colors });
|
|
676
|
+
const next = new ThemeBuilder(copyState(this._state, nextTheme));
|
|
677
|
+
next._state.emittedScales.add("colors");
|
|
678
|
+
return next;
|
|
679
|
+
}
|
|
680
|
+
addColorModes(initialMode, modeConfig) {
|
|
681
|
+
const nestedColors = this._state.theme.colors || {};
|
|
682
|
+
const flatColors = flattenToDotPaths(nestedColors);
|
|
683
|
+
const flatColorKeys = Object.keys(flatColors);
|
|
684
|
+
for (const [modeName, modeAliases] of Object.entries(modeConfig)) validateModeAliases(modeName, modeAliases, nestedColors, flatColorKeys, "");
|
|
685
|
+
const nextTheme = merge({}, this._state.theme, {
|
|
686
|
+
modes: modeConfig,
|
|
687
|
+
mode: initialMode
|
|
688
|
+
});
|
|
689
|
+
return new ThemeBuilder(copyState(this._state, nextTheme));
|
|
690
|
+
}
|
|
691
|
+
addScale(config) {
|
|
692
|
+
const { name, values, emit } = config;
|
|
693
|
+
const nextTheme = merge({}, this._state.theme, { [name]: values });
|
|
694
|
+
const next = new ThemeBuilder(copyState(this._state, nextTheme));
|
|
695
|
+
if (emit) next._state.emittedScales.add(name);
|
|
696
|
+
return next;
|
|
697
|
+
}
|
|
698
|
+
declareContextualVars(vars) {
|
|
699
|
+
for (const scale of Object.keys(vars)) if (!(scale in this._state.theme)) throw new Error(`declareContextualVars: scale '${scale}' not found — call addColors or addScale first`);
|
|
700
|
+
const next = new ThemeBuilder(copyState(this._state, this._state.theme));
|
|
701
|
+
for (const [scale, names] of Object.entries(vars)) {
|
|
702
|
+
const existing = next._state.contextualVars.get(scale) || [];
|
|
703
|
+
next._state.contextualVars.set(scale, [...existing, ...names]);
|
|
704
|
+
}
|
|
705
|
+
return next;
|
|
706
|
+
}
|
|
707
|
+
extendScale(key, updateFn) {
|
|
708
|
+
const nextTheme = merge({}, this._state.theme, { [key]: updateFn(this._state.theme[key]) });
|
|
709
|
+
return new ThemeBuilder(copyState(this._state, nextTheme));
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Finalize the theme build.
|
|
713
|
+
* Flattens nested data at the boundary — produces manifest and serialize().
|
|
714
|
+
*/
|
|
715
|
+
build() {
|
|
716
|
+
const theme = merge({}, this._state.theme);
|
|
717
|
+
const emittedScales = this._state.emittedScales;
|
|
718
|
+
const contextualVars = this._state.contextualVars;
|
|
719
|
+
const { tokenMap, variableMap, variables, modeVariables, modeTokens } = flattenTheme(theme, emittedScales);
|
|
720
|
+
resolveTokenRefs(tokenMap, variableMap, emittedScales);
|
|
721
|
+
const bpVariables = {};
|
|
722
|
+
if (theme.breakpoints && isObject(theme.breakpoints)) for (const [key, value] of Object.entries(theme.breakpoints)) bpVariables[`--breakpoint-${key}`] = `${value}px`;
|
|
723
|
+
let contextualVarsSerialized;
|
|
724
|
+
if (contextualVars.size > 0) {
|
|
725
|
+
contextualVarsSerialized = {};
|
|
726
|
+
for (const [scale, vars] of contextualVars) contextualVarsSerialized[scale] = vars;
|
|
727
|
+
}
|
|
728
|
+
const variableCss = buildVariableCss(variables, bpVariables, modeVariables);
|
|
729
|
+
const manifest = {
|
|
730
|
+
tokenMap: {
|
|
731
|
+
...tokenMap,
|
|
732
|
+
...Object.fromEntries(Object.entries(theme.breakpoints || {}).map(([k, v]) => [`breakpoints.${k}`, String(v)]))
|
|
733
|
+
},
|
|
734
|
+
variableMap,
|
|
735
|
+
modes: modeTokens,
|
|
736
|
+
variableCss,
|
|
737
|
+
...contextualVarsSerialized ? { contextualVars: contextualVarsSerialized } : {}
|
|
738
|
+
};
|
|
739
|
+
Object.defineProperty(theme, "manifest", {
|
|
740
|
+
value: manifest,
|
|
741
|
+
enumerable: false,
|
|
742
|
+
configurable: false,
|
|
743
|
+
writable: false
|
|
744
|
+
});
|
|
745
|
+
Object.defineProperty(theme, "serialize", {
|
|
746
|
+
value: () => ({
|
|
747
|
+
scalesJson: JSON.stringify(manifest.tokenMap),
|
|
748
|
+
variableMapJson: JSON.stringify(manifest.variableMap),
|
|
749
|
+
variableCss: manifest.variableCss,
|
|
750
|
+
contextualVarsJson: JSON.stringify(manifest.contextualVars ?? {})
|
|
751
|
+
}),
|
|
752
|
+
enumerable: false,
|
|
753
|
+
configurable: false,
|
|
754
|
+
writable: false
|
|
755
|
+
});
|
|
756
|
+
Object.defineProperty(theme, "varRef", {
|
|
757
|
+
value: (tokenPath) => {
|
|
758
|
+
const varName = variableMap[tokenPath];
|
|
759
|
+
if (varName) return `var(${varName})`;
|
|
760
|
+
const dotIdx = tokenPath.indexOf(".");
|
|
761
|
+
if (dotIdx === -1) return void 0;
|
|
762
|
+
const scale = tokenPath.slice(0, dotIdx);
|
|
763
|
+
const key = tokenPath.slice(dotIdx + 1);
|
|
764
|
+
const scaleObj = theme[scale];
|
|
765
|
+
if (!isObject(scaleObj)) return void 0;
|
|
766
|
+
const val = walkDotPath(scaleObj, key);
|
|
767
|
+
return val !== void 0 ? String(val) : void 0;
|
|
768
|
+
},
|
|
769
|
+
enumerable: false,
|
|
770
|
+
configurable: false,
|
|
771
|
+
writable: false
|
|
772
|
+
});
|
|
773
|
+
return theme;
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
function createTheme() {
|
|
777
|
+
return new ThemeBuilder(createState());
|
|
778
|
+
}
|
|
779
|
+
/** Token ref pattern: {scale.key} or {scale.key.sub} */
|
|
780
|
+
const TOKEN_REF_RE = /\{([^}]+)\}/g;
|
|
781
|
+
/**
|
|
782
|
+
* Flatten the nested theme into dot-path keyed token map and CSS variable declarations.
|
|
783
|
+
* This is the ONLY place where flattening happens.
|
|
784
|
+
*/
|
|
785
|
+
function flattenTheme(theme, emittedScales) {
|
|
786
|
+
const tokenMap = {};
|
|
787
|
+
const variableMap = {};
|
|
788
|
+
const variables = {};
|
|
789
|
+
const modeVariables = {};
|
|
790
|
+
const modeTokens = {};
|
|
791
|
+
for (const [scaleName, scaleValue] of Object.entries(theme)) {
|
|
792
|
+
if (scaleName.startsWith("_")) continue;
|
|
793
|
+
if (scaleName === "breakpoints" || scaleName === "mode" || scaleName === "modes") continue;
|
|
794
|
+
if (typeof scaleValue === "function") continue;
|
|
795
|
+
if (!isObject(scaleValue)) continue;
|
|
796
|
+
const flat = flattenToDotPaths(scaleValue);
|
|
797
|
+
const isEmitted = emittedScales.has(scaleName);
|
|
798
|
+
for (const [dotKey, rawValue] of Object.entries(flat)) {
|
|
799
|
+
const tokenPath = `${scaleName}.${dotKey}`;
|
|
800
|
+
const dashKey = dotToDash(dotKey);
|
|
801
|
+
const varName = `--${scaleName === "colors" ? "color" : scaleName}-${dashKey}`;
|
|
802
|
+
if (isEmitted) {
|
|
803
|
+
tokenMap[tokenPath] = `var(${varName})`;
|
|
804
|
+
variableMap[tokenPath] = varName;
|
|
805
|
+
variables[varName] = String(rawValue);
|
|
806
|
+
} else tokenMap[tokenPath] = String(rawValue);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (theme.modes && isObject(theme.modes) && theme.colors && isObject(theme.colors)) {
|
|
810
|
+
const flatColors = flattenToDotPaths(theme.colors);
|
|
811
|
+
for (const [modeName, modeAliases] of Object.entries(theme.modes)) {
|
|
812
|
+
if (!isObject(modeAliases)) continue;
|
|
813
|
+
const flatAliases = flattenToDotPaths(modeAliases);
|
|
814
|
+
const modeVars = {};
|
|
815
|
+
const modeVals = {};
|
|
816
|
+
for (const [aliasDotKey, colorRef] of Object.entries(flatAliases)) {
|
|
817
|
+
if (typeof colorRef !== "string") continue;
|
|
818
|
+
const varName = `--color-${dotToDash(aliasDotKey)}`;
|
|
819
|
+
const rawValue = flatColors[colorRef];
|
|
820
|
+
modeVals[`colors.${aliasDotKey}`] = rawValue !== void 0 ? String(rawValue) : String(colorRef);
|
|
821
|
+
modeVars[varName] = rawValue !== void 0 ? String(rawValue) : String(colorRef);
|
|
822
|
+
}
|
|
823
|
+
modeVariables[modeName] = modeVars;
|
|
824
|
+
modeTokens[modeName] = modeVals;
|
|
825
|
+
}
|
|
826
|
+
const initialMode = theme.mode;
|
|
827
|
+
if (initialMode && modeVariables[initialMode]) {
|
|
828
|
+
const initialModeVars = {};
|
|
829
|
+
const flatInitialAliases = flattenToDotPaths(theme.modes[initialMode]);
|
|
830
|
+
for (const [aliasDotKey, colorRef] of Object.entries(flatInitialAliases)) {
|
|
831
|
+
if (typeof colorRef !== "string") continue;
|
|
832
|
+
const varName = `--color-${dotToDash(aliasDotKey)}`;
|
|
833
|
+
const paletteVarName = variableMap[`colors.${colorRef}`];
|
|
834
|
+
if (paletteVarName) initialModeVars[varName] = `var(${paletteVarName})`;
|
|
835
|
+
tokenMap[`colors.${aliasDotKey}`] = `var(${varName})`;
|
|
836
|
+
variableMap[`colors.${aliasDotKey}`] = varName;
|
|
837
|
+
}
|
|
838
|
+
Object.assign(variables, initialModeVars);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
tokenMap,
|
|
843
|
+
variableMap,
|
|
844
|
+
variables,
|
|
845
|
+
modeVariables,
|
|
846
|
+
modeTokens
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Resolve token refs ({scale.key}) in all flattened token values.
|
|
851
|
+
* Operates on the flattened tokenMap — does NOT mutate the nested theme.
|
|
852
|
+
*/
|
|
853
|
+
function resolveTokenRefs(tokenMap, _variableMap, _emittedScales) {
|
|
854
|
+
for (const [tokenPath, value] of Object.entries(tokenMap)) {
|
|
855
|
+
if (typeof value !== "string") continue;
|
|
856
|
+
if (!value.includes("{")) continue;
|
|
857
|
+
if (value.startsWith("var(")) continue;
|
|
858
|
+
const scaleName = tokenPath.split(".")[0];
|
|
859
|
+
const resolved = value.replace(TOKEN_REF_RE, (match, ref) => {
|
|
860
|
+
if (ref.split(".")[0] === scaleName) {
|
|
861
|
+
console.warn(`[animus] Self-referential token ref {${ref}} in scale '${scaleName}' — skipped`);
|
|
862
|
+
return match;
|
|
863
|
+
}
|
|
864
|
+
let lookupPath = ref;
|
|
865
|
+
let opacity;
|
|
866
|
+
const slashIdx = ref.indexOf("/");
|
|
867
|
+
if (slashIdx !== -1) {
|
|
868
|
+
lookupPath = ref.slice(0, slashIdx);
|
|
869
|
+
opacity = ref.slice(slashIdx + 1);
|
|
870
|
+
}
|
|
871
|
+
const refValue = tokenMap[lookupPath];
|
|
872
|
+
if (refValue === void 0) {
|
|
873
|
+
console.warn(`[animus] Token ref {${ref}} — path '${lookupPath}' not found in token map`);
|
|
874
|
+
return match;
|
|
875
|
+
}
|
|
876
|
+
if (opacity) {
|
|
877
|
+
const alpha = Number.parseInt(opacity, 10);
|
|
878
|
+
if (alpha === 0) return "transparent";
|
|
879
|
+
if (alpha !== 100) return `color-mix(in srgb, ${refValue} ${alpha}%, transparent)`;
|
|
880
|
+
}
|
|
881
|
+
return refValue;
|
|
882
|
+
});
|
|
883
|
+
if (resolved !== value) tokenMap[tokenPath] = resolved;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
/** Build CSS variable blocks from flattened data. */
|
|
887
|
+
function buildVariableCss(rootVariables, breakpointVariables, modeVariables) {
|
|
888
|
+
const parts = [];
|
|
889
|
+
const rootLines = [];
|
|
890
|
+
for (const [varName, value] of Object.entries(rootVariables)) rootLines.push(` ${varName}: ${value};`);
|
|
891
|
+
for (const [varName, value] of Object.entries(breakpointVariables)) rootLines.push(` ${varName}: ${value};`);
|
|
892
|
+
if (rootLines.length > 0) parts.push(`:root {\n${rootLines.join("\n")}\n}`);
|
|
893
|
+
for (const [modeName, modeVars] of Object.entries(modeVariables)) {
|
|
894
|
+
const modeLines = [];
|
|
895
|
+
for (const [varName, value] of Object.entries(modeVars)) modeLines.push(` ${varName}: ${value};`);
|
|
896
|
+
if (modeLines.length > 0) parts.push(`[data-color-mode="${modeName}"] {\n${modeLines.join("\n")}\n}`);
|
|
897
|
+
}
|
|
898
|
+
return parts.join("\n\n");
|
|
293
899
|
}
|
|
294
900
|
//#endregion
|
|
295
|
-
export { Animus, AnimusExtended, AnimusExtendedWithAll, AnimusWithAll,
|
|
901
|
+
export { Animus, AnimusExtended, AnimusExtendedWithAll, AnimusWithAll, BUILT_IN_SELECTORS, SystemBuilder, ThemeBuilder, borderShorthand, compose, createClassResolver, createComponent, createComposedFamily, createScale, createSystem, createTheme, createTransform, gridItem, gridItemRatio, numericOrStringScale, numericScale, percentageOrAbsolute, size, stringScale };
|