@animus-ui/system 0.1.0-next.9 → 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 -17
- 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 +11 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +666 -395
- 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 +2 -18
- package/dist/runtime/index.d.ts.map +1 -1
- 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 +54 -54
- package/dist/theme/createTheme.d.ts.map +1 -1
- package/dist/theme/index.d.ts +0 -2
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/serializeTokens.d.ts +0 -14
- package/dist/theme/serializeTokens.d.ts.map +1 -1
- package/dist/theme/utils.d.ts +20 -4
- package/dist/theme/utils.d.ts.map +1 -1
- 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 +141 -23
- package/dist/types/component.d.ts.map +1 -1
- package/dist/types/config.d.ts +50 -5
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/properties.d.ts +1 -2
- 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/theme.d.ts +73 -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 +47 -15
- 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,204 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
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
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
* Bare numerics on properties NOT in this set receive `px`.
|
|
7
|
-
* Matches @emotion/unitless and React DOM's style handling.
|
|
8
|
-
*/
|
|
9
|
-
const UNITLESS_PROPERTIES = new Set([
|
|
10
|
-
"animation-iteration-count",
|
|
11
|
-
"border-image-outset",
|
|
12
|
-
"border-image-slice",
|
|
13
|
-
"border-image-width",
|
|
14
|
-
"box-flex",
|
|
15
|
-
"box-flex-group",
|
|
16
|
-
"box-ordinal-group",
|
|
17
|
-
"column-count",
|
|
18
|
-
"columns",
|
|
19
|
-
"flex",
|
|
20
|
-
"flex-grow",
|
|
21
|
-
"flex-positive",
|
|
22
|
-
"flex-shrink",
|
|
23
|
-
"flex-negative",
|
|
24
|
-
"flex-order",
|
|
25
|
-
"font-weight",
|
|
26
|
-
"grid-area",
|
|
27
|
-
"grid-column",
|
|
28
|
-
"grid-column-end",
|
|
29
|
-
"grid-column-span",
|
|
30
|
-
"grid-column-start",
|
|
31
|
-
"grid-row",
|
|
32
|
-
"grid-row-end",
|
|
33
|
-
"grid-row-span",
|
|
34
|
-
"grid-row-start",
|
|
35
|
-
"line-clamp",
|
|
36
|
-
"line-height",
|
|
37
|
-
"opacity",
|
|
38
|
-
"order",
|
|
39
|
-
"orphans",
|
|
40
|
-
"tab-size",
|
|
41
|
-
"widows",
|
|
42
|
-
"z-index",
|
|
43
|
-
"zoom",
|
|
44
|
-
"fill-opacity",
|
|
45
|
-
"flood-opacity",
|
|
46
|
-
"stop-opacity",
|
|
47
|
-
"stroke-dasharray",
|
|
48
|
-
"stroke-dashoffset",
|
|
49
|
-
"stroke-miterlimit",
|
|
50
|
-
"stroke-opacity",
|
|
51
|
-
"stroke-width"
|
|
52
|
-
]);
|
|
53
|
-
/**
|
|
54
|
-
* Apply unit fallback to a value for a given CSS property.
|
|
55
|
-
* Unitless numeric values on properties that expect length units receive `px`.
|
|
56
|
-
*/
|
|
57
|
-
function applyUnitFallback(value, cssProperty) {
|
|
58
|
-
if (typeof value === "number") {
|
|
59
|
-
if (UNITLESS_PROPERTIES.has(cssProperty)) return String(value);
|
|
60
|
-
return `${value}px`;
|
|
61
|
-
}
|
|
62
|
-
return String(value);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Serialize a system prop value to a lookup key matching the Rust
|
|
66
|
-
* css_generator's serialize_value_key output format:
|
|
67
|
-
* - Numbers and strings → their string representation
|
|
68
|
-
* - Responsive objects → sorted "key:value" pairs joined by "|"
|
|
69
|
-
*/
|
|
70
|
-
function serializeValueKey(value) {
|
|
71
|
-
if (typeof value === "number" || typeof value === "string") return String(value);
|
|
72
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.keys(value).sort().map((k) => `${k}:${value[k]}`).join("|");
|
|
73
|
-
return String(value);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Resolve a dynamic prop value through scale lookup → transform → unit fallback.
|
|
77
|
-
* Scale lookup uses pre-resolved values shipped from the extraction pipeline.
|
|
78
|
-
*/
|
|
79
|
-
function resolveValue(value, dc) {
|
|
80
|
-
const key = String(value);
|
|
81
|
-
const scaleResolved = dc.scaleValues?.[key];
|
|
82
|
-
if (scaleResolved != null) {
|
|
83
|
-
const transformed = dc.transform ? dc.transform(scaleResolved) : scaleResolved;
|
|
84
|
-
return String(transformed);
|
|
85
|
-
}
|
|
86
|
-
return applyUnitFallback(dc.transform ? dc.transform(value) : value, dc.varName);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Create a lightweight component that applies extracted CSS class names.
|
|
90
|
-
* Replaces Emotion's styled() for extracted components.
|
|
91
|
-
*
|
|
92
|
-
* The element parameter accepts either an HTML tag string (e.g. 'button') or
|
|
93
|
-
* a React component reference (e.g. NextLink). When a component reference is
|
|
94
|
-
* used, prop forwarding skips the HTML-attribute validity check — all
|
|
95
|
-
* non-filtered props are forwarded to the component.
|
|
96
|
-
*
|
|
97
|
-
* The optional systemPropMap parameter provides the shared prop→value→className
|
|
98
|
-
* lookup table, served as a virtual module by the Vite plugin.
|
|
99
|
-
*
|
|
100
|
-
* The optional dynamicPropConfig parameter provides CSS variable fallback
|
|
101
|
-
* metadata for props with detected dynamic usage.
|
|
6
|
+
* Deep merge utility — replaces lodash.merge for variant accumulation.
|
|
102
7
|
*/
|
|
103
|
-
function
|
|
104
|
-
const variantProps = config.variants ? Object.keys(config.variants) : [];
|
|
105
|
-
const stateProps = config.states || [];
|
|
106
|
-
const systemPropNames = config.systemPropNames || [];
|
|
107
|
-
const filterProps = new Set([
|
|
108
|
-
...variantProps,
|
|
109
|
-
...stateProps,
|
|
110
|
-
...systemPropNames
|
|
111
|
-
]);
|
|
112
|
-
const isComponentElement = typeof element !== "string";
|
|
113
|
-
const Component = forwardRef((props, ref) => {
|
|
114
|
-
const classes = [className];
|
|
115
|
-
const prevDynKey = useRef("");
|
|
116
|
-
const prevDynStyle = useRef(null);
|
|
117
|
-
if (config.variants) for (const [prop, vc] of Object.entries(config.variants)) {
|
|
118
|
-
const value = props[prop] ?? vc.default;
|
|
119
|
-
if (value != null) classes.push(`${className}--${prop}-${value}`);
|
|
120
|
-
}
|
|
121
|
-
if (config.states) {
|
|
122
|
-
for (const state of config.states) if (props[state]) classes.push(`${className}--${state}`);
|
|
123
|
-
}
|
|
124
|
-
let dynKeyParts;
|
|
125
|
-
let dynStyle;
|
|
126
|
-
if (systemPropNames.length > 0) {
|
|
127
|
-
const { customPropMap, customDynamicConfig } = config;
|
|
128
|
-
for (const propName of systemPropNames) {
|
|
129
|
-
if (!(propName in props)) continue;
|
|
130
|
-
const propValue = props[propName];
|
|
131
|
-
if (propValue == null) continue;
|
|
132
|
-
const key = serializeValueKey(propValue);
|
|
133
|
-
const cls = customPropMap?.[propName]?.[key] ?? systemPropMap?.[propName]?.[key];
|
|
134
|
-
if (cls) classes.push(cls);
|
|
135
|
-
else {
|
|
136
|
-
const dc = customDynamicConfig?.[propName] ?? dynamicPropConfig?.[propName];
|
|
137
|
-
if (dc) {
|
|
138
|
-
if (!dynKeyParts) dynKeyParts = [];
|
|
139
|
-
if (!dynStyle) dynStyle = {};
|
|
140
|
-
if (typeof propValue === "object" && propValue !== null && !Array.isArray(propValue)) for (const [bp, bpVal] of Object.entries(propValue)) {
|
|
141
|
-
if (bpVal == null) continue;
|
|
142
|
-
if (bp === "_") {
|
|
143
|
-
classes.push(dc.slotClass);
|
|
144
|
-
const finalVal = resolveValue(bpVal, dc);
|
|
145
|
-
dynStyle[dc.varName] = finalVal;
|
|
146
|
-
dynKeyParts.push(`${dc.varName}:${finalVal}`);
|
|
147
|
-
} else {
|
|
148
|
-
classes.push(`${dc.slotClass}-${bp}`);
|
|
149
|
-
const varName = `${dc.varName}-${bp}`;
|
|
150
|
-
const finalVal = resolveValue(bpVal, dc);
|
|
151
|
-
dynStyle[varName] = finalVal;
|
|
152
|
-
dynKeyParts.push(`${varName}:${finalVal}`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
classes.push(dc.slotClass);
|
|
157
|
-
const finalVal = resolveValue(propValue, dc);
|
|
158
|
-
dynStyle[dc.varName] = finalVal;
|
|
159
|
-
dynKeyParts.push(`${dc.varName}:${finalVal}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (props.className) classes.push(props.className);
|
|
166
|
-
const domProps = {
|
|
167
|
-
ref,
|
|
168
|
-
className: classes.join(" ")
|
|
169
|
-
};
|
|
170
|
-
for (const [key, value] of Object.entries(props)) {
|
|
171
|
-
if (key === "className") continue;
|
|
172
|
-
if (filterProps.has(key)) continue;
|
|
173
|
-
if (!isComponentElement) {}
|
|
174
|
-
domProps[key] = value;
|
|
175
|
-
}
|
|
176
|
-
if (dynKeyParts && dynStyle) {
|
|
177
|
-
const dynKey = dynKeyParts.join("|");
|
|
178
|
-
if (dynKey !== prevDynKey.current) {
|
|
179
|
-
prevDynKey.current = dynKey;
|
|
180
|
-
prevDynStyle.current = dynStyle;
|
|
181
|
-
}
|
|
182
|
-
domProps.style = props.style ? {
|
|
183
|
-
...props.style,
|
|
184
|
-
...prevDynStyle.current
|
|
185
|
-
} : prevDynStyle.current;
|
|
186
|
-
}
|
|
187
|
-
return createElement(element, domProps);
|
|
188
|
-
});
|
|
189
|
-
Component.displayName = className;
|
|
190
|
-
return Object.assign(Component, { extend: () => {
|
|
191
|
-
throw new Error(`Cannot extend extracted component "${className}" at runtime. Extensions must be authored in source code using the builder API (e.g. import the original component and call .extend() there) so the extraction pipeline can resolve them at build time.`);
|
|
192
|
-
} });
|
|
193
|
-
}
|
|
194
|
-
//#endregion
|
|
195
|
-
//#region src/AnimusExtended.ts
|
|
196
|
-
function deepMerge$1(target, source) {
|
|
8
|
+
function deepMerge(target, source) {
|
|
197
9
|
const result = { ...target };
|
|
198
|
-
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]);
|
|
199
11
|
else result[key] = source[key];
|
|
200
12
|
return result;
|
|
201
13
|
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/AnimusExtended.ts
|
|
202
16
|
var AnimusExtendedWithAll = class {
|
|
203
17
|
propRegistry = {};
|
|
204
18
|
groupRegistry = {};
|
|
@@ -207,7 +21,8 @@ var AnimusExtendedWithAll = class {
|
|
|
207
21
|
variants = {};
|
|
208
22
|
activeGroups = {};
|
|
209
23
|
custom = {};
|
|
210
|
-
|
|
24
|
+
compounds = [];
|
|
25
|
+
constructor(props, groups, base, variants, states, activeGroups, custom, compounds = []) {
|
|
211
26
|
this.propRegistry = props;
|
|
212
27
|
this.groupRegistry = groups;
|
|
213
28
|
this.baseStyles = base;
|
|
@@ -215,9 +30,10 @@ var AnimusExtendedWithAll = class {
|
|
|
215
30
|
this.statesConfig = states;
|
|
216
31
|
this.activeGroups = activeGroups;
|
|
217
32
|
this.custom = custom;
|
|
33
|
+
this.compounds = compounds;
|
|
218
34
|
}
|
|
219
35
|
extend() {
|
|
220
|
-
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);
|
|
221
37
|
}
|
|
222
38
|
asElement(component) {
|
|
223
39
|
const Component = createComponent(component, "", this._buildComponentConfig());
|
|
@@ -229,8 +45,8 @@ var AnimusExtendedWithAll = class {
|
|
|
229
45
|
const extendFn = this.extend.bind(this);
|
|
230
46
|
return Object.assign(Component, { extend: extendFn });
|
|
231
47
|
}
|
|
232
|
-
|
|
233
|
-
return
|
|
48
|
+
asClass() {
|
|
49
|
+
return createClassResolver("", this._buildComponentConfig());
|
|
234
50
|
}
|
|
235
51
|
_buildComponentConfig() {
|
|
236
52
|
const variantConfig = {};
|
|
@@ -252,45 +68,50 @@ var AnimusExtendedWithAll = class {
|
|
|
252
68
|
};
|
|
253
69
|
var AnimusExtendedWithSystem = class extends AnimusExtendedWithAll {
|
|
254
70
|
props(config) {
|
|
255
|
-
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);
|
|
256
72
|
}
|
|
257
73
|
};
|
|
258
74
|
var AnimusExtendedWithStates = class extends AnimusExtendedWithSystem {
|
|
259
|
-
|
|
260
|
-
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);
|
|
261
77
|
}
|
|
262
78
|
};
|
|
263
|
-
var
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
+
}]);
|
|
267
85
|
}
|
|
268
86
|
states(config) {
|
|
269
|
-
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);
|
|
270
100
|
}
|
|
271
101
|
};
|
|
272
102
|
var AnimusExtendedWithBase = class extends AnimusExtendedWithVariants {
|
|
273
103
|
variant(options) {
|
|
274
104
|
const prop = options.prop || "variant";
|
|
275
|
-
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);
|
|
276
106
|
}
|
|
277
107
|
};
|
|
278
108
|
var AnimusExtended = class extends AnimusExtendedWithBase {
|
|
279
109
|
styles(config) {
|
|
280
|
-
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);
|
|
281
111
|
}
|
|
282
112
|
};
|
|
283
113
|
//#endregion
|
|
284
114
|
//#region src/Animus.ts
|
|
285
|
-
/**
|
|
286
|
-
* Deep merge utility — replaces lodash.merge for variant accumulation.
|
|
287
|
-
*/
|
|
288
|
-
function deepMerge(target, source) {
|
|
289
|
-
const result = { ...target };
|
|
290
|
-
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]);
|
|
291
|
-
else result[key] = source[key];
|
|
292
|
-
return result;
|
|
293
|
-
}
|
|
294
115
|
var AnimusWithAll = class {
|
|
295
116
|
propRegistry = {};
|
|
296
117
|
groupRegistry = {};
|
|
@@ -299,7 +120,8 @@ var AnimusWithAll = class {
|
|
|
299
120
|
variants = {};
|
|
300
121
|
activeGroups = {};
|
|
301
122
|
custom = {};
|
|
302
|
-
|
|
123
|
+
compounds = [];
|
|
124
|
+
constructor(props, groups, base, variants, states, activeGroups, custom, compounds = []) {
|
|
303
125
|
this.propRegistry = props;
|
|
304
126
|
this.groupRegistry = groups;
|
|
305
127
|
this.baseStyles = base;
|
|
@@ -307,9 +129,10 @@ var AnimusWithAll = class {
|
|
|
307
129
|
this.statesConfig = states;
|
|
308
130
|
this.activeGroups = activeGroups;
|
|
309
131
|
this.custom = custom;
|
|
132
|
+
this.compounds = compounds;
|
|
310
133
|
}
|
|
311
134
|
extend() {
|
|
312
|
-
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);
|
|
313
136
|
}
|
|
314
137
|
asElement(component) {
|
|
315
138
|
const Component = createComponent(component, "", this._buildComponentConfig());
|
|
@@ -321,8 +144,8 @@ var AnimusWithAll = class {
|
|
|
321
144
|
const extendFn = this.extend.bind(this);
|
|
322
145
|
return Object.assign(Component, { extend: extendFn });
|
|
323
146
|
}
|
|
324
|
-
|
|
325
|
-
return
|
|
147
|
+
asClass() {
|
|
148
|
+
return createClassResolver("", this._buildComponentConfig());
|
|
326
149
|
}
|
|
327
150
|
_buildComponentConfig() {
|
|
328
151
|
const variantConfig = {};
|
|
@@ -343,31 +166,48 @@ var AnimusWithAll = class {
|
|
|
343
166
|
}
|
|
344
167
|
};
|
|
345
168
|
var AnimusWithSystem = class extends AnimusWithAll {
|
|
346
|
-
constructor(props, groups, base, variants, states, activeGroups) {
|
|
347
|
-
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);
|
|
348
171
|
}
|
|
349
172
|
props(config) {
|
|
350
|
-
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);
|
|
351
174
|
}
|
|
352
175
|
};
|
|
353
176
|
var AnimusWithStates = class extends AnimusWithSystem {
|
|
354
|
-
constructor(props, groups, base, variants, states) {
|
|
355
|
-
super(props, groups, base, variants, states, {});
|
|
177
|
+
constructor(props, groups, base, variants, states, compounds = []) {
|
|
178
|
+
super(props, groups, base, variants, states, {}, compounds);
|
|
356
179
|
}
|
|
357
|
-
|
|
358
|
-
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);
|
|
359
182
|
}
|
|
360
183
|
};
|
|
361
|
-
var
|
|
362
|
-
constructor(props, groups, base, variants) {
|
|
363
|
-
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
|
+
}]);
|
|
364
193
|
}
|
|
365
194
|
states(config) {
|
|
366
|
-
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
|
+
}]);
|
|
367
207
|
}
|
|
368
208
|
variant(options) {
|
|
369
209
|
const prop = options.prop || "variant";
|
|
370
|
-
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);
|
|
371
211
|
}
|
|
372
212
|
};
|
|
373
213
|
var AnimusWithBase = class extends AnimusWithVariants {
|
|
@@ -376,7 +216,7 @@ var AnimusWithBase = class extends AnimusWithVariants {
|
|
|
376
216
|
}
|
|
377
217
|
variant(options) {
|
|
378
218
|
const prop = options.prop || "variant";
|
|
379
|
-
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);
|
|
380
220
|
}
|
|
381
221
|
};
|
|
382
222
|
var Animus = class extends AnimusWithBase {
|
|
@@ -388,64 +228,275 @@ var Animus = class extends AnimusWithBase {
|
|
|
388
228
|
}
|
|
389
229
|
};
|
|
390
230
|
//#endregion
|
|
391
|
-
//#region src/
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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);
|
|
398
268
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
|
408
383
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
|
413
401
|
};
|
|
402
|
+
nextOrder += 10;
|
|
414
403
|
}
|
|
415
|
-
|
|
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
|
+
}
|
|
416
424
|
//#endregion
|
|
417
425
|
//#region src/SystemBuilder.ts
|
|
418
426
|
var SystemBuilder = class SystemBuilder {
|
|
419
427
|
#propRegistry;
|
|
420
428
|
#groupRegistry;
|
|
421
|
-
#
|
|
422
|
-
|
|
429
|
+
#selectorRegistry;
|
|
430
|
+
#includesRegistry;
|
|
431
|
+
constructor(propRegistry, groupRegistry, selectorRegistry, includesRegistry) {
|
|
423
432
|
this.#propRegistry = propRegistry || {};
|
|
424
433
|
this.#groupRegistry = groupRegistry || {};
|
|
425
|
-
this.#
|
|
434
|
+
this.#selectorRegistry = selectorRegistry || { ...BUILT_IN_SELECTORS };
|
|
435
|
+
this.#includesRegistry = includesRegistry || [];
|
|
426
436
|
}
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
return new SystemBuilder(
|
|
437
|
+
addSelectors(selectors) {
|
|
438
|
+
const merged = mergeSelectors(this.#selectorRegistry, selectors);
|
|
439
|
+
return new SystemBuilder(this.#propRegistry, this.#groupRegistry, merged, this.#includesRegistry);
|
|
430
440
|
}
|
|
431
|
-
|
|
432
|
-
|
|
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);
|
|
433
469
|
}
|
|
434
470
|
build() {
|
|
435
471
|
const animus = new Animus(this.#propRegistry, this.#groupRegistry);
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
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);
|
|
439
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
|
+
};
|
|
440
488
|
}
|
|
441
489
|
};
|
|
442
|
-
function serializeInstance(propRegistry, groupRegistry,
|
|
490
|
+
function serializeInstance(propRegistry, groupRegistry, selectorRegistry) {
|
|
443
491
|
const serialized = {};
|
|
444
492
|
const transforms = {};
|
|
445
493
|
for (const [propName, entry] of Object.entries(propRegistry)) {
|
|
446
494
|
const s = { property: entry.property };
|
|
447
495
|
if (entry.properties && entry.properties.length > 0) s.properties = [...entry.properties];
|
|
448
|
-
|
|
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;
|
|
449
500
|
if (entry.transform) {
|
|
450
501
|
const fn = entry.transform;
|
|
451
502
|
const name = fn.transformName ?? fn.name;
|
|
@@ -454,18 +505,20 @@ function serializeInstance(propRegistry, groupRegistry, globalStyles) {
|
|
|
454
505
|
transforms[name] = fn;
|
|
455
506
|
}
|
|
456
507
|
}
|
|
508
|
+
if (entry.currentVar) s.currentVar = entry.currentVar;
|
|
457
509
|
serialized[propName] = s;
|
|
458
510
|
}
|
|
459
|
-
const
|
|
511
|
+
const { selectors, order } = serializeSelectorMap(selectorRegistry);
|
|
512
|
+
return {
|
|
460
513
|
propConfig: JSON.stringify(serialized),
|
|
461
514
|
groupRegistry: JSON.stringify(groupRegistry),
|
|
462
|
-
transforms
|
|
515
|
+
transforms,
|
|
516
|
+
selectorAliases: JSON.stringify(selectors),
|
|
517
|
+
selectorOrder: JSON.stringify(order)
|
|
463
518
|
};
|
|
464
|
-
if (globalStyles) result.globalStyles = globalStyles;
|
|
465
|
-
return result;
|
|
466
519
|
}
|
|
467
|
-
function createSystem() {
|
|
468
|
-
return new SystemBuilder();
|
|
520
|
+
function createSystem(config) {
|
|
521
|
+
return new SystemBuilder(void 0, void 0, void 0, config?.includes ?? []);
|
|
469
522
|
}
|
|
470
523
|
//#endregion
|
|
471
524
|
//#region src/theme/utils.ts
|
|
@@ -485,146 +538,364 @@ function merge(target, ...sources) {
|
|
|
485
538
|
}
|
|
486
539
|
return target;
|
|
487
540
|
}
|
|
488
|
-
|
|
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) {
|
|
489
563
|
const result = {};
|
|
490
|
-
for (const key of Object.keys(
|
|
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
|
+
}
|
|
491
570
|
return result;
|
|
492
571
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
...carry,
|
|
501
|
-
...flattenScale(current, nextKey)
|
|
502
|
-
};
|
|
503
|
-
return {
|
|
504
|
-
...carry,
|
|
505
|
-
[nextKey]: object[key]
|
|
506
|
-
};
|
|
507
|
-
}, {});
|
|
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, "-");
|
|
508
579
|
}
|
|
580
|
+
/** Map over object values — matches lodash.mapValues overload signatures */
|
|
509
581
|
//#endregion
|
|
510
|
-
//#region src/theme/
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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);
|
|
522
620
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
tokenReferences[key] = `var(${varName})`;
|
|
531
|
-
merge(tokenVariables, templateBreakpoints(tokens[key], varName, theme));
|
|
532
|
-
});
|
|
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) {
|
|
533
628
|
return {
|
|
534
|
-
|
|
535
|
-
|
|
629
|
+
theme: theme || { breakpoints: {} },
|
|
630
|
+
emittedScales: /* @__PURE__ */ new Set(),
|
|
631
|
+
contextualVars: /* @__PURE__ */ new Map()
|
|
536
632
|
};
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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;
|
|
558
672
|
}
|
|
559
|
-
/**
|
|
560
|
-
*
|
|
561
|
-
* @param colors A map of color tokens to add to the theme. These tokens are immediately converted to CSS Variables `--color-${key}`.
|
|
562
|
-
* @example .addColors({ navy: 'navy', hyper: 'purple' })
|
|
563
|
-
*/
|
|
564
673
|
addColors(colors) {
|
|
565
|
-
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
_tokens: { colors: flatColors }
|
|
571
|
-
});
|
|
572
|
-
return this;
|
|
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;
|
|
573
679
|
}
|
|
574
|
-
/**
|
|
575
|
-
*
|
|
576
|
-
* @param initialMode A key of the object passed for modes. This sets the default state for the theme and transforms the correct variables.
|
|
577
|
-
* @param modes A map of color modes with keys of each possible mode with a value of alias to color keys. This must be called after `addColors`
|
|
578
|
-
* @example .addColorModes('light', { light: { primary: 'hyper' }, { dark: { primary: 'navy' } } })
|
|
579
|
-
*/
|
|
580
680
|
addColorModes(initialMode, modeConfig) {
|
|
581
|
-
const
|
|
582
|
-
const
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
modes,
|
|
587
|
-
mode: initialMode
|
|
588
|
-
_getColorValue: getColorValue,
|
|
589
|
-
_variables: { mode: variables },
|
|
590
|
-
_tokens: { modes: mapValues(modes, (mode) => mapValues(mode, getColorValue)) }
|
|
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
|
|
591
688
|
});
|
|
592
|
-
return this;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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;
|
|
603
706
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
* @param updateFn A function that accepts an argument of the current values at the specified keys an returns a map of new values to merge.
|
|
608
|
-
* @example .updateScale('fonts', ({ basic }) => ({ basicFallback: `{basic}, Montserrat` }))
|
|
609
|
-
*/
|
|
610
|
-
updateScale(key, updateFn) {
|
|
611
|
-
this.#theme = merge({}, this.#theme, { [key]: updateFn(this.#theme[key]) });
|
|
612
|
-
return this;
|
|
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));
|
|
613
710
|
}
|
|
614
711
|
/**
|
|
615
|
-
*
|
|
616
|
-
*
|
|
712
|
+
* Finalize the theme build.
|
|
713
|
+
* Flattens nested data at the boundary — produces manifest and serialize().
|
|
617
714
|
*/
|
|
618
715
|
build() {
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
|
623
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;
|
|
624
774
|
}
|
|
625
775
|
};
|
|
626
|
-
function createTheme(
|
|
627
|
-
return new ThemeBuilder(
|
|
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");
|
|
628
899
|
}
|
|
629
900
|
//#endregion
|
|
630
|
-
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 };
|