@yahoo/uds-create-config 5.53.1 → 5.55.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.
Files changed (51) hide show
  1. package/dist/capture/node-ops.js +1 -1
  2. package/dist/configs/system.js +2 -0
  3. package/dist/entities/canvas/node-gestures.js +1 -1
  4. package/dist/entities/canvas/node-patches.js +1 -1
  5. package/dist/entities/system/Component.d.ts +5 -2
  6. package/dist/entities/system/Component.js +121 -8
  7. package/dist/entities/system/LinkedSystem.js +2 -2
  8. package/dist/entities/system/Operation.js +1 -1
  9. package/dist/entities/system/StyleProperty.js +5 -0
  10. package/dist/entities/system/component-forwards.js +1 -1
  11. package/dist/entities/system/component-source-fields.js +1 -1
  12. package/dist/entities/system/component-style-set.js +1 -1
  13. package/dist/entities/system/conditional-defaults.d.ts +45 -0
  14. package/dist/entities/system/conditional-defaults.js +142 -0
  15. package/dist/entities/system/copy-plan.js +1 -1
  16. package/dist/entities/system/css-motion-reach.js +1 -1
  17. package/dist/entities/system/defineComponent.d.ts +11 -2
  18. package/dist/entities/system/defineComponent.js +3 -0
  19. package/dist/entities/system/drawn-motion.d.ts +2 -1
  20. package/dist/entities/system/drawn-motion.js +23 -16
  21. package/dist/entities/system/link-plan.js +1 -1
  22. package/dist/entities/system/motion-convert.js +1 -1
  23. package/dist/entities/system/motion-variants.js +2 -2
  24. package/dist/framework/Config.d.ts +1 -1
  25. package/dist/framework/Config.js +13 -4
  26. package/dist/framework/class-names.d.ts +14 -9
  27. package/dist/framework/class-names.js +37 -22
  28. package/dist/framework/default-conditions.d.ts +26 -0
  29. package/dist/framework/default-conditions.js +35 -0
  30. package/dist/framework/defineEntity.js +1 -1
  31. package/dist/framework/inline-sources.js +1 -1
  32. package/dist/framework/layer-styles.js +7 -2
  33. package/dist/framework/module-views.d.ts +9 -10
  34. package/dist/framework/plan.js +1 -1
  35. package/dist/framework/projections.d.ts +36 -4
  36. package/dist/framework/projections.js +97 -24
  37. package/dist/framework/render-spec.js +10 -2
  38. package/dist/framework/schema-version.d.ts +1 -1
  39. package/dist/framework/schema-version.js +5 -5
  40. package/dist/framework/schemas.js +2 -2
  41. package/dist/framework/source-integrity.js +1 -1
  42. package/dist/index.d.ts +6 -3
  43. package/dist/index.js +7 -4
  44. package/dist/migrations/20261023152030_conditional_defaults.d.ts +6 -0
  45. package/dist/migrations/20261023152030_conditional_defaults.js +8 -0
  46. package/dist/spec/bake.js +14 -6
  47. package/dist/spec/cell.d.ts +2 -1
  48. package/dist/spec/cell.js +14 -6
  49. package/dist/spec/detach.js +5 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
- import { rejectData } from "../framework/rejection.js";
2
1
  import { pathOf } from "../framework/utils/refs.js";
2
+ import { rejectData } from "../framework/rejection.js";
3
3
  import { pathInput } from "../framework/schemas.js";
4
4
  import { CapturedNode } from "./captured-tree.js";
5
5
  import { specFromCapture } from "./capture-to-spec.js";
@@ -29,6 +29,7 @@ import { lintSettingsMigration } from "../migrations/20261020183330_lint_setting
29
29
  import { directLayerCompositesMigration } from "../migrations/20261022124223_direct_layer_composites.js";
30
30
  import { cssContentScriptsMigration } from "../migrations/20261022152120_css_content_scripts.js";
31
31
  import { linkTypeMigration } from "../migrations/20261022184639_link_type.js";
32
+ import { conditionalDefaultsMigration } from "../migrations/20261023152030_conditional_defaults.js";
32
33
  import { CanvasRole } from "../entities/system/CanvasRole.js";
33
34
  import { GlobalStyle } from "../entities/system/GlobalStyle.js";
34
35
  import { Guidance } from "../entities/system/Guidance.js";
@@ -191,5 +192,6 @@ if (!registeredSchemaMigrations().has(20261020183330)) {
191
192
  }
192
193
  if (!registeredSchemaMigrations().has(20261022152120)) registerSchemaMigrations(cssContentScriptsMigration);
193
194
  if (!registeredSchemaMigrations().has(20261022184639)) registerSchemaMigrations(linkTypeMigration);
195
+ if (!registeredSchemaMigrations().has(20261023152030)) registerSchemaMigrations(conditionalDefaultsMigration);
194
196
  //#endregion
195
197
  export { System };
@@ -1,5 +1,5 @@
1
- import { rejectData } from "../../framework/rejection.js";
2
1
  import { pathOf, ref } from "../../framework/utils/refs.js";
2
+ import { rejectData } from "../../framework/rejection.js";
3
3
  import { pathInput } from "../../framework/schemas.js";
4
4
  import { childrenPolicy } from "../system/element.js";
5
5
  import { surfaceProps } from "../../framework/prop-surface.js";
@@ -1,5 +1,5 @@
1
- import { rejectData } from "../../framework/rejection.js";
2
1
  import { ref } from "../../framework/utils/refs.js";
2
+ import { rejectData } from "../../framework/rejection.js";
3
3
  //#region src/entities/canvas/node-patches.ts
4
4
  /**
5
5
  * A spec as the node rows of one new placed node. A placed node's content is its subtree rows, one
@@ -9,10 +9,11 @@ import { Named } from "../../framework/named.js";
9
9
  import { PackageClass } from "./Package.js";
10
10
  import { StyleClass } from "./style-bag.js";
11
11
  import { TokenClass } from "./Token.js";
12
+ import { StoredDefaultCondition } from "../../framework/default-conditions.js";
12
13
  import { StoredPreviewCondition } from "../../framework/preview-store.js";
13
14
  import { RepeatSpec } from "../../framework/render-spec.js";
14
15
  import { ComponentSourceInput } from "./component-source-fields.js";
15
- import { AsInput, DefaultsOf, LayerMap, PhantomCallProps, PhantomReadProps, PropMap, RenderArgs, StateMap, StyleRuleInput } from "./defineComponent.js";
16
+ import { AsInput, AuthoredDefaultsOf, LayerMap, PhantomCallProps, PhantomReadProps, PropMap, RenderArgs, StateMap, StyleRuleInput } from "./defineComponent.js";
16
17
  import { listSlots, namedSlots, slotHostedBy, slotOf, slotsFor } from "../../spec/slot.js";
17
18
  import { cellAt } from "../../spec/cell.js";
18
19
  import { instanceAt } from "../../spec/instance.js";
@@ -658,6 +659,7 @@ declare const componentFields: z.ZodObject<{
658
659
  renderProps: z.ZodOptional<z.ZodArray<z.ZodString>>;
659
660
  anatomy: z.ZodOptional<AnatomyNodeClass>;
660
661
  defaultProps: z.ZodOptional<StyleClass>;
662
+ defaultPropsWhen: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodType<StoredDefaultCondition, unknown, z.core.$ZodTypeInternals<StoredDefaultCondition, unknown>>>, z.ZodTransform<Record<string, StoredDefaultCondition>, StoredDefaultCondition[]>>, SubEntityClass<z.ZodType<StoredDefaultCondition, unknown, z.core.$ZodTypeInternals<StoredDefaultCondition, unknown>>>]>>;
661
663
  preview: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodType<StoredPreviewCondition, unknown, z.core.$ZodTypeInternals<StoredPreviewCondition, unknown>>>, z.ZodTransform<Record<string, StoredPreviewCondition>, StoredPreviewCondition[]>>, SubEntityClass<z.ZodType<StoredPreviewCondition, unknown, z.core.$ZodTypeInternals<StoredPreviewCondition, unknown>>>]>>;
662
664
  file: z.ZodOptional<z.ZodObject<{
663
665
  __ref: z.ZodString;
@@ -699,6 +701,7 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
699
701
  renderProps: z.ZodOptional<z.ZodArray<z.ZodString>>;
700
702
  anatomy: z.ZodOptional<AnatomyNodeClass>;
701
703
  defaultProps: z.ZodOptional<StyleClass>;
704
+ defaultPropsWhen: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodType<StoredDefaultCondition, unknown, z.core.$ZodTypeInternals<StoredDefaultCondition, unknown>>>, z.ZodTransform<Record<string, StoredDefaultCondition>, StoredDefaultCondition[]>>, SubEntityClass<z.ZodType<StoredDefaultCondition, unknown, z.core.$ZodTypeInternals<StoredDefaultCondition, unknown>>>]>>;
702
705
  preview: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodType<StoredPreviewCondition, unknown, z.core.$ZodTypeInternals<StoredPreviewCondition, unknown>>>, z.ZodTransform<Record<string, StoredPreviewCondition>, StoredPreviewCondition[]>>, SubEntityClass<z.ZodType<StoredPreviewCondition, unknown, z.core.$ZodTypeInternals<StoredPreviewCondition, unknown>>>]>>;
703
706
  file: z.ZodOptional<z.ZodObject<{
704
707
  __ref: z.ZodString;
@@ -954,7 +957,7 @@ type ComponentReference<Contract = ComponentContractOf<string>, Path extends str
954
957
  /** An extension's `defaultProps`, checked against the props it ends up with the way
955
958
  * `defineComponent` checks its own: a declared prop takes its values, and any other key stays open. */
956
959
  type ExtensionDefaults<Contract, Delta> = {
957
- readonly defaultProps?: DefaultsOf<ResolvedProps<Contract, Delta>>;
960
+ readonly defaultProps?: AuthoredDefaultsOf<ResolvedProps<Contract, Delta>>;
958
961
  };
959
962
  /**
960
963
  * Two style rules with the same condition, as the one rule they are: each layer's bag merged, the
@@ -1,7 +1,8 @@
1
1
  import "../../configs/platform.js";
2
- import { reject, rejectData } from "../../framework/rejection.js";
3
- import { canonicalCondition, collectRefs, isRef, kindOf, memberRef, pathOf, ref, refLeaf, refSchema, sourceOf, splitRef } from "../../framework/utils/refs.js";
4
2
  import { isPlainObject } from "../../framework/utils/field-path.js";
3
+ import { canonicalCondition, collectRefs, isRef, kindOf, memberRef, pathOf, ref, refLeaf, refSchema, sourceOf, splitRef } from "../../framework/utils/refs.js";
4
+ import { storedDefaultConditions } from "../../framework/default-conditions.js";
5
+ import { reject, rejectData } from "../../framework/rejection.js";
5
6
  import { pathInput } from "../../framework/schemas.js";
6
7
  import { derivedColor, gradient, oklchColor } from "./color.js";
7
8
  import { shadowValue } from "./shadow.js";
@@ -26,6 +27,7 @@ import { instanceAt } from "../../spec/instance.js";
26
27
  import { forwardOptions, forwardsWrite } from "./component-forwards.js";
27
28
  import { assertComponentSourceInput } from "./component-source-fields.js";
28
29
  import { checkStyleSet, styleSetEdits } from "./component-style-set.js";
30
+ import { storedDefaults } from "./conditional-defaults.js";
29
31
  import { forwardRouteInvariants, refusedRoutes } from "./forward-route-invariants.js";
30
32
  import { iconDomainInvariants } from "./icon-domain-invariants.js";
31
33
  import { rootLayerInvariants } from "./root-layer-invariants.js";
@@ -346,8 +348,16 @@ const VariantValue = defineSubEntity({
346
348
  * which takes its styling with it. */
347
349
  deprecated: z.boolean().optional()
348
350
  }),
349
- namedValuesIn: ["defaultProps", "preview/*/props"],
350
- orphans: ["styles", "preview"]
351
+ namedValuesIn: [
352
+ "defaultProps",
353
+ "preview/*/props",
354
+ "defaultPropsWhen/*/props"
355
+ ],
356
+ orphans: [
357
+ "styles",
358
+ "preview",
359
+ "defaultPropsWhen"
360
+ ]
351
361
  });
352
362
  /**
353
363
  * A variant's domain: the values it owns, a ref that delegates to whatever owns them, or a list of
@@ -557,8 +567,16 @@ const Prop = defineSubEntity({
557
567
  namespaces: "value",
558
568
  namesMembersOf: { when: "props" },
559
569
  subEntities: { value: VariantValue },
560
- keyedByNameIn: ["defaultProps", "preview/*/props"],
561
- orphans: ["styles", "preview"]
570
+ keyedByNameIn: [
571
+ "defaultProps",
572
+ "preview/*/props",
573
+ "defaultPropsWhen/*/props"
574
+ ],
575
+ orphans: [
576
+ "styles",
577
+ "preview",
578
+ "defaultPropsWhen"
579
+ ]
562
580
  });
563
581
  /** {@link Prop}'s per-owner schema. Reached only through the memo in its `fields`. */
564
582
  function propSchemaFor(config, owner) {
@@ -1168,6 +1186,61 @@ const Preview = defineSubEntity({
1168
1186
  });
1169
1187
  }
1170
1188
  });
1189
+ 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 `conditionalDefault(fallback, [{ when, value }])` inside `.defaultProps`.";
1190
+ 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.";
1191
+ const defaultConditionBase = z.object({
1192
+ when: z.record(z.string(), z.unknown()),
1193
+ props: z.record(z.string(), z.unknown())
1194
+ });
1195
+ const defaultConditionSchemas = /* @__PURE__ */ new WeakMap();
1196
+ /** The props a conditional default may set or condition on: the component's own scalar props, the
1197
+ * ones its module binds and tests. `as` decides the element, so it never depends on another prop. */
1198
+ function conditionallyDefaultable(props) {
1199
+ return Object.entries(props ?? {}).filter(([name, decl]) => {
1200
+ const type = decl?.type;
1201
+ return name !== "as" && (type === "variant" || type === "string" || type === "number" || type === "boolean");
1202
+ });
1203
+ }
1204
+ /** Whether a conditional default sets nothing: its `props` are empty. */
1205
+ function defaultConditionSetsNothing(entry) {
1206
+ const props = entry?.props;
1207
+ return !props || Object.keys(props).length === 0;
1208
+ }
1209
+ const DefaultCondition = defineSubEntity({
1210
+ name: "defaultCondition",
1211
+ label: "Conditional default",
1212
+ labelPlural: "Conditional defaults",
1213
+ key: (entry) => canonicalWhen(entry?.when),
1214
+ vacant: defaultConditionSetsNothing,
1215
+ namesMembersOf: { when: "props" },
1216
+ base: defaultConditionBase,
1217
+ fields: ({ config, parent }) => {
1218
+ const body = parent;
1219
+ if (!config || !body) return defaultConditionBase;
1220
+ return perOwner({
1221
+ cache: defaultConditionSchemas,
1222
+ config,
1223
+ owner: body,
1224
+ build: () => {
1225
+ const styleField = Style.fields;
1226
+ const when = {};
1227
+ const props = {};
1228
+ for (const [name, decl] of conditionallyDefaultable(body.props)) {
1229
+ when[name] = conditionValue(name, decl, config).optional();
1230
+ props[name] = styleField({
1231
+ config,
1232
+ parent: body,
1233
+ key: name
1234
+ }).optional();
1235
+ }
1236
+ return z.strictObject({
1237
+ when: z.strictObject(when, { error: defaultConditionShapeError }).refine((value) => Object.keys(value).length > 0, { error: "a conditional default holds under a condition; an unconditional one belongs in `defaultProps`." }),
1238
+ props: z.strictObject(props, { error: defaultConditionShapeError })
1239
+ }, { error: defaultConditionShapeError });
1240
+ }
1241
+ });
1242
+ }
1243
+ });
1171
1244
  /** Why a slot refuses an element, for a write the config turns away. */
1172
1245
  function slotRefusal({ slot, element }) {
1173
1246
  const named = isRef(element) ? element.__ref : String(element);
@@ -1724,6 +1797,9 @@ const componentFields = z.strictObject({
1724
1797
  renderProps: z.array(z.string()).optional().describe("Props the render reads beyond those declared, read off the source at build. Not authored: declare a prop in `props` instead."),
1725
1798
  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."),
1726
1799
  defaultProps: Style.optional(),
1800
+ /** The defaults that hold only under a condition on the component's other props — see
1801
+ * `conditionalDefault`. */
1802
+ defaultPropsWhen: orList(DefaultCondition).optional().describe(DEFAULT_CONDITIONS_DESCRIPTION),
1727
1803
  /** What the tile and the matrix render with — see `preview({ defaultProps, when })`. Read off
1728
1804
  * `.preview()` at build, and written per cell from the matrix. */
1729
1805
  preview: orList(Preview).optional().describe(PREVIEW_DESCRIPTION),
@@ -1769,6 +1845,39 @@ const componentFields = z.strictObject({
1769
1845
  message: `\`${name}\` conditions on itself. \`when\` names a sibling prop and one of its values.`
1770
1846
  });
1771
1847
  }
1848
+ const conditions = storedDefaultConditions(body);
1849
+ const defaulted = new Set(Object.values(conditions).flatMap(({ props }) => Object.keys(props ?? {})));
1850
+ const defaultable = new Set(conditionallyDefaultable(body.props).map(([name]) => name));
1851
+ for (const [key, { when, props }] of Object.entries(conditions)) {
1852
+ for (const [field, names] of [["when", Object.keys(when ?? {})], ["props", Object.keys(props ?? {})]]) for (const name of names) {
1853
+ if (defaultable.has(name)) continue;
1854
+ ctx.issues.push({
1855
+ code: "custom",
1856
+ input: field === "when" ? when : props,
1857
+ path: [
1858
+ "defaultPropsWhen",
1859
+ key,
1860
+ field,
1861
+ name
1862
+ ],
1863
+ message: `\`${name}\` isn't one of this component's own variant, string, number or boolean props, so a conditional default can't name it. ${defaultConditionShapeError}`
1864
+ });
1865
+ }
1866
+ for (const name of Object.keys(when ?? {})) {
1867
+ if (!defaulted.has(name)) continue;
1868
+ ctx.issues.push({
1869
+ code: "custom",
1870
+ input: when,
1871
+ path: [
1872
+ "defaultPropsWhen",
1873
+ key,
1874
+ "when",
1875
+ name
1876
+ ],
1877
+ message: `\`${name}\` has a conditional default of its own, so it can't be part of a condition. Condition on the props it depends on instead.`
1878
+ });
1879
+ }
1880
+ }
1772
1881
  if (isPlainObject(body.states) && Object.keys(body.states).length > 0) {
1773
1882
  const props = isPlainObject(body.props) ? body.props : {};
1774
1883
  if (Object.hasOwn(props, "states")) ctx.issues.push({
@@ -1905,7 +2014,8 @@ const ComponentEntity = defineEntity({
1905
2014
  styles: StyleRule,
1906
2015
  preview: Preview,
1907
2016
  anatomy: AnatomyNode,
1908
- defaultProps: Style
2017
+ defaultProps: Style,
2018
+ defaultPropsWhen: DefaultCondition
1909
2019
  },
1910
2020
  computed: {
1911
2021
  /**
@@ -2846,7 +2956,10 @@ function componentRef(path) {
2846
2956
  */
2847
2957
  const step = (delta) => {
2848
2958
  assertComponentSourceInput(delta);
2849
- const extended = base.extend(delta);
2959
+ const extended = base.extend({
2960
+ ...delta,
2961
+ ...storedDefaults(delta)
2962
+ });
2850
2963
  Object.defineProperty(extended, "render", {
2851
2964
  enumerable: false,
2852
2965
  configurable: false,
@@ -1,6 +1,6 @@
1
- import { rejectData } from "../../framework/rejection.js";
2
- import { isRef, kindOf, pathOf, refSchema } from "../../framework/utils/refs.js";
3
1
  import { isPlainObject } from "../../framework/utils/field-path.js";
2
+ import { isRef, kindOf, pathOf, refSchema } from "../../framework/utils/refs.js";
3
+ import { rejectData } from "../../framework/rejection.js";
4
4
  import { pathInput } from "../../framework/schemas.js";
5
5
  import { kindBorrowable } from "../../framework/registry.js";
6
6
  import { defineEntity } from "../../framework/defineEntity.js";
@@ -1,5 +1,5 @@
1
- import { CONFIG_OPERATION_KIND } from "../../framework/config-operation-kind.js";
2
1
  import { isPlainObject } from "../../framework/utils/field-path.js";
2
+ import { CONFIG_OPERATION_KIND } from "../../framework/config-operation-kind.js";
3
3
  import { resolveInputSchema } from "../../framework/schemas.js";
4
4
  import { opsForKind } from "../../framework/registry.js";
5
5
  import { opOf } from "../../framework/config-op.js";
@@ -158,6 +158,10 @@ const STYLE_PROP_VALUE_FORMS = {
158
158
  })
159
159
  };
160
160
  const KINDS = Object.keys(STYLE_PROP_VALUE_FORMS);
161
+ const CONTEXT_FREE_VALUE_FORMS = KINDS.map((kind) => ({
162
+ kind,
163
+ schema: STYLE_PROP_VALUE_FORMS[kind]()
164
+ }));
161
165
  /** `{ numeric: true }` for an alias declared as a number. */
162
166
  function numericOf(alias) {
163
167
  return typeof alias === "number" ? { numeric: true } : {};
@@ -262,6 +266,7 @@ function stylePropValueSchema(ctx = {}) {
262
266
  /** The form a stored entry is, or `undefined` for a value no form admits. With a config, a ref is
263
267
  * `token` or `tokenGroup` as the config says; without one it reads as `token`. */
264
268
  function stylePropValueFormOf(entry, ctx = {}) {
269
+ if (ctx.config === void 0 && ctx.properties === void 0) return CONTEXT_FREE_VALUE_FORMS.find(({ schema }) => schema.safeParse(entry).success)?.kind;
265
270
  return KINDS.find((kind) => STYLE_PROP_VALUE_FORMS[kind](ctx).safeParse(entry).success);
266
271
  }
267
272
  /** Which form a stored entry is, asked of the config that holds it. */
@@ -1,5 +1,5 @@
1
- import { reject } from "../../framework/rejection.js";
2
1
  import { isRef, kindOf, memberRef, pathOf } from "../../framework/utils/refs.js";
2
+ import { reject } from "../../framework/rejection.js";
3
3
  import { forwardedPropName } from "./element.js";
4
4
  import { propOwner, resolveComponentProps } from "../../framework/projections.js";
5
5
  import { forwardClaims } from "../../framework/prop-surface.js";
@@ -1,5 +1,5 @@
1
- import { rejectData } from "../../framework/rejection.js";
2
1
  import { isPlainObject } from "../../framework/utils/field-path.js";
2
+ import { rejectData } from "../../framework/rejection.js";
3
3
  //#region src/entities/system/component-source-fields.ts
4
4
  /** Source constructors leave these facts to the render extractor and module build.
5
5
  * Stored component bodies, including Create drafts, still carry them. */
@@ -1,5 +1,5 @@
1
- import { reject } from "../../framework/rejection.js";
2
1
  import { canonicalCondition } from "../../framework/utils/refs.js";
2
+ import { reject } from "../../framework/rejection.js";
3
3
  //#region src/entities/system/component-style-set.ts
4
4
  /** A modifier as a style bag keys it: its leaf with a leading `_`. */
5
5
  function bagKey(modifier) {
@@ -0,0 +1,45 @@
1
+ import { StoredDefaultCondition } from "../../framework/default-conditions.js";
2
+
3
+ //#region src/entities/system/conditional-defaults.d.ts
4
+ declare const CONDITIONAL_DEFAULT: unique symbol;
5
+ /** One condition of a {@link ConditionalDefault}: the value the prop defaults to while `when` holds. */
6
+ interface ConditionalDefaultEntry<V = unknown> {
7
+ readonly when: Readonly<Record<string, unknown>>;
8
+ readonly value: V;
9
+ }
10
+ /** A default chosen by the component's other props — see {@link conditionalDefault}. */
11
+ interface ConditionalDefault<V = unknown> {
12
+ readonly [CONDITIONAL_DEFAULT]: true;
13
+ readonly fallback: V;
14
+ readonly entries: readonly ConditionalDefaultEntry<V>[];
15
+ }
16
+ /**
17
+ * A default that depends on the component's other props, for `.defaultProps` and
18
+ * `.extend({ defaultProps })` only.
19
+ *
20
+ * ```ts
21
+ * .defaultProps({
22
+ * modal: true,
23
+ * includeScrim: conditionalDefault(true, [{ when: { modal: false }, value: false }]),
24
+ * })
25
+ * ```
26
+ *
27
+ * `fallback` applies when no entry's `when` holds; when several hold, the later entry wins. A `when`
28
+ * is the grammar a style rule uses, over the component's own props. A value the call site passes
29
+ * still wins over any of it.
30
+ */
31
+ declare function conditionalDefault<const V>(fallback: V, entries: readonly ConditionalDefaultEntry<V>[]): ConditionalDefault<V>;
32
+ declare function isConditionalDefault(value: unknown): value is ConditionalDefault;
33
+ /**
34
+ * Stored defaults as authored: the inverse of {@link splitConditionalDefaults}, each prop
35
+ * `defaultPropsWhen` names given back as a {@link conditionalDefault} over its fallback.
36
+ */
37
+ declare function joinConditionalDefaults({
38
+ defaultProps,
39
+ defaultPropsWhen
40
+ }: {
41
+ defaultProps: Readonly<Record<string, unknown>> | undefined;
42
+ defaultPropsWhen: Readonly<Record<string, StoredDefaultCondition>> | undefined;
43
+ }): Record<string, unknown>;
44
+ //#endregion
45
+ export { ConditionalDefault, ConditionalDefaultEntry, conditionalDefault, isConditionalDefault, joinConditionalDefaults };
@@ -0,0 +1,142 @@
1
+ import { isPlainObject } from "../../framework/utils/field-path.js";
2
+ import { canonicalCondition } from "../../framework/utils/refs.js";
3
+ //#region src/entities/system/conditional-defaults.ts
4
+ const CONDITIONAL_DEFAULT = Symbol.for("uds.conditionalDefault");
5
+ /**
6
+ * A default that depends on the component's other props, for `.defaultProps` and
7
+ * `.extend({ defaultProps })` only.
8
+ *
9
+ * ```ts
10
+ * .defaultProps({
11
+ * modal: true,
12
+ * includeScrim: conditionalDefault(true, [{ when: { modal: false }, value: false }]),
13
+ * })
14
+ * ```
15
+ *
16
+ * `fallback` applies when no entry's `when` holds; when several hold, the later entry wins. A `when`
17
+ * is the grammar a style rule uses, over the component's own props. A value the call site passes
18
+ * still wins over any of it.
19
+ */
20
+ function conditionalDefault(fallback, entries) {
21
+ return {
22
+ [CONDITIONAL_DEFAULT]: true,
23
+ fallback,
24
+ entries
25
+ };
26
+ }
27
+ function isConditionalDefault(value) {
28
+ return typeof value === "object" && value !== null && value[CONDITIONAL_DEFAULT] === true;
29
+ }
30
+ /**
31
+ * Authored defaults as stored: each {@link conditionalDefault} leaves its fallback in `defaultProps`
32
+ * and its entries in `defaultPropsWhen`. `defaultPropsWhen` is `undefined` when nothing was
33
+ * conditional, so a body without one stores exactly what it did before.
34
+ *
35
+ * Stored by condition, every prop shares one order of conditions, while precedence is per prop. The
36
+ * order taken keeps each prop's own; two props that order the same two conditions oppositely can't
37
+ * share one, and are refused.
38
+ */
39
+ function splitConditionalDefaults(authored) {
40
+ const defaultProps = {};
41
+ const perProp = /* @__PURE__ */ new Map();
42
+ for (const [prop, value] of Object.entries(authored)) {
43
+ if (!isConditionalDefault(value)) {
44
+ defaultProps[prop] = value;
45
+ continue;
46
+ }
47
+ if (value.fallback !== void 0) defaultProps[prop] = value.fallback;
48
+ const entries = /* @__PURE__ */ new Map();
49
+ for (const entry of value.entries) {
50
+ const key = canonicalCondition(entry.when);
51
+ entries.delete(key);
52
+ entries.set(key, entry);
53
+ }
54
+ perProp.set(prop, entries);
55
+ }
56
+ if (perProp.size === 0) return { defaultProps };
57
+ const order = conditionOrder(perProp);
58
+ const defaultPropsWhen = {};
59
+ for (const key of order) {
60
+ let when;
61
+ const props = {};
62
+ for (const [prop, entries] of perProp) {
63
+ const entry = entries.get(key);
64
+ if (!entry) continue;
65
+ when ??= { ...entry.when };
66
+ props[prop] = entry.value;
67
+ }
68
+ if (when) defaultPropsWhen[key] = {
69
+ when,
70
+ props
71
+ };
72
+ }
73
+ return {
74
+ defaultProps,
75
+ defaultPropsWhen
76
+ };
77
+ }
78
+ /**
79
+ * `defaultProps` as stored: each `conditionalDefault` splits into its fallback, which stays in
80
+ * `defaultProps`, and its conditions, which go to `defaultPropsWhen`. Empty for a body whose
81
+ * defaults are all plain, so it stores what it did before.
82
+ */
83
+ function storedDefaults(body) {
84
+ if (!isPlainObject(body.defaultProps)) return {};
85
+ const { defaultProps, defaultPropsWhen } = splitConditionalDefaults(body.defaultProps);
86
+ return defaultPropsWhen ? {
87
+ defaultProps,
88
+ defaultPropsWhen
89
+ } : {};
90
+ }
91
+ /**
92
+ * One order of every condition that keeps each prop's own order, by first appearance where props
93
+ * don't constrain it.
94
+ */
95
+ function conditionOrder(perProp) {
96
+ const seen = [];
97
+ const after = /* @__PURE__ */ new Map();
98
+ for (const [prop, entries] of perProp) {
99
+ const keys = [...entries.keys()];
100
+ keys.forEach((key, at) => {
101
+ if (!seen.includes(key)) seen.push(key);
102
+ const earlier = after.get(key) ?? /* @__PURE__ */ new Map();
103
+ for (const before of keys.slice(0, at)) earlier.set(before, prop);
104
+ after.set(key, earlier);
105
+ });
106
+ }
107
+ for (const [key, earlier] of after) for (const [before, prop] of earlier) {
108
+ const other = after.get(before)?.get(key);
109
+ if (other === void 0) continue;
110
+ throw new Error(`\`${prop}\` puts \`${before}\` before \`${key}\` and \`${other}\` puts them the other way round. Conditional defaults share one order of conditions, so give both props the same order.`);
111
+ }
112
+ const order = [];
113
+ const placed = /* @__PURE__ */ new Set();
114
+ const placing = /* @__PURE__ */ new Set();
115
+ const place = (key) => {
116
+ if (placed.has(key)) return;
117
+ if (placing.has(key)) throw new Error(`Conditional defaults order \`${key}\` both before and after another condition across their props. They share one order of conditions, so give every prop the same order.`);
118
+ placing.add(key);
119
+ for (const before of after.get(key)?.keys() ?? []) place(before);
120
+ placing.delete(key);
121
+ placed.add(key);
122
+ order.push(key);
123
+ };
124
+ for (const key of seen) place(key);
125
+ return order;
126
+ }
127
+ /**
128
+ * Stored defaults as authored: the inverse of {@link splitConditionalDefaults}, each prop
129
+ * `defaultPropsWhen` names given back as a {@link conditionalDefault} over its fallback.
130
+ */
131
+ function joinConditionalDefaults({ defaultProps, defaultPropsWhen }) {
132
+ const out = { ...defaultProps };
133
+ const entries = /* @__PURE__ */ new Map();
134
+ for (const { when, props } of Object.values(defaultPropsWhen ?? {})) for (const [prop, value] of Object.entries(props ?? {})) entries.set(prop, [...entries.get(prop) ?? [], {
135
+ when,
136
+ value
137
+ }]);
138
+ for (const [prop, list] of entries) out[prop] = conditionalDefault(out[prop], list);
139
+ return out;
140
+ }
141
+ //#endregion
142
+ export { conditionalDefault, isConditionalDefault, joinConditionalDefaults, storedDefaults };
@@ -1,5 +1,5 @@
1
- import { rejectData } from "../../framework/rejection.js";
2
1
  import { MINTED, collectRefs, isMintedRow, kindOf, pathOf, rewriteRefs, sourceOf, splitRef } from "../../framework/utils/refs.js";
2
+ import { rejectData } from "../../framework/rejection.js";
3
3
  import { kindBorrowable } from "../../framework/registry.js";
4
4
  import { File } from "./File.js";
5
5
  import { componentModuleFilePaths, componentRegistryImports } from "./component-module.js";
@@ -1,5 +1,5 @@
1
- import { isRef, kindOf, pathOf } from "../../framework/utils/refs.js";
2
1
  import { isPlainObject } from "../../framework/utils/field-path.js";
2
+ import { isRef, kindOf, pathOf } from "../../framework/utils/refs.js";
3
3
  import { createSliceMemo } from "../../framework/memo.js";
4
4
  import { drawnMotionBody, drawnMotionName } from "./drawn-motion.js";
5
5
  import { convertMotion } from "./motion-convert.js";
@@ -3,6 +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 { ConditionalDefault } from "./conditional-defaults.js";
6
7
  import { VariantValues } from "./Component.js";
7
8
  import { AllHTMLAttributes, ComponentPropsWithRef, ComponentType, ElementType, JSX, ReactElement, ReactNode, Ref } from "react";
8
9
 
@@ -663,6 +664,11 @@ type ForwardedPropsOf<L extends LayerMap, F extends string> = [NonRootForward<L,
663
664
  type DefaultsOf<P extends PropMap> = Partial<DeclaredProps<P>> & {
664
665
  [key: string]: unknown;
665
666
  };
667
+ /** {@link DefaultsOf} as `.defaultProps` takes it: a declared prop other than `as` may also default
668
+ * through {@link conditionalDefault}. */
669
+ type AuthoredDefaultsOf<P extends PropMap> = { [K in keyof DeclaredProps<P>]?: K extends 'as' ? DeclaredProps<P>[K] : DeclaredProps<P>[K] | ConditionalDefault<DeclaredProps<P>[K]> } & {
670
+ [key: string]: unknown;
671
+ };
666
672
  /**
667
673
  * `T` without the keys in `K` — `Omit` that leaves an index signature alone.
668
674
  *
@@ -778,8 +784,11 @@ F extends string = never, /** The states `.states()` declared — what the rende
778
784
  *
779
785
  * `const`, so an `as` keeps its literal type: without it `{ as: 'input' }` widens to `string` and
780
786
  * there's no tag left to resolve the element's props against.
787
+ *
788
+ * A default that depends on another prop is a `conditionalDefault`:
789
+ * `includeScrim: conditionalDefault(true, [{ when: { modal: false }, value: false }])`.
781
790
  */
782
- defaultProps<const D2 extends DefaultsOf<P>>(defaults: D2): ComponentBuilder<L, P, E, D2, F, S>;
791
+ defaultProps<const D2 extends AuthoredDefaultsOf<P>>(defaults: D2): ComponentBuilder<L, P, E, D2, F, S>;
783
792
  /**
784
793
  * The layers whose element props this component ALSO exposes, keyed by layer.
785
794
  *
@@ -892,4 +901,4 @@ interface DefineComponent {
892
901
  declare function definePropsFirst<const P extends PropMap<LayerPath>>(props: P & NoInfer<{ [K in keyof P]: Exact<P[K], AuthoredProp<LayerPath>> }>): ComponentBuilder<LayerMap, P>;
893
902
  declare const defineComponent: DefineComponent;
894
903
  //#endregion
895
- export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, DefaultsOf, GlyphComponent, GlyphValue, LayerElement, LayerMap, LinkedHandle, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, SlotCodeForm, SlotDecl, StateMap, StyleRuleInput, defineComponent };
904
+ export { AsInput, AuthoredComponent, AuthoredDefaultsOf, AuthoredProp, ComponentBuilder, ComponentPropsOf, GlyphComponent, GlyphValue, LayerElement, LayerMap, LinkedHandle, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, SlotCodeForm, SlotDecl, StateMap, StyleRuleInput, defineComponent };
@@ -2,6 +2,7 @@ import { canonicalCondition, isRef, kindOf, memberRef, pathOf } from "../../fram
2
2
  import { glyphIdentityOf, glyphPackageRefOf, isComponentType } from "./glyph-identity.js";
3
3
  import "../../framework/preview-store.js";
4
4
  import { assertComponentSourceInput } from "./component-source-fields.js";
5
+ import { isConditionalDefault, storedDefaults } from "./conditional-defaults.js";
5
6
  import { Component, layerRef, storedStyleRules } from "./Component.js";
6
7
  //#region src/entities/system/defineComponent.ts
7
8
  function build(body) {
@@ -145,6 +146,7 @@ function storableElement(value) {
145
146
  */
146
147
  function storableValue(value) {
147
148
  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 }] })`.");
148
150
  const type = typeof value;
149
151
  if (type === "string" || type === "number" || type === "boolean") return value;
150
152
  if (isComponentType(value)) return storableComponentRef(value);
@@ -388,6 +390,7 @@ function toStored(body) {
388
390
  if (Object.keys(keyed).length === 0) delete stored.preview;
389
391
  else stored.preview = keyed;
390
392
  }
393
+ Object.assign(stored, storedDefaults(body));
391
394
  if (body.props && typeof body.props === "object") {
392
395
  const props = {};
393
396
  for (const [name, decl] of Object.entries(body.props)) props[name] = storedProp(decl, body.layers);
@@ -22,7 +22,8 @@ declare function drawnMotionName(config: Config, value: unknown): string | undef
22
22
  declare function drawnMotionBody(config: Config, name: string): Record<string, unknown> | undefined;
23
23
  /**
24
24
  * Every linked preset `config` plays under its pin's name: each preset a reached source holds that
25
- * this config's refs reach, unless the link draws it with a row of this config's own.
25
+ * this config's refs reach, unless the link draws it with a row of this config's own, and the preset
26
+ * of a nearer link's that a routed pin draws with in its place ({@link linkedDraw}).
26
27
  */
27
28
  declare function linkedMotionPresets(config: Config): readonly LinkedMotionPreset[];
28
29
  //#endregion