@yahoo/uds-create-config 5.25.1 → 5.26.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 (46) hide show
  1. package/dist/configs/system.js +2 -0
  2. package/dist/entities/native/NativeToken.js +4 -1
  3. package/dist/entities/system/Component.d.ts +35 -2
  4. package/dist/entities/system/Component.js +224 -9
  5. package/dist/entities/system/Settings.js +1 -1
  6. package/dist/entities/system/StyleProperty.js +1 -1
  7. package/dist/entities/system/component-module.d.ts +17 -1
  8. package/dist/entities/system/component-module.js +54 -2
  9. package/dist/entities/system/defineComponent.d.ts +53 -13
  10. package/dist/entities/system/defineComponent.js +1 -0
  11. package/dist/entities/system/forward-route-invariants.js +1 -1
  12. package/dist/entities/system/icon-domain-invariants.js +1 -1
  13. package/dist/entities/system/linked-token-values.js +9 -2
  14. package/dist/entities/system/root-layer-invariants.js +1 -1
  15. package/dist/entities/system/token-values.js +1 -1
  16. package/dist/framework/Config.d.ts +1 -1
  17. package/dist/framework/Config.js +1 -1
  18. package/dist/framework/class-names.d.ts +27 -1
  19. package/dist/framework/class-names.js +56 -3
  20. package/dist/framework/component-states.d.ts +40 -0
  21. package/dist/framework/component-states.js +53 -0
  22. package/dist/framework/interaction-conditions.d.ts +48 -0
  23. package/dist/framework/interaction-conditions.js +104 -0
  24. package/dist/framework/layer-styles.d.ts +5 -1
  25. package/dist/framework/layer-styles.js +18 -4
  26. package/dist/framework/projections.js +44 -2
  27. package/dist/framework/render-spec.js +14 -4
  28. package/dist/framework/schema-version.d.ts +1 -1
  29. package/dist/framework/schema-version.js +5 -5
  30. package/dist/framework/validate-spec.js +14 -0
  31. package/dist/index.d.ts +5 -3
  32. package/dist/index.js +6 -4
  33. package/dist/migrations/20261013154037_component_states.d.ts +19 -0
  34. package/dist/migrations/20261013154037_component_states.js +74 -0
  35. package/dist/renderer/componentEntry.d.ts +7 -1
  36. package/dist/renderer/componentEntry.js +30 -1
  37. package/dist/renderer/forced-stamp.d.ts +26 -0
  38. package/dist/renderer/forced-stamp.js +50 -0
  39. package/dist/renderer/index.d.ts +3 -1
  40. package/dist/renderer/index.js +3 -1
  41. package/dist/renderer/use-forced-stamp.d.ts +11 -0
  42. package/dist/renderer/use-forced-stamp.js +50 -0
  43. package/dist/spec/specToJsx.d.ts +6 -0
  44. package/dist/spec/specToJsx.js +5 -0
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +2 -2
@@ -1,6 +1,9 @@
1
+ import { UDS_FORCE_STATES } from "../framework/component-states.js";
2
+ import { forcedModifierAttributes } from "../framework/class-names.js";
1
3
  import { registerComputed } from "./computed.js";
2
4
  import { RendererErrorBoundary } from "./RendererErrorBoundary.js";
3
5
  import { specContent } from "./spec-content.js";
6
+ import { useForcedModifierStamp } from "./use-forced-stamp.js";
4
7
  import React from "react";
5
8
  //#region src/renderer/componentEntry.tsx
6
9
  /**
@@ -33,11 +36,12 @@ function definedOnly(options) {
33
36
  * data, so it never imports this renderer: whoever assembles a registry makes the entry.
34
37
  */
35
38
  function makeComponentEntry(options) {
36
- const { component: Comp, childless, name, computed } = {
39
+ const { component: Comp, childless, name, computed, states } = {
37
40
  ...options.component.__udsEntry,
38
41
  ...definedOnly(options)
39
42
  };
40
43
  const label = name ?? Comp.displayName ?? Comp.name ?? "component";
44
+ const componentPath = name?.startsWith("component:") ? name.slice(10) : label;
41
45
  if (computed) registerComputed(computed);
42
46
  return function ComponentEntry({ props: elementProps, children, slots }) {
43
47
  let props = elementProps ?? {};
@@ -51,6 +55,18 @@ function makeComponentEntry(options) {
51
55
  props = filled;
52
56
  }
53
57
  const { children: propChildren, ...rest } = props;
58
+ if (states?.some((state) => state in rest)) {
59
+ const forced = { ...rest["__udsForceStates"] ?? {} };
60
+ for (const state of states) {
61
+ if (!(state in rest)) continue;
62
+ forced[state] = rest[state];
63
+ delete rest[state];
64
+ }
65
+ rest[UDS_FORCE_STATES] = forced;
66
+ }
67
+ let forcedAttrs = {};
68
+ const pins = rest[UDS_FORCE_STATES];
69
+ if (pins && typeof pins === "object") forcedAttrs = forcedModifierAttributes(pins);
54
70
  let rendered;
55
71
  if (childless) rendered = React.createElement(Comp, rest);
56
72
  else {
@@ -60,6 +76,10 @@ function makeComponentEntry(options) {
60
76
  });
61
77
  rendered = finalChildren !== void 0 && finalChildren !== null ? React.createElement(Comp, rest, finalChildren) : React.createElement(Comp, rest);
62
78
  }
79
+ if (Object.keys(forcedAttrs).length > 0) rendered = React.createElement(ForcedHost, {
80
+ component: componentPath,
81
+ attributes: forcedAttrs
82
+ }, rendered);
63
83
  return React.createElement(RendererErrorBoundary, {
64
84
  name: label,
65
85
  resetKey: elementProps
@@ -84,5 +104,14 @@ function entryRenderer(entry) {
84
104
  });
85
105
  };
86
106
  }
107
+ /** A layout-neutral wrapper that marks the forced component's own elements under it. */
108
+ function ForcedHost({ component, attributes, children }) {
109
+ const host = React.useRef(null);
110
+ useForcedModifierStamp(host, component, attributes);
111
+ return React.createElement("div", {
112
+ ref: host,
113
+ style: { display: "contents" }
114
+ }, children);
115
+ }
87
116
  //#endregion
88
117
  export { entryRenderer, makeComponentEntry, slotFillValue };
@@ -0,0 +1,26 @@
1
+ //#region src/renderer/forced-stamp.d.ts
2
+ /**
3
+ * Mark a forced component's own elements with its force attributes, inside `container`.
4
+ *
5
+ * The design sheet's force twin matches an element that carries the attribute itself, so a state is
6
+ * forced exactly where this puts it: the component's root, and its other elements tagged with its
7
+ * own `data-uds-component` — except the root of a component nested in it. That root is tagged with
8
+ * the parent's markers (the props it was given land over its own), but it is the nearest tagged
9
+ * ancestor of the nested component's own non-root parts, which is how it's told apart. A nested
10
+ * IconButton then keeps its own hover and focus ring, as the browser would. Returns the undo.
11
+ *
12
+ * Each attribute's value lists the components forced on the element, and this adds `component` to
13
+ * it: the twin matches only its own component's name (`forceStateSelector`), so a component
14
+ * whose root is another's instance — one element for both — doesn't light the inner one's state.
15
+ */
16
+ declare function stampForcedModifiers({
17
+ container,
18
+ component,
19
+ attributes
20
+ }: {
21
+ container: Element;
22
+ component: string;
23
+ attributes: Readonly<Record<string, string>>;
24
+ }): () => void;
25
+ //#endregion
26
+ export { stampForcedModifiers };
@@ -0,0 +1,50 @@
1
+ //#region src/renderer/forced-stamp.ts
2
+ /**
3
+ * Mark a forced component's own elements with its force attributes, inside `container`.
4
+ *
5
+ * The design sheet's force twin matches an element that carries the attribute itself, so a state is
6
+ * forced exactly where this puts it: the component's root, and its other elements tagged with its
7
+ * own `data-uds-component` — except the root of a component nested in it. That root is tagged with
8
+ * the parent's markers (the props it was given land over its own), but it is the nearest tagged
9
+ * ancestor of the nested component's own non-root parts, which is how it's told apart. A nested
10
+ * IconButton then keeps its own hover and focus ring, as the browser would. Returns the undo.
11
+ *
12
+ * Each attribute's value lists the components forced on the element, and this adds `component` to
13
+ * it: the twin matches only its own component's name (`forceStateSelector`), so a component
14
+ * whose root is another's instance — one element for both — doesn't light the inner one's state.
15
+ */
16
+ function stampForcedModifiers({ container, component, attributes }) {
17
+ const names = Object.keys(attributes);
18
+ if (names.length === 0) return () => {};
19
+ const tagged = [...container.querySelectorAll("[data-uds-component]")];
20
+ const nestedRoots = /* @__PURE__ */ new Set();
21
+ for (const element of tagged) {
22
+ if (element.getAttribute("data-uds-component") === component) continue;
23
+ if (element.getAttribute("data-uds-layer") === "root") continue;
24
+ const owner = element.parentElement?.closest("[data-uds-component]");
25
+ if (owner && owner.getAttribute("data-uds-component") === component) nestedRoots.add(owner);
26
+ }
27
+ const stamped = [];
28
+ for (const element of tagged) {
29
+ if (element.getAttribute("data-uds-component") !== component) continue;
30
+ if (!(element.getAttribute("data-uds-layer") === "root") && nestedRoots.has(element)) continue;
31
+ for (const name of names) {
32
+ const owners = ownersOf(element, name);
33
+ if (!owners.includes(component)) element.setAttribute(name, [...owners, component].join(" "));
34
+ }
35
+ stamped.push(element);
36
+ }
37
+ return () => {
38
+ for (const element of stamped) for (const name of names) {
39
+ const owners = ownersOf(element, name).filter((owner) => owner !== component);
40
+ if (owners.length > 0) element.setAttribute(name, owners.join(" "));
41
+ else element.removeAttribute(name);
42
+ }
43
+ };
44
+ }
45
+ /** The components a force attribute already names on an element. */
46
+ function ownersOf(element, name) {
47
+ return (element.getAttribute(name) ?? "").split(/\s+/).filter(Boolean);
48
+ }
49
+ //#endregion
50
+ export { stampForcedModifiers };
@@ -1,5 +1,6 @@
1
1
  import { ComponentEntry, ComponentEntryFacts, MakeComponentEntryOptions, entryRenderer, makeComponentEntry, slotFillValue } from "./componentEntry.js";
2
2
  import { computedFunctions, registerComputed, withComputedFunctions } from "./computed.js";
3
+ import { stampForcedModifiers } from "./forced-stamp.js";
3
4
  import { GlyphEntriesResult, GlyphMembers, glyphRegistryEntries } from "./glyphEntries.js";
4
5
  import { FragmentRenderer } from "./primitives/FragmentRenderer.js";
5
6
  import { SlotRenderer } from "./primitives/SlotRenderer.js";
@@ -11,7 +12,8 @@ import { GlyphPropMap, wrapGlyphProps } from "./wrappers/glyph-props.js";
11
12
  import { VERBATIM_ATTRIBUTES, aliasHtmlTypes } from "./wrappers/html-aliases.js";
12
13
  import { RegistryDescriptor, wrapRegistry } from "./wrapRegistry.js";
13
14
  import { UdsRenderer, UdsRendererProps } from "./UdsRenderer.js";
15
+ import { useForcedModifierStamp } from "./use-forced-stamp.js";
14
16
  import { wrapEventBridge } from "./wrappers/event-bridge.js";
15
17
  import { wrapNormalizeHex } from "./wrappers/hex-normalize.js";
16
18
  import { wrapInlineStyleProps } from "./wrappers/inline-styles.js";
17
- export { type ComponentEntry, type ComponentEntryFacts, FragmentRenderer, type Glyph, type GlyphEntriesResult, type GlyphLibrary, type GlyphMembers, type GlyphPropMap, type MakeComponentEntryOptions, REVEALED_LAYER_ATTR, type RegistryDescriptor, RendererErrorBoundary, type ResolvedGlyph, RuntimeComponentFailureContext, type RuntimeComponentFailureReporter, RuntimeComponentRebuildContext, type RuntimeComponentRebuildReporter, type RuntimeComponentScope, type RuntimeComponentStatus, type RuntimeComponentStore, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, type UdsRendererProps, VERBATIM_ATTRIBUTES, aliasHtmlTypes, computedFunctions, entryRenderer, glyphRegistryEntries, installRuntimeComponentStore, makeComponentEntry, registerComputed, resolveGlyph, runtimeComponent, slotFillValue, specContent, withComputedFunctions, wrapEventBridge, wrapGlyphProps, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
19
+ export { type ComponentEntry, type ComponentEntryFacts, FragmentRenderer, type Glyph, type GlyphEntriesResult, type GlyphLibrary, type GlyphMembers, type GlyphPropMap, type MakeComponentEntryOptions, REVEALED_LAYER_ATTR, type RegistryDescriptor, RendererErrorBoundary, type ResolvedGlyph, RuntimeComponentFailureContext, type RuntimeComponentFailureReporter, RuntimeComponentRebuildContext, type RuntimeComponentRebuildReporter, type RuntimeComponentScope, type RuntimeComponentStatus, type RuntimeComponentStore, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, type UdsRendererProps, VERBATIM_ATTRIBUTES, aliasHtmlTypes, computedFunctions, entryRenderer, glyphRegistryEntries, installRuntimeComponentStore, makeComponentEntry, registerComputed, resolveGlyph, runtimeComponent, slotFillValue, specContent, stampForcedModifiers, useForcedModifierStamp, withComputedFunctions, wrapEventBridge, wrapGlyphProps, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
@@ -1,6 +1,8 @@
1
1
  import { computedFunctions, registerComputed, withComputedFunctions } from "./computed.js";
2
2
  import { RendererErrorBoundary } from "./RendererErrorBoundary.js";
3
3
  import { REVEALED_LAYER_ATTR, specContent } from "./spec-content.js";
4
+ import { stampForcedModifiers } from "./forced-stamp.js";
5
+ import { useForcedModifierStamp } from "./use-forced-stamp.js";
4
6
  import { entryRenderer, makeComponentEntry, slotFillValue } from "./componentEntry.js";
5
7
  import { resolveGlyph } from "./resolveGlyph.js";
6
8
  import { glyphRegistryEntries } from "./glyphEntries.js";
@@ -14,4 +16,4 @@ import { VERBATIM_ATTRIBUTES, aliasHtmlTypes } from "./wrappers/html-aliases.js"
14
16
  import { wrapInlineStyleProps } from "./wrappers/inline-styles.js";
15
17
  import { wrapRegistry } from "./wrapRegistry.js";
16
18
  import { UdsRenderer } from "./UdsRenderer.js";
17
- export { FragmentRenderer, REVEALED_LAYER_ATTR, RendererErrorBoundary, RuntimeComponentFailureContext, RuntimeComponentRebuildContext, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, VERBATIM_ATTRIBUTES, aliasHtmlTypes, computedFunctions, entryRenderer, glyphRegistryEntries, installRuntimeComponentStore, makeComponentEntry, registerComputed, resolveGlyph, runtimeComponent, slotFillValue, specContent, withComputedFunctions, wrapEventBridge, wrapGlyphProps, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
19
+ export { FragmentRenderer, REVEALED_LAYER_ATTR, RendererErrorBoundary, RuntimeComponentFailureContext, RuntimeComponentRebuildContext, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, VERBATIM_ATTRIBUTES, aliasHtmlTypes, computedFunctions, entryRenderer, glyphRegistryEntries, installRuntimeComponentStore, makeComponentEntry, registerComputed, resolveGlyph, runtimeComponent, slotFillValue, specContent, stampForcedModifiers, useForcedModifierStamp, withComputedFunctions, wrapEventBridge, wrapGlyphProps, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
@@ -0,0 +1,11 @@
1
+ import { RefObject } from "react";
2
+
3
+ //#region src/renderer/use-forced-stamp.d.ts
4
+ /**
5
+ * Keep a forced component's own elements under `ref` marked with `attributes` (see
6
+ * {@link stampForcedModifiers}) as its DOM renders and re-renders: marked after every commit, and
7
+ * again when the subtree changes underneath (a lazy layer mounting, a module swapping in).
8
+ */
9
+ declare function useForcedModifierStamp(ref: RefObject<Element | null>, component: string, attributes: Readonly<Record<string, string>>): void;
10
+ //#endregion
11
+ export { useForcedModifierStamp };
@@ -0,0 +1,50 @@
1
+ import { stampForcedModifiers } from "./forced-stamp.js";
2
+ import { useLayoutEffect, useRef } from "react";
3
+ //#region src/renderer/use-forced-stamp.ts
4
+ /**
5
+ * Keep a forced component's own elements under `ref` marked with `attributes` (see
6
+ * {@link stampForcedModifiers}) as its DOM renders and re-renders: marked after every commit, and
7
+ * again when the subtree changes underneath (a lazy layer mounting, a module swapping in).
8
+ */
9
+ function useForcedModifierStamp(ref, component, attributes) {
10
+ const key = Object.keys(attributes).sort().join("|");
11
+ const latest = useRef(attributes);
12
+ latest.current = attributes;
13
+ useLayoutEffect(() => {
14
+ const container = ref.current;
15
+ if (!container || key === "") return;
16
+ const attributes = latest.current;
17
+ let undo = stampForcedModifiers({
18
+ container,
19
+ component,
20
+ attributes
21
+ });
22
+ const observer = new MutationObserver(() => {
23
+ observer.disconnect();
24
+ undo();
25
+ undo = stampForcedModifiers({
26
+ container,
27
+ component,
28
+ attributes
29
+ });
30
+ observer.observe(container, {
31
+ childList: true,
32
+ subtree: true
33
+ });
34
+ });
35
+ observer.observe(container, {
36
+ childList: true,
37
+ subtree: true
38
+ });
39
+ return () => {
40
+ observer.disconnect();
41
+ undo();
42
+ };
43
+ }, [
44
+ ref,
45
+ component,
46
+ key
47
+ ]);
48
+ }
49
+ //#endregion
50
+ export { useForcedModifierStamp };
@@ -32,6 +32,12 @@ interface SpecToJsxOptions {
32
32
  * package that exports it.
33
33
  */
34
34
  glyphMembers?: Readonly<Record<string, readonly string[]>>;
35
+ /**
36
+ * Component type → the states it declares. A design-time instance may pin one (a canvas mock of a
37
+ * filled field); a state is never a prop, so code leaves it out rather than writing
38
+ * `<Input filled />`, which would force the look in production.
39
+ */
40
+ states?: Readonly<Record<string, readonly string[]>>;
35
41
  }
36
42
  interface SpecToJsxResult {
37
43
  /** Generated JSX source code */
@@ -1,3 +1,5 @@
1
+ import { UDS_FORCE_STATES } from "../framework/component-states.js";
2
+ import "../framework/class-names.js";
1
3
  import { glyphJsxForm, glyphMemberOf, jsxIdentifier, parseGlyphType } from "./glyph-jsx.js";
2
4
  import { serializeProps } from "@json-render/codegen";
3
5
  //#region src/spec/specToJsx.ts
@@ -108,6 +110,8 @@ function renderElement(key, spec, depth, ctx, ancestors) {
108
110
  if (ancestors.has(key)) return `${indent}{/* cyclic reference to "${key}" omitted */}`;
109
111
  const withSelf = new Set(ancestors).add(key);
110
112
  const { children: _children, ...propsWithoutSlots } = element.props ?? {};
113
+ delete propsWithoutSlots[UDS_FORCE_STATES];
114
+ for (const state of ctx.states?.[element.type] ?? []) delete propsWithoutSlots[state];
111
115
  const slotKeys = collectSlotKeys(element.slots);
112
116
  const slotAttrs = [];
113
117
  for (const [propName, fills] of Object.entries(element.slots ?? {})) if (fills.length === 1) {
@@ -207,6 +211,7 @@ function specToJsx(spec, options) {
207
211
  glyphModules: options?.glyphModules,
208
212
  glyphProps: options?.glyphProps,
209
213
  glyphMembers: options?.glyphMembers,
214
+ states: options?.states,
210
215
  members: /* @__PURE__ */ new Map()
211
216
  };
212
217
  const jsxTree = renderElement(spec.root, spec, 0, ctx, /* @__PURE__ */ new Set());