@yahoo/uds-create-config 5.76.0 → 5.77.1

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.
@@ -1,19 +1,59 @@
1
1
  import { isRef, pathOf } from "../framework/utils/refs.js";
2
2
  import { className, renderStyleValue, resolveComponentProps, resolveTokenValue, stylePropEntries } from "../framework/projections.js";
3
3
  import { intoName, routesContent, routesIntoText } from "../framework/prop-surface.js";
4
- import { componentLayerClass, componentRuleClasses, stylePropClasses } from "../framework/class-names.js";
4
+ import { arbitraryClassName, componentLayerClass, componentRuleClasses, stylePropClasses } from "../framework/class-names.js";
5
5
  //#region src/capture/class-grammar.ts
6
6
  const VIEWPORT_UNIT = /\d(vh|vw|dvh|dvw|svh|svw|lvh|lvw|vmin|vmax)\b/;
7
7
  /** `0px` → `0`, collapsed spaces, lowercase keywords; what two spellings of one value share. */
8
+ /** CSS properties whose value is a colour. */
9
+ const COLOR_PROPERTY = /(^|-)(color|fill|stroke)$|^background$/;
10
+ /** Whether an off-scale value is a colour (`#f00`, `rgb(…)`, a named colour), not a length or a number. */
11
+ function isColorValue(value) {
12
+ const v = value.trim().toLowerCase();
13
+ if (/^(#|rgba?\(|hsla?\(|oklch\(|oklab\(|lab\(|lch\(|color\(|color-mix\()/.test(v)) return true;
14
+ if (/^(color|colour):/.test(v)) return true;
15
+ if (/^[-\d.]/.test(v) || /^(calc|min|max|clamp|var)\(/.test(v)) return false;
16
+ return /^[a-z]+$/.test(v) && v !== "auto" && v !== "none";
17
+ }
8
18
  function normalizeCssValue(value) {
9
19
  return value.trim().replace(/\s+/g, " ").replace(/(^|[\s,(])0(?:px|rem|em|%)(?=$|[\s,)])/g, "$10").toLowerCase();
10
20
  }
11
21
  function classGrammar(config) {
12
22
  const styleProps = /* @__PURE__ */ new Map();
13
- for (const entity of config.list("style-property")) for (const [leaf, cls] of stylePropClasses(config, entity.path)) if (!styleProps.has(cls)) styleProps.set(cls, {
14
- path: entity.path,
15
- leaf
16
- });
23
+ const arbitraryStems = /* @__PURE__ */ new Map();
24
+ for (const entity of config.list("style-property")) {
25
+ for (const [leaf, cls] of stylePropClasses(config, entity.path)) if (!styleProps.has(cls)) styleProps.set(cls, {
26
+ path: entity.path,
27
+ leaf
28
+ });
29
+ if (entity.toJSON().arbitrary) {
30
+ const stem = arbitraryClassName({
31
+ config,
32
+ path: entity.path,
33
+ value: "[x]"
34
+ }).slice(0, -3);
35
+ arbitraryStems.set(stem, [...arbitraryStems.get(stem) ?? [], entity.path]);
36
+ }
37
+ }
38
+ const arbitraryMatch = (cls) => {
39
+ const open = cls.indexOf("[");
40
+ if (open <= 0) return void 0;
41
+ const close = cls.indexOf("]", open);
42
+ if (close === -1) return void 0;
43
+ const rest = cls.slice(close + 1);
44
+ if (rest !== "" && !/^\/(?:\[[^\]]+\]|[\w.-]+)$/.test(rest)) return void 0;
45
+ const paths = arbitraryStems.get(cls.slice(0, open)) ?? [];
46
+ const inner = cls.slice(open + 1, close);
47
+ if (!inner || paths.length === 0) return void 0;
48
+ return {
49
+ path: paths.length === 1 ? paths[0] : paths.find((each) => writesColor(each) === isColorValue(inner)) ?? paths[0],
50
+ leaf: `[${inner}]`
51
+ };
52
+ };
53
+ function writesColor(path) {
54
+ const { properties = [] } = config.resolve("style-property", path)?.toJSON() ?? {};
55
+ return properties.some((property) => COLOR_PROPERTY.test(property));
56
+ }
17
57
  const composites = config.list("composite").map((entity) => ({
18
58
  path: entity.path,
19
59
  cls: className(config, "composite", entity.path)
@@ -126,7 +166,7 @@ function classGrammar(config) {
126
166
  };
127
167
  };
128
168
  return {
129
- styleProp: (cls) => styleProps.get(cls),
169
+ styleProp: (cls) => styleProps.get(cls) ?? arbitraryMatch(cls),
130
170
  stylePropsFor: (cssProperty) => byCss.get(cssProperty.toLowerCase()) ?? [],
131
171
  viewportLeaf(path, leaf) {
132
172
  return (valuesOf(path).get(leaf) ?? []).some((v) => VIEWPORT_UNIT.test(v));
@@ -2183,7 +2183,7 @@ const ComponentEntity = defineEntity({
2183
2183
  input: z.object({ path: pathInput("component") }),
2184
2184
  readOnly: false,
2185
2185
  label: "Create blank",
2186
- description: "Create an empty component at a new qualified `path`, to design on the canvas first: one `div` root layer and the node that renders it, with no props, no variants, no slot, no styles and no preview content. This is exactly what Studio's \"+\" creates. Takes only `path`. Use it for \"a new component\", \"a blank component\" or \"start a component from nothing\"; `create` is for a component whose body is already known.",
2186
+ description: "Create an empty component at a new qualified `path`, to design on the canvas first: one `div` root layer and the node that renders it, with no props, no variants, no slot, no styles and no preview content. This is exactly what Create's \"+\" creates. Takes only `path`. Use it for \"a new component\", \"a blank component\" or \"start a component from nothing\"; `create` is for a component whose body is already known.",
2187
2187
  scope: "item",
2188
2188
  creates: true,
2189
2189
  title: (entity) => `Create blank ${entity}`,
@@ -2202,7 +2202,7 @@ const ComponentEntity = defineEntity({
2202
2202
  example: ({ config }) => forwardsCreateExample(config)
2203
2203
  },
2204
2204
  "sub-upsert/forwards": { description: `Set what one layer forwards, added when absent and replaced whole when present. ${FORWARDS_DESCRIPTION}` },
2205
- "sub-update/forwards": { description: `Replace what one layer forwards: the member is \`'*'\` or a list, so \`data\` is the whole new value. ${FORWARDS_DESCRIPTION}` },
2205
+ "sub-update/forwards": { description: `Replace what one layer forwards: the member is a list of prop names, so \`data\` is the whole new list. ${FORWARDS_DESCRIPTION}` },
2206
2206
  /**
2207
2207
  * Add an element to a composition — a layer and the node that targets it, together.
2208
2208
  *
@@ -2500,7 +2500,7 @@ count: z.number().int().min(1).max(50).optional() }).prefault({})
2500
2500
  }),
2501
2501
  readOnly: false,
2502
2502
  label: "Forward layer props",
2503
- description: "Expose a layer's props at the component's top level (\"forward everything from the root but icon\", \"also forward width\"): `props` is `\"*\"` for every prop the layer's element takes, or a list added to what the layer forwards now, or that replaces it with `replace`; `except` leaves props out of either. A prop the element lacks, one the component declares itself, and one a sibling layer already forwards are refused. Read `component/get` for what a layer forwards now.",
2503
+ description: "Expose a layer's props at the component's top level (\"also forward width\", \"forward onClick from the root\"): `props` is a list of prop names, added to what the layer forwards now, or replacing it with `replace`. A prop the element lacks, one the component declares itself, and one a sibling layer already forwards are refused. Read `component/get` for what a layer forwards now.",
2504
2504
  scope: "item",
2505
2505
  title: (entity) => `Forward ${entity} layer props`,
2506
2506
  handler: (input, config) => {
@@ -14,7 +14,7 @@ import { z } from "zod";
14
14
  * non-serializable projection (a zod schema, a function). Each fn declares its own provenance.
15
15
  *
16
16
  * A derived kind self-derives on registration, sourcing from the config it's registered into;
17
- * `Config.register` routes it by whether the KIND is derived. See `docs/config-v2.md`.
17
+ * `Config.register` routes it by whether the kind is derived.
18
18
  */
19
19
  /** What `extend` accepts. Checked at runtime because `extend` is shadowed: an unrecognized key is a
20
20
  * typo, or a zod raw shape reaching this method expecting zod's `extend`. Either should say so
package/dist/index.js CHANGED
@@ -66,11 +66,11 @@ import { styleSetEdits } from "./entities/system/component-style-set.js";
66
66
  import { convertLinkedTokenValue, prepareLinkedTokenBody } from "./entities/system/linked-token-values.js";
67
67
  import { Token, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers } from "./entities/system/Token.js";
68
68
  import { Component, SLOT_CODE_FORMS, blankComponentBody, canonicalWhen, forwardedNames, layerRef, ownValues, ownsItsValues, valueRef } from "./entities/system/Component.js";
69
+ import { Icon, iconVariantAs } from "./entities/system/Icon.js";
70
+ import { glyphExportNames, glyphType, iconCategories, iconLibraries, iconLibrary, iconToolLibraries, isDeclaredElementProp, librariesBoundBy, libraryHolding, memberVariantOptions, placedGlyphElement, resolveIconLibraries } from "./entities/system/icon-library.js";
69
71
  import { CapturedNode, capturedFromDom, elementFromCaptured } from "./capture/captured-tree.js";
70
72
  import { classGrammar } from "./capture/class-grammar.js";
71
73
  import { specFromCapture } from "./capture/capture-to-spec.js";
72
- import { Icon, iconVariantAs } from "./entities/system/Icon.js";
73
- import { glyphExportNames, glyphType, iconCategories, iconLibraries, iconLibrary, iconToolLibraries, isDeclaredElementProp, librariesBoundBy, libraryHolding, memberVariantOptions, placedGlyphElement, resolveIconLibraries } from "./entities/system/icon-library.js";
74
74
  import { glyphSourcesOf, iconResolver, nameIconsInTree } from "./capture/icon-resolver.js";
75
75
  import { PLACED_NODE_KEY, specToNodePatches } from "./entities/canvas/node-patches.js";
76
76
  import { Device } from "./entities/system/Device.js";