@animus-ui/system 0.1.9 → 0.1.12
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 +124 -51
- package/dist/Animus.d.ts +3 -2
- package/dist/Animus.d.ts.map +1 -1
- package/dist/AnimusExtended.d.ts +3 -2
- package/dist/AnimusExtended.d.ts.map +1 -1
- package/dist/SystemBuilder.d.ts +184 -155
- package/dist/SystemBuilder.d.ts.map +1 -1
- package/dist/appearance/index.d.ts +0 -68
- package/dist/appearance/index.d.ts.map +1 -1
- package/dist/appearance/index.js +0 -91
- package/dist/assert-root-slot-C3Lp9MNJ.js +10 -0
- package/dist/asset.d.ts +4 -13
- package/dist/asset.d.ts.map +1 -1
- package/dist/bootstrap/createAppearanceBootstrap.d.ts +0 -57
- package/dist/bootstrap/createAppearanceBootstrap.d.ts.map +1 -1
- package/dist/bootstrap/index.d.ts.map +1 -1
- package/dist/bootstrap/index.js +4 -58
- package/dist/class-resolver.d.ts +6 -0
- package/dist/class-resolver.d.ts.map +1 -0
- package/dist/class-resolver.js +2 -0
- package/dist/compose.d.ts +2 -16
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +3 -17
- package/dist/composeWithContext.d.ts +4 -15
- package/dist/composeWithContext.d.ts.map +1 -1
- package/dist/composeWithContext.js +5 -16
- package/dist/conditions.d.ts +24 -141
- package/dist/conditions.d.ts.map +1 -1
- package/dist/createClassResolver-EpwTL9Sa.js +228 -0
- package/dist/createComposedFamily-C4NovMcd.js +115 -0
- package/dist/groups/index.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +318 -408
- package/dist/keyframes.d.ts +2 -23
- package/dist/keyframes.d.ts.map +1 -1
- package/dist/runtime/assert-root-slot.d.ts +2 -13
- package/dist/runtime/assert-root-slot.d.ts.map +1 -1
- package/dist/runtime/createClassResolver.d.ts +10 -9
- package/dist/runtime/createClassResolver.d.ts.map +1 -1
- package/dist/runtime/createComposedFamily.d.ts +2 -7
- package/dist/runtime/createComposedFamily.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +0 -16
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/is-dev.d.ts +2 -42
- package/dist/runtime/is-dev.d.ts.map +1 -1
- package/dist/runtime/resolveClasses.d.ts +8 -33
- package/dist/runtime/resolveClasses.d.ts.map +1 -1
- package/dist/runtime/witness.d.ts +0 -8
- package/dist/runtime/witness.d.ts.map +1 -1
- package/dist/runtime-entry.d.ts +2 -7
- package/dist/runtime-entry.d.ts.map +1 -1
- package/dist/runtime-entry.js +2 -1
- package/dist/selectors.d.ts +6 -45
- package/dist/selectors.d.ts.map +1 -1
- package/dist/{size-DHdmBc64.js → size-C6jqxiQy.js} +7 -34
- package/dist/theme/createTheme.d.ts +26 -95
- package/dist/theme/createTheme.d.ts.map +1 -1
- package/dist/theme/flattenScale.d.ts +4 -10
- package/dist/theme/flattenScale.d.ts.map +1 -1
- package/dist/theme/resolveReferences.d.ts +7 -35
- package/dist/theme/resolveReferences.d.ts.map +1 -1
- package/dist/theme/serializeTokens.d.ts +0 -3
- package/dist/theme/serializeTokens.d.ts.map +1 -1
- package/dist/theme/types.d.ts +4 -26
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/utils.d.ts +1 -18
- package/dist/theme/utils.d.ts.map +1 -1
- package/dist/transforms/border.d.ts +1 -4
- package/dist/transforms/border.d.ts.map +1 -1
- package/dist/transforms/createTransform.d.ts +6 -31
- package/dist/transforms/createTransform.d.ts.map +1 -1
- package/dist/transforms/grid.d.ts +2 -12
- package/dist/transforms/grid.d.ts.map +1 -1
- package/dist/transforms/size.d.ts +2 -8
- package/dist/transforms/size.d.ts.map +1 -1
- package/dist/types/component.d.ts +9 -105
- package/dist/types/component.d.ts.map +1 -1
- package/dist/types/config.d.ts +12 -141
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/theme.d.ts +8 -165
- package/dist/types/theme.d.ts.map +1 -1
- package/dist/utils/deepMerge.d.ts.map +1 -1
- package/package.json +17 -4
- package/dist/assert-root-slot-B83scqPh.js +0 -21
- package/dist/createComposedFamily-3ZugjBpU.js +0 -472
|
@@ -1,472 +0,0 @@
|
|
|
1
|
-
import { t as assertRootSlot } from "./assert-root-slot-B83scqPh.js";
|
|
2
|
-
import { Children, cloneElement, createElement, forwardRef, isValidElement } from "react";
|
|
3
|
-
import { isUnitlessProperty } from "@animus-ui/properties";
|
|
4
|
-
//#region src/runtime/is-dev.ts
|
|
5
|
-
/**
|
|
6
|
-
* The runtime's dev-build signal, shared by every development-only path
|
|
7
|
-
* (drop diagnostic, reachability witness). Several hosts have to be satisfied
|
|
8
|
-
* at once, and they force this exact shape:
|
|
9
|
-
*
|
|
10
|
-
* - **Bundles built through an Animus plugin.** `__ANIMUS_DEV__` is a
|
|
11
|
-
* build-time define supplied by the Vite plugin's `config` hook and by the
|
|
12
|
-
* Next plugin's DefinePlugin entry, each keyed on its bundler's own
|
|
13
|
-
* dev/build signal. It is tested FIRST, and as the CONDITIONAL OF THE
|
|
14
|
-
* INITIALIZER ITSELF — not as an early return inside the IIFE below. That
|
|
15
|
-
* distinction is the whole point: define-replacement turns the test into
|
|
16
|
-
* `typeof false === 'boolean'`, and only in this position does the
|
|
17
|
-
* production minifier propagate the resulting constant into every `IS_DEV`
|
|
18
|
-
* reference, so the gated branches and their diagnostic strings leave the
|
|
19
|
-
* bundle entirely. Hidden inside the IIFE, the same guard collapses to a
|
|
20
|
-
* variable the minifier keeps, and every diagnostic string ships. Never
|
|
21
|
-
* "tidy" this ternary into the fallback.
|
|
22
|
-
* - **Browser bundles without the define.** `process.env.NODE_ENV` is a define
|
|
23
|
-
* token that Vite and Next rewrite to a string literal; `typeof process` is
|
|
24
|
-
* not rewritten by anything, so guarding the token with it strands every dev
|
|
25
|
-
* branch in a browser (no `process` global → the conjunction is false in dev
|
|
26
|
-
* too). The token must therefore be read bare.
|
|
27
|
-
* - **Next under Turbopack.** The Next plugin supplies the define from its
|
|
28
|
-
* `webpack()` hook, and a Turbopack-active run never calls that hook (the
|
|
29
|
-
* Turbopack path wires rules and aliases instead), so those builds carry no
|
|
30
|
-
* token and land on the `process.env.NODE_ENV` fallback below — correct
|
|
31
|
-
* answer, no elimination.
|
|
32
|
-
* - **QuickJS (the Rust system-loader).** It evaluates the system dist with no
|
|
33
|
-
* define pass and no `process`, so the `typeof` test is simply false and the
|
|
34
|
-
* bare read then throws ReferenceError at module scope — caught here, and
|
|
35
|
-
* not-dev is the correct answer for a loader that only reads config.
|
|
36
|
-
* `import.meta` is unavailable there (a script-context eval makes it a syntax
|
|
37
|
-
* error), so it is never used.
|
|
38
|
-
* - **Node (SSR, tests).** Reads the real `process.env.NODE_ENV`.
|
|
39
|
-
*
|
|
40
|
-
* Evaluated once, at module load: in every host the answer is fixed for the
|
|
41
|
-
* lifetime of the bundle.
|
|
42
|
-
*
|
|
43
|
-
* The define is the sanctioned foldable path, and the only one. The try/catch
|
|
44
|
-
* fallback is deliberately NOT foldable — a host that supplies no token keeps
|
|
45
|
-
* its (gated-off, never-executed) diagnostic strings, which is the accepted
|
|
46
|
-
* price of a diagnostic that actually fires in a browser dev build. Restore
|
|
47
|
-
* elimination by supplying the define from a bundler plugin.
|
|
48
|
-
*/
|
|
49
|
-
const IS_DEV = typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : (() => {
|
|
50
|
-
try {
|
|
51
|
-
if (typeof process !== "undefined" && typeof process.env === "undefined") return true;
|
|
52
|
-
return process.env.NODE_ENV !== "production";
|
|
53
|
-
} catch {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
})();
|
|
57
|
-
//#endregion
|
|
58
|
-
//#region src/runtime/witness.ts
|
|
59
|
-
/**
|
|
60
|
-
* Dev-mode reachability witness: records every class-resolution outcome into a
|
|
61
|
-
* bounded in-page ring buffer at globalThis.__ANIMUS_WITNESS__. Development
|
|
62
|
-
* only — a production build records nothing and never creates the handle
|
|
63
|
-
* (IS_DEV is false). See is-dev.ts for the define/fold story and which hosts
|
|
64
|
-
* eliminate this code rather than merely gate it off. The handle name is the
|
|
65
|
-
* greppable exclusion token.
|
|
66
|
-
*/
|
|
67
|
-
const WITNESS_CAP = 5e3;
|
|
68
|
-
function recordWitness(component, prop, value, outcome) {
|
|
69
|
-
if (!IS_DEV) return;
|
|
70
|
-
const g = globalThis;
|
|
71
|
-
const buf = g.__ANIMUS_WITNESS__ ??= [];
|
|
72
|
-
buf.push({
|
|
73
|
-
component,
|
|
74
|
-
prop,
|
|
75
|
-
value: String(value),
|
|
76
|
-
outcome
|
|
77
|
-
});
|
|
78
|
-
if (buf.length > 5e3) buf.splice(0, buf.length - WITNESS_CAP);
|
|
79
|
-
}
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region src/runtime/resolveClasses.ts
|
|
82
|
-
/**
|
|
83
|
-
* Apply unit fallback to a value for the CSS properties the value lands on.
|
|
84
|
-
* `isUnitlessProperty` owns the spelling question, so either convention works.
|
|
85
|
-
*
|
|
86
|
-
* A mixed property set resolves to unitless: an unsuffixed number on a length
|
|
87
|
-
* property is dropped by the CSS parser, whereas `px` on a unitless property
|
|
88
|
-
* renders and silently changes layout — prefer the drop.
|
|
89
|
-
*
|
|
90
|
-
* An empty property list keeps the unconditional `px` — configs emitted before
|
|
91
|
-
* `property` was carried name no property at all.
|
|
92
|
-
*/
|
|
93
|
-
function applyUnitFallback(value, cssProperties) {
|
|
94
|
-
if (typeof value === "number") {
|
|
95
|
-
if (cssProperties.some(isUnitlessProperty)) return String(value);
|
|
96
|
-
return `${value}px`;
|
|
97
|
-
}
|
|
98
|
-
return String(value);
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Serialize a system prop value to a lookup key matching the Rust
|
|
102
|
-
* css_generator's serialize_value_key output format.
|
|
103
|
-
*/
|
|
104
|
-
function serializeValueKey(value) {
|
|
105
|
-
if (typeof value === "number" || typeof value === "string") return String(value);
|
|
106
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.keys(value).sort().map((k) => `${k}:${value[k]}`).join("|");
|
|
107
|
-
return String(value);
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Validity predicate for transform results (design D5): only strings and
|
|
111
|
-
* finite numbers may reach the stylesheet. The gate applies solely where a
|
|
112
|
-
* configured transform ran — scale hits without a transform are exempt.
|
|
113
|
-
*/
|
|
114
|
-
function isValidTransformResult(result) {
|
|
115
|
-
return typeof result === "string" || typeof result === "number" && Number.isFinite(result);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Shape descriptor for an invalid transform result, shared by the drop
|
|
119
|
-
* warning and its tests. `null` and arrays are named before `typeof` would
|
|
120
|
-
* fold them into `object`; non-finite numbers get their own name because
|
|
121
|
-
* finite ones are valid. Precondition: callers pass only results that failed
|
|
122
|
-
* `isValidTransformResult` — valid strings/finite numbers are out of domain
|
|
123
|
-
* and would be mislabeled.
|
|
124
|
-
*/
|
|
125
|
-
function describeResultShape(result) {
|
|
126
|
-
if (result === null) return "null";
|
|
127
|
-
if (Array.isArray(result)) return "array";
|
|
128
|
-
if (typeof result === "number") return "non-finite-number";
|
|
129
|
-
return typeof result;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Resolve one entry through scale lookup → transform → unit fallback,
|
|
133
|
-
* validating the transform result in both the scale-resolved and raw arms.
|
|
134
|
-
* An invalid result yields its shape descriptor instead of a string.
|
|
135
|
-
*/
|
|
136
|
-
function resolveEntry(value, dc) {
|
|
137
|
-
const key = String(value);
|
|
138
|
-
const scaleResolved = dc.scaleValues?.[key];
|
|
139
|
-
if (scaleResolved != null) {
|
|
140
|
-
const transformed = dc.transform ? dc.transform(scaleResolved) : scaleResolved;
|
|
141
|
-
if (dc.transform && !isValidTransformResult(transformed)) return { shape: describeResultShape(transformed) };
|
|
142
|
-
return String(transformed);
|
|
143
|
-
}
|
|
144
|
-
const transformed = dc.transform ? dc.transform(value) : value;
|
|
145
|
-
if (dc.transform && !isValidTransformResult(transformed)) return { shape: describeResultShape(transformed) };
|
|
146
|
-
if (typeof transformed !== "number") return String(transformed);
|
|
147
|
-
return applyUnitFallback(transformed, dc.properties && dc.properties.length > 0 ? dc.properties : dc.property ? [dc.property] : []);
|
|
148
|
-
}
|
|
149
|
-
const warnedDrops = /* @__PURE__ */ new Set();
|
|
150
|
-
function warnDroppedValue(baseClassName, propName, serializedValue) {
|
|
151
|
-
if (IS_DEV) {
|
|
152
|
-
const dedupeKey = `${baseClassName}|${propName}`;
|
|
153
|
-
if (warnedDrops.has(dedupeKey)) return;
|
|
154
|
-
warnedDrops.add(dedupeKey);
|
|
155
|
-
console.warn(`[animus:drop] ${baseClassName}: value ${serializedValue} on prop '${propName}' matched no static class and no dynamic slot — it will not render. If this prop should accept runtime values, ensure its dynamic config is emitted.`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Apply variant classes in declaration order, recording a static witness per
|
|
160
|
-
* resolved variant. When a value comes from a defaultVariant fallback (prop not
|
|
161
|
-
* passed), emit --{prop}-default instead of --{prop}-{value} so the compose
|
|
162
|
-
* override rule cannot match, allowing inheritance from the parent to win.
|
|
163
|
-
*/
|
|
164
|
-
function applyVariantClasses(classes, baseClassName, props, config) {
|
|
165
|
-
if (!config.variants) return;
|
|
166
|
-
for (const [prop, vc] of Object.entries(config.variants)) {
|
|
167
|
-
const value = props[prop] ?? vc.default;
|
|
168
|
-
if (value != null) {
|
|
169
|
-
const isDefault = !(prop in props) && vc.default != null;
|
|
170
|
-
classes.push(`${baseClassName}--${prop}-${isDefault ? "default" : value}`);
|
|
171
|
-
recordWitness(baseClassName, prop, value, "static");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Apply compound classes: push each compound's className when every condition
|
|
177
|
-
* matches (against the prop value or its variant default). No witness records.
|
|
178
|
-
*/
|
|
179
|
-
function applyCompoundClasses(classes, props, config) {
|
|
180
|
-
if (!config.compounds) return;
|
|
181
|
-
for (const compound of config.compounds) {
|
|
182
|
-
let match = true;
|
|
183
|
-
for (const [prop, expected] of Object.entries(compound.conditions)) {
|
|
184
|
-
const current = props[prop] ?? config.variants?.[prop]?.default;
|
|
185
|
-
if (Array.isArray(expected) ? !expected.includes(current) : current !== expected) {
|
|
186
|
-
match = false;
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (match) classes.push(compound.className);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Apply state classes and track active states for data-attribute passthrough,
|
|
195
|
-
* recording a static witness per active state.
|
|
196
|
-
*/
|
|
197
|
-
function applyStateClasses(classes, baseClassName, props, config, activeStates) {
|
|
198
|
-
if (!config.states) return;
|
|
199
|
-
for (const state of config.states) if (props[state]) {
|
|
200
|
-
classes.push(`${baseClassName}--${state}`);
|
|
201
|
-
activeStates.push(state);
|
|
202
|
-
recordWitness(baseClassName, state, "true", "static");
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const warnedInvalidResults = /* @__PURE__ */ new Set();
|
|
206
|
-
function warnInvalidTransformResult(baseClassName, propName, shape) {
|
|
207
|
-
if (IS_DEV) {
|
|
208
|
-
const dedupeKey = `${baseClassName}|${propName}`;
|
|
209
|
-
if (warnedInvalidResults.has(dedupeKey)) return;
|
|
210
|
-
warnedInvalidResults.add(dedupeKey);
|
|
211
|
-
console.warn(`[animus:drop] ${baseClassName}: transform for prop '${propName}' returned ${shape} — expected string or finite number; value dropped`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Expand a resolved dynamic prop into slot classes and CSS-variable style
|
|
216
|
-
* entries. Responsive objects expand per breakpoint: the `_` base breakpoint
|
|
217
|
-
* uses the bare slotClass and varName; named breakpoints suffix both
|
|
218
|
-
* (`${slotClass}-${bp}` and `${varName}-${bp}`). Scalar values push the bare
|
|
219
|
-
* slotClass and set varName directly.
|
|
220
|
-
*
|
|
221
|
-
* Two-phase so the drop is atomic: every entry resolves into a staging list
|
|
222
|
-
* before any mutation. One invalid transform result anywhere — the scalar, or
|
|
223
|
-
* any single breakpoint — returns that failure (first offending shape) with
|
|
224
|
-
* `classes` and `dynStyle` untouched. On success the staged entries apply in
|
|
225
|
-
* the pre-existing push order and the function returns `null`; the caller
|
|
226
|
-
* records the witness from the outcome.
|
|
227
|
-
*/
|
|
228
|
-
function applyDynamicProp(classes, dynStyle, propValue, dc) {
|
|
229
|
-
const staged = [];
|
|
230
|
-
if (typeof propValue === "object" && propValue !== null && !Array.isArray(propValue)) for (const [bp, bpVal] of Object.entries(propValue)) {
|
|
231
|
-
if (bpVal == null) continue;
|
|
232
|
-
const resolved = resolveEntry(bpVal, dc);
|
|
233
|
-
if (typeof resolved !== "string") return resolved;
|
|
234
|
-
staged.push(bp === "_" ? [
|
|
235
|
-
dc.slotClass,
|
|
236
|
-
dc.varName,
|
|
237
|
-
resolved
|
|
238
|
-
] : [
|
|
239
|
-
`${dc.slotClass}-${bp}`,
|
|
240
|
-
`${dc.varName}-${bp}`,
|
|
241
|
-
resolved
|
|
242
|
-
]);
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
const resolved = resolveEntry(propValue, dc);
|
|
246
|
-
if (typeof resolved !== "string") return resolved;
|
|
247
|
-
staged.push([
|
|
248
|
-
dc.slotClass,
|
|
249
|
-
dc.varName,
|
|
250
|
-
resolved
|
|
251
|
-
]);
|
|
252
|
-
}
|
|
253
|
-
for (const [slotClass, varName, resolved] of staged) {
|
|
254
|
-
classes.push(slotClass);
|
|
255
|
-
dynStyle[varName] = resolved;
|
|
256
|
-
}
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Resolve className parts from props, using extracted configuration.
|
|
261
|
-
* This is the shared logic between createComponent and createClassResolver.
|
|
262
|
-
*/
|
|
263
|
-
function resolveClasses(baseClassName, props, config, systemPropMap, dynamicPropConfig) {
|
|
264
|
-
const classes = [baseClassName];
|
|
265
|
-
let dynStyle;
|
|
266
|
-
applyVariantClasses(classes, baseClassName, props, config);
|
|
267
|
-
applyCompoundClasses(classes, props, config);
|
|
268
|
-
const activeStates = [];
|
|
269
|
-
applyStateClasses(classes, baseClassName, props, config, activeStates);
|
|
270
|
-
const systemPropNames = config.systemPropNames || [];
|
|
271
|
-
if (systemPropNames.length > 0) {
|
|
272
|
-
const { customPropMap, customDynamicConfig } = config;
|
|
273
|
-
for (const propName of systemPropNames) {
|
|
274
|
-
if (!(propName in props)) continue;
|
|
275
|
-
const propValue = props[propName];
|
|
276
|
-
if (propValue == null) continue;
|
|
277
|
-
const key = serializeValueKey(propValue);
|
|
278
|
-
const cls = customPropMap?.[propName]?.[key] ?? systemPropMap?.[propName]?.[key];
|
|
279
|
-
if (cls) {
|
|
280
|
-
classes.push(cls);
|
|
281
|
-
recordWitness(baseClassName, propName, key, "static");
|
|
282
|
-
} else {
|
|
283
|
-
const dc = customDynamicConfig?.[propName] ?? dynamicPropConfig?.[propName];
|
|
284
|
-
if (dc) {
|
|
285
|
-
const staged = dynStyle ?? {};
|
|
286
|
-
const invalid = applyDynamicProp(classes, staged, propValue, dc);
|
|
287
|
-
if (invalid === null) {
|
|
288
|
-
dynStyle = staged;
|
|
289
|
-
recordWitness(baseClassName, propName, key, "dynamic");
|
|
290
|
-
} else {
|
|
291
|
-
warnInvalidTransformResult(baseClassName, propName, invalid.shape);
|
|
292
|
-
recordWitness(baseClassName, propName, key, "drop");
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
warnDroppedValue(baseClassName, propName, key);
|
|
296
|
-
recordWitness(baseClassName, propName, key, "drop");
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return {
|
|
302
|
-
classes,
|
|
303
|
-
dynamicStyle: dynStyle,
|
|
304
|
-
activeStates
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
//#endregion
|
|
308
|
-
//#region src/runtime/index.ts
|
|
309
|
-
/**
|
|
310
|
-
* Merge multiple refs (callback or object) into a single callback ref.
|
|
311
|
-
* Creates a new callback on each call — no memoization possible in a
|
|
312
|
-
* hook-free runtime (RSC compatibility). Matches @radix-ui/react-compose-refs.
|
|
313
|
-
*/
|
|
314
|
-
function composeRefs(...refs) {
|
|
315
|
-
return (node) => {
|
|
316
|
-
for (const ref of refs) if (typeof ref === "function") ref(node);
|
|
317
|
-
else if (ref) ref.current = node;
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Forward props to the underlying element, filtering out Animus-managed props.
|
|
322
|
-
*
|
|
323
|
-
* data-* and aria-* attributes always pass through to the DOM, even when used
|
|
324
|
-
* as variant/state keys. This enables headless UI interop: the attribute is
|
|
325
|
-
* consumed for styling AND forwarded to the element so external frameworks
|
|
326
|
-
* (Radix, Ark-UI) see it.
|
|
327
|
-
*
|
|
328
|
-
* filterProps covers all Animus props, so unknown props pass through: for DOM
|
|
329
|
-
* elements React handles any unknown-attribute warnings in dev mode, and for
|
|
330
|
-
* component elements all non-filtered props are forwarded to the component.
|
|
331
|
-
*/
|
|
332
|
-
function forwardProps(props, filterProps, domProps) {
|
|
333
|
-
for (const [key, value] of Object.entries(props)) {
|
|
334
|
-
if (key === "className") continue;
|
|
335
|
-
if (!(key.startsWith("data-") || key.startsWith("aria-")) && filterProps.has(key)) continue;
|
|
336
|
-
domProps[key] = value;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* asChild render path: don't render our own element — merge the resolved
|
|
341
|
-
* className/ref/style onto the single child element, plus the parent's own
|
|
342
|
-
* forwardable props (event handlers, role, aria-*, data-*, id, tabIndex).
|
|
343
|
-
*
|
|
344
|
-
* Conflict rule is child-wins: the parent's props are spread UNDER the child's
|
|
345
|
-
* own, so a handler or attribute declared on the child replaces the parent's
|
|
346
|
-
* rather than chaining with it.
|
|
347
|
-
*/
|
|
348
|
-
function renderAsChild(className, filterProps, props, ref, classes, dynamicStyle) {
|
|
349
|
-
const child = Children.only(props.children);
|
|
350
|
-
if (!isValidElement(child)) throw new Error(`${className}: asChild requires a single React element as children`);
|
|
351
|
-
const childRef = child.ref;
|
|
352
|
-
const mergedClassName = [classes.join(" "), child.props.className].filter(Boolean).join(" ");
|
|
353
|
-
const mergedStyle = dynamicStyle || props.style || child.props.style ? {
|
|
354
|
-
...props.style,
|
|
355
|
-
...child.props.style,
|
|
356
|
-
...dynamicStyle
|
|
357
|
-
} : void 0;
|
|
358
|
-
const parentProps = {};
|
|
359
|
-
forwardProps(props, filterProps, parentProps);
|
|
360
|
-
delete parentProps.children;
|
|
361
|
-
delete parentProps.style;
|
|
362
|
-
delete parentProps.ref;
|
|
363
|
-
return cloneElement(child, {
|
|
364
|
-
...parentProps,
|
|
365
|
-
...child.props,
|
|
366
|
-
ref: composeRefs(ref, childRef),
|
|
367
|
-
className: mergedClassName,
|
|
368
|
-
...mergedStyle ? { style: mergedStyle } : {}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Normal render path: render our own element (or the `as` override), forwarding
|
|
373
|
-
* filtered props and applying any dynamic CSS-variable style.
|
|
374
|
-
*/
|
|
375
|
-
function renderElement(element, filterProps, props, ref, classes, dynamicStyle) {
|
|
376
|
-
const target = props.as || element;
|
|
377
|
-
const domProps = {
|
|
378
|
-
ref,
|
|
379
|
-
className: classes.join(" ")
|
|
380
|
-
};
|
|
381
|
-
forwardProps(props, filterProps, domProps);
|
|
382
|
-
if (dynamicStyle) domProps.style = props.style ? {
|
|
383
|
-
...props.style,
|
|
384
|
-
...dynamicStyle
|
|
385
|
-
} : dynamicStyle;
|
|
386
|
-
return createElement(target, domProps);
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Create a lightweight component that applies extracted CSS class names.
|
|
390
|
-
* Replaces Emotion's styled() for extracted components.
|
|
391
|
-
*
|
|
392
|
-
* The element parameter accepts either an HTML tag string (e.g. 'button') or
|
|
393
|
-
* a React component reference (e.g. NextLink). When a component reference is
|
|
394
|
-
* used, prop forwarding skips the HTML-attribute validity check — all
|
|
395
|
-
* non-filtered props are forwarded to the component.
|
|
396
|
-
*
|
|
397
|
-
* The optional systemPropMap parameter provides the shared prop→value→className
|
|
398
|
-
* lookup table, served as a virtual module by the Vite plugin.
|
|
399
|
-
*
|
|
400
|
-
* The optional dynamicPropConfig parameter provides CSS variable fallback
|
|
401
|
-
* metadata for props with detected dynamic usage.
|
|
402
|
-
*/
|
|
403
|
-
function createComponent(element, className, config, systemPropMap, dynamicPropConfig) {
|
|
404
|
-
const variantProps = config.variants ? Object.keys(config.variants) : [];
|
|
405
|
-
const stateProps = config.states || [];
|
|
406
|
-
const systemPropNames = config.systemPropNames || [];
|
|
407
|
-
const filterProps = new Set([
|
|
408
|
-
"as",
|
|
409
|
-
"asChild",
|
|
410
|
-
...variantProps,
|
|
411
|
-
...stateProps,
|
|
412
|
-
...systemPropNames
|
|
413
|
-
]);
|
|
414
|
-
const Component = forwardRef((props, ref) => {
|
|
415
|
-
const { classes, dynamicStyle } = resolveClasses(className, props, config, systemPropMap, dynamicPropConfig);
|
|
416
|
-
if (props.className) classes.push(props.className);
|
|
417
|
-
return props.asChild ? renderAsChild(className, filterProps, props, ref, classes, dynamicStyle) : renderElement(element, filterProps, props, ref, classes, dynamicStyle);
|
|
418
|
-
});
|
|
419
|
-
Component.displayName = className;
|
|
420
|
-
const variantDefaults = {};
|
|
421
|
-
if (config.variants) {
|
|
422
|
-
for (const [prop, vc] of Object.entries(config.variants)) if (vc.default != null) variantDefaults[prop] = vc.default;
|
|
423
|
-
}
|
|
424
|
-
return Object.assign(Component, {
|
|
425
|
-
variantDefaults: Object.freeze(variantDefaults),
|
|
426
|
-
extend: () => {
|
|
427
|
-
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.`);
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
//#endregion
|
|
432
|
-
//#region src/runtime/createClassResolver.ts
|
|
433
|
-
/**
|
|
434
|
-
* createClassResolver — framework-agnostic className resolution.
|
|
435
|
-
*
|
|
436
|
-
* Produced by .asClass() terminal. Same resolution logic as createComponent
|
|
437
|
-
* (variants, states, compounds, system props) but returns a className string
|
|
438
|
-
* instead of a React element.
|
|
439
|
-
*/
|
|
440
|
-
function createClassResolver(className, config, systemPropMap, dynamicPropConfig) {
|
|
441
|
-
return (props) => {
|
|
442
|
-
const { classes } = resolveClasses(className, props || {}, config, systemPropMap, dynamicPropConfig);
|
|
443
|
-
return classes.join(" ");
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
//#endregion
|
|
447
|
-
//#region src/runtime/createComposedFamily.ts
|
|
448
|
-
/**
|
|
449
|
-
* createComposedFamily — extraction-time replacement for compose().
|
|
450
|
-
*
|
|
451
|
-
* The transform emitter replaces `compose({ Root, Body }, { shared, name })`
|
|
452
|
-
* with `createComposedFamily({ Root, Body }, { name })`.
|
|
453
|
-
*
|
|
454
|
-
* RSC-safe: uses only forwardRef and createElement — no createContext,
|
|
455
|
-
* no useContext, no hooks.
|
|
456
|
-
*/
|
|
457
|
-
function createComposedFamily(slots, config) {
|
|
458
|
-
assertRootSlot(slots, "createComposedFamily");
|
|
459
|
-
const { name } = config;
|
|
460
|
-
const result = {};
|
|
461
|
-
for (const [slotName, SourceComponent] of Object.entries(slots)) {
|
|
462
|
-
const Wrapper = forwardRef((props, ref) => createElement(SourceComponent, {
|
|
463
|
-
...props,
|
|
464
|
-
ref
|
|
465
|
-
}, props.children));
|
|
466
|
-
Wrapper.displayName = `${name}.${slotName}`;
|
|
467
|
-
result[slotName] = Wrapper;
|
|
468
|
-
}
|
|
469
|
-
return result;
|
|
470
|
-
}
|
|
471
|
-
//#endregion
|
|
472
|
-
export { createClassResolver as n, createComponent as r, createComposedFamily as t };
|