@animus-ui/system 0.1.22 → 0.1.24
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/class-resolver.d.ts +2 -2
- package/dist/class-resolver.js +1 -1
- package/dist/{component-stJhG_A-.d.ts → component-D9Fie8Ci.d.ts} +58 -31
- package/dist/component-D9Fie8Ci.d.ts.map +1 -0
- package/dist/{compose-BQi7TDtL.d.ts → compose-A-kGRKdR.d.ts} +2 -2
- package/dist/{compose-BQi7TDtL.d.ts.map → compose-A-kGRKdR.d.ts.map} +1 -1
- package/dist/compose.d.ts +1 -1
- package/dist/composeWithContext.d.ts +1 -1
- package/dist/composeWithContext.d.ts.map +1 -1
- package/dist/composeWithContext.js +4 -4
- package/dist/createClassResolver-C-UOgE_d.d.ts +115 -0
- package/dist/createClassResolver-C-UOgE_d.d.ts.map +1 -0
- package/dist/{createClassResolver-DtHjTYa7.js → createClassResolver-DdrmIC5J.js} +321 -60
- package/dist/{createComposedFamily-BbA_d0QH.js → createComposedFamily-BFoaDqk7.js} +25 -8
- package/dist/{createComposedFamily-BkrvSsfl.d.ts → createComposedFamily-BJQ5Cn76.d.ts} +3 -3
- package/dist/createComposedFamily-BJQ5Cn76.d.ts.map +1 -0
- package/dist/groups/index.d.ts +1 -1
- package/dist/{index-C1AAiaiJ.d.ts → index-CQ7BFlKp.d.ts} +23 -23
- package/dist/index-CQ7BFlKp.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +26 -8
- package/dist/runtime-entry.d.ts +3 -3
- package/dist/runtime-entry.js +2 -2
- package/package.json +2 -2
- package/dist/component-stJhG_A-.d.ts.map +0 -1
- package/dist/createClassResolver-Do1nmvkf.d.ts +0 -71
- package/dist/createClassResolver-Do1nmvkf.d.ts.map +0 -1
- package/dist/createComposedFamily-BkrvSsfl.d.ts.map +0 -1
- package/dist/index-C1AAiaiJ.d.ts.map +0 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isUnitlessProperty } from "@animus-ui/properties";
|
|
2
2
|
//#region src/runtime/is-dev.ts
|
|
3
3
|
/**
|
|
4
4
|
* The define token is tested as the initializer's own conditional, never from
|
|
5
|
-
* inside the fallback: only there can a minifier
|
|
5
|
+
* inside the fallback: only there can a minifier fold it. Dev-only work tests
|
|
6
|
+
* the token in place as well, `typeof __ANIMUS_DEV__ === 'boolean' ?
|
|
7
|
+
* __ANIMUS_DEV__ : IS_DEV`: esbuild inlines no module-level constant into a
|
|
8
|
+
* chunk that starts with an import, so a gate on `IS_DEV` alone survives a
|
|
9
|
+
* production bundle.
|
|
6
10
|
*/
|
|
7
11
|
const IS_DEV = typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : (() => {
|
|
8
12
|
try {
|
|
@@ -13,6 +17,117 @@ const IS_DEV = typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : (() => {
|
|
|
13
17
|
}
|
|
14
18
|
})();
|
|
15
19
|
//#endregion
|
|
20
|
+
//#region src/runtime/value-scan.ts
|
|
21
|
+
const ESCAPE = String.raw`\\(?:[\da-fA-F]{1,6}(?:\r\n|[ \t\n\r\f])?|[^\n\r\f\da-fA-F])`;
|
|
22
|
+
const NAME_CHAR = String.raw`(?:[\w\u0080--]|${ESCAPE})`;
|
|
23
|
+
/** One unit per match: a comment (1), whitespace (2), an opaque string or
|
|
24
|
+
* `<!--` (3), a name (4), a `!` (5), or any other character. In a string, a
|
|
25
|
+
* backslash escapes a CRLF as one newline. */
|
|
26
|
+
const UNIT = new RegExp(String.raw`(\/\*[\s\S]*?(?:\*\/|$))|([ \t\n\r\f]+)|("(?:\\(?:\r\n|[\s\S])|[^"\\\n\r\f])*"?|'(?:\\(?:\r\n|[\s\S])|[^'\\\n\r\f])*'?|<!--)|((?:-?(?:[a-zA-Z_\u0080-]|${ESCAPE})|--)${NAME_CHAR}*)|(!)|[\s\S]`, "gy");
|
|
27
|
+
/** An unquoted `url(` body, through its `)`. */
|
|
28
|
+
const URL_BODY = /(?:\\[\s\S]|[^)\\])*\)?/y;
|
|
29
|
+
const decode = (name) => name.replace(/\\([\da-fA-F]{1,6})(?:\r\n|[ \t\n\r\f])?|\\([\s\S])/g, (_, hex, char) => {
|
|
30
|
+
if (hex === void 0) return char ?? "";
|
|
31
|
+
const code = Number.parseInt(hex, 16);
|
|
32
|
+
return code > 0 && code <= 1114111 && (code < 55296 || code > 57343) ? String.fromCodePoint(code) : "�";
|
|
33
|
+
});
|
|
34
|
+
function units(css) {
|
|
35
|
+
const out = [];
|
|
36
|
+
UNIT.lastIndex = 0;
|
|
37
|
+
for (let match; (match = UNIT.exec(css)) !== null;) {
|
|
38
|
+
const start = match.index;
|
|
39
|
+
let end = UNIT.lastIndex;
|
|
40
|
+
if (match[1] !== void 0) continue;
|
|
41
|
+
if (match[2] !== void 0) out.push([
|
|
42
|
+
0,
|
|
43
|
+
start,
|
|
44
|
+
end
|
|
45
|
+
]);
|
|
46
|
+
else if (match[5] !== void 0) out.push([
|
|
47
|
+
1,
|
|
48
|
+
start,
|
|
49
|
+
end
|
|
50
|
+
]);
|
|
51
|
+
else if (match[4] !== void 0) {
|
|
52
|
+
const name = decode(match[4]);
|
|
53
|
+
const previous = out.at(-1);
|
|
54
|
+
if (!(previous !== void 0 && previous[0] === 3 && previous[2] === start && /[\d#@]/.test(css[start - 1])) && css[end] === "(") {
|
|
55
|
+
let body = end + 1;
|
|
56
|
+
while (/[ \t\n\r\f]/.test(css[body] ?? "")) body += 1;
|
|
57
|
+
if (name.toLowerCase() === "url" && css[body] !== "\"" && css[body] !== "'") {
|
|
58
|
+
URL_BODY.lastIndex = end + 1;
|
|
59
|
+
URL_BODY.exec(css);
|
|
60
|
+
end = UNIT.lastIndex = URL_BODY.lastIndex;
|
|
61
|
+
out.push([
|
|
62
|
+
3,
|
|
63
|
+
start,
|
|
64
|
+
end
|
|
65
|
+
]);
|
|
66
|
+
} else {
|
|
67
|
+
UNIT.lastIndex = end + 1;
|
|
68
|
+
out.push([
|
|
69
|
+
4,
|
|
70
|
+
start,
|
|
71
|
+
end + 1,
|
|
72
|
+
name
|
|
73
|
+
]);
|
|
74
|
+
}
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
out.push([
|
|
78
|
+
2,
|
|
79
|
+
start,
|
|
80
|
+
end,
|
|
81
|
+
name
|
|
82
|
+
]);
|
|
83
|
+
} else out.push([
|
|
84
|
+
3,
|
|
85
|
+
start,
|
|
86
|
+
end
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/** A value's trailing priority, as the tokenizer's `importantPriority` reads
|
|
92
|
+
* it: exactly one `!`; `value!` with nothing between is the shorthand, and
|
|
93
|
+
* `value !important`, the keyword in any case or escaped, the full form. */
|
|
94
|
+
function trailingPriority(css) {
|
|
95
|
+
const trimmed = css.trimEnd();
|
|
96
|
+
const endsImportant = trimmed.length >= 9 && /^important$/i.test(trimmed.slice(-9));
|
|
97
|
+
if (!css.endsWith("!") && !endsImportant) return void 0;
|
|
98
|
+
const all = units(css);
|
|
99
|
+
const bangs = all.filter((unit) => unit[0] === 1);
|
|
100
|
+
if (bangs.length !== 1) return void 0;
|
|
101
|
+
const at = all.indexOf(bangs[0]);
|
|
102
|
+
if (at === all.length - 1) {
|
|
103
|
+
const previous = all[at - 1];
|
|
104
|
+
return previous !== void 0 && previous[0] !== 0 && previous[2] === bangs[0][1] ? {
|
|
105
|
+
spelling: "shorthand",
|
|
106
|
+
end: previous[2]
|
|
107
|
+
} : void 0;
|
|
108
|
+
}
|
|
109
|
+
const after = all.slice(at + 1).filter((unit) => unit[0] !== 0);
|
|
110
|
+
if (after.length !== 1 || after[0][0] !== 2 || !/^important$/i.test(after[0][3] ?? "")) return;
|
|
111
|
+
const value = all.slice(0, at).findLast((unit) => unit[0] !== 0);
|
|
112
|
+
return value && {
|
|
113
|
+
spelling: "important",
|
|
114
|
+
end: value[2]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** Whether `css` reads `variable` through a `var()` at any depth, its
|
|
118
|
+
* fallbacks included: a `var` function, in any case or escaped, whose first
|
|
119
|
+
* argument is the variable's decoded name. */
|
|
120
|
+
function readsVariable(css, variable) {
|
|
121
|
+
if (!css.includes("(")) return false;
|
|
122
|
+
const target = decode(variable);
|
|
123
|
+
const all = units(css);
|
|
124
|
+
return all.some((unit, index) => {
|
|
125
|
+
if (unit[0] !== 4 || unit[3]?.toLowerCase() !== "var") return false;
|
|
126
|
+
const first = all.slice(index + 1).find((next) => next[0] !== 0);
|
|
127
|
+
return first !== void 0 && first[0] === 2 && first[3] === target;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
16
131
|
//#region src/runtime/witness.ts
|
|
17
132
|
const WITNESS_CAP = 5e3;
|
|
18
133
|
function recordWitness(component, prop, value, outcome) {
|
|
@@ -117,15 +232,6 @@ function isAdmittedWithoutToken(value, dc) {
|
|
|
117
232
|
if (typeof value !== "string") return true;
|
|
118
233
|
return value === "0" || dc.keywords?.includes(value) === true || value.includes("{") || CONTAINER_UNIT.test(value) || dc.property !== void 0 && SIZE_PROPERTY.test(dc.property) && (SIZE_LENGTH.test(value) || value.startsWith("calc("));
|
|
119
234
|
}
|
|
120
|
-
/** A CSS-wide keyword is a cascade instruction, not a value: no transform
|
|
121
|
-
* sees it, and through an inline variable it would act on the variable. */
|
|
122
|
-
const CSS_WIDE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
123
|
-
"initial",
|
|
124
|
-
"inherit",
|
|
125
|
-
"unset",
|
|
126
|
-
"revert",
|
|
127
|
-
"revert-layer"
|
|
128
|
-
]);
|
|
129
235
|
function resolveEntry(value, dc) {
|
|
130
236
|
const key = String(value);
|
|
131
237
|
let scaleResolved = dc.scaleValues?.[key];
|
|
@@ -137,8 +243,7 @@ function resolveEntry(value, dc) {
|
|
|
137
243
|
if (scaleResolved == null && dc.strict && dc.scaleValues && !isAdmittedWithoutToken(value, dc)) return { entry: value };
|
|
138
244
|
const input = scaleResolved ?? value;
|
|
139
245
|
let transformed = input;
|
|
140
|
-
|
|
141
|
-
if (dc.transform && !keyword) {
|
|
246
|
+
if (dc.transform) {
|
|
142
247
|
try {
|
|
143
248
|
transformed = dc.transform(input);
|
|
144
249
|
} catch (cause) {
|
|
@@ -161,6 +266,29 @@ function warnDroppedValue(baseClassName, propName, serializedValue, customOwned)
|
|
|
161
266
|
}
|
|
162
267
|
}
|
|
163
268
|
/**
|
|
269
|
+
* A runtime value that reaches a slot condition a production build removes:
|
|
270
|
+
* the build's usage analysis saw no runtime value for the prop there, so the
|
|
271
|
+
* value arrived in a way it does not follow, and in production it has
|
|
272
|
+
* neither a class nor a slot. An entry a literal class serves, as
|
|
273
|
+
* `applyDynamicProp` serves an `!important` literal, takes no slot.
|
|
274
|
+
*/
|
|
275
|
+
function warnPrunedSlot(baseClassName, propName, serializedValue, propValue, dc, propClasses, typed) {
|
|
276
|
+
if (!IS_DEV || !dc.productionConditions) return;
|
|
277
|
+
const production = dc.productionConditions;
|
|
278
|
+
const [responsive, entries] = responsiveEntries(propValue);
|
|
279
|
+
const servedByClass = (bp, value) => {
|
|
280
|
+
if (dc.kind === "declarations" || !responsive || typeof value !== "string") return false;
|
|
281
|
+
const priority = trailingPriority(value);
|
|
282
|
+
return !!priority && (priority.spelling === "important" || !isCustomOnly(slotProperties(dc))) && entryClass(propClasses, typed, bp, value) !== void 0;
|
|
283
|
+
};
|
|
284
|
+
const pruned = entries.filter(([bp, value]) => !production.includes(bp) && !servedByClass(bp, value)).map(([bp]) => bp);
|
|
285
|
+
if (pruned.length === 0) return;
|
|
286
|
+
const dedupeKey = `${baseClassName}|${propName}|pruned`;
|
|
287
|
+
if (warnedDrops.has(dedupeKey)) return;
|
|
288
|
+
warnedDrops.add(dedupeKey);
|
|
289
|
+
console.warn(`[animus:drop] ${baseClassName}: value ${serializedValue} on prop '${propName}' uses its runtime slot at ${pruned.join(", ")}, which a production build removes — in production it will not render there. The build found no runtime value for this prop, so this one arrives through code it does not follow, such as a library that clones the element.`);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
164
292
|
* A value taken from a variant default emits `--{prop}-default`, not the
|
|
165
293
|
* value, so the compose override rule misses and the parent's value wins.
|
|
166
294
|
* An explicit `undefined` takes the default as an omitted prop does.
|
|
@@ -172,7 +300,7 @@ function applyVariantClasses(classes, baseClassName, props, config) {
|
|
|
172
300
|
if (value != null) {
|
|
173
301
|
const isDefault = props[prop] == null && vc.default != null;
|
|
174
302
|
classes.push(`${baseClassName}--${prop}-${isDefault ? "default" : value}`);
|
|
175
|
-
recordWitness(baseClassName, prop, value, "static");
|
|
303
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) recordWitness(baseClassName, prop, value, "static");
|
|
176
304
|
}
|
|
177
305
|
}
|
|
178
306
|
}
|
|
@@ -195,7 +323,7 @@ function applyStateClasses(classes, baseClassName, props, config, activeStates)
|
|
|
195
323
|
for (const state of config.states) if (props[state]) {
|
|
196
324
|
classes.push(`${baseClassName}--${state}`);
|
|
197
325
|
activeStates.push(state);
|
|
198
|
-
recordWitness(baseClassName, state, "true", "static");
|
|
326
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) recordWitness(baseClassName, state, "true", "static");
|
|
199
327
|
}
|
|
200
328
|
}
|
|
201
329
|
const warnedInvalidResults = /* @__PURE__ */ new Set();
|
|
@@ -217,6 +345,23 @@ function warnIgnoredImportant(baseClassName, propName, value) {
|
|
|
217
345
|
console.warn(`[animus:important] ${baseClassName}: !important is ignored on the runtime-generated value ${JSON.stringify(value)} of prop '${propName}' — a runtime value reaches CSS through a variable, which cannot carry it; declare the value literally to keep !important`);
|
|
218
346
|
}
|
|
219
347
|
}
|
|
348
|
+
const warnedUnitless = /* @__PURE__ */ new Set();
|
|
349
|
+
/**
|
|
350
|
+
* A number reaching a slot that only custom properties read, with no
|
|
351
|
+
* transform, stays unitless, as at build time; reported once per prop. Zero
|
|
352
|
+
* is a length without a unit, and a scale key is not written as is.
|
|
353
|
+
*/
|
|
354
|
+
function warnUnitlessCustomProperty(baseClassName, propName, propValue, dc) {
|
|
355
|
+
if (!IS_DEV) return;
|
|
356
|
+
const properties = slotProperties(dc);
|
|
357
|
+
if (dc.transform || dc.transformId || dc.transformName || !isCustomOnly(properties)) return;
|
|
358
|
+
const number = responsiveEntries(propValue)[1].map(([, value]) => value).find((value) => typeof value === "number" && Number.isFinite(value) && value !== 0 && dc.scaleValues?.[String(value)] == null);
|
|
359
|
+
if (number === void 0) return;
|
|
360
|
+
const dedupeKey = `${baseClassName}|${propName}`;
|
|
361
|
+
if (warnedUnitless.has(dedupeKey)) return;
|
|
362
|
+
warnedUnitless.add(dedupeKey);
|
|
363
|
+
console.warn(`[animus:unit] ${baseClassName}: prop '${propName}' writes the number ${String(number)} to ${properties.join(", ")} without a unit — a custom property has no unit context, so the number stays unitless; give the value a unit, or bind a transform that adds one`);
|
|
364
|
+
}
|
|
220
365
|
const warnedThrows = /* @__PURE__ */ new Set();
|
|
221
366
|
const warnedStrictMisses = /* @__PURE__ */ new Set();
|
|
222
367
|
/** Keyed by value, like a throw: each rejected input is reported once. */
|
|
@@ -264,29 +409,17 @@ function warnTransformThrow(baseClassName, propName, transformName, serializedVa
|
|
|
264
409
|
}
|
|
265
410
|
}
|
|
266
411
|
/**
|
|
267
|
-
* Whether `resolved` reads `currentVar` through a `var()` at any depth, its
|
|
268
|
-
* fallbacks included: a function token whose decoded name is `var` in any
|
|
269
|
-
* case, with the decoded variable name as its first argument. Comments,
|
|
270
|
-
* quoted strings and `url()` read nothing. The extractor's static path skips
|
|
271
|
-
* its `currentVar` write by the same predicate.
|
|
272
|
-
*/
|
|
273
|
-
function readsCurrentVar(resolved, currentVar) {
|
|
274
|
-
if (!resolved.includes("(")) return false;
|
|
275
|
-
const destination = decodedIdentifier(currentVar);
|
|
276
|
-
return variableReads(componentValues(tokenize(resolved))).some((read) => read.name === destination);
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
412
|
* A value that reads the prop's own `currentVar` takes the slot that leaves
|
|
280
|
-
* it alone, since writing it would make the variable cyclic.
|
|
413
|
+
* it alone, since writing it would make the variable cyclic. The extractor's
|
|
414
|
+
* static path skips its `currentVar` write by the same predicate.
|
|
281
415
|
*/
|
|
282
416
|
function slotClassFor(dc, resolved) {
|
|
283
|
-
return dc.currentVar !== void 0 &&
|
|
417
|
+
return dc.currentVar !== void 0 && readsVariable(resolved, dc.currentVar) ? `${dc.slotClass}--keep` : dc.slotClass;
|
|
284
418
|
}
|
|
285
419
|
/**
|
|
286
420
|
* Resolution is staged so a drop is atomic: one entry that misses a strict
|
|
287
421
|
* scale, or one transform that throws or returns an invalid result, leaves
|
|
288
|
-
* `classes` and `dynStyle` untouched.
|
|
289
|
-
* keyword takes the class extraction emits for it at that breakpoint.
|
|
422
|
+
* `classes` and `dynStyle` untouched.
|
|
290
423
|
*
|
|
291
424
|
* A slot is a CSS variable, which cannot carry `!important`. A literal that
|
|
292
425
|
* carries it, at a breakpoint, takes the class extraction emits for every
|
|
@@ -296,16 +429,15 @@ function slotClassFor(dc, resolved) {
|
|
|
296
429
|
* priority, which `ignoredImportant` hears of. Where only custom properties
|
|
297
430
|
* read the value, a trailing `!` is no priority, as at build time.
|
|
298
431
|
*/
|
|
299
|
-
function applyDynamicProp(classes, dynStyle, propValue, dc,
|
|
432
|
+
function applyDynamicProp(classes, dynStyle, propValue, dc, propClasses, typed, ignoredImportant) {
|
|
300
433
|
const staged = [];
|
|
301
434
|
const [responsive, entries] = responsiveEntries(propValue);
|
|
302
|
-
const lookup = (bp, value) => literalClass(bp === "_" ? value : { [bp]: value });
|
|
303
435
|
for (const [bp, authored] of entries) {
|
|
304
436
|
let value = authored;
|
|
305
437
|
let ignored;
|
|
306
|
-
const priority = typeof authored === "string" ?
|
|
438
|
+
const priority = typeof authored === "string" ? trailingPriority(authored) : void 0;
|
|
307
439
|
if (typeof authored === "string" && priority && (priority.spelling === "important" || !isCustomOnly(slotProperties(dc)))) {
|
|
308
|
-
const literal = responsive ?
|
|
440
|
+
const literal = responsive ? entryClass(propClasses, typed, bp, authored) : void 0;
|
|
309
441
|
if (literal) {
|
|
310
442
|
staged.push([literal]);
|
|
311
443
|
continue;
|
|
@@ -313,11 +445,6 @@ function applyDynamicProp(classes, dynStyle, propValue, dc, literalClass, ignore
|
|
|
313
445
|
ignored = authored;
|
|
314
446
|
if (priority.spelling === "shorthand") value = `${authored.slice(0, priority.end)} !important`;
|
|
315
447
|
}
|
|
316
|
-
const keywordClass = responsive && CSS_WIDE_KEYWORDS.has(value) ? lookup(bp, value) : void 0;
|
|
317
|
-
if (keywordClass) {
|
|
318
|
-
staged.push([keywordClass]);
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
448
|
let resolved = resolveEntry(value, dc);
|
|
322
449
|
if (typeof resolved !== "string") return "entry" in resolved && responsive ? {
|
|
323
450
|
...resolved,
|
|
@@ -325,13 +452,8 @@ function applyDynamicProp(classes, dynStyle, propValue, dc, literalClass, ignore
|
|
|
325
452
|
} : resolved;
|
|
326
453
|
if (ignored !== void 0) {
|
|
327
454
|
ignoredImportant(ignored);
|
|
328
|
-
const end =
|
|
455
|
+
const end = trailingPriority(resolved)?.end;
|
|
329
456
|
resolved = end === void 0 ? resolved : resolved.slice(0, end);
|
|
330
|
-
const bareKeyword = CSS_WIDE_KEYWORDS.has(resolved) ? lookup(bp, resolved) : void 0;
|
|
331
|
-
if (bareKeyword) {
|
|
332
|
-
staged.push([bareKeyword]);
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
457
|
}
|
|
336
458
|
const slotClass = slotClassFor(dc, resolved);
|
|
337
459
|
staged.push(bp === "_" ? [
|
|
@@ -345,8 +467,16 @@ function applyDynamicProp(classes, dynStyle, propValue, dc, literalClass, ignore
|
|
|
345
467
|
]);
|
|
346
468
|
}
|
|
347
469
|
for (const [cls, varName, resolved] of staged) {
|
|
470
|
+
if (varName !== void 0 && resolved !== void 0) {
|
|
471
|
+
if (varName in dynStyle) {
|
|
472
|
+
const keep = `${dc.slotClass}--keep`;
|
|
473
|
+
const twin = cls.startsWith(keep) ? dc.slotClass + cls.slice(keep.length) : keep + cls.slice(dc.slotClass.length);
|
|
474
|
+
const at = classes.indexOf(cls);
|
|
475
|
+
classes.splice(at >= 0 ? at : classes.indexOf(twin), 1);
|
|
476
|
+
}
|
|
477
|
+
dynStyle[varName] = resolved;
|
|
478
|
+
}
|
|
348
479
|
classes.push(cls);
|
|
349
|
-
if (varName !== void 0 && resolved !== void 0) dynStyle[varName] = resolved;
|
|
350
480
|
}
|
|
351
481
|
return null;
|
|
352
482
|
}
|
|
@@ -379,6 +509,29 @@ function applyDeclarationProp(classes, dynStyle, propValue, dc) {
|
|
|
379
509
|
return null;
|
|
380
510
|
}
|
|
381
511
|
/**
|
|
512
|
+
* The static class of `value` written alone at breakpoint `bp` (`_` the
|
|
513
|
+
* base), keyed as extraction keys it.
|
|
514
|
+
*/
|
|
515
|
+
function entryClass(propClasses, typed, bp, value) {
|
|
516
|
+
return propClasses?.[(typed ? typedValueKey : serializeValueKey)(bp === "_" ? value : { [bp]: value })];
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* The static classes of a responsive value's entries, when every entry has
|
|
520
|
+
* one: extraction gives a value proven to be one of a few at each
|
|
521
|
+
* breakpoint a class per entry, and no slot.
|
|
522
|
+
*/
|
|
523
|
+
function entryClasses(propValue, propClasses, typed) {
|
|
524
|
+
const [responsive, entries] = responsiveEntries(propValue);
|
|
525
|
+
if (!responsive || entries.length === 0) return void 0;
|
|
526
|
+
const found = [];
|
|
527
|
+
for (const [bp, value] of entries) {
|
|
528
|
+
const cls = entryClass(propClasses, typed, bp, value);
|
|
529
|
+
if (!cls) return void 0;
|
|
530
|
+
found.push(cls);
|
|
531
|
+
}
|
|
532
|
+
return found;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
382
535
|
* A responsive value without its nullish breakpoints, so it keys the same
|
|
383
536
|
* static class as the value written without them; `undefined` when none
|
|
384
537
|
* remain, as if the prop were omitted.
|
|
@@ -390,6 +543,20 @@ function withoutAbsentBreakpoints(value) {
|
|
|
390
543
|
const present = entries.filter(([, entry]) => entry != null);
|
|
391
544
|
return present.length > 0 ? Object.fromEntries(present) : void 0;
|
|
392
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* The config with each admitted prop named once, at its first position. An
|
|
548
|
+
* extracted config lists every admitted group's props, and groups can share
|
|
549
|
+
* one, as flexbox and grid share `alignItems`.
|
|
550
|
+
*/
|
|
551
|
+
function withUniqueSystemPropNames(config) {
|
|
552
|
+
const names = config.systemPropNames;
|
|
553
|
+
if (!names) return config;
|
|
554
|
+
const unique = [...new Set(names)];
|
|
555
|
+
return unique.length === names.length ? config : {
|
|
556
|
+
...config,
|
|
557
|
+
systemPropNames: unique
|
|
558
|
+
};
|
|
559
|
+
}
|
|
393
560
|
function resolveClasses(baseClassName, props, config, systemPropMap, dynamicPropConfig) {
|
|
394
561
|
const classes = [baseClassName];
|
|
395
562
|
let dynStyle;
|
|
@@ -399,33 +566,42 @@ function resolveClasses(baseClassName, props, config, systemPropMap, dynamicProp
|
|
|
399
566
|
applyStateClasses(classes, baseClassName, props, config, activeStates);
|
|
400
567
|
const systemPropNames = config.systemPropNames || [];
|
|
401
568
|
if (systemPropNames.length > 0) {
|
|
402
|
-
const { customPropMap, customDynamicConfig } = config;
|
|
569
|
+
const { customPropMap, customDynamicConfig, supersededBy } = config;
|
|
403
570
|
for (const propName of systemPropNames) {
|
|
404
571
|
const propValue = withoutAbsentBreakpoints(props[propName]);
|
|
405
572
|
if (propValue == null) continue;
|
|
573
|
+
if (supersededBy?.[propName]?.some((later) => withoutAbsentBreakpoints(props[later]) != null)) continue;
|
|
406
574
|
const key = serializeValueKey(propValue);
|
|
407
575
|
const customOwned = customPropMap?.[propName] !== void 0 || customDynamicConfig?.[propName] !== void 0;
|
|
408
576
|
const [classMap, typedProps] = customOwned ? [customPropMap, config.typedCustomProps] : [systemPropMap, config.typedSystemProps];
|
|
409
577
|
const typed = typedProps?.includes(propName) === true;
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
578
|
+
const propClasses = classMap?.[propName];
|
|
579
|
+
const cls = propClasses?.[typed ? typedValueKey(propValue) : key];
|
|
580
|
+
const found = cls ? [cls] : propClasses && entryClasses(propValue, propClasses, typed);
|
|
581
|
+
if (found) {
|
|
582
|
+
classes.push(...found);
|
|
583
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) recordWitness(baseClassName, propName, key, "static");
|
|
414
584
|
} else {
|
|
415
585
|
const dc = customOwned ? customDynamicConfig?.[propName] : dynamicPropConfig?.[propName];
|
|
416
586
|
if (dc) {
|
|
417
587
|
const staged = dynStyle ?? {};
|
|
418
|
-
const failure = dc.kind === "declarations" ? applyDeclarationProp(classes, staged, propValue, dc) : applyDynamicProp(classes, staged, propValue, dc,
|
|
588
|
+
const failure = dc.kind === "declarations" ? applyDeclarationProp(classes, staged, propValue, dc) : applyDynamicProp(classes, staged, propValue, dc, propClasses, typed, (value) => {
|
|
589
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) warnIgnoredImportant(baseClassName, propName, value);
|
|
590
|
+
});
|
|
419
591
|
if (failure === null) {
|
|
420
592
|
dynStyle = staged;
|
|
421
|
-
|
|
422
|
-
|
|
593
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) {
|
|
594
|
+
recordWitness(baseClassName, propName, key, "dynamic");
|
|
595
|
+
warnPrunedSlot(baseClassName, propName, key, propValue, dc, propClasses, typed);
|
|
596
|
+
if (dc.kind !== "declarations") warnUnitlessCustomProperty(baseClassName, propName, propValue, dc);
|
|
597
|
+
}
|
|
598
|
+
} else if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) {
|
|
423
599
|
if ("shape" in failure) warnInvalidTransformResult(baseClassName, propName, failure.shape);
|
|
424
600
|
else if ("entry" in failure) warnStrictScaleMiss(baseClassName, propName, key, failure);
|
|
425
601
|
else warnTransformThrow(baseClassName, propName, dc.transformName, key, failure.cause);
|
|
426
602
|
recordWitness(baseClassName, propName, key, "drop");
|
|
427
603
|
}
|
|
428
|
-
} else {
|
|
604
|
+
} else if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) {
|
|
429
605
|
warnDroppedValue(baseClassName, propName, key, customOwned);
|
|
430
606
|
recordWitness(baseClassName, propName, key, "drop");
|
|
431
607
|
}
|
|
@@ -439,19 +615,104 @@ function resolveClasses(baseClassName, props, config, systemPropMap, dynamicProp
|
|
|
439
615
|
};
|
|
440
616
|
}
|
|
441
617
|
//#endregion
|
|
618
|
+
//#region src/runtime/uncompiled.ts
|
|
619
|
+
/**
|
|
620
|
+
* What rendering a component that Animus did not compile does: nothing, a
|
|
621
|
+
* console warning once per component, or a thrown error on every render.
|
|
622
|
+
* Without compilation its classes are empty and no CSS exists for it.
|
|
623
|
+
* Production stays silent until the project owner chooses its level, and its
|
|
624
|
+
* bundles carry no report: the calls in `createComponent` and
|
|
625
|
+
* `createClassResolver` test the define token in place, which a minifier
|
|
626
|
+
* removes. A production level changes this line and lifts those guards.
|
|
627
|
+
*/
|
|
628
|
+
const UNCOMPILED_RENDER = IS_DEV ? "warn" : "off";
|
|
629
|
+
/**
|
|
630
|
+
* The record a builder terminal attaches to the component it builds at
|
|
631
|
+
* runtime. Only an uncompiled definition reaches a terminal: a compiled
|
|
632
|
+
* one is replaced with the component's extracted classes. `undefined` when
|
|
633
|
+
* uncompiled renders are not reported.
|
|
634
|
+
*/
|
|
635
|
+
function uncompiledDefinition(target) {
|
|
636
|
+
if (UNCOMPILED_RENDER === "off") return void 0;
|
|
637
|
+
return {
|
|
638
|
+
target,
|
|
639
|
+
origin: declaringModule((/* @__PURE__ */ new Error()).stack),
|
|
640
|
+
reported: false
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Reports a render of an uncompiled definition at the configured level.
|
|
645
|
+
* Defining or importing one reports nothing; only a render does.
|
|
646
|
+
*/
|
|
647
|
+
function reportUncompiledRender(definition, displayName) {
|
|
648
|
+
if (!definition) return;
|
|
649
|
+
if (UNCOMPILED_RENDER === "error") throw new Error(uncompiledMessage(definition, displayName));
|
|
650
|
+
if (definition.reported) return;
|
|
651
|
+
definition.reported = true;
|
|
652
|
+
console.warn(uncompiledMessage(definition, displayName));
|
|
653
|
+
}
|
|
654
|
+
function uncompiledMessage({ target, origin }, displayName) {
|
|
655
|
+
const component = displayName ? `${displayName} (${target})` : target;
|
|
656
|
+
const pkg = origin && packageOf(origin);
|
|
657
|
+
const declared = origin ? ` declared in ${origin}` : "";
|
|
658
|
+
const subject = pkg ? `the package ${pkg}` : "its module";
|
|
659
|
+
return `[animus:uncompiled] ${component}${declared} rendered without Animus compiling it: its classes are empty and no CSS exists for it. Add the Animus plugin for this app's host (Vite, Next.js, Rollup) so it compiles ${subject}, or build ${subject} with the Animus CLI.`;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* The caller of the builder terminal: the frame after the terminal's own.
|
|
663
|
+
* Engines differ in frame syntax, so only `file:line:column` is read.
|
|
664
|
+
*/
|
|
665
|
+
function declaringModule(stack) {
|
|
666
|
+
return (stack ?? "").split("\n").map((line) => /((?:[a-z][\w+.-]*:\/\/|\/)[^\s()]+:\d+:\d+)/i.exec(line)?.[1]).filter((frame) => frame !== void 0)[2];
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* The installed package a path lies in, as `node_modules` names it, or as
|
|
670
|
+
* Vite's dependency optimizer names the file it bundles the package into:
|
|
671
|
+
* `@scope/name/sub` becomes `.vite/deps/@scope_name_sub.js`, a `.` becomes
|
|
672
|
+
* `__`, and a shared chunk names no package.
|
|
673
|
+
*/
|
|
674
|
+
function packageOf(path) {
|
|
675
|
+
const installed = /\/node_modules\/((?:@[^/]+\/)?[^/.][^/]*)\//.exec(path);
|
|
676
|
+
if (installed) return installed[1];
|
|
677
|
+
const optimized = /\/node_modules\/\.vite\/deps(?:_[^/]*)?\/([^/?#:]+?)\.m?js\b/.exec(path)?.[1];
|
|
678
|
+
if (!optimized || optimized.startsWith("chunk-")) return void 0;
|
|
679
|
+
const flat = optimized.replace(/__/g, ".");
|
|
680
|
+
return /^(@[^_]+_)?[^_]+/.exec(flat)?.[0].replace("_", "/");
|
|
681
|
+
}
|
|
682
|
+
//#endregion
|
|
442
683
|
//#region src/runtime/createClassResolver.ts
|
|
443
684
|
function serializeDynamicStyle(style) {
|
|
444
685
|
return Object.entries(style).map(([property, value]) => `${property}: ${value}`).join("; ");
|
|
445
686
|
}
|
|
446
|
-
function createClassResolver(className,
|
|
687
|
+
function createClassResolver(className, resolverConfig, systemPropMap, dynamicPropConfig) {
|
|
688
|
+
const config = withUniqueSystemPropNames(resolverConfig);
|
|
447
689
|
const resolveAttributes = (props) => {
|
|
690
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) reportUncompiledRender(config.uncompiled, void 0);
|
|
448
691
|
const { classes, dynamicStyle } = resolveClasses(className, props || {}, config, systemPropMap, dynamicPropConfig);
|
|
449
692
|
const attributes = { class: classes.join(" ") };
|
|
450
693
|
if (dynamicStyle && Object.keys(dynamicStyle).length > 0) attributes.style = serializeDynamicStyle(dynamicStyle);
|
|
451
694
|
return attributes;
|
|
452
695
|
};
|
|
453
|
-
const
|
|
454
|
-
|
|
696
|
+
const resolveProps = (props) => {
|
|
697
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) reportUncompiledRender(config.uncompiled, void 0);
|
|
698
|
+
const { classes, dynamicStyle } = resolveClasses(className, props || {}, config, systemPropMap, dynamicPropConfig);
|
|
699
|
+
if (props?.className) classes.push(props.className);
|
|
700
|
+
const attributes = { className: classes.join(" ") };
|
|
701
|
+
const style = props?.style ? {
|
|
702
|
+
...dynamicStyle,
|
|
703
|
+
...props.style
|
|
704
|
+
} : dynamicStyle;
|
|
705
|
+
if (style && Object.keys(style).length > 0) attributes.style = style;
|
|
706
|
+
return attributes;
|
|
707
|
+
};
|
|
708
|
+
const resolver = (props) => {
|
|
709
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) reportUncompiledRender(config.uncompiled, void 0);
|
|
710
|
+
return resolveClasses(className, props || {}, config, systemPropMap, dynamicPropConfig).classes.join(" ");
|
|
711
|
+
};
|
|
712
|
+
return Object.assign(resolver, {
|
|
713
|
+
attrs: resolveAttributes,
|
|
714
|
+
props: resolveProps
|
|
715
|
+
});
|
|
455
716
|
}
|
|
456
717
|
//#endregion
|
|
457
|
-
export { resolveClasses as n, createClassResolver as t };
|
|
718
|
+
export { withUniqueSystemPropNames as a, resolveClasses as i, reportUncompiledRender as n, IS_DEV as o, uncompiledDefinition as r, createClassResolver as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as withUniqueSystemPropNames, i as resolveClasses, n as reportUncompiledRender, o as IS_DEV } from "./createClassResolver-DdrmIC5J.js";
|
|
2
2
|
import { t as assertRootSlot } from "./assert-root-slot-C3Lp9MNJ.js";
|
|
3
3
|
import { Children, cloneElement, createElement, forwardRef } from "react";
|
|
4
4
|
//#region src/runtime/index.ts
|
|
@@ -117,25 +117,42 @@ function renderSlot(children, props, ref, variables) {
|
|
|
117
117
|
function renderAsChild(children, props, ref) {
|
|
118
118
|
return renderSlot(children, props, ref);
|
|
119
119
|
}
|
|
120
|
+
/** Animus's classes, then the caller's. */
|
|
121
|
+
function joinClassNames(classes, className) {
|
|
122
|
+
return className ? `${classes} ${className}` : classes;
|
|
123
|
+
}
|
|
120
124
|
/**
|
|
121
125
|
* A string terminal renders `as` in place of its tag. A component terminal
|
|
122
126
|
* always renders its component, which receives `as` and `asChild` like any
|
|
123
127
|
* other prop, so its own wiring runs whatever element it ends up rendering.
|
|
128
|
+
*
|
|
129
|
+
* A component target may take `className` and `style` as callbacks that it
|
|
130
|
+
* calls with its own state, as Base UI's do. It gets a callback in their
|
|
131
|
+
* place, which merges the caller's result as a plain value merges: Animus's
|
|
132
|
+
* classes first, and Animus's dynamic style over the caller's style.
|
|
124
133
|
*/
|
|
125
134
|
function renderElement(element, filterProps, props, ref, classes, dynamicStyle) {
|
|
126
|
-
const
|
|
135
|
+
const ownsElement = typeof element === "string";
|
|
136
|
+
const target = ownsElement ? props.as || element : element;
|
|
137
|
+
const { className, style } = props;
|
|
138
|
+
const ownClassName = classes.join(" ");
|
|
127
139
|
const domProps = {
|
|
128
140
|
ref,
|
|
129
|
-
className:
|
|
141
|
+
className: !ownsElement && typeof className === "function" ? (state) => joinClassNames(ownClassName, className(state)) : joinClassNames(ownClassName, className)
|
|
130
142
|
};
|
|
131
143
|
forwardProps(props, filterProps, domProps);
|
|
132
|
-
if (dynamicStyle) domProps.style =
|
|
133
|
-
...
|
|
144
|
+
if (dynamicStyle && !ownsElement && typeof style === "function") domProps.style = (state) => ({
|
|
145
|
+
...style(state),
|
|
146
|
+
...dynamicStyle
|
|
147
|
+
});
|
|
148
|
+
else if (dynamicStyle) domProps.style = style ? {
|
|
149
|
+
...style,
|
|
134
150
|
...dynamicStyle
|
|
135
151
|
} : dynamicStyle;
|
|
136
152
|
return createElement(target, domProps);
|
|
137
153
|
}
|
|
138
|
-
function createComponent(element, className,
|
|
154
|
+
function createComponent(element, className, componentConfig, systemPropMap, dynamicPropConfig) {
|
|
155
|
+
const config = withUniqueSystemPropNames(componentConfig);
|
|
139
156
|
const variantProps = config.variants ? Object.keys(config.variants) : [];
|
|
140
157
|
const stateProps = config.states || [];
|
|
141
158
|
const systemPropNames = config.systemPropNames || [];
|
|
@@ -147,10 +164,10 @@ function createComponent(element, className, config, systemPropMap, dynamicPropC
|
|
|
147
164
|
...systemPropNames
|
|
148
165
|
]);
|
|
149
166
|
const Component = forwardRef((props, ref) => {
|
|
167
|
+
if (typeof __ANIMUS_DEV__ === "boolean" ? __ANIMUS_DEV__ : IS_DEV) reportUncompiledRender(config.uncompiled, Component.displayName || void 0);
|
|
150
168
|
const { classes, dynamicStyle } = resolveClasses(className, props, config, systemPropMap, dynamicPropConfig);
|
|
151
|
-
if (props.className) classes.push(props.className);
|
|
152
169
|
if (props.asChild && ownsPolymorphism) {
|
|
153
|
-
const slotProps = { className: classes.join(" ") };
|
|
170
|
+
const slotProps = { className: joinClassNames(classes.join(" "), props.className) };
|
|
154
171
|
forwardProps(props, filterProps, slotProps);
|
|
155
172
|
return renderSlot(props.children, slotProps, ref, dynamicStyle);
|
|
156
173
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as ClassResolverConfig, o as DynamicPropConfig, s as SystemPropMap } from "./createClassResolver-C-UOgE_d.js";
|
|
2
2
|
import { ForwardRefExoticComponent, ReactElement, ReactNode, Ref, forwardRef } from "react";
|
|
3
3
|
//#region src/runtime/index.d.ts
|
|
4
4
|
interface ComponentConfig extends ClassResolverConfig {}
|
|
@@ -28,7 +28,7 @@ type AnimusComponent = ReturnType<typeof forwardRef> & {
|
|
|
28
28
|
* leaves its children out without `asChild`, where React would throw.
|
|
29
29
|
*/
|
|
30
30
|
declare function renderAsChild(children: ReactNode, props: object, ref?: Ref<any>): ReactElement;
|
|
31
|
-
declare function createComponent(element: ElementType, className: string,
|
|
31
|
+
declare function createComponent(element: ElementType, className: string, componentConfig: ComponentConfig, systemPropMap?: SystemPropMap, dynamicPropConfig?: DynamicPropConfig): AnimusComponent;
|
|
32
32
|
//#endregion
|
|
33
33
|
//#region src/runtime/createComposedFamily.d.ts
|
|
34
34
|
interface ComposedFamilyConfig {
|
|
@@ -37,4 +37,4 @@ interface ComposedFamilyConfig {
|
|
|
37
37
|
declare function createComposedFamily(slots: Record<string, ForwardRefExoticComponent<any>>, config: ComposedFamilyConfig): Record<string, ForwardRefExoticComponent<any>>;
|
|
38
38
|
//#endregion
|
|
39
39
|
export { createComponent as n, renderAsChild as r, createComposedFamily as t };
|
|
40
|
-
//# sourceMappingURL=createComposedFamily-
|
|
40
|
+
//# sourceMappingURL=createComposedFamily-BJQ5Cn76.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createComposedFamily-BJQ5Cn76.d.ts","names":[],"sources":["../src/runtime/index.ts","../src/runtime/createComposedFamily.ts"],"mappings":";;;UAqBU,wBAAwB;KAE7B,uBAAuB,MAAM;KAE7B,YAAY,YAAY;KAExB,kBAAkB,kBAAkB;EACvC;EACA,iBAAiB,SAAS;EAC1B,kBAAkB,SAAS,eAAe;;;;;;;;;;;;;;;;;;;;iBAyJ5B,cACd,UAAU,WACV,eACA,MAAM,WACL;iBAkDa,gBACd,SAAS,aACT,mBACA,iBAAiB,iBACjB,gBAAgB,eAChB,oBAAoB,oBACnB;;;UCrOO;EACR;;iBAGc,qBACd,OAAO,eAAe,iCACtB,QAAQ,uBACP,eAAe"}
|
package/dist/groups/index.d.ts
CHANGED