@animus-ui/system 0.1.0-next.17 → 0.1.0-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Animus.d.ts +1 -0
- package/dist/Animus.d.ts.map +1 -1
- package/dist/AnimusExtended.d.ts +1 -0
- package/dist/AnimusExtended.d.ts.map +1 -1
- package/dist/compose.d.ts +21 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/groups/index.d.ts +13 -1
- package/dist/groups/index.d.ts.map +1 -1
- package/dist/groups/index.js +24 -12
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +257 -117
- package/dist/runtime/createClassResolver.d.ts +10 -0
- package/dist/runtime/createClassResolver.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +2 -23
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/resolveClasses.d.ts +58 -0
- package/dist/runtime/resolveClasses.d.ts.map +1 -0
- package/dist/theme/createTheme.d.ts +37 -36
- package/dist/theme/createTheme.d.ts.map +1 -1
- package/dist/types/component.d.ts +102 -1
- package/dist/types/component.d.ts.map +1 -1
- package/dist/types/config.d.ts +12 -2
- 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/theme.d.ts +29 -0
- package/dist/types/theme.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
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-Dge_rsuz.js";
|
|
2
|
-
import { createElement, forwardRef, useRef } from "react";
|
|
3
|
-
//#region src/runtime/
|
|
2
|
+
import { createContext, createElement, forwardRef, useContext, useRef } from "react";
|
|
3
|
+
//#region src/runtime/resolveClasses.ts
|
|
4
4
|
/**
|
|
5
5
|
* CSS properties that accept unitless numeric values.
|
|
6
6
|
* Bare numerics on properties NOT in this set receive `px`.
|
|
7
|
-
* Matches @emotion/unitless and React DOM's style handling.
|
|
8
7
|
*/
|
|
9
8
|
const UNITLESS_PROPERTIES = new Set([
|
|
10
9
|
"animation-iteration-count",
|
|
@@ -52,7 +51,6 @@ const UNITLESS_PROPERTIES = new Set([
|
|
|
52
51
|
]);
|
|
53
52
|
/**
|
|
54
53
|
* Apply unit fallback to a value for a given CSS property.
|
|
55
|
-
* Unitless numeric values on properties that expect length units receive `px`.
|
|
56
54
|
*/
|
|
57
55
|
function applyUnitFallback(value, cssProperty) {
|
|
58
56
|
if (typeof value === "number") {
|
|
@@ -63,9 +61,7 @@ function applyUnitFallback(value, cssProperty) {
|
|
|
63
61
|
}
|
|
64
62
|
/**
|
|
65
63
|
* 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 "|"
|
|
64
|
+
* css_generator's serialize_value_key output format.
|
|
69
65
|
*/
|
|
70
66
|
function serializeValueKey(value) {
|
|
71
67
|
if (typeof value === "number" || typeof value === "string") return String(value);
|
|
@@ -74,7 +70,6 @@ function serializeValueKey(value) {
|
|
|
74
70
|
}
|
|
75
71
|
/**
|
|
76
72
|
* Resolve a dynamic prop value through scale lookup → transform → unit fallback.
|
|
77
|
-
* Scale lookup uses pre-resolved values shipped from the extraction pipeline.
|
|
78
73
|
*/
|
|
79
74
|
function resolveValue(value, dc) {
|
|
80
75
|
const key = String(value);
|
|
@@ -86,6 +81,74 @@ function resolveValue(value, dc) {
|
|
|
86
81
|
return applyUnitFallback(dc.transform ? dc.transform(value) : value, dc.varName);
|
|
87
82
|
}
|
|
88
83
|
/**
|
|
84
|
+
* Resolve className parts from props, using extracted configuration.
|
|
85
|
+
* This is the shared logic between createComponent and createClassResolver.
|
|
86
|
+
*/
|
|
87
|
+
function resolveClasses(baseClassName, props, config, systemPropMap, dynamicPropConfig) {
|
|
88
|
+
const classes = [baseClassName];
|
|
89
|
+
let dynStyle;
|
|
90
|
+
if (config.variants) for (const [prop, vc] of Object.entries(config.variants)) {
|
|
91
|
+
const value = props[prop] ?? vc.default;
|
|
92
|
+
if (value != null) classes.push(`${baseClassName}--${prop}-${value}`);
|
|
93
|
+
}
|
|
94
|
+
if (config.compounds) for (const compound of config.compounds) {
|
|
95
|
+
let match = true;
|
|
96
|
+
for (const [prop, expected] of Object.entries(compound.conditions)) {
|
|
97
|
+
const current = props[prop] ?? config.variants?.[prop]?.default;
|
|
98
|
+
if (Array.isArray(expected) ? !expected.includes(current) : current !== expected) {
|
|
99
|
+
match = false;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (match) classes.push(compound.className);
|
|
104
|
+
}
|
|
105
|
+
if (config.states) {
|
|
106
|
+
for (const state of config.states) if (props[state]) classes.push(`${baseClassName}--${state}`);
|
|
107
|
+
}
|
|
108
|
+
const systemPropNames = config.systemPropNames || [];
|
|
109
|
+
if (systemPropNames.length > 0) {
|
|
110
|
+
const { customPropMap, customDynamicConfig } = config;
|
|
111
|
+
for (const propName of systemPropNames) {
|
|
112
|
+
if (!(propName in props)) continue;
|
|
113
|
+
const propValue = props[propName];
|
|
114
|
+
if (propValue == null) continue;
|
|
115
|
+
const key = serializeValueKey(propValue);
|
|
116
|
+
const cls = customPropMap?.[propName]?.[key] ?? systemPropMap?.[propName]?.[key];
|
|
117
|
+
if (cls) classes.push(cls);
|
|
118
|
+
else {
|
|
119
|
+
const dc = customDynamicConfig?.[propName] ?? dynamicPropConfig?.[propName];
|
|
120
|
+
if (dc) {
|
|
121
|
+
if (!dynStyle) dynStyle = {};
|
|
122
|
+
if (typeof propValue === "object" && propValue !== null && !Array.isArray(propValue)) for (const [bp, bpVal] of Object.entries(propValue)) {
|
|
123
|
+
if (bpVal == null) continue;
|
|
124
|
+
if (bp === "_") {
|
|
125
|
+
classes.push(dc.slotClass);
|
|
126
|
+
const finalVal = resolveValue(bpVal, dc);
|
|
127
|
+
dynStyle[dc.varName] = finalVal;
|
|
128
|
+
} else {
|
|
129
|
+
classes.push(`${dc.slotClass}-${bp}`);
|
|
130
|
+
const varName = `${dc.varName}-${bp}`;
|
|
131
|
+
const finalVal = resolveValue(bpVal, dc);
|
|
132
|
+
dynStyle[varName] = finalVal;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
classes.push(dc.slotClass);
|
|
137
|
+
const finalVal = resolveValue(propValue, dc);
|
|
138
|
+
dynStyle[dc.varName] = finalVal;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
classes,
|
|
146
|
+
dynamicStyle: dynStyle
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/runtime/index.ts
|
|
151
|
+
/**
|
|
89
152
|
* Create a lightweight component that applies extracted CSS class names.
|
|
90
153
|
* Replaces Emotion's styled() for extracted components.
|
|
91
154
|
*
|
|
@@ -105,74 +168,17 @@ function createComponent(element, className, config, systemPropMap, dynamicPropC
|
|
|
105
168
|
const stateProps = config.states || [];
|
|
106
169
|
const systemPropNames = config.systemPropNames || [];
|
|
107
170
|
const filterProps = new Set([
|
|
171
|
+
"as",
|
|
108
172
|
...variantProps,
|
|
109
173
|
...stateProps,
|
|
110
174
|
...systemPropNames
|
|
111
175
|
]);
|
|
112
|
-
const isComponentElement = typeof element !== "string";
|
|
113
176
|
const Component = forwardRef((props, ref) => {
|
|
114
|
-
const
|
|
177
|
+
const renderElement = props.as || element;
|
|
178
|
+
const isComponentElement = typeof renderElement !== "string";
|
|
115
179
|
const prevDynKey = useRef("");
|
|
116
180
|
const prevDynStyle = useRef(null);
|
|
117
|
-
|
|
118
|
-
const value = props[prop] ?? vc.default;
|
|
119
|
-
if (value != null) classes.push(`${className}--${prop}-${value}`);
|
|
120
|
-
}
|
|
121
|
-
if (config.compounds) for (const compound of config.compounds) {
|
|
122
|
-
let match = true;
|
|
123
|
-
for (const [prop, expected] of Object.entries(compound.conditions)) {
|
|
124
|
-
const current = props[prop] ?? config.variants?.[prop]?.default;
|
|
125
|
-
if (Array.isArray(expected) ? !expected.includes(current) : current !== expected) {
|
|
126
|
-
match = false;
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (match) classes.push(compound.className);
|
|
131
|
-
}
|
|
132
|
-
if (config.states) {
|
|
133
|
-
for (const state of config.states) if (props[state]) classes.push(`${className}--${state}`);
|
|
134
|
-
}
|
|
135
|
-
let dynKeyParts;
|
|
136
|
-
let dynStyle;
|
|
137
|
-
if (systemPropNames.length > 0) {
|
|
138
|
-
const { customPropMap, customDynamicConfig } = config;
|
|
139
|
-
for (const propName of systemPropNames) {
|
|
140
|
-
if (!(propName in props)) continue;
|
|
141
|
-
const propValue = props[propName];
|
|
142
|
-
if (propValue == null) continue;
|
|
143
|
-
const key = serializeValueKey(propValue);
|
|
144
|
-
const cls = customPropMap?.[propName]?.[key] ?? systemPropMap?.[propName]?.[key];
|
|
145
|
-
if (cls) classes.push(cls);
|
|
146
|
-
else {
|
|
147
|
-
const dc = customDynamicConfig?.[propName] ?? dynamicPropConfig?.[propName];
|
|
148
|
-
if (dc) {
|
|
149
|
-
if (!dynKeyParts) dynKeyParts = [];
|
|
150
|
-
if (!dynStyle) dynStyle = {};
|
|
151
|
-
if (typeof propValue === "object" && propValue !== null && !Array.isArray(propValue)) for (const [bp, bpVal] of Object.entries(propValue)) {
|
|
152
|
-
if (bpVal == null) continue;
|
|
153
|
-
if (bp === "_") {
|
|
154
|
-
classes.push(dc.slotClass);
|
|
155
|
-
const finalVal = resolveValue(bpVal, dc);
|
|
156
|
-
dynStyle[dc.varName] = finalVal;
|
|
157
|
-
dynKeyParts.push(`${dc.varName}:${finalVal}`);
|
|
158
|
-
} else {
|
|
159
|
-
classes.push(`${dc.slotClass}-${bp}`);
|
|
160
|
-
const varName = `${dc.varName}-${bp}`;
|
|
161
|
-
const finalVal = resolveValue(bpVal, dc);
|
|
162
|
-
dynStyle[varName] = finalVal;
|
|
163
|
-
dynKeyParts.push(`${varName}:${finalVal}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
classes.push(dc.slotClass);
|
|
168
|
-
const finalVal = resolveValue(propValue, dc);
|
|
169
|
-
dynStyle[dc.varName] = finalVal;
|
|
170
|
-
dynKeyParts.push(`${dc.varName}:${finalVal}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
181
|
+
const { classes, dynamicStyle } = resolveClasses(className, props, config, systemPropMap, dynamicPropConfig);
|
|
176
182
|
if (props.className) classes.push(props.className);
|
|
177
183
|
const domProps = {
|
|
178
184
|
ref,
|
|
@@ -184,25 +190,41 @@ function createComponent(element, className, config, systemPropMap, dynamicPropC
|
|
|
184
190
|
if (!isComponentElement) {}
|
|
185
191
|
domProps[key] = value;
|
|
186
192
|
}
|
|
187
|
-
if (
|
|
188
|
-
const dynKey =
|
|
193
|
+
if (dynamicStyle) {
|
|
194
|
+
const dynKey = Object.entries(dynamicStyle).map(([k, v]) => `${k}:${v}`).join("|");
|
|
189
195
|
if (dynKey !== prevDynKey.current) {
|
|
190
196
|
prevDynKey.current = dynKey;
|
|
191
|
-
prevDynStyle.current =
|
|
197
|
+
prevDynStyle.current = dynamicStyle;
|
|
192
198
|
}
|
|
193
199
|
domProps.style = props.style ? {
|
|
194
200
|
...props.style,
|
|
195
201
|
...prevDynStyle.current
|
|
196
202
|
} : prevDynStyle.current;
|
|
197
203
|
}
|
|
198
|
-
return createElement(
|
|
204
|
+
return createElement(renderElement, domProps);
|
|
199
205
|
});
|
|
200
206
|
Component.displayName = className;
|
|
207
|
+
Component.__variantKeys = new Set(variantProps);
|
|
201
208
|
return Object.assign(Component, { extend: () => {
|
|
202
209
|
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.`);
|
|
203
210
|
} });
|
|
204
211
|
}
|
|
205
212
|
//#endregion
|
|
213
|
+
//#region src/runtime/createClassResolver.ts
|
|
214
|
+
/**
|
|
215
|
+
* createClassResolver — framework-agnostic className resolution.
|
|
216
|
+
*
|
|
217
|
+
* Produced by .asClass() terminal. Same resolution logic as createComponent
|
|
218
|
+
* (variants, states, compounds, system props) but returns a className string
|
|
219
|
+
* instead of a React element.
|
|
220
|
+
*/
|
|
221
|
+
function createClassResolver(className, config, systemPropMap, dynamicPropConfig) {
|
|
222
|
+
return (props) => {
|
|
223
|
+
const { classes } = resolveClasses(className, props || {}, config, systemPropMap, dynamicPropConfig);
|
|
224
|
+
return classes.join(" ");
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
206
228
|
//#region src/AnimusExtended.ts
|
|
207
229
|
function deepMerge$1(target, source) {
|
|
208
230
|
const result = { ...target };
|
|
@@ -242,6 +264,9 @@ var AnimusExtendedWithAll = class {
|
|
|
242
264
|
const extendFn = this.extend.bind(this);
|
|
243
265
|
return Object.assign(Component, { extend: extendFn });
|
|
244
266
|
}
|
|
267
|
+
asClass() {
|
|
268
|
+
return createClassResolver("", this._buildComponentConfig());
|
|
269
|
+
}
|
|
245
270
|
build() {
|
|
246
271
|
return Object.assign((() => ({})), { extend: this.extend.bind(this) });
|
|
247
272
|
}
|
|
@@ -350,6 +375,9 @@ var AnimusWithAll = class {
|
|
|
350
375
|
const extendFn = this.extend.bind(this);
|
|
351
376
|
return Object.assign(Component, { extend: extendFn });
|
|
352
377
|
}
|
|
378
|
+
asClass() {
|
|
379
|
+
return createClassResolver("", this._buildComponentConfig());
|
|
380
|
+
}
|
|
353
381
|
build() {
|
|
354
382
|
return Object.assign((() => ({})), { extend: this.extend.bind(this) });
|
|
355
383
|
}
|
|
@@ -434,6 +462,67 @@ var Animus = class extends AnimusWithBase {
|
|
|
434
462
|
}
|
|
435
463
|
};
|
|
436
464
|
//#endregion
|
|
465
|
+
//#region src/compose.ts
|
|
466
|
+
const EMPTY_SHARED = {};
|
|
467
|
+
/**
|
|
468
|
+
* Compose independently-authored Animus components into a sealed,
|
|
469
|
+
* namespaced component family with shared variant propagation via
|
|
470
|
+
* React context.
|
|
471
|
+
*
|
|
472
|
+
* - **Enforce**: TypeScript ensures shared keys exist on Root (the
|
|
473
|
+
* provider). Non-Root slots that have the key consume it from
|
|
474
|
+
* context; slots without the key are unaffected.
|
|
475
|
+
* - **Wire**: Root provides shared variant values via context.
|
|
476
|
+
* Child slots consume from context. Direct props override context.
|
|
477
|
+
* - **Seal**: Output components are plain ForwardRefExoticComponent —
|
|
478
|
+
* no `.extend()`, no builder methods. One-way door from builder-land
|
|
479
|
+
* to component-land.
|
|
480
|
+
*/
|
|
481
|
+
function compose(slots, options) {
|
|
482
|
+
const sharedKeys = Object.keys(options.shared);
|
|
483
|
+
const FamilyContext = createContext(EMPTY_SHARED);
|
|
484
|
+
const rootSlot = slots.Root ?? slots.root;
|
|
485
|
+
const familyName = rootSlot?.displayName ? rootSlot.displayName.replace(/[-_].*$/, "") : "Composed";
|
|
486
|
+
const result = {};
|
|
487
|
+
for (const [name, SourceComponent] of Object.entries(slots)) {
|
|
488
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
489
|
+
if (name.toLowerCase() === "root") {
|
|
490
|
+
const RootWrapper = forwardRef((props, ref) => {
|
|
491
|
+
const sharedValues = {};
|
|
492
|
+
for (const key of sharedKeys) if (key in props) sharedValues[key] = props[key];
|
|
493
|
+
return createElement(FamilyContext.Provider, { value: sharedKeys.length > 0 ? sharedValues : EMPTY_SHARED }, createElement(SourceComponent, {
|
|
494
|
+
...props,
|
|
495
|
+
ref
|
|
496
|
+
}));
|
|
497
|
+
});
|
|
498
|
+
RootWrapper.displayName = `${familyName}.${capitalizedName}`;
|
|
499
|
+
result[capitalizedName] = RootWrapper;
|
|
500
|
+
} else {
|
|
501
|
+
const knownKeys = SourceComponent.__variantKeys;
|
|
502
|
+
const ChildWrapper = forwardRef((props, ref) => {
|
|
503
|
+
const shared = useContext(FamilyContext);
|
|
504
|
+
let merged;
|
|
505
|
+
if (knownKeys && sharedKeys.length > 0) {
|
|
506
|
+
const filtered = {};
|
|
507
|
+
for (const key of sharedKeys) if (knownKeys.has(key) && key in shared) filtered[key] = shared[key];
|
|
508
|
+
merged = {
|
|
509
|
+
...filtered,
|
|
510
|
+
...props,
|
|
511
|
+
ref
|
|
512
|
+
};
|
|
513
|
+
} else merged = {
|
|
514
|
+
...props,
|
|
515
|
+
ref
|
|
516
|
+
};
|
|
517
|
+
return createElement(SourceComponent, merged);
|
|
518
|
+
});
|
|
519
|
+
ChildWrapper.displayName = `${familyName}.${capitalizedName}`;
|
|
520
|
+
result[capitalizedName] = ChildWrapper;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
//#endregion
|
|
437
526
|
//#region src/PropertyBuilder.ts
|
|
438
527
|
var PropertyBuilder = class PropertyBuilder {
|
|
439
528
|
#props;
|
|
@@ -770,46 +859,39 @@ function validateColors(colors) {
|
|
|
770
859
|
for (const [key, value] of Object.entries(colors)) if (isObject(value)) validateColors(value);
|
|
771
860
|
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.`);
|
|
772
861
|
}
|
|
773
|
-
var ThemeBuilder = class {
|
|
862
|
+
var ThemeBuilder = class ThemeBuilder {
|
|
774
863
|
#theme = {};
|
|
864
|
+
#emittedScales = /* @__PURE__ */ new Set();
|
|
775
865
|
constructor(baseTheme) {
|
|
776
866
|
this.#theme = baseTheme;
|
|
777
867
|
}
|
|
778
|
-
/**
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
createScaleVariables(key) {
|
|
784
|
-
const { variables, tokens } = serializeTokens(this.#theme[key], key, this.#theme);
|
|
785
|
-
this.#theme = merge({}, this.#theme, {
|
|
786
|
-
[key]: tokens,
|
|
787
|
-
_variables: { [key]: variables },
|
|
788
|
-
_tokens: { [key]: this.#theme[key] }
|
|
789
|
-
});
|
|
790
|
-
return this;
|
|
868
|
+
/** Create a new builder checkpoint, carrying forward emittedScales state. */
|
|
869
|
+
#checkpoint(nextTheme) {
|
|
870
|
+
const next = new ThemeBuilder(nextTheme);
|
|
871
|
+
for (const s of this.#emittedScales) next.#emittedScales.add(s);
|
|
872
|
+
return next;
|
|
791
873
|
}
|
|
792
874
|
/**
|
|
793
|
-
*
|
|
794
|
-
* @param colors A map of color tokens to add to the theme. These tokens are immediately converted to CSS Variables `--color-${key}`.
|
|
875
|
+
* @param colors A map of color tokens. Immediately converted to CSS variables `--color-${key}`.
|
|
795
876
|
* @example .addColors({ navy: 'navy', hyper: 'purple' })
|
|
796
877
|
*/
|
|
797
878
|
addColors(colors) {
|
|
798
879
|
validateColors(colors);
|
|
799
880
|
const flatColors = flattenScale(colors);
|
|
800
881
|
const { variables, tokens } = serializeTokens(flatColors, "color", this.#theme);
|
|
801
|
-
|
|
882
|
+
const nextTheme = merge({}, this.#theme, {
|
|
802
883
|
colors: tokens,
|
|
803
884
|
_variables: { root: variables },
|
|
804
885
|
_tokens: { colors: flatColors }
|
|
805
886
|
});
|
|
806
|
-
|
|
887
|
+
const next = this.#checkpoint(nextTheme);
|
|
888
|
+
next.#emittedScales.add("colors");
|
|
889
|
+
return next;
|
|
807
890
|
}
|
|
808
891
|
/**
|
|
809
|
-
*
|
|
810
|
-
* @param
|
|
811
|
-
* @
|
|
812
|
-
* @example .addColorModes('light', { light: { primary: 'hyper' }, { dark: { primary: 'navy' } } })
|
|
892
|
+
* @param initialMode Default color mode key.
|
|
893
|
+
* @param modeConfig Map of color modes with semantic aliases pointing to palette keys.
|
|
894
|
+
* @example .addColorModes('dark', { dark: { primary: 'ember' }, light: { primary: 'void' } })
|
|
813
895
|
*/
|
|
814
896
|
addColorModes(initialMode, modeConfig) {
|
|
815
897
|
const availableColors = this.#theme._tokens?.colors ? Object.keys(this.#theme._tokens.colors) : Object.keys(this.#theme.colors || {});
|
|
@@ -818,7 +900,7 @@ var ThemeBuilder = class {
|
|
|
818
900
|
const modes = mapValues(modeConfig, (mode) => flattenScale(mode));
|
|
819
901
|
const { tokens: colors, variables } = serializeTokens(mapValues(merge({}, this.#theme.modes?.[initialMode], modes[initialMode]), (color) => this.#theme.colors[color]), "color", this.#theme);
|
|
820
902
|
const getColorValue = (color) => this.#theme._tokens?.colors?.[color];
|
|
821
|
-
|
|
903
|
+
const nextTheme = merge({}, this.#theme, {
|
|
822
904
|
colors,
|
|
823
905
|
modes,
|
|
824
906
|
mode: initialMode,
|
|
@@ -826,36 +908,46 @@ var ThemeBuilder = class {
|
|
|
826
908
|
_variables: { mode: variables },
|
|
827
909
|
_tokens: { modes: mapValues(modes, (mode) => mapValues(mode, getColorValue)) }
|
|
828
910
|
});
|
|
829
|
-
return this;
|
|
911
|
+
return this.#checkpoint(nextTheme);
|
|
830
912
|
}
|
|
831
913
|
/**
|
|
914
|
+
* Add a named scale to the theme.
|
|
915
|
+
*
|
|
916
|
+
* @param config.name - Scale name (e.g. 'space', 'sizes')
|
|
917
|
+
* @param config.values - Scale value map
|
|
918
|
+
* @param config.emit - When true, generates CSS variables (default: false)
|
|
832
919
|
*
|
|
833
|
-
* @
|
|
834
|
-
*
|
|
835
|
-
*
|
|
920
|
+
* @example
|
|
921
|
+
* .addScale({ name: 'space', values: { 0: '0', 8: '0.5rem', 16: '1rem' } })
|
|
922
|
+
* .addScale({ name: 'sizes', emit: true, values: { navHeight: '48px' } })
|
|
836
923
|
*/
|
|
837
|
-
addScale(
|
|
838
|
-
|
|
839
|
-
|
|
924
|
+
addScale(config) {
|
|
925
|
+
const { name, values, emit } = config;
|
|
926
|
+
const flattened = flattenScale(values);
|
|
927
|
+
let nextTheme;
|
|
928
|
+
if (emit) {
|
|
929
|
+
const { variables, tokens } = serializeTokens(flattened, name, this.#theme);
|
|
930
|
+
nextTheme = merge({}, this.#theme, {
|
|
931
|
+
[name]: tokens,
|
|
932
|
+
_variables: { [name]: variables },
|
|
933
|
+
_tokens: { [name]: flattened }
|
|
934
|
+
});
|
|
935
|
+
} else nextTheme = merge({}, this.#theme, { [name]: flattened });
|
|
936
|
+
const next = this.#checkpoint(nextTheme);
|
|
937
|
+
if (emit) next.#emittedScales.add(name);
|
|
938
|
+
return next;
|
|
840
939
|
}
|
|
841
940
|
/**
|
|
842
|
-
*
|
|
843
|
-
* @
|
|
844
|
-
* @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.
|
|
845
|
-
* @example .updateScale('fonts', ({ basic }) => ({ basicFallback: `{basic}, Montserrat` }))
|
|
941
|
+
* @param key A current key of theme to update with computed values.
|
|
942
|
+
* @example .updateScale('fonts', ({ basic }) => ({ basicFallback: `${basic}, Montserrat` }))
|
|
846
943
|
*/
|
|
847
944
|
updateScale(key, updateFn) {
|
|
848
|
-
|
|
849
|
-
return this;
|
|
945
|
+
const nextTheme = merge({}, this.#theme, { [key]: updateFn(this.#theme[key]) });
|
|
946
|
+
return this.#checkpoint(nextTheme);
|
|
850
947
|
}
|
|
851
|
-
/**
|
|
852
|
-
* This finalizes the theme build and returns the final theme and variables to be provided.
|
|
853
|
-
* Simplify flattens the deeply nested MergeTheme chain into a shallow object type.
|
|
854
|
-
*
|
|
855
|
-
* The returned theme object also has a non-enumerable `.manifest` property containing
|
|
856
|
-
* a structured ThemeManifest for plugin consumption.
|
|
857
|
-
*/
|
|
948
|
+
/** Finalize the theme build. Returns the theme with a non-enumerable `.manifest` property. */
|
|
858
949
|
build() {
|
|
950
|
+
resolveThemeTokenRefs(this.#theme, this.#emittedScales);
|
|
859
951
|
const { variables } = serializeTokens(mapValues(this.#theme.breakpoints, (val) => `${val}px`), "breakpoint", this.#theme);
|
|
860
952
|
const theme = merge({}, this.#theme, {
|
|
861
953
|
_variables: { breakpoints: variables },
|
|
@@ -874,6 +966,54 @@ var ThemeBuilder = class {
|
|
|
874
966
|
function createTheme(base) {
|
|
875
967
|
return new ThemeBuilder(base);
|
|
876
968
|
}
|
|
969
|
+
/** Token ref pattern: {scale.key} */
|
|
970
|
+
const TOKEN_REF_RE = /\{([^}]+)\}/g;
|
|
971
|
+
/**
|
|
972
|
+
* Resolve token refs ({scale.key}) in all scale values.
|
|
973
|
+
* Only refs to emitted scales (those with CSS variables) are valid.
|
|
974
|
+
* Runs once at build() time after all scales have been collected.
|
|
975
|
+
*/
|
|
976
|
+
function resolveThemeTokenRefs(theme, emittedScales) {
|
|
977
|
+
for (const [scaleName, scaleValue] of Object.entries(theme)) {
|
|
978
|
+
if (scaleName.startsWith("_")) continue;
|
|
979
|
+
if (scaleName === "breakpoints" || scaleName === "mode" || scaleName === "modes") continue;
|
|
980
|
+
if (typeof scaleValue === "function") continue;
|
|
981
|
+
if (!isObject(scaleValue)) continue;
|
|
982
|
+
for (const [key, value] of Object.entries(scaleValue)) {
|
|
983
|
+
if (typeof value !== "string") continue;
|
|
984
|
+
if (!value.includes("{")) continue;
|
|
985
|
+
const resolved = value.replace(TOKEN_REF_RE, (match, ref) => {
|
|
986
|
+
const dotIdx = ref.indexOf(".");
|
|
987
|
+
if (dotIdx === -1) return match;
|
|
988
|
+
const refScale = ref.slice(0, dotIdx);
|
|
989
|
+
const refKey = ref.slice(dotIdx + 1);
|
|
990
|
+
if (refScale === scaleName) {
|
|
991
|
+
console.warn(`[animus] Self-referential token ref {${ref}} in scale '${scaleName}' — skipped`);
|
|
992
|
+
return match;
|
|
993
|
+
}
|
|
994
|
+
const targetScale = theme[refScale];
|
|
995
|
+
if (!targetScale || !isObject(targetScale)) {
|
|
996
|
+
console.warn(`[animus] Token ref {${ref}} references unknown scale '${refScale}'`);
|
|
997
|
+
return match;
|
|
998
|
+
}
|
|
999
|
+
const resolvedValue = targetScale[refKey];
|
|
1000
|
+
if (resolvedValue === void 0) {
|
|
1001
|
+
console.warn(`[animus] Token ref {${ref}} — key '${refKey}' not found in scale '${refScale}'`);
|
|
1002
|
+
return match;
|
|
1003
|
+
}
|
|
1004
|
+
return String(resolvedValue);
|
|
1005
|
+
});
|
|
1006
|
+
if (resolved !== value) {
|
|
1007
|
+
scaleValue[key] = resolved;
|
|
1008
|
+
if (theme._tokens?.[scaleName]) theme._tokens[scaleName][key] = resolved;
|
|
1009
|
+
if (theme._variables?.[scaleName]) {
|
|
1010
|
+
const varName = `--${scaleName}-${key.replace("$", "")}`;
|
|
1011
|
+
if (theme._variables[scaleName][varName] !== void 0) theme._variables[scaleName][varName] = resolved;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
877
1017
|
/**
|
|
878
1018
|
* Assemble a ThemeManifest from the built theme object.
|
|
879
1019
|
*
|
|
@@ -952,4 +1092,4 @@ function flattenModeTokensCss(lines, obj, prefix) {
|
|
|
952
1092
|
}
|
|
953
1093
|
}
|
|
954
1094
|
//#endregion
|
|
955
|
-
export { Animus, AnimusExtended, AnimusExtendedWithAll, AnimusWithAll, PropertyBuilder, SystemBuilder, ThemeBuilder, borderShorthand, createComponent, createScale, createSystem, createTheme, createTransform, flattenScale, gridItem, gridItemRatio, numericOrStringScale, numericScale, percentageOrAbsolute, serializeTokens, size, stringScale };
|
|
1095
|
+
export { Animus, AnimusExtended, AnimusExtendedWithAll, AnimusWithAll, PropertyBuilder, SystemBuilder, ThemeBuilder, borderShorthand, compose, createClassResolver, createComponent, createScale, createSystem, createTheme, createTransform, flattenScale, gridItem, gridItemRatio, numericOrStringScale, numericScale, percentageOrAbsolute, serializeTokens, size, stringScale };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createClassResolver — framework-agnostic className resolution.
|
|
3
|
+
*
|
|
4
|
+
* Produced by .asClass() terminal. Same resolution logic as createComponent
|
|
5
|
+
* (variants, states, compounds, system props) but returns a className string
|
|
6
|
+
* instead of a React element.
|
|
7
|
+
*/
|
|
8
|
+
import { type ClassResolverConfig, type DynamicPropConfig, type SystemPropMap } from './resolveClasses';
|
|
9
|
+
export declare function createClassResolver(className: string, config: ClassResolverConfig, systemPropMap?: SystemPropMap, dynamicPropConfig?: DynamicPropConfig): (props?: Record<string, unknown>) => string;
|
|
10
|
+
//# sourceMappingURL=createClassResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createClassResolver.d.ts","sourceRoot":"","sources":["../../src/runtime/createClassResolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEtB,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAE1B,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,mBAAmB,EAC3B,aAAa,CAAC,EAAE,aAAa,EAC7B,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAW7C"}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
import { forwardRef } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
default?: string;
|
|
2
|
+
import { type ClassResolverConfig, type DynamicPropConfig, type SystemPropMap } from './resolveClasses';
|
|
3
|
+
interface ComponentConfig extends ClassResolverConfig {
|
|
5
4
|
}
|
|
6
|
-
interface CompoundConfig {
|
|
7
|
-
conditions: Record<string, string | string[]>;
|
|
8
|
-
className: string;
|
|
9
|
-
}
|
|
10
|
-
interface ComponentConfig {
|
|
11
|
-
variants?: Record<string, VariantConfig>;
|
|
12
|
-
compounds?: CompoundConfig[];
|
|
13
|
-
states?: string[];
|
|
14
|
-
systemPropNames?: string[];
|
|
15
|
-
customPropMap?: Record<string, Record<string, string>>;
|
|
16
|
-
customDynamicConfig?: DynamicPropConfig;
|
|
17
|
-
}
|
|
18
|
-
type SystemPropMap = Record<string, Record<string, string>>;
|
|
19
|
-
type DynamicPropConfig = Record<string, {
|
|
20
|
-
varName: string;
|
|
21
|
-
slotClass: string;
|
|
22
|
-
transformName?: string;
|
|
23
|
-
transform?: (value: string | number) => string | number;
|
|
24
|
-
scaleValues?: Record<string, string>;
|
|
25
|
-
}>;
|
|
26
5
|
type ElementType = string | React.ComponentType<any>;
|
|
27
6
|
type AnimusComponent = ReturnType<typeof forwardRef> & {
|
|
28
7
|
extend: () => never;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,UAAU,EAAU,MAAM,OAAO,CAAC;AAE1D,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,UAAU,EAAU,MAAM,OAAO,CAAC;AAE1D,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEtB,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAE1B,UAAU,eAAgB,SAAQ,mBAAmB;CAAG;AAIxD,KAAK,WAAW,GAAG,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;AAErD,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG;IACrD,MAAM,EAAE,MAAM,KAAK,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EACvB,aAAa,CAAC,EAAE,aAAa,EAC7B,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,eAAe,CAoFjB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared className resolution logic used by both createComponent (React)
|
|
3
|
+
* and createClassResolver (framework-agnostic).
|
|
4
|
+
*
|
|
5
|
+
* Factored to ensure behavioral parity between .asElement() and .asClass() outputs.
|
|
6
|
+
*/
|
|
7
|
+
interface VariantConfig {
|
|
8
|
+
options: string[];
|
|
9
|
+
default?: string;
|
|
10
|
+
}
|
|
11
|
+
interface CompoundConfig {
|
|
12
|
+
conditions: Record<string, string | string[]>;
|
|
13
|
+
className: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ClassResolverConfig {
|
|
16
|
+
variants?: Record<string, VariantConfig>;
|
|
17
|
+
compounds?: CompoundConfig[];
|
|
18
|
+
states?: string[];
|
|
19
|
+
systemPropNames?: string[];
|
|
20
|
+
customPropMap?: Record<string, Record<string, string>>;
|
|
21
|
+
customDynamicConfig?: DynamicPropConfig;
|
|
22
|
+
}
|
|
23
|
+
export type SystemPropMap = Record<string, Record<string, string>>;
|
|
24
|
+
export type DynamicPropConfig = Record<string, {
|
|
25
|
+
varName: string;
|
|
26
|
+
slotClass: string;
|
|
27
|
+
transformName?: string;
|
|
28
|
+
transform?: (value: string | number) => string | number;
|
|
29
|
+
scaleValues?: Record<string, string>;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Apply unit fallback to a value for a given CSS property.
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyUnitFallback(value: string | number, cssProperty: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Serialize a system prop value to a lookup key matching the Rust
|
|
37
|
+
* css_generator's serialize_value_key output format.
|
|
38
|
+
*/
|
|
39
|
+
export declare function serializeValueKey(value: unknown): string;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a dynamic prop value through scale lookup → transform → unit fallback.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveValue(value: unknown, dc: {
|
|
44
|
+
varName: string;
|
|
45
|
+
transform?: (value: string | number) => string | number;
|
|
46
|
+
scaleValues?: Record<string, string>;
|
|
47
|
+
}): string;
|
|
48
|
+
export interface ClassResolution {
|
|
49
|
+
classes: string[];
|
|
50
|
+
dynamicStyle?: Record<string, string>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve className parts from props, using extracted configuration.
|
|
54
|
+
* This is the shared logic between createComponent and createClassResolver.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveClasses(baseClassName: string, props: Record<string, any>, config: ClassResolverConfig, systemPropMap?: SystemPropMap, dynamicPropConfig?: DynamicPropConfig): ClassResolution;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=resolveClasses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveClasses.d.ts","sourceRoot":"","sources":["../../src/runtime/resolveClasses.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,cAAc;IACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACzC,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,mBAAmB,CAAC,EAAE,iBAAiB,CAAC;CACzC;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEnE,MAAM,MAAM,iBAAiB,GAAG,MAAM,CACpC,MAAM,EACN;IACE,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CACF,CAAC;AAmDF;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,MAAM,CAQR;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAWxD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,EAAE,EAAE;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,GACA,MAAM,CAaR;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,MAAM,EAAE,mBAAmB,EAC3B,aAAa,CAAC,EAAE,aAAa,EAC7B,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,eAAe,CAgGjB"}
|