@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.
Files changed (86) hide show
  1. package/README.md +124 -51
  2. package/dist/Animus.d.ts +3 -2
  3. package/dist/Animus.d.ts.map +1 -1
  4. package/dist/AnimusExtended.d.ts +3 -2
  5. package/dist/AnimusExtended.d.ts.map +1 -1
  6. package/dist/SystemBuilder.d.ts +184 -155
  7. package/dist/SystemBuilder.d.ts.map +1 -1
  8. package/dist/appearance/index.d.ts +0 -68
  9. package/dist/appearance/index.d.ts.map +1 -1
  10. package/dist/appearance/index.js +0 -91
  11. package/dist/assert-root-slot-C3Lp9MNJ.js +10 -0
  12. package/dist/asset.d.ts +4 -13
  13. package/dist/asset.d.ts.map +1 -1
  14. package/dist/bootstrap/createAppearanceBootstrap.d.ts +0 -57
  15. package/dist/bootstrap/createAppearanceBootstrap.d.ts.map +1 -1
  16. package/dist/bootstrap/index.d.ts.map +1 -1
  17. package/dist/bootstrap/index.js +4 -58
  18. package/dist/class-resolver.d.ts +6 -0
  19. package/dist/class-resolver.d.ts.map +1 -0
  20. package/dist/class-resolver.js +2 -0
  21. package/dist/compose.d.ts +2 -16
  22. package/dist/compose.d.ts.map +1 -1
  23. package/dist/compose.js +3 -17
  24. package/dist/composeWithContext.d.ts +4 -15
  25. package/dist/composeWithContext.d.ts.map +1 -1
  26. package/dist/composeWithContext.js +5 -16
  27. package/dist/conditions.d.ts +24 -141
  28. package/dist/conditions.d.ts.map +1 -1
  29. package/dist/createClassResolver-EpwTL9Sa.js +228 -0
  30. package/dist/createComposedFamily-C4NovMcd.js +115 -0
  31. package/dist/groups/index.js +1 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +318 -408
  35. package/dist/keyframes.d.ts +2 -23
  36. package/dist/keyframes.d.ts.map +1 -1
  37. package/dist/runtime/assert-root-slot.d.ts +2 -13
  38. package/dist/runtime/assert-root-slot.d.ts.map +1 -1
  39. package/dist/runtime/createClassResolver.d.ts +10 -9
  40. package/dist/runtime/createClassResolver.d.ts.map +1 -1
  41. package/dist/runtime/createComposedFamily.d.ts +2 -7
  42. package/dist/runtime/createComposedFamily.d.ts.map +1 -1
  43. package/dist/runtime/index.d.ts +0 -16
  44. package/dist/runtime/index.d.ts.map +1 -1
  45. package/dist/runtime/is-dev.d.ts +2 -42
  46. package/dist/runtime/is-dev.d.ts.map +1 -1
  47. package/dist/runtime/resolveClasses.d.ts +8 -33
  48. package/dist/runtime/resolveClasses.d.ts.map +1 -1
  49. package/dist/runtime/witness.d.ts +0 -8
  50. package/dist/runtime/witness.d.ts.map +1 -1
  51. package/dist/runtime-entry.d.ts +2 -7
  52. package/dist/runtime-entry.d.ts.map +1 -1
  53. package/dist/runtime-entry.js +2 -1
  54. package/dist/selectors.d.ts +6 -45
  55. package/dist/selectors.d.ts.map +1 -1
  56. package/dist/{size-DHdmBc64.js → size-C6jqxiQy.js} +7 -34
  57. package/dist/theme/createTheme.d.ts +26 -95
  58. package/dist/theme/createTheme.d.ts.map +1 -1
  59. package/dist/theme/flattenScale.d.ts +4 -10
  60. package/dist/theme/flattenScale.d.ts.map +1 -1
  61. package/dist/theme/resolveReferences.d.ts +7 -35
  62. package/dist/theme/resolveReferences.d.ts.map +1 -1
  63. package/dist/theme/serializeTokens.d.ts +0 -3
  64. package/dist/theme/serializeTokens.d.ts.map +1 -1
  65. package/dist/theme/types.d.ts +4 -26
  66. package/dist/theme/types.d.ts.map +1 -1
  67. package/dist/theme/utils.d.ts +1 -18
  68. package/dist/theme/utils.d.ts.map +1 -1
  69. package/dist/transforms/border.d.ts +1 -4
  70. package/dist/transforms/border.d.ts.map +1 -1
  71. package/dist/transforms/createTransform.d.ts +6 -31
  72. package/dist/transforms/createTransform.d.ts.map +1 -1
  73. package/dist/transforms/grid.d.ts +2 -12
  74. package/dist/transforms/grid.d.ts.map +1 -1
  75. package/dist/transforms/size.d.ts +2 -8
  76. package/dist/transforms/size.d.ts.map +1 -1
  77. package/dist/types/component.d.ts +9 -105
  78. package/dist/types/component.d.ts.map +1 -1
  79. package/dist/types/config.d.ts +12 -141
  80. package/dist/types/config.d.ts.map +1 -1
  81. package/dist/types/theme.d.ts +8 -165
  82. package/dist/types/theme.d.ts.map +1 -1
  83. package/dist/utils/deepMerge.d.ts.map +1 -1
  84. package/package.json +17 -4
  85. package/dist/assert-root-slot-B83scqPh.js +0 -21
  86. 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 };