synthesisui 0.16.124 → 0.16.126
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/component-codegen.js +75 -2
- package/dist/doctor/variant-read.js +38 -1
- package/package.json +1 -1
|
@@ -125,6 +125,79 @@ function elementFor(name, recipe) {
|
|
|
125
125
|
}
|
|
126
126
|
/** Variant axes → typed props. An axis whose options ⊆ {true,false} is a
|
|
127
127
|
* boolean prop; empty axes (no visual effect) are skipped. */
|
|
128
|
+
/**
|
|
129
|
+
* THE AXES A PART'S OWN LAYERS READ, which is where an imported recipe keeps them.
|
|
130
|
+
*
|
|
131
|
+
* `axesOf(part.variants)` is right for a recipe we generated and empty for one we READ: their
|
|
132
|
+
* conditional look arrives as `layers` with `when.variant`, never as a per-part `variants`
|
|
133
|
+
* map. So a generated part component wrote no `data-status`, the compiled rule
|
|
134
|
+
* `.ds-card-status-label[data-status="waiting"]` matched nothing, and the status colours of
|
|
135
|
+
* their card - the whole point of that component - appeared nowhere. 37 conditions across 7 of
|
|
136
|
+
* their components (test49, dono, 04/08).
|
|
137
|
+
*
|
|
138
|
+
* The option list comes from the COMPONENT's `variants`, because that is where a read recipe
|
|
139
|
+
* declares them; the part only names which axes it reacts to.
|
|
140
|
+
*/
|
|
141
|
+
function layerAxes(part, variants) {
|
|
142
|
+
const named = new Set();
|
|
143
|
+
for (const layer of part.layers ?? [])
|
|
144
|
+
for (const key of Object.keys(layer.when?.variant ?? {}))
|
|
145
|
+
if (variants?.[key])
|
|
146
|
+
named.add(key);
|
|
147
|
+
/**
|
|
148
|
+
* BUILT HERE RATHER THAN THROUGH `axesOf`, and that is the whole reason this was invisible.
|
|
149
|
+
*
|
|
150
|
+
* `axesOf` keeps an option only when its style block carries declarations, which is right
|
|
151
|
+
* for a recipe we generated - there the option IS the styles. A recipe we READ declares
|
|
152
|
+
* `variants.status.waiting = {}` and puts every declaration in `layers`, so `axesOf`
|
|
153
|
+
* answered "no axes" for a component with four of them.
|
|
154
|
+
*/
|
|
155
|
+
/**
|
|
156
|
+
* A CONDITION ON SOMETHING THAT IS NOT AN AXIS IS STILL A LOOK SOMEBODY HAS TO TRIGGER.
|
|
157
|
+
*
|
|
158
|
+
* Their chevron rotates while the card is closed, keyed on `isOpen` - state the component
|
|
159
|
+
* holds, so no axis and no option list. It becomes a boolean prop on the generated part, the
|
|
160
|
+
* same shape the usage snippet emits, because otherwise the rule ships and the chevron never
|
|
161
|
+
* turns.
|
|
162
|
+
*/
|
|
163
|
+
const boolish = new Map();
|
|
164
|
+
for (const layer of part.layers ?? [])
|
|
165
|
+
for (const [key, option] of Object.entries(layer.when?.variant ?? {})) {
|
|
166
|
+
if (variants?.[key])
|
|
167
|
+
continue;
|
|
168
|
+
boolish.set(key, (boolish.get(key) ?? new Set()).add(String(option)));
|
|
169
|
+
}
|
|
170
|
+
const out = [];
|
|
171
|
+
for (const [key, seen] of boolish)
|
|
172
|
+
out.push({
|
|
173
|
+
key,
|
|
174
|
+
prop: camel(key),
|
|
175
|
+
attr: kebab(key),
|
|
176
|
+
boolean: true,
|
|
177
|
+
styledFalse: seen.has("false"),
|
|
178
|
+
options: [...seen],
|
|
179
|
+
});
|
|
180
|
+
for (const key of named) {
|
|
181
|
+
const options = Object.keys(variants?.[key] ?? {});
|
|
182
|
+
if (options.length === 0)
|
|
183
|
+
continue;
|
|
184
|
+
out.push({
|
|
185
|
+
key,
|
|
186
|
+
prop: camel(key),
|
|
187
|
+
attr: kebab(key),
|
|
188
|
+
boolean: options.every((o) => o === "true" || o === "false"),
|
|
189
|
+
styledFalse: options.includes("false"),
|
|
190
|
+
options,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
/** The axes of a part: the ones it declares, plus the ones its layers read. */
|
|
196
|
+
function partAxesOf(part, variants) {
|
|
197
|
+
const own = axesOf(part.variants ?? {});
|
|
198
|
+
const keys = new Set(own.map((a) => a.key));
|
|
199
|
+
return [...own, ...layerAxes(part, variants).filter((a) => !keys.has(a.key))];
|
|
200
|
+
}
|
|
128
201
|
function axesOf(variants) {
|
|
129
202
|
const axes = [];
|
|
130
203
|
for (const [axis, options] of Object.entries(variants ?? {})) {
|
|
@@ -736,7 +809,7 @@ localName = name) {
|
|
|
736
809
|
const part = recipe.parts?.[partName];
|
|
737
810
|
if (!part)
|
|
738
811
|
return "";
|
|
739
|
-
const partAxes =
|
|
812
|
+
const partAxes = partAxesOf(part, recipe.variants);
|
|
740
813
|
const partComp = `${comp}${pascal(partName)}`;
|
|
741
814
|
const { tag: partTag, attrs: partAttrs, voidEl: partVoid, } = partTagFor(part, formOf.get(partName));
|
|
742
815
|
const partEl = asElement(partTag, partAttrs, partVoid);
|
|
@@ -873,7 +946,7 @@ ${orderedParts
|
|
|
873
946
|
if (!part)
|
|
874
947
|
return "";
|
|
875
948
|
const partComp = `${comp}${pascal(partName)}`;
|
|
876
|
-
const partAxes =
|
|
949
|
+
const partAxes = partAxesOf(part, recipe.variants);
|
|
877
950
|
const { tag: partTag, attrs: partAttrs, voidEl: partVoid, } = partTagFor(part, formOf.get(partName));
|
|
878
951
|
const partEl = asElement(partTag, partAttrs, partVoid);
|
|
879
952
|
/**
|
|
@@ -525,6 +525,36 @@ export function readVariants(source) {
|
|
|
525
525
|
* inline conditions with a class list, 2 of them negated.
|
|
526
526
|
*/
|
|
527
527
|
const INLINE_COND = /(!?)\s*([A-Za-z_$][\w$.]*)\s*(?:===|==)\s*["']([^"']+)["']\s*&&\s*(["'`])((?:(?!\4).)*)\4|(!?)\s*([A-Za-z_$][\w$]*)\s*&&\s*(["'`])((?:(?!\8).)*)\8/g;
|
|
528
|
+
/**
|
|
529
|
+
* THE `cn()` CALL AN OFFSET SITS INSIDE, and its unconditional classes.
|
|
530
|
+
*
|
|
531
|
+
* A condition read out of the whole file has no element: `readInlineConditions(source)`
|
|
532
|
+
* scanned everything and every layer it produced landed on the ROOT. Their collapsible card
|
|
533
|
+
* writes `cursor-pointer` and `hover:bg-…` in the TRIGGER's call and `rotate-180` in the
|
|
534
|
+
* chevron's, and all three arrived as layers of the card - so
|
|
535
|
+
* `.ds-collapsible-card[data-is-open="false"] { transform: rotate(180deg) }` would have
|
|
536
|
+
* turned the whole card upside down at rest (test48, dono, 04/08).
|
|
537
|
+
*
|
|
538
|
+
* The unconditional literals of the enclosing call are exactly what the sketch stored for
|
|
539
|
+
* that node, so `placeLayers` can resolve them to an index. Same identification the lookup
|
|
540
|
+
* reader already uses - see `callSiteClasses`.
|
|
541
|
+
*/
|
|
542
|
+
function enclosingCallClasses(source, at) {
|
|
543
|
+
const CALL = /className=\{\s*(?:[A-Za-z_$][\w$]*\.)?(?:cn|clsx|classNames|cx|twMerge|twJoin|classcat)\(/g;
|
|
544
|
+
CALL.lastIndex = 0;
|
|
545
|
+
let found = null;
|
|
546
|
+
for (const m of source.matchAll(CALL)) {
|
|
547
|
+
const open = (m.index ?? 0) + m[0].length - 1;
|
|
548
|
+
if (open > at)
|
|
549
|
+
break;
|
|
550
|
+
const end = endOf(source, open);
|
|
551
|
+
if (end === -1 || end <= at)
|
|
552
|
+
continue;
|
|
553
|
+
const statics = unconditionalClasses(source.slice(open + 1, end - 1));
|
|
554
|
+
found = statics.length > 0 ? statics.join(" ") : null;
|
|
555
|
+
}
|
|
556
|
+
return found;
|
|
557
|
+
}
|
|
528
558
|
export function readInlineConditions(source) {
|
|
529
559
|
const layers = [];
|
|
530
560
|
INLINE_COND.lastIndex = 0;
|
|
@@ -547,7 +577,14 @@ export function readInlineConditions(source) {
|
|
|
547
577
|
if (classes.length === 0)
|
|
548
578
|
continue;
|
|
549
579
|
const per = splitByCondition(classes, { [prop]: option });
|
|
550
|
-
|
|
580
|
+
/**
|
|
581
|
+
* WHOSE ELEMENT IT IS, off the call this condition sits in. Absent when the call has no
|
|
582
|
+
* unconditional literal to identify it, and then the layer stays on the component -
|
|
583
|
+
* which is the old behaviour, and honest: we could not place it.
|
|
584
|
+
*/
|
|
585
|
+
const on = enclosingCallClasses(source, m.index ?? 0);
|
|
586
|
+
for (const layer of per.layers)
|
|
587
|
+
layers.push(on ? { ...layer, onClasses: on } : layer);
|
|
551
588
|
}
|
|
552
589
|
return layers;
|
|
553
590
|
}
|
package/package.json
CHANGED