@yahoo/uds-create-config 5.68.1 → 5.68.3
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/entities/documentation/Documentation.d.ts +1 -1
- package/dist/entities/system/defineComponent.d.ts +38 -15
- package/dist/entities/system/defineComponent.js +0 -18
- package/dist/framework/anatomy-check.d.ts +11 -3
- package/dist/framework/anatomy-check.js +42 -1
- package/dist/framework/render-spec.js +22 -8
- package/dist/renderer/wrapRegistry.js +5 -1
- package/dist/renderer/wrappers/component-refs.d.ts +4 -1
- package/dist/renderer/wrappers/component-refs.js +20 -6
- package/dist/renderer/wrappers/html-aliases.d.ts +4 -0
- package/dist/renderer/wrappers/html-aliases.js +6 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -15,8 +15,8 @@ declare const DocumentationDefinition: EntityClass<z.ZodObject<{
|
|
|
15
15
|
uds: "uds";
|
|
16
16
|
}>>;
|
|
17
17
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
18
|
-
mobile: "mobile";
|
|
19
18
|
web: "web";
|
|
19
|
+
mobile: "mobile";
|
|
20
20
|
}>>;
|
|
21
21
|
}, z.core.$strict>, z.ZodObject<Record<never, never>, z.core.$strip>, "documentation", {
|
|
22
22
|
readonly search: {
|
|
@@ -14,9 +14,8 @@ import { AllHTMLAttributes, ComponentPropsWithRef, ComponentType, ElementType, J
|
|
|
14
14
|
* Enough of another component to BE a layer's element — the handle `defineComponent` returns, with
|
|
15
15
|
* its declarations forgotten so a builder of any shape fits.
|
|
16
16
|
*
|
|
17
|
-
* Named separately from `AuthoredComponent
|
|
18
|
-
*
|
|
19
|
-
* from. `ComponentBuilder<{root:'button'}, …>` satisfies this; it does not satisfy `AuthoredComponent`.
|
|
17
|
+
* Named separately from `AuthoredComponent`, which is what a registry holds; a layer only needs to know
|
|
18
|
+
* the value is a component, and keeps the referenced component's own type for its props.
|
|
20
19
|
*/
|
|
21
20
|
interface ComponentElement extends Authored {
|
|
22
21
|
readonly kind: 'component';
|
|
@@ -582,11 +581,13 @@ type OpenTail<L extends LayerMap> = Registered extends true ? StyleProps & Modif
|
|
|
582
581
|
*/
|
|
583
582
|
type ComponentPropsOf<L extends LayerMap, P extends PropMap, D = Record<never, never>, E = Record<never, never>, F extends string = never, As = never> = ConsumerDeclaredProps<P, D, E, L, ComponentBuilder<L, P, E, D, F>> & Partial<E> & Redeclared<RootSurface<L, D, As>, P, E> & Redeclared<ForwardedPropsOf<L, F>, P, E> & AsPropsOf<As, P, E> & Redeclared<OpenTail<L>, P, E>;
|
|
584
583
|
/**
|
|
585
|
-
* `T` less the keys the component declares and the keys its render extra names
|
|
586
|
-
*
|
|
587
|
-
* extra (`extends Record<string, unknown>`) must not stop the declared
|
|
584
|
+
* `T` less the keys the component declares and the keys its render extra names. {@link Without} leaves
|
|
585
|
+
* `T` whole when its key set is open, so an open set of either kind is left out of the union rather
|
|
586
|
+
* than opening it: an open render extra (`extends Record<string, unknown>`) must not stop the declared
|
|
587
|
+
* props from shadowing. Both sets come out in one mapped type, since this runs once per component over
|
|
588
|
+
* every style prop and modifier the system registers.
|
|
588
589
|
*/
|
|
589
|
-
type Redeclared<T, P, E> = Without<Without<T, keyof P
|
|
590
|
+
type Redeclared<T, P, E> = string extends Redefined<E> ? Without<T, keyof P> : string extends keyof P ? Without<T, Redefined<E>> : Without<T, keyof P | Redefined<E>>;
|
|
590
591
|
/**
|
|
591
592
|
* The phantom call signature's parameter. `As` is inferred from `as` alone; the rest of the surface
|
|
592
593
|
* is `NoInfer`, so inference never walks it. Open to inference, a spread of
|
|
@@ -642,7 +643,13 @@ type IntrinsicAttributeNames<As> = As extends keyof JSX.IntrinsicElements ? keyo
|
|
|
642
643
|
* there is no telling a stray attribute from a style prop, and an unbuilt consumer compiles the way
|
|
643
644
|
* the registry promises.
|
|
644
645
|
*/
|
|
645
|
-
type ForeignAttributesOf<As, P, E> = Registered extends false ? Record<never, never> : [As] extends [keyof JSX.IntrinsicElements] ? { [K in Exclude<
|
|
646
|
+
type ForeignAttributesOf<As, P, E> = Registered extends false ? Record<never, never> : [As] extends [keyof JSX.IntrinsicElements] ? { [K in Exclude<AttributeNames, IntrinsicAttributeNames<As> | keyof P | keyof E>]?: never } : Record<never, never>;
|
|
647
|
+
/**
|
|
648
|
+
* The HTML attribute names less `as` and the registered style props, which are the same for every
|
|
649
|
+
* component. A single `Exclude` over this concrete union takes out the rest: one over a deferred
|
|
650
|
+
* `Exclude<…, IntrinsicAttributeNames<As>>` would build an intersection per name while `As` is generic.
|
|
651
|
+
*/
|
|
652
|
+
type AttributeNames = Exclude<keyof AllHTMLAttributes<HTMLElement>, 'as' | keyof RegisteredStyleProps>;
|
|
646
653
|
/**
|
|
647
654
|
* The props a component picks up from the layers it FORWARDS.
|
|
648
655
|
*
|
|
@@ -753,19 +760,24 @@ type WhenAny<K, T> = [K] extends [never] ? unknown : T;
|
|
|
753
760
|
* with `P` in full as its type argument.
|
|
754
761
|
*/
|
|
755
762
|
type CompactProps<P extends PropMap> = [StyleRefKeys<P> | StyleRefPropKeys<P>] extends [never] ? P : { [K in keyof P as K extends StyleRefKeys<P> | StyleRefPropKeys<P> ? never : K]: P[K] } & WhenAny<StyleRefKeys<P>, StyleRefs<StyleRefKeys<P> & string>> & WhenAny<StyleRefPropKeys<P>, StyleRefProps<StyleRefPropKeys<P> & string>>;
|
|
756
|
-
|
|
763
|
+
/**
|
|
764
|
+
* Every type parameter is declared `in out`. Measured instead, TypeScript relates two instantiations
|
|
765
|
+
* with `P` generic, which crosses every registered style prop and modifier name with every other, and
|
|
766
|
+
* past 316 of them that is over its 100,000-member union limit (TS2590).
|
|
767
|
+
*/
|
|
768
|
+
interface ComponentBuilder<in out L extends LayerMap, in out P extends PropMap,
|
|
757
769
|
/** Props the RENDER takes that the config doesn't describe — React handlers, mostly (`onPress`).
|
|
758
770
|
* They're part of the component's surface but not of its data, which is why they're declared at
|
|
759
771
|
* `.render()` rather than in `.props()`. */
|
|
760
|
-
E = Record<never, never>,
|
|
772
|
+
in out E = Record<never, never>,
|
|
761
773
|
/** The defaults as AUTHORED, kept because one of them decides the type of every other prop: an
|
|
762
774
|
* `as` fixes which element this renders. See {@link ElementOf}. */
|
|
763
|
-
D = Record<never, never>,
|
|
775
|
+
in out D = Record<never, never>,
|
|
764
776
|
/** The layers this component FORWARDS from, as a union of their names. Accumulates through
|
|
765
777
|
* `.forwards()` so `ComponentPropsOf` can union each forwarded layer's element props — `root` is
|
|
766
778
|
* already covered structurally by {@link ElementOf}, so for a root-only forward this changes no
|
|
767
779
|
* inferred type, which is what makes replacing `inherits` a no-op at every call site. */
|
|
768
|
-
F extends string = never, /** The states `.states()` declared — what the render knows and reports, never a caller's prop. */S extends StateMap = Record<never, never>> extends Authored {
|
|
780
|
+
in out F extends string = never, /** The states `.states()` declared — what the render knows and reports, never a caller's prop. */in out S extends StateMap = Record<never, never>> extends Authored {
|
|
769
781
|
/**
|
|
770
782
|
* The phantom call signature — the authored value is never rendered (the build substitutes the
|
|
771
783
|
* emitted module), but a call site is checked against it. Generic over the element a call site
|
|
@@ -884,8 +896,19 @@ F extends string = never, /** The states `.states()` declared — what the rende
|
|
|
884
896
|
toStored(): Record<string, unknown>;
|
|
885
897
|
assignPath(path: string): void;
|
|
886
898
|
}
|
|
887
|
-
/**
|
|
888
|
-
|
|
899
|
+
/**
|
|
900
|
+
* A component under authorship, with its declarations forgotten — what a registry holds. It names only
|
|
901
|
+
* the members every builder has with the same type, so a builder of any shape is assignable to it.
|
|
902
|
+
*/
|
|
903
|
+
interface AuthoredComponent extends Authored {
|
|
904
|
+
readonly kind: 'component';
|
|
905
|
+
readonly body: Record<string, unknown>;
|
|
906
|
+
readonly path?: string;
|
|
907
|
+
toStored(): Record<string, unknown>;
|
|
908
|
+
assignPath(path: string): void;
|
|
909
|
+
}
|
|
910
|
+
/** What the whole-body form returns: a builder whose declarations are all at their widest. */
|
|
911
|
+
type WidestComponentBuilder = ComponentBuilder<LayerMap, PropMap, unknown, unknown>;
|
|
889
912
|
/**
|
|
890
913
|
* A linked component's handle as the system that links it holds it. The build emits one for each
|
|
891
914
|
* linked component the link changes, and the system's linked alias resolves to it first (UDS-4611).
|
|
@@ -931,7 +954,7 @@ interface DefineComponent {
|
|
|
931
954
|
root: El;
|
|
932
955
|
}, Record<never, never>>;
|
|
933
956
|
/** The whole body at once, for a component with nothing to narrow step by step. */
|
|
934
|
-
(body?: ComponentSourceInput<Record<string, unknown>>):
|
|
957
|
+
(body?: ComponentSourceInput<Record<string, unknown>>): WidestComponentBuilder;
|
|
935
958
|
layers<const L extends LayerMap>(layers: L): ComponentBuilder<L, Record<never, never>>;
|
|
936
959
|
props: typeof definePropsFirst;
|
|
937
960
|
}
|
|
@@ -402,24 +402,6 @@ function toStored(body) {
|
|
|
402
402
|
styles: storedStyleRules(rules)
|
|
403
403
|
};
|
|
404
404
|
}
|
|
405
|
-
/**
|
|
406
|
-
* Author a component.
|
|
407
|
-
*
|
|
408
|
-
* Callable for the case with nothing to narrow — `defineComponent({ layers: { root: 'div' } })` — and
|
|
409
|
-
* chainable for every other, since a chain is what lets each step's types come from the last.
|
|
410
|
-
*/
|
|
411
|
-
/**
|
|
412
|
-
* Author a component.
|
|
413
|
-
*
|
|
414
|
-
* Called with an ELEMENT — `defineComponent('input')`, `defineComponent(Component.ref('Box'))` — that
|
|
415
|
-
* element becomes the root layer, because a component with one layer IS that element and saying so
|
|
416
|
-
* twice adds nothing. It's also what makes the element's props type correctly: v1 expressed this as a
|
|
417
|
-
* `defaultProps: { as: 'input' }` on a `div`-rooted component, which left the config claiming the
|
|
418
|
-
* element was a `div` while every call site treated it as an input.
|
|
419
|
-
*
|
|
420
|
-
* The render follows from it too — one layer needs no anatomy, so the build generates the element
|
|
421
|
-
* rather than reading a `.render()` that would only restate the layer.
|
|
422
|
-
*/
|
|
423
405
|
function defineComponentImpl(bodyOrElement) {
|
|
424
406
|
if (isComponentElement(bodyOrElement)) return build({ layers: { root: bodyOrElement } });
|
|
425
407
|
if (typeof bodyOrElement === "function" || isRef(bodyOrElement) && kindOf(bodyOrElement) === "package") throw new Error("[uds] defineComponent(<package export>): a package export cannot be a component's only layer yet — the generated one-layer render emits web elements. Declare `layers` and a `.render()` instead.");
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* them.
|
|
12
12
|
*
|
|
13
13
|
* Two ways in. {@link snapshotHtml} reads the markup `react-dom/server` produces, which is what
|
|
14
|
-
* `uds anatomy` compares in a Bun process — structure, text
|
|
14
|
+
* `uds anatomy` compares in a Bun process — structure, text, classes, inline style and a plain tag's
|
|
15
|
+
* attributes, since no layout runs there.
|
|
15
16
|
* A browser surface snapshots live DOM instead (the studio's `?anatomyCheck=true`), and adds computed
|
|
16
17
|
* style to each node; {@link diffRendered} compares whatever a node carries.
|
|
17
18
|
*/
|
|
@@ -34,10 +35,17 @@ interface RenderedNode {
|
|
|
34
35
|
readonly style: Readonly<Record<string, string>>;
|
|
35
36
|
/** `true` when `style` holds inline declarations rather than a computed subset. */
|
|
36
37
|
readonly inlineStyle?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* A plain tag's attributes — an element that is no component's layer, such as an svg `<path>`,
|
|
40
|
+
* whose attributes are often its paint (`fill-opacity`, `stroke-width`, `d`). Left out for a layer,
|
|
41
|
+
* whose attributes come from its component, and where the snapshot can't read them. See
|
|
42
|
+
* {@link paintAttributesOf} for what is skipped.
|
|
43
|
+
*/
|
|
44
|
+
readonly attributes?: Readonly<Record<string, string>>;
|
|
37
45
|
/** What the alignment matches on — see {@link renderedSignatureOf}. */
|
|
38
46
|
readonly signature: string;
|
|
39
47
|
}
|
|
40
|
-
type AnatomyIssueKind = /** The module renders this element and the anatomy does not. */'missing' /** The anatomy renders this element and the module does not. */ | 'extra' /** Both render it — aligned as the same layer — as different elements (`div` against `span`). */ | 'tag' /** Both render it, with different own text. */ | 'text' /** Both render it, with a computed style that differs beyond tolerance. */ | 'style'
|
|
48
|
+
type AnatomyIssueKind = /** The module renders this element and the anatomy does not. */'missing' /** The anatomy renders this element and the module does not. */ | 'extra' /** Both render it — aligned as the same layer — as different elements (`div` against `span`). */ | 'tag' /** Both render it, with different own text. */ | 'text' /** Both render it as a plain tag, with an attribute that differs or that one side lacks. */ | 'attribute' /** Both render it, with a computed style that differs beyond tolerance. */ | 'style'
|
|
41
49
|
/** Both render it, with different class sets. Classes are how a style arrives, so where a
|
|
42
50
|
* computed style is available this is informational; where none is (markup only) it is the
|
|
43
51
|
* paint difference itself — see `diffRendered`'s `classesFail`. */
|
|
@@ -90,7 +98,7 @@ interface DiffOptions {
|
|
|
90
98
|
*
|
|
91
99
|
* Elements align by signature in document order; what the module renders and the anatomy does not
|
|
92
100
|
* is `missing`, the reverse is `extra`. Aligned elements then compare own text, computed style (when
|
|
93
|
-
* both sides carry it) and class set.
|
|
101
|
+
* both sides carry it), a plain tag's attributes (when both sides carry them) and class set.
|
|
94
102
|
*/
|
|
95
103
|
declare function diffRendered(anatomy: readonly RenderedNode[], instance: readonly RenderedNode[], options?: DiffOptions): AnatomyReport;
|
|
96
104
|
/** The allowlist spelling of an issue: kind, node, and the property for a style difference. */
|
|
@@ -161,6 +161,31 @@ function ownTextOf(element) {
|
|
|
161
161
|
for (const child of element.children) if ("text" in child) text += child.text;
|
|
162
162
|
return text.replace(/\s+/g, " ").trim();
|
|
163
163
|
}
|
|
164
|
+
/** Attributes that name an id rather than paint: React mints ids per render, so the two sides differ. */
|
|
165
|
+
const ID_ATTRIBUTES = new Set([
|
|
166
|
+
"id",
|
|
167
|
+
"for",
|
|
168
|
+
"aria-activedescendant",
|
|
169
|
+
"aria-controls",
|
|
170
|
+
"aria-describedby",
|
|
171
|
+
"aria-labelledby",
|
|
172
|
+
"aria-owns"
|
|
173
|
+
]);
|
|
174
|
+
/**
|
|
175
|
+
* The attributes of a plain tag that the two renderings should agree on: every one but `class` and
|
|
176
|
+
* `style` (compared on their own), `data-*` (markers), and what names an id — an id attribute, or a
|
|
177
|
+
* value pointing at one (`url(#…)`, `#…`).
|
|
178
|
+
*/
|
|
179
|
+
function paintAttributesOf(attributes) {
|
|
180
|
+
const out = {};
|
|
181
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
182
|
+
if (name === "class" || name === "style" || name.startsWith("data-")) continue;
|
|
183
|
+
if (ID_ATTRIBUTES.has(name)) continue;
|
|
184
|
+
if (value.includes("url(#") || value.startsWith("#")) continue;
|
|
185
|
+
out[name] = value;
|
|
186
|
+
}
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
164
189
|
function firstComponentRoot(node) {
|
|
165
190
|
for (const child of node.children) {
|
|
166
191
|
if ("text" in child) continue;
|
|
@@ -190,6 +215,7 @@ function snapshotHtml(html) {
|
|
|
190
215
|
classes: (element.attributes.class ?? "").split(/\s+/).filter(Boolean).sort(),
|
|
191
216
|
style: inlineStyleOf(element.attributes.style),
|
|
192
217
|
inlineStyle: true,
|
|
218
|
+
...component === void 0 ? { attributes: paintAttributesOf(element.attributes) } : {},
|
|
193
219
|
signature: renderedSignatureOf(element.tag, component, layer)
|
|
194
220
|
});
|
|
195
221
|
for (const child of element.children) if (!("text" in child)) visit(child);
|
|
@@ -255,7 +281,7 @@ function nodeLabel(node, ordinal) {
|
|
|
255
281
|
*
|
|
256
282
|
* Elements align by signature in document order; what the module renders and the anatomy does not
|
|
257
283
|
* is `missing`, the reverse is `extra`. Aligned elements then compare own text, computed style (when
|
|
258
|
-
* both sides carry it) and class set.
|
|
284
|
+
* both sides carry it), a plain tag's attributes (when both sides carry them) and class set.
|
|
259
285
|
*/
|
|
260
286
|
function diffRendered(anatomy, instance, options = {}) {
|
|
261
287
|
const issues = [];
|
|
@@ -310,6 +336,21 @@ function diffRendered(anatomy, instance, options = {}) {
|
|
|
310
336
|
instance: vb === void 0 ? void 0 : normalizeStyleValue(vb)
|
|
311
337
|
});
|
|
312
338
|
}
|
|
339
|
+
if (a.attributes && b.attributes) {
|
|
340
|
+
const names = [...new Set([...Object.keys(a.attributes), ...Object.keys(b.attributes)])].sort();
|
|
341
|
+
for (const name of names) {
|
|
342
|
+
const va = a.attributes[name];
|
|
343
|
+
const vb = b.attributes[name];
|
|
344
|
+
if (va === vb) continue;
|
|
345
|
+
issues.push({
|
|
346
|
+
kind: "attribute",
|
|
347
|
+
node: label,
|
|
348
|
+
property: name,
|
|
349
|
+
anatomy: va,
|
|
350
|
+
instance: vb
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
313
354
|
const onlyA = a.classes.filter((c) => !b.classes.includes(c));
|
|
314
355
|
const onlyB = b.classes.filter((c) => !a.classes.includes(c));
|
|
315
356
|
if (onlyA.length || onlyB.length) issues.push({
|
|
@@ -975,6 +975,7 @@ function slotTargets(config, path, body) {
|
|
|
975
975
|
}
|
|
976
976
|
function computeSlotTargets(config, body) {
|
|
977
977
|
const byLayer = /* @__PURE__ */ new Map();
|
|
978
|
+
const targeted = /* @__PURE__ */ new Map();
|
|
978
979
|
const untargeted = [];
|
|
979
980
|
for (const [from, decl] of Object.entries(body.props ?? {})) {
|
|
980
981
|
if (decl?.type === "slot") {
|
|
@@ -988,11 +989,15 @@ function computeSlotTargets(config, body) {
|
|
|
988
989
|
if (decl?.type !== "forward") continue;
|
|
989
990
|
if (!routesContent(config, body.layers, decl)) continue;
|
|
990
991
|
const target = refOf(decl.target);
|
|
991
|
-
if (target)
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
992
|
+
if (target) {
|
|
993
|
+
const layer = pathOf(target);
|
|
994
|
+
const route = {
|
|
995
|
+
...decl,
|
|
996
|
+
from
|
|
997
|
+
};
|
|
998
|
+
byLayer.set(layer, route);
|
|
999
|
+
targeted.set(layer, [...(targeted.get(layer) ?? []).filter((other) => intoName(other.into) !== intoName(route.into)), route]);
|
|
1000
|
+
} else untargeted.push({
|
|
996
1001
|
...decl,
|
|
997
1002
|
from,
|
|
998
1003
|
untargeted: true
|
|
@@ -1003,6 +1008,7 @@ function computeSlotTargets(config, body) {
|
|
|
1003
1008
|
if (root && content && !byLayer.has(root)) byLayer.set(root, content);
|
|
1004
1009
|
return {
|
|
1005
1010
|
byLayer,
|
|
1011
|
+
targeted,
|
|
1006
1012
|
untargeted
|
|
1007
1013
|
};
|
|
1008
1014
|
}
|
|
@@ -1024,9 +1030,9 @@ function computeSlotTargets(config, body) {
|
|
|
1024
1030
|
function slotTargetsOf(config, path) {
|
|
1025
1031
|
const body = bodyOf(config, path);
|
|
1026
1032
|
if (!body) return [];
|
|
1027
|
-
const {
|
|
1033
|
+
const { targeted: targetedByLayer, untargeted } = slotTargets(config, path, body);
|
|
1028
1034
|
const root = rootKeyOf(body.layers ?? {});
|
|
1029
|
-
return [...[...
|
|
1035
|
+
return [...[...targetedByLayer].flatMap(([layer, routes]) => routes.map((decl) => [layer, decl])).map(([layer, decl]) => ({
|
|
1030
1036
|
layer,
|
|
1031
1037
|
from: decl.from,
|
|
1032
1038
|
into: intoName(decl.into)
|
|
@@ -1288,7 +1294,7 @@ function expandInstance(sink, path, instanceProps, content, hint, instanceSlots)
|
|
|
1288
1294
|
...Object.fromEntries(Object.entries(pins).map(([key, value]) => key.startsWith("_") ? [key, value === true || value === "true"] : [key, value]))
|
|
1289
1295
|
};
|
|
1290
1296
|
const consumed = consumedProps(config, path, body);
|
|
1291
|
-
const { byLayer: targets } = slotTargets(config, path, body);
|
|
1297
|
+
const { byLayer: targets, targeted: targetedRoutes } = slotTargets(config, path, body);
|
|
1292
1298
|
const valueRoutes = /* @__PURE__ */ new Map();
|
|
1293
1299
|
for (const route of valueForwards(config, body)) {
|
|
1294
1300
|
const routes = valueRoutes.get(route.layer) ?? [];
|
|
@@ -1331,6 +1337,7 @@ function expandInstance(sink, path, instanceProps, content, hint, instanceSlots)
|
|
|
1331
1337
|
let contentPlaced = false;
|
|
1332
1338
|
const routedFroms = /* @__PURE__ */ new Set();
|
|
1333
1339
|
for (const target of targets.values()) routedFroms.add(target.from);
|
|
1340
|
+
for (const routes of targetedRoutes.values()) for (const route of routes) routedFroms.add(route.from);
|
|
1334
1341
|
const positioned = /* @__PURE__ */ new Set();
|
|
1335
1342
|
for (const node of Object.values(anatomy)) {
|
|
1336
1343
|
const prop = slotAnatomyPropOf(node);
|
|
@@ -1428,6 +1435,8 @@ function expandInstance(sink, path, instanceProps, content, hint, instanceSlots)
|
|
|
1428
1435
|
}
|
|
1429
1436
|
const slot = layer ? targets.get(layer) : void 0;
|
|
1430
1437
|
const slotValue = slot && !subtreeBinds(anatomy, key, slot.from) && !(positioned.has(slot.from) && isText(instanceProps[slot.from])) ? instanceProps[slot.from] : void 0;
|
|
1438
|
+
const siblingRoutes = layer ? (targetedRoutes.get(layer) ?? []).filter((route) => route !== slot) : [];
|
|
1439
|
+
const siblingValues = Object.fromEntries(siblingRoutes.filter((route) => instanceProps[route.from] !== void 0 && !subtreeBinds(anatomy, key, route.from)).map((route) => [intoName(route.into), instanceProps[route.from]]));
|
|
1431
1440
|
const props = layer ? {
|
|
1432
1441
|
...withoutKeys(authoredProps(config, node.props), ruleKeysFor(config, path, layer, state, pins)),
|
|
1433
1442
|
"data-uds-component": path,
|
|
@@ -1438,6 +1447,7 @@ function expandInstance(sink, path, instanceProps, content, hint, instanceSlots)
|
|
|
1438
1447
|
...isRoot ? componentStateAttributes(componentStates(config, path), state) : {},
|
|
1439
1448
|
...isRoot || type === void 0 || kindOf(type) !== "component" ? forcedModifierAttributes(pins, path) : {},
|
|
1440
1449
|
...isRoot ? rest : {},
|
|
1450
|
+
...siblingValues,
|
|
1441
1451
|
...slot && slotValue !== void 0 ? { [intoName(slot.into)]: slotValue } : {},
|
|
1442
1452
|
...layerProps[layer],
|
|
1443
1453
|
className: classesFor(config, path, layer, state, [
|
|
@@ -1513,6 +1523,10 @@ function expandInstance(sink, path, instanceProps, content, hint, instanceSlots)
|
|
|
1513
1523
|
if (placedFills.length) slots[name] = placedFills;
|
|
1514
1524
|
}
|
|
1515
1525
|
if (overridden && routed) slots[overridden] = [...routed];
|
|
1526
|
+
for (const route of siblingRoutes) {
|
|
1527
|
+
const fills = instanceSlots?.[route.from];
|
|
1528
|
+
if (fills?.length) slots[intoName(route.into)] = [...fills];
|
|
1529
|
+
}
|
|
1516
1530
|
if (routed?.length && !overridden && !(slot && positioned.has(slot.from))) children.push(...routed);
|
|
1517
1531
|
const placed = place(nested, type, props, children, keyHint, Object.keys(slots).length ? slots : void 0);
|
|
1518
1532
|
if (visible === void 0) withCondition(nested, placed, { visible: node.visible });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FragmentRenderer } from "./primitives/FragmentRenderer.js";
|
|
2
2
|
import { SlotRenderer } from "./primitives/SlotRenderer.js";
|
|
3
|
+
import { glyphMemberFor } from "../spec/glyph-members.js";
|
|
3
4
|
import { wrapComponentRefs } from "./wrappers/component-refs.js";
|
|
4
5
|
import { wrapEventBridge } from "./wrappers/event-bridge.js";
|
|
5
6
|
import { wrapGlyphProps } from "./wrappers/glyph-props.js";
|
|
@@ -28,7 +29,10 @@ function wrapRegistry(reg, descriptor, iconMembers) {
|
|
|
28
29
|
};
|
|
29
30
|
const stylePropNames = descriptor?.stylePropClasses ? new Set([...Object.keys(descriptor.stylePropClasses), ...Object.values(descriptor.cssPropertyToStyleProp ?? {})]) : void 0;
|
|
30
31
|
const holder = {};
|
|
31
|
-
const final = aliasHtmlTypes(wrapInlineStyleProps(wrapNormalizeHex(wrapEventBridge(wrapGlyphProps(wrapComponentRefs(base, (type) => holder.registry?.[type]
|
|
32
|
+
const final = aliasHtmlTypes(wrapInlineStyleProps(wrapNormalizeHex(wrapEventBridge(wrapGlyphProps(wrapComponentRefs(base, (type) => holder.registry?.[type], iconMembers ? (ref) => glyphMemberFor({
|
|
33
|
+
ref,
|
|
34
|
+
members: iconMembers
|
|
35
|
+
}) : void 0), descriptor?.glyphProps, iconMembers)), stylePropNames), {
|
|
32
36
|
cssPropertyToStyleProp: descriptor?.cssPropertyToStyleProp,
|
|
33
37
|
layoutComponents: descriptor?.layoutComponents,
|
|
34
38
|
textComponents: descriptor?.textComponents
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentRegistry, ComponentRenderer } from '@json-render/react';
|
|
2
|
+
import { type PreviewRef } from '../../spec/preview-elements';
|
|
2
3
|
/**
|
|
3
4
|
* A component handed as a prop VALUE (`icon={DotsThreeIcon}`) crosses the config as a bare ref,
|
|
4
5
|
* `{ __ref: 'icon:phosphor/DotsThreeIcon' }`, keyed the way the registry keys the component. This
|
|
@@ -11,4 +12,6 @@ import type { ComponentRegistry, ComponentRenderer } from '@json-render/react';
|
|
|
11
12
|
* like any spec element. One component per ref, so the receiving component's element type is stable
|
|
12
13
|
* across renders and React keeps the glyph mounted.
|
|
13
14
|
*/
|
|
14
|
-
export declare function wrapComponentRefs(reg: ComponentRegistry, resolve: (type: string) => ComponentRenderer | undefined
|
|
15
|
+
export declare function wrapComponentRefs(reg: ComponentRegistry, resolve: (type: string) => ComponentRenderer | undefined,
|
|
16
|
+
/** The member an `icon:` ref names, for a glyph nested inside a prop's data. */
|
|
17
|
+
glyphOf?: (ref: PreviewRef) => unknown): ComponentRegistry;
|
|
@@ -20,14 +20,24 @@ function hasRefDeep(value) {
|
|
|
20
20
|
if (isComponentRef(value)) return true;
|
|
21
21
|
return (Array.isArray(value) ? value : Object.values(value)).some(hasRefDeep);
|
|
22
22
|
}
|
|
23
|
-
function reviveDeep(value, revive) {
|
|
23
|
+
function reviveDeep({ value, revive, glyphOf, nested = false }) {
|
|
24
24
|
if (typeof value !== "object" || value === null) return value;
|
|
25
25
|
if ("$$typeof" in value) return value;
|
|
26
26
|
if (isPreviewElement(value)) return value;
|
|
27
|
-
if (isComponentRef(value)) return revive(value);
|
|
28
|
-
if (Array.isArray(value)) return value.map((item) => reviveDeep(
|
|
27
|
+
if (isComponentRef(value)) return (nested ? glyphOf?.(value) : void 0) ?? revive(value);
|
|
28
|
+
if (Array.isArray(value)) return value.map((item) => reviveDeep({
|
|
29
|
+
value: item,
|
|
30
|
+
revive,
|
|
31
|
+
glyphOf,
|
|
32
|
+
nested: true
|
|
33
|
+
}));
|
|
29
34
|
const out = {};
|
|
30
|
-
for (const [key, member] of Object.entries(value)) out[key] = reviveDeep(
|
|
35
|
+
for (const [key, member] of Object.entries(value)) out[key] = reviveDeep({
|
|
36
|
+
value: member,
|
|
37
|
+
revive,
|
|
38
|
+
glyphOf,
|
|
39
|
+
nested: true
|
|
40
|
+
});
|
|
31
41
|
return out;
|
|
32
42
|
}
|
|
33
43
|
/**
|
|
@@ -42,7 +52,7 @@ function reviveDeep(value, revive) {
|
|
|
42
52
|
* like any spec element. One component per ref, so the receiving component's element type is stable
|
|
43
53
|
* across renders and React keeps the glyph mounted.
|
|
44
54
|
*/
|
|
45
|
-
function wrapComponentRefs(reg, resolve) {
|
|
55
|
+
function wrapComponentRefs(reg, resolve, glyphOf) {
|
|
46
56
|
const revived = /* @__PURE__ */ new Map();
|
|
47
57
|
const revive = (ref) => {
|
|
48
58
|
const type = ref.__ref;
|
|
@@ -72,7 +82,11 @@ function wrapComponentRefs(reg, resolve) {
|
|
|
72
82
|
...renderProps,
|
|
73
83
|
element: {
|
|
74
84
|
...renderProps.element,
|
|
75
|
-
props:
|
|
85
|
+
props: Object.fromEntries(Object.entries(props).map(([name, value]) => [name, reviveDeep({
|
|
86
|
+
value,
|
|
87
|
+
revive,
|
|
88
|
+
glyphOf
|
|
89
|
+
})]))
|
|
76
90
|
}
|
|
77
91
|
});
|
|
78
92
|
};
|
|
@@ -5,6 +5,10 @@ import { ComponentRegistry } from "@json-render/react";
|
|
|
5
5
|
* Set to `true` on an intrinsic element whose props are the tag's own attributes. A style-prop key
|
|
6
6
|
* whose value names no class is then kept as the attribute rather than dropped, so a captured
|
|
7
7
|
* `<svg width={16}>` keeps its `width`. The key never reaches the DOM.
|
|
8
|
+
*
|
|
9
|
+
* An element a component's render placed inline (the anatomy marker, `data-uds-anatomy`) is one
|
|
10
|
+
* without the flag: its props are the JSX attributes the module renders as attributes, so a
|
|
11
|
+
* `<path fillOpacity={0}>` keeps its `fill-opacity` as the module does.
|
|
8
12
|
*/
|
|
9
13
|
declare const VERBATIM_ATTRIBUTES = "__verbatimAttributes";
|
|
10
14
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "../../framework/render-spec.js";
|
|
1
2
|
import { specContent } from "../spec-content.js";
|
|
2
3
|
import React from "react";
|
|
3
4
|
//#region src/renderer/wrappers/html-aliases.tsx
|
|
@@ -16,6 +17,10 @@ const FORBIDDEN_INTRINSIC_PROPS = new Set(["dangerouslySetInnerHTML"]);
|
|
|
16
17
|
* Set to `true` on an intrinsic element whose props are the tag's own attributes. A style-prop key
|
|
17
18
|
* whose value names no class is then kept as the attribute rather than dropped, so a captured
|
|
18
19
|
* `<svg width={16}>` keeps its `width`. The key never reaches the DOM.
|
|
20
|
+
*
|
|
21
|
+
* An element a component's render placed inline (the anatomy marker, `data-uds-anatomy`) is one
|
|
22
|
+
* without the flag: its props are the JSX attributes the module renders as attributes, so a
|
|
23
|
+
* `<path fillOpacity={0}>` keeps its `fill-opacity` as the module does.
|
|
19
24
|
*/
|
|
20
25
|
const VERBATIM_ATTRIBUTES = "__verbatimAttributes";
|
|
21
26
|
const NullEl = () => null;
|
|
@@ -43,7 +48,7 @@ function aliasHtmlTypes(reg, stylePropClasses) {
|
|
|
43
48
|
if (typeof prop === "string" && INTRINSIC_TAG_RE.test(prop)) {
|
|
44
49
|
const IntrinsicEl = ({ element, children, slots }) => {
|
|
45
50
|
const props = element.props;
|
|
46
|
-
const verbatim = props?.[
|
|
51
|
+
const verbatim = props?.["__verbatimAttributes"] === true || typeof props?.["data-uds-anatomy"] === "string";
|
|
47
52
|
const intrinsicProps = {};
|
|
48
53
|
const styleClasses = [];
|
|
49
54
|
for (const [key, value] of Object.entries(props ?? {})) {
|