@yahoo/uds-create-config 5.57.0 → 5.58.0
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/authoring/define-component.d.ts +2 -1
- package/dist/entities/system/Component.d.ts +6 -3
- package/dist/entities/system/Component.js +5 -5
- package/dist/entities/system/LinkedSystem.js +2 -2
- package/dist/entities/system/copy-plan.js +55 -7
- package/dist/entities/system/defineComponent.d.ts +27 -31
- package/dist/entities/system/defineComponent.js +12 -19
- package/dist/entities/system/link-plan.d.ts +3 -0
- package/dist/entities/system/link-plan.js +24 -0
- package/dist/entities/system/prop-rules.d.ts +10 -0
- package/dist/entities/system/prop-rules.js +42 -0
- package/dist/framework/Config.d.ts +48 -0
- package/dist/framework/Config.js +97 -11
- package/dist/framework/default-conditions.d.ts +28 -1
- package/dist/framework/default-conditions.js +39 -2
- package/dist/framework/projections.d.ts +2 -2
- package/dist/framework/projections.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/entities/system/conditional-defaults.d.ts +0 -67
- package/dist/entities/system/conditional-defaults.js +0 -179
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* The returned handle is a ref AND an `Authored`, so it composes into other components and
|
|
18
18
|
* registers through the generic bridge.
|
|
19
19
|
*/
|
|
20
|
+
import { type PropsOrRules } from '../entities/system/prop-rules';
|
|
20
21
|
import type { Authored } from '../framework/authoring';
|
|
21
22
|
import { type Ref } from '../framework/utils/refs';
|
|
22
23
|
/** A component handle — a ref (import/reference it), an `Authored` (register it), and a
|
|
@@ -69,7 +70,7 @@ interface Body<L, SP extends string, MOD extends string, P extends Record<string
|
|
|
69
70
|
when: Record<string, string>;
|
|
70
71
|
layers: LayerOverrides<L, SP, MOD>;
|
|
71
72
|
}>;
|
|
72
|
-
defaultProps?: Record<string, unknown
|
|
73
|
+
defaultProps?: PropsOrRules<Record<string, unknown>>;
|
|
73
74
|
label?: string;
|
|
74
75
|
description?: string;
|
|
75
76
|
}
|
|
@@ -13,7 +13,8 @@ import { StoredDefaultCondition } from "../../framework/default-conditions.js";
|
|
|
13
13
|
import { StoredPreviewCondition } from "../../framework/preview-store.js";
|
|
14
14
|
import { RepeatSpec } from "../../framework/render-spec.js";
|
|
15
15
|
import { ComponentSourceInput } from "./component-source-fields.js";
|
|
16
|
-
import {
|
|
16
|
+
import { PropsOrRules } from "./prop-rules.js";
|
|
17
|
+
import { AsInput, AuthoredDefaultsOf, LayerMap, PhantomCallProps, PhantomReadProps, PropMap, RenderArgs, StateMap, StyleRuleInput, UnconditionalDefaults } from "./defineComponent.js";
|
|
17
18
|
import { listSlots, namedSlots, slotHostedBy, slotOf, slotsFor } from "../../spec/slot.js";
|
|
18
19
|
import { cellAt } from "../../spec/cell.js";
|
|
19
20
|
import { instanceAt } from "../../spec/instance.js";
|
|
@@ -871,7 +872,9 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
|
|
|
871
872
|
}: ComputedInput) => string[];
|
|
872
873
|
}, false, Record<never, never>, Record<never, never>>;
|
|
873
874
|
/** Source extensions declare their structure through .render(). */
|
|
874
|
-
type ComponentRefDelta = ComponentSourceInput<Partial<CreateInput<typeof componentFields
|
|
875
|
+
type ComponentRefDelta = Omit<ComponentSourceInput<Partial<CreateInput<typeof componentFields>>>, 'defaultProps'> & {
|
|
876
|
+
/** A record, or `.defaultProps`'s rule list; checked against the props by `ExtensionDefaults`. */readonly defaultProps?: PropsOrRules<Record<string, unknown>>;
|
|
877
|
+
};
|
|
875
878
|
type ExactComponentDelta<Delta> = Delta & { [Key in Exclude<keyof Delta, keyof ComponentRefDelta>]: never };
|
|
876
879
|
type ContractLayers<Contract> = Contract extends {
|
|
877
880
|
layers: infer Layers;
|
|
@@ -908,7 +911,7 @@ type ResolvedDefaults<Contract, Delta> = Delta extends {
|
|
|
908
911
|
readonly defaultProps: infer Defaults;
|
|
909
912
|
} ? OverlayContract<Contract extends {
|
|
910
913
|
defaultProps: infer Base;
|
|
911
|
-
} ? Base : Record<never, never>, Defaults
|
|
914
|
+
} ? Base : Record<never, never>, UnconditionalDefaults<Defaults>> : Contract extends {
|
|
912
915
|
defaultProps: infer Defaults;
|
|
913
916
|
} ? Defaults : Record<never, never>;
|
|
914
917
|
type ResolvedForwards<Contract, Delta> = Delta extends {
|
|
@@ -27,9 +27,9 @@ import { instanceAt } from "../../spec/instance.js";
|
|
|
27
27
|
import { forwardOptions, forwardsWrite } from "./component-forwards.js";
|
|
28
28
|
import { assertComponentSourceInput } from "./component-source-fields.js";
|
|
29
29
|
import { checkStyleSet, styleSetEdits } from "./component-style-set.js";
|
|
30
|
-
import { storedDefaults } from "./conditional-defaults.js";
|
|
31
30
|
import { forwardRouteInvariants, refusedRoutes } from "./forward-route-invariants.js";
|
|
32
31
|
import { iconDomainInvariants } from "./icon-domain-invariants.js";
|
|
32
|
+
import { storedDefaults } from "./prop-rules.js";
|
|
33
33
|
import { rootLayerInvariants } from "./root-layer-invariants.js";
|
|
34
34
|
import { Token } from "./Token.js";
|
|
35
35
|
import { z } from "zod";
|
|
@@ -1199,7 +1199,7 @@ const Preview = defineSubEntity({
|
|
|
1199
1199
|
});
|
|
1200
1200
|
}
|
|
1201
1201
|
});
|
|
1202
|
-
const DEFAULT_CONDITIONS_DESCRIPTION = "Defaults that hold only under a condition on this component's other props, laid over `defaultProps` the way a style rule is over the unconditional one: a list of entries, each identified by its condition. `{ \"when\": { \"modal\": false }, \"props\": { \"includeScrim\": false } }` defaults `includeScrim` to `false` while `modal` is `false`, and to its `defaultProps` value otherwise. When several entries hold, the later one wins on the prop it sets. Both sides name the component's own variant, string, number or boolean props, a prop set here is never part of a condition, and a value the call site passes always wins. Authored as
|
|
1202
|
+
const DEFAULT_CONDITIONS_DESCRIPTION = "Defaults that hold only under a condition on this component's other props, laid over `defaultProps` the way a style rule is over the unconditional one: a list of entries, each identified by its condition. `{ \"when\": { \"modal\": false }, \"props\": { \"includeScrim\": false } }` defaults `includeScrim` to `false` while `modal` is `false`, and to its `defaultProps` value otherwise. When several entries hold, the later one wins on the prop it sets. Both sides name the component's own variant, string, number or boolean props, a prop set here is never part of a condition, and a value the call site passes always wins. Authored as a rule in `.defaultProps([{ when, props }])`.";
|
|
1203
1203
|
const defaultConditionShapeError = "a conditional default is `{ \"when\": { \"<prop>\": <value> }, \"props\": { \"<prop>\": <value> } }`: both name this component's own variant, string, number or boolean props (not `as`), `when` at one of their values.";
|
|
1204
1204
|
const defaultConditionBase = z.object({
|
|
1205
1205
|
when: z.record(z.string(), z.unknown()),
|
|
@@ -1811,9 +1811,9 @@ const componentFields = z.strictObject({
|
|
|
1811
1811
|
anatomy: AnatomyNode.optional().describe("The tree of what this component renders, keyed by node. Each node names what it renders (a layer, for anything a style rule reaches) and lists its `children`; the node keyed `root` is the top. A one-layer component may omit it. Anatomy read from an extension's own source file replaces the inherited tree; edits to a borrowed render merge into its tree."),
|
|
1812
1812
|
defaultProps: Style.optional(),
|
|
1813
1813
|
/** The defaults that hold only under a condition on the component's other props — see
|
|
1814
|
-
*
|
|
1814
|
+
* `.defaultProps([{ when, props }])`. */
|
|
1815
1815
|
defaultPropsWhen: orList(DefaultCondition).optional().describe(DEFAULT_CONDITIONS_DESCRIPTION),
|
|
1816
|
-
/** What the tile and the matrix render with — see
|
|
1816
|
+
/** What the tile and the matrix render with — see `.preview({ defaultProps })`. Read off
|
|
1817
1817
|
* `.preview()` at build, and written per cell from the matrix. */
|
|
1818
1818
|
preview: orList(Preview).optional().describe(PREVIEW_DESCRIPTION),
|
|
1819
1819
|
/**
|
|
@@ -2989,7 +2989,7 @@ function componentRef(path) {
|
|
|
2989
2989
|
assertComponentSourceInput(delta);
|
|
2990
2990
|
const extended = base.extend({
|
|
2991
2991
|
...delta,
|
|
2992
|
-
...storedDefaults(delta)
|
|
2992
|
+
...delta.defaultProps === void 0 ? {} : storedDefaults(delta.defaultProps)
|
|
2993
2993
|
});
|
|
2994
2994
|
Object.defineProperty(extended, "render", {
|
|
2995
2995
|
enumerable: false,
|
|
@@ -352,7 +352,7 @@ const LinkedSystemDefinition = defineEntity({
|
|
|
352
352
|
input: unlinkInput,
|
|
353
353
|
readOnly: false,
|
|
354
354
|
label: "Unlink from",
|
|
355
|
-
description: "Stop following a linked system's entities, in `link`'s shape or everything when `data` is omitted. The rows the declaration minted go with it, and so do the linked rows that point at them, since left linked they would bring them back; nothing of yours moves. A row of yours pointing at one of theirs refuses this — `copy` what is used first, or `break` with `keep: true`. `unlink-plan` names everything that goes.",
|
|
355
|
+
description: "Stop following a linked system's entities, in `link`'s shape or everything when `data` is omitted. The rows the declaration minted go with it, and so do the linked rows that point at them, since left linked they would bring them back, and the pin's `options` entries left with no row to adjust; nothing of yours moves. A row of yours pointing at one of theirs refuses this — `copy` what is used first, or `break` with `keep: true`. `unlink-plan` names everything that goes.",
|
|
356
356
|
scope: "item",
|
|
357
357
|
approval: "reference-change",
|
|
358
358
|
title: (entity) => `Unlink from ${entity}`,
|
|
@@ -374,7 +374,7 @@ const LinkedSystemDefinition = defineEntity({
|
|
|
374
374
|
input: unlinkInput,
|
|
375
375
|
readOnly: true,
|
|
376
376
|
label: "Plan an unlink",
|
|
377
|
-
description: "What `unlink` with the same input removes — the minted rows the narrower declaration no longer reaches, with the linked rows taken along because they point at them named in `cascade` — and why the config would refuse it, a collision it would leave among them. Writes nothing.",
|
|
377
|
+
description: "What `unlink` with the same input removes — the minted rows the narrower declaration no longer reaches, with the linked rows taken along because they point at them named in `cascade`, and the `options` entries of the pin that go with them in `dropped` — and why the config would refuse it, a collision it would leave among them. Writes nothing.",
|
|
378
378
|
scope: "item",
|
|
379
379
|
title: (entity) => `Plan unlinking from ${entity}`,
|
|
380
380
|
example: pinExample,
|
|
@@ -96,10 +96,20 @@ function planCopy({ config, input }) {
|
|
|
96
96
|
const taking = [];
|
|
97
97
|
const folders = [];
|
|
98
98
|
const reused = [];
|
|
99
|
+
/** Folders this config draws through another pin, which a copied ref binds to as it does a reused row. */
|
|
100
|
+
const heldFolders = /* @__PURE__ */ new Set();
|
|
99
101
|
/** Every `kind:path` `take` has answered, whichever way, so the walk below visits each once. */
|
|
100
102
|
const seen = /* @__PURE__ */ new Set();
|
|
101
103
|
const declared = declarationOf(config, slug);
|
|
102
104
|
const folder = (kind, path) => {
|
|
105
|
+
const through = config.groupBorrowedFrom({
|
|
106
|
+
kind,
|
|
107
|
+
groupPath: path
|
|
108
|
+
});
|
|
109
|
+
if (through !== void 0 && through !== slug && input.onConflict !== "replace") {
|
|
110
|
+
heldFolders.add(`${kind}:${path}`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
103
113
|
if (!folders.some((f) => f.kind === kind && f.path === path)) folders.push({
|
|
104
114
|
kind,
|
|
105
115
|
path
|
|
@@ -258,6 +268,32 @@ function planCopy({ config, input }) {
|
|
|
258
268
|
}
|
|
259
269
|
}
|
|
260
270
|
if (taking.length === 0 && wholesale) rejectData(`"${slug}" has nothing to copy.`);
|
|
271
|
+
/**
|
|
272
|
+
* The files of a sibling a copied render imports, when the sibling itself isn't copied — this config
|
|
273
|
+
* holds that path already, through another pin or as its own. The render imports the source's
|
|
274
|
+
* module, not whatever answers the path here, so that module lands without the component, and so do
|
|
275
|
+
* the files of the siblings it imports in turn. A copied sibling brings its own files.
|
|
276
|
+
*/
|
|
277
|
+
const siblingFiles = ({ sibling, component }) => {
|
|
278
|
+
const key = `files:${sibling}`;
|
|
279
|
+
if (seen.has(key)) return;
|
|
280
|
+
seen.add(key);
|
|
281
|
+
if (taking.some((row) => row.kind === "component" && row.at === sibling)) return;
|
|
282
|
+
for (const path of renderSourceFiles({
|
|
283
|
+
source,
|
|
284
|
+
path: sibling
|
|
285
|
+
})) takeFile({
|
|
286
|
+
path,
|
|
287
|
+
component
|
|
288
|
+
});
|
|
289
|
+
for (const next of renderImportedComponents({
|
|
290
|
+
source,
|
|
291
|
+
path: sibling
|
|
292
|
+
})) siblingFiles({
|
|
293
|
+
sibling: next,
|
|
294
|
+
component
|
|
295
|
+
});
|
|
296
|
+
};
|
|
261
297
|
/** Take what `body` points at by ref. */
|
|
262
298
|
const follow = (body) => {
|
|
263
299
|
for (const target of collectRefs(body)) {
|
|
@@ -318,12 +354,18 @@ function planCopy({ config, input }) {
|
|
|
318
354
|
for (const sibling of renderImportedComponents({
|
|
319
355
|
source,
|
|
320
356
|
path: row.theirs
|
|
321
|
-
}))
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
357
|
+
})) {
|
|
358
|
+
take({
|
|
359
|
+
kind: "component",
|
|
360
|
+
theirs: sibling,
|
|
361
|
+
at: sibling,
|
|
362
|
+
named: false
|
|
363
|
+
});
|
|
364
|
+
siblingFiles({
|
|
365
|
+
sibling,
|
|
366
|
+
component: row.theirs
|
|
367
|
+
});
|
|
368
|
+
}
|
|
327
369
|
for (const path of renderSourceFiles({
|
|
328
370
|
source,
|
|
329
371
|
path: row.theirs
|
|
@@ -336,11 +378,17 @@ function planCopy({ config, input }) {
|
|
|
336
378
|
const arriving = new Set([
|
|
337
379
|
...taking.map((row) => `${row.kind}:${row.at}`),
|
|
338
380
|
...reused.map((row) => `${row.kind}:${row.path}`),
|
|
381
|
+
...heldFolders,
|
|
339
382
|
...folders.map((folder) => `${folder.kind}:${folder.path}`)
|
|
340
383
|
]);
|
|
341
384
|
const patches = [];
|
|
342
385
|
for (const { kind, path } of folders) {
|
|
343
|
-
const meta = ownMeta(
|
|
386
|
+
const meta = ownMeta(config.copyOfGroup({
|
|
387
|
+
slug,
|
|
388
|
+
kind,
|
|
389
|
+
path,
|
|
390
|
+
alongside: arriving
|
|
391
|
+
}) ?? {});
|
|
344
392
|
const pointed = config.groupBorrowedFrom({
|
|
345
393
|
kind,
|
|
346
394
|
groupPath: path
|
|
@@ -3,7 +3,7 @@ import { RegisteredComponentHandles, RegisteredGroupPaths, RegisteredModifiers,
|
|
|
3
3
|
import { Authored } from "../../framework/authoring.js";
|
|
4
4
|
import { StyleValue } from "./style-bag.js";
|
|
5
5
|
import { ComponentSourceInput } from "./component-source-fields.js";
|
|
6
|
-
import {
|
|
6
|
+
import { PropsOrRules } from "./prop-rules.js";
|
|
7
7
|
import { VariantValues } from "./Component.js";
|
|
8
8
|
import { AllHTMLAttributes, ComponentPropsWithRef, ComponentType, ElementType, JSX, ReactElement, ReactNode, Ref } from "react";
|
|
9
9
|
|
|
@@ -664,11 +664,17 @@ type ForwardedPropsOf<L extends LayerMap, F extends string> = [NonRootForward<L,
|
|
|
664
664
|
type DefaultsOf<P extends PropMap> = Partial<DeclaredProps<P>> & {
|
|
665
665
|
[key: string]: unknown;
|
|
666
666
|
};
|
|
667
|
-
/** {@link DefaultsOf} as `.defaultProps`
|
|
668
|
-
*
|
|
669
|
-
type AuthoredDefaultsOf<P extends PropMap> =
|
|
670
|
-
|
|
671
|
-
|
|
667
|
+
/** {@link DefaultsOf} as `.defaultProps` and `.extend({ defaultProps })` take it: the defaults, or
|
|
668
|
+
* a list of rules giving them under conditions. */
|
|
669
|
+
type AuthoredDefaultsOf<P extends PropMap> = PropsOrRules<DefaultsOf<P>>;
|
|
670
|
+
/** The defaults a component applies with nothing set: a record as given, or its unconditional
|
|
671
|
+
* rules folded together. */
|
|
672
|
+
type UnconditionalDefaults<D> = D extends readonly (infer Rule)[] ? [UnconditionalProps<Rule>] extends [never] ? Record<never, never> : UnionToIntersection<UnconditionalProps<Rule>> : D;
|
|
673
|
+
type UnconditionalProps<Rule> = Rule extends {
|
|
674
|
+
when: Readonly<Record<string, unknown>>;
|
|
675
|
+
} ? never : Rule extends {
|
|
676
|
+
props: infer Props;
|
|
677
|
+
} ? Props : never;
|
|
672
678
|
/**
|
|
673
679
|
* `T` without the keys in `K` — `Omit` that leaves an index signature alone.
|
|
674
680
|
*
|
|
@@ -785,10 +791,11 @@ F extends string = never, /** The states `.states()` declared — what the rende
|
|
|
785
791
|
* `const`, so an `as` keeps its literal type: without it `{ as: 'input' }` widens to `string` and
|
|
786
792
|
* there's no tag left to resolve the element's props against.
|
|
787
793
|
*
|
|
788
|
-
*
|
|
789
|
-
*
|
|
794
|
+
* Defaults that depend on other props are rules, the way style rules are: a rule without `when`
|
|
795
|
+
* holds always, and a later rule wins on the props it sets.
|
|
796
|
+
* `[{ props: { includeScrim: true } }, { when: { modal: false }, props: { includeScrim: false } }]`.
|
|
790
797
|
*/
|
|
791
|
-
defaultProps<const D2 extends AuthoredDefaultsOf<P>>(defaults: D2): ComponentBuilder<L, P, E, D2
|
|
798
|
+
defaultProps<const D2 extends AuthoredDefaultsOf<P>>(defaults: D2): ComponentBuilder<L, P, E, UnconditionalDefaults<D2>, F, S>;
|
|
792
799
|
/**
|
|
793
800
|
* The layers whose element props this component ALSO exposes, keyed by layer.
|
|
794
801
|
*
|
|
@@ -832,34 +839,23 @@ F extends string = never, /** The states `.states()` declared — what the rende
|
|
|
832
839
|
*/
|
|
833
840
|
renderCanvas<E2 extends E = E>(fn: (args: Without<RenderArgs<L, P, S>, keyof E2> & E2) => unknown): ComponentBuilder<L, P, E, D, F, S>;
|
|
834
841
|
/**
|
|
835
|
-
* What a preview
|
|
842
|
+
* What a preview tile renders with. `defaultProps` takes what `.defaultProps` does: a record, or a
|
|
843
|
+
* list of rules.
|
|
836
844
|
*
|
|
837
|
-
* Only
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
845
|
+
* Only content, because the grid itself is derived: every axis a matrix could sweep is declared in
|
|
846
|
+
* `styles`, as the conditions the rules apply under, and the modifier columns are the
|
|
847
|
+
* `_hover`/`_active` blocks those rules contain. See `previewMatrix`.
|
|
848
|
+
*
|
|
849
|
+
* A rule with `when` gives the content a cell needs at one point on an axis:
|
|
850
|
+
* `{ when: { type: 'image' }, props: { src } }` gives the image kind a photo. A condition naming a
|
|
851
|
+
* prop no rule styles makes that prop an axis too, with the value named.
|
|
842
852
|
*/
|
|
843
853
|
preview(spec: {
|
|
844
|
-
defaultProps?: DefaultsOf<P
|
|
845
|
-
/**
|
|
846
|
-
* Preview props that hold only under a condition — the same `when` grammar a style rule uses.
|
|
847
|
-
* The grid is derived from the styles; what it cannot derive is the CONTENT a cell needs at a
|
|
848
|
-
* given point on an axis: `{ when: { type: 'image' }, props: { src } }` gives the image kind a
|
|
849
|
-
* photo, `{ when: { invalid: true }, props: { selected: true } }` shows the invalid state checked.
|
|
850
|
-
* A condition naming a prop no rule styles makes that prop an axis too, with the value named —
|
|
851
|
-
* so a preview can say a boolean is worth a column the way a rule would.
|
|
852
|
-
*/
|
|
853
|
-
when?: readonly PreviewCondition<P>[];
|
|
854
|
+
defaultProps?: PropsOrRules<DefaultsOf<P>>;
|
|
854
855
|
}): ComponentBuilder<L, P, E, D, F, S>;
|
|
855
856
|
toStored(): Record<string, unknown>;
|
|
856
857
|
assignPath(path: string): void;
|
|
857
858
|
}
|
|
858
|
-
/** Preview props that hold under a condition on the cell's props — see `preview({ when })`. */
|
|
859
|
-
interface PreviewCondition<P extends PropMap = PropMap> {
|
|
860
|
-
readonly when: Partial<Record<keyof P & string, unknown>>;
|
|
861
|
-
readonly props: DefaultsOf<P>;
|
|
862
|
-
}
|
|
863
859
|
/** A component under authorship, with its declarations forgotten — what a registry holds. */
|
|
864
860
|
type AuthoredComponent = ComponentBuilder<LayerMap, PropMap, unknown, unknown>;
|
|
865
861
|
/**
|
|
@@ -901,4 +897,4 @@ interface DefineComponent {
|
|
|
901
897
|
declare function definePropsFirst<const P extends PropMap<LayerPath>>(props: P & NoInfer<{ [K in keyof P]: Exact<P[K], AuthoredProp<LayerPath>> }>): ComponentBuilder<LayerMap, P>;
|
|
902
898
|
declare const defineComponent: DefineComponent;
|
|
903
899
|
//#endregion
|
|
904
|
-
export { AsInput, AuthoredComponent, AuthoredDefaultsOf, AuthoredProp, ComponentBuilder, ComponentPropsOf, GlyphComponent, GlyphValue, LayerElement, LayerMap, LinkedHandle, ModifierProps, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, SlotCodeForm, SlotDecl, StateMap, StyleProps, StyleRuleInput, defineComponent };
|
|
900
|
+
export { AsInput, AuthoredComponent, AuthoredDefaultsOf, AuthoredProp, ComponentBuilder, ComponentPropsOf, GlyphComponent, GlyphValue, LayerElement, LayerMap, LinkedHandle, ModifierProps, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, SlotCodeForm, SlotDecl, StateMap, StyleProps, StyleRuleInput, UnconditionalDefaults, defineComponent };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isRef, kindOf, memberRef, pathOf } from "../../framework/utils/refs.js";
|
|
2
2
|
import { glyphIdentityOf, glyphPackageRefOf, isComponentType } from "./glyph-identity.js";
|
|
3
|
-
import "../../framework/preview-store.js";
|
|
4
3
|
import { assertComponentSourceInput } from "./component-source-fields.js";
|
|
5
|
-
import {
|
|
4
|
+
import { keyedPropRules, storedDefaults } from "./prop-rules.js";
|
|
6
5
|
import { Component, layerRef, storedStyleRules } from "./Component.js";
|
|
7
6
|
//#region src/entities/system/defineComponent.ts
|
|
8
7
|
function build(body) {
|
|
@@ -25,10 +24,7 @@ function build(body) {
|
|
|
25
24
|
metadata: (meta) => next(meta),
|
|
26
25
|
render: () => handle,
|
|
27
26
|
renderCanvas: () => handle,
|
|
28
|
-
preview: (spec) => {
|
|
29
|
-
if (spec?.defaultProps === void 0 && !spec?.when?.length) return handle;
|
|
30
|
-
return next({ preview: spec });
|
|
31
|
-
},
|
|
27
|
+
preview: (spec) => spec?.defaultProps === void 0 ? handle : next({ preview: spec.defaultProps }),
|
|
32
28
|
toStored: () => toStored(body),
|
|
33
29
|
assignPath(path) {
|
|
34
30
|
handle.path = path;
|
|
@@ -146,7 +142,6 @@ function storableElement(value) {
|
|
|
146
142
|
*/
|
|
147
143
|
function storableValue(value) {
|
|
148
144
|
if (value === null) return null;
|
|
149
|
-
if (isConditionalDefault(value)) throw new Error("`conditionalDefault` is a default, so it goes in `.defaultProps` or `.extend({ defaultProps })`. A preview conditions its props with `.preview({ when: [{ when, props }] })`.");
|
|
150
145
|
const type = typeof value;
|
|
151
146
|
if (type === "string" || type === "number" || type === "boolean") return value;
|
|
152
147
|
if (isComponentType(value)) return storableComponentRef(value);
|
|
@@ -376,21 +371,19 @@ function toStored(body) {
|
|
|
376
371
|
assertComponentSourceInput(body);
|
|
377
372
|
const stored = { ...body };
|
|
378
373
|
if (body.preview !== void 0) {
|
|
379
|
-
const authored = body.preview;
|
|
380
374
|
const keyed = {};
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
when
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
};
|
|
375
|
+
for (const [key, { when, props }] of keyedPropRules(body.preview)) {
|
|
376
|
+
const storable = storableProps(props);
|
|
377
|
+
if (storable === void 0 && when === null) continue;
|
|
378
|
+
keyed[key] = {
|
|
379
|
+
when,
|
|
380
|
+
props: storable ?? {}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
390
383
|
if (Object.keys(keyed).length === 0) delete stored.preview;
|
|
391
384
|
else stored.preview = keyed;
|
|
392
385
|
}
|
|
393
|
-
Object.assign(stored, storedDefaults(body));
|
|
386
|
+
if (body.defaultProps !== void 0) Object.assign(stored, storedDefaults(body.defaultProps));
|
|
394
387
|
if (body.props && typeof body.props === "object") {
|
|
395
388
|
const props = {};
|
|
396
389
|
for (const [name, decl] of Object.entries(body.props)) props[name] = storedProp(decl, body.layers);
|
|
@@ -64,6 +64,9 @@ interface UnlinkPlan {
|
|
|
64
64
|
/** Linked rows unlinked with the named ones because they point at them: left linked, they would
|
|
65
65
|
* bring the named rows back on the next derive. */
|
|
66
66
|
readonly cascade: readonly EntityAddress[];
|
|
67
|
+
/** The pin's `options` entries that go with the unlink: one on a path that stops deriving, and one
|
|
68
|
+
* on a path drawn from elsewhere that names a row that goes. Neither has a row to adjust. */
|
|
69
|
+
readonly dropped: readonly EntityAddress[];
|
|
67
70
|
/** Why the config would refuse. `undefined` when it would go through. */
|
|
68
71
|
readonly blocked?: string;
|
|
69
72
|
/** The paths the unlink would leave two linked systems offering as different entities, when that
|
|
@@ -329,6 +329,7 @@ function planUnlink({ config, input }) {
|
|
|
329
329
|
declared: void 0,
|
|
330
330
|
removed: [],
|
|
331
331
|
cascade: [],
|
|
332
|
+
dropped: [],
|
|
332
333
|
patches: []
|
|
333
334
|
};
|
|
334
335
|
let declared;
|
|
@@ -397,16 +398,39 @@ function planUnlink({ config, input }) {
|
|
|
397
398
|
}
|
|
398
399
|
if (input.data !== void 0 && removed.length === 0) blocked = stillReached({ slug });
|
|
399
400
|
}
|
|
401
|
+
const dropped = after ? deadDirectives({
|
|
402
|
+
config,
|
|
403
|
+
after,
|
|
404
|
+
slug,
|
|
405
|
+
removed
|
|
406
|
+
}) : [];
|
|
407
|
+
if (dropped.length > 0) {
|
|
408
|
+
const update = patches[patches.length - 1];
|
|
409
|
+
if (dropped.length === config.sourceDirectives(slug).length) update.data.options = null;
|
|
410
|
+
else for (const { accessor, path } of dropped) update.data[`options.${accessor}.${path}`] = null;
|
|
411
|
+
}
|
|
400
412
|
return {
|
|
401
413
|
declared,
|
|
402
414
|
removed,
|
|
403
415
|
cascade,
|
|
416
|
+
dropped: dropped.map(({ kind, path }) => ({
|
|
417
|
+
kind,
|
|
418
|
+
path
|
|
419
|
+
})),
|
|
404
420
|
...blocked === void 0 ? {} : { blocked },
|
|
405
421
|
...refused.collisions ? { collisions: refused.collisions } : {},
|
|
406
422
|
patches
|
|
407
423
|
};
|
|
408
424
|
}
|
|
409
425
|
/**
|
|
426
|
+
* The pin's directives an unlink leaves with nothing to do: on a path that no longer derives through
|
|
427
|
+
* the pin, and either on a row the unlink retires or naming one.
|
|
428
|
+
*/
|
|
429
|
+
function deadDirectives({ config, after, slug, removed }) {
|
|
430
|
+
const gone = new Set(removed.map(({ kind, path }) => `${kind}:${path}`));
|
|
431
|
+
return config.sourceDirectives(slug).filter((directive) => directive.kind !== void 0 && after.resolve(directive.kind, directive.path)?.borrowedFrom !== slug && (gone.has(`${directive.kind}:${directive.path}`) || collectRefs(config.sourceOverlay(slug, directive.kind, directive.path)).some((target) => gone.has(target))));
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
410
434
|
* Leave paths of a linked system out, with a `!` entry each in the pin's `link`. Stronger than
|
|
411
435
|
* `unlink`: nothing derives there however the link reaches it, a linked token naming it included,
|
|
412
436
|
* and the linked tokens' overrides under an excluded modifier are left out of the build. `link` with
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/entities/system/prop-rules.d.ts
|
|
2
|
+
/** One rule: `props` hold while every prop `when` names has its value, or always with no `when`. */
|
|
3
|
+
interface PropRule<Props = Record<string, unknown>> {
|
|
4
|
+
readonly when?: Readonly<Record<string, unknown>> | null;
|
|
5
|
+
readonly props: Props;
|
|
6
|
+
}
|
|
7
|
+
/** The props given unconditionally, or a list of rules. */
|
|
8
|
+
type PropsOrRules<Props> = Props | readonly PropRule<Props>[];
|
|
9
|
+
//#endregion
|
|
10
|
+
export { PropRule, PropsOrRules };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { canonicalCondition } from "../../framework/utils/refs.js";
|
|
2
|
+
//#region src/entities/system/prop-rules.ts
|
|
3
|
+
/**
|
|
4
|
+
* Rules keyed by condition, `*` for the unconditional one. Two rules with the same condition are
|
|
5
|
+
* one: their props merge, the later value winning, and the merged rule takes the later one's place.
|
|
6
|
+
*/
|
|
7
|
+
function keyedPropRules(authored) {
|
|
8
|
+
const rules = Array.isArray(authored) ? authored : [{ props: authored }];
|
|
9
|
+
const keyed = /* @__PURE__ */ new Map();
|
|
10
|
+
for (const rule of rules) {
|
|
11
|
+
const key = canonicalCondition(rule.when);
|
|
12
|
+
const prior = keyed.get(key);
|
|
13
|
+
keyed.delete(key);
|
|
14
|
+
keyed.set(key, {
|
|
15
|
+
when: key === "*" ? null : { ...rule.when },
|
|
16
|
+
props: {
|
|
17
|
+
...prior?.props,
|
|
18
|
+
...rule.props
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return keyed;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Authored defaults as stored: the unconditional rule in `defaultProps`, each other in
|
|
26
|
+
* `defaultPropsWhen`. A plain record stores as it was written, with no `defaultPropsWhen`.
|
|
27
|
+
*/
|
|
28
|
+
function storedDefaults(authored) {
|
|
29
|
+
if (!Array.isArray(authored)) return { defaultProps: { ...authored } };
|
|
30
|
+
const keyed = keyedPropRules(authored);
|
|
31
|
+
const defaultPropsWhen = {};
|
|
32
|
+
for (const [key, { when, props }] of keyed) if (when) defaultPropsWhen[key] = {
|
|
33
|
+
when,
|
|
34
|
+
props
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
defaultProps: keyed.get("*")?.props ?? {},
|
|
38
|
+
...Object.keys(defaultPropsWhen).length > 0 ? { defaultPropsWhen } : {}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { keyedPropRules, storedDefaults };
|
|
@@ -1942,6 +1942,42 @@ declare class Config {
|
|
|
1942
1942
|
alongside?: ReadonlySet<string>;
|
|
1943
1943
|
extend?: Record<string, unknown>;
|
|
1944
1944
|
}): Record<string, unknown> | undefined;
|
|
1945
|
+
/**
|
|
1946
|
+
* One of a source's folders' meta as a copy stores it — its refs spelled from here as
|
|
1947
|
+
* {@link copyOf} spells a body's. The meta as the source holds it names its own pins by their slugs
|
|
1948
|
+
* there: the Bridge's `icons` folder names `package:@yahoo/uds-icons@yos`, and stored as it was that
|
|
1949
|
+
* names a pin this config doesn't have. `undefined` when the source has no folder there.
|
|
1950
|
+
*/
|
|
1951
|
+
copyOfGroup({
|
|
1952
|
+
slug,
|
|
1953
|
+
kind,
|
|
1954
|
+
path,
|
|
1955
|
+
alongside
|
|
1956
|
+
}: {
|
|
1957
|
+
slug: string;
|
|
1958
|
+
kind: string;
|
|
1959
|
+
path: string; /** `kind:path` targets landing as this config's own in the same edit. */
|
|
1960
|
+
alongside?: ReadonlySet<string>;
|
|
1961
|
+
}): Record<string, unknown> | undefined;
|
|
1962
|
+
/**
|
|
1963
|
+
* `body` with each ref routed through another system's pin (`@yos-v3~yos`) respelled through this
|
|
1964
|
+
* config's own pin to the same system, where that pin's source holds the target too: a copy then
|
|
1965
|
+
* names what this config reaches directly rather than keeping the pin it came through referenced.
|
|
1966
|
+
* Bare when this config already holds the target as a pointer through that pin.
|
|
1967
|
+
*/
|
|
1968
|
+
private throughDirectPins;
|
|
1969
|
+
/**
|
|
1970
|
+
* The pin of this config's own that reaches the system a routed source ends at — `yahoo-os` for
|
|
1971
|
+
* `yos-v3~yos`, when this config pins directly what `yos-v3` pins as `yos`. Matched on the pin
|
|
1972
|
+
* kind's `sourceOrigin`, so the two pins may name different versions. `undefined` for a source of
|
|
1973
|
+
* one hop, or one whose system no pin here reaches.
|
|
1974
|
+
*
|
|
1975
|
+
* The two pins match when they agree on every origin field both set, and share at least one. Not on
|
|
1976
|
+
* the whole key: a pin by path is stamped with its sibling's id when it's pushed, so the Bridge's
|
|
1977
|
+
* `yos` reads `configPath` and `systemId` where this config's pin to the same system names only the
|
|
1978
|
+
* id — and a `configPath` is relative to the config holding it, so it can't be compared across two.
|
|
1979
|
+
*/
|
|
1980
|
+
private directRoute;
|
|
1945
1981
|
/**
|
|
1946
1982
|
* Whether a copied row no longer matches what its source offers at the path it was copied from.
|
|
1947
1983
|
*
|
|
@@ -2255,6 +2291,16 @@ declare class Config {
|
|
|
2255
2291
|
* that reaches derivation — a deleted extension whose source reached the row — says what it did.
|
|
2256
2292
|
*/
|
|
2257
2293
|
private assertDerivedDropsUnreferenced;
|
|
2294
|
+
/**
|
|
2295
|
+
* Whether every directive of `pin` that names `target` sits on a path that no longer derives through
|
|
2296
|
+
* it — retired by this derivation, or drawn from another source already. Such a directive lays its
|
|
2297
|
+
* value over no row, so it goes with the link rather than being stranded by it: a pin's own
|
|
2298
|
+
* `options` never refuse removing its link. A directive on a row the pin still mints does, since
|
|
2299
|
+
* that row would be left pointing at nothing.
|
|
2300
|
+
*
|
|
2301
|
+
* `false` when `pin` is not a source, or names `target` somewhere other than a directive.
|
|
2302
|
+
*/
|
|
2303
|
+
private directivesGoWith;
|
|
2258
2304
|
/**
|
|
2259
2305
|
* This config with a row for every entity it inherits — the build's output. A pin patch runs this
|
|
2260
2306
|
* inside {@link apply}; a source attaching runs it through the session.
|
|
@@ -2513,6 +2559,8 @@ declare class Config {
|
|
|
2513
2559
|
*/
|
|
2514
2560
|
sourceIdentityKey(kind: string, path: string): string | undefined;
|
|
2515
2561
|
private sourceKey;
|
|
2562
|
+
/** Each of a pin's `of` fields as it reads, `''` where unset, in the kind's order. */
|
|
2563
|
+
private sourceFields;
|
|
2516
2564
|
/**
|
|
2517
2565
|
* Where what this config shows at `path` originates: the system it was first authored in, followed
|
|
2518
2566
|
* through however many pins it took to get here. `undefined` for a row this config authored itself.
|