@yahoo/uds-create-config 4.17.0 → 4.18.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/configs/CanvasConfig.d.ts +98 -0
- package/dist/configs/react-native-system.d.ts +69 -3
- package/dist/configs/system.d.ts +77 -7
- package/dist/configs/system.js +3 -1
- package/dist/css/root-font-size.d.ts +16 -0
- package/dist/css/root-font-size.js +97 -0
- package/dist/css/value-type.js +5 -1
- package/dist/entities/native/NativeModifier.d.ts +1 -1
- package/dist/entities/native/NativeSettings.d.ts +1 -0
- package/dist/entities/system/Component.d.ts +50 -0
- package/dist/entities/system/Component.js +8 -1
- package/dist/entities/system/Modifier.d.ts +2 -2
- package/dist/entities/system/Settings.d.ts +3 -0
- package/dist/entities/system/Settings.js +12 -1
- package/dist/entities/system/StyleProperty.js +2 -3
- package/dist/entities/system/Token.d.ts +16 -3
- package/dist/entities/system/Token.js +21 -1
- package/dist/entities/system/token-index.js +1 -1
- package/dist/entities/system/token-values.d.ts +138 -0
- package/dist/entities/system/token-values.js +154 -0
- package/dist/framework/Config.d.ts +1 -1
- package/dist/framework/Config.js +1 -1
- package/dist/framework/defineEntity.d.ts +1 -1
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/utils/identifier.d.ts +8 -0
- package/dist/framework/utils/identifier.js +62 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -3
- package/dist/migrations/2.0.0/raw-css.js +1 -1
- package/dist/migrations/2.0.0/v1-artifact.d.ts +39 -4
- package/dist/migrations/2.0.0/v1-artifact.js +1 -1
- package/dist/migrations/20260923160425_render_token_refs.d.ts +14 -0
- package/dist/migrations/20260923160425_render_token_refs.js +10 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { isLengthUnit } from "./value-type.js";
|
|
2
|
+
import parse from "css-tree/parser";
|
|
3
|
+
/** The `font-size` keywords, which fix no pixel value a build can name. */
|
|
4
|
+
const KEYWORDS = new Set([
|
|
5
|
+
"xx-small",
|
|
6
|
+
"x-small",
|
|
7
|
+
"small",
|
|
8
|
+
"medium",
|
|
9
|
+
"large",
|
|
10
|
+
"x-large",
|
|
11
|
+
"xx-large",
|
|
12
|
+
"xxx-large",
|
|
13
|
+
"smaller",
|
|
14
|
+
"larger",
|
|
15
|
+
"inherit",
|
|
16
|
+
"initial",
|
|
17
|
+
"unset"
|
|
18
|
+
]);
|
|
19
|
+
/** The functions a `font-size` may be, each of which fixes no pixel value a build can name. */
|
|
20
|
+
const FUNCTIONS = new Set([
|
|
21
|
+
"calc",
|
|
22
|
+
"clamp",
|
|
23
|
+
"min",
|
|
24
|
+
"max",
|
|
25
|
+
"var",
|
|
26
|
+
"env"
|
|
27
|
+
]);
|
|
28
|
+
/**
|
|
29
|
+
* The value parsed, or `undefined` when it is not a `font-size` a stylesheet can carry.
|
|
30
|
+
*/
|
|
31
|
+
function parseRootFontSize(value) {
|
|
32
|
+
const css = value.trim();
|
|
33
|
+
if (css === "") return void 0;
|
|
34
|
+
if (!balanced(css)) return void 0;
|
|
35
|
+
let malformed = false;
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = parse(css, {
|
|
39
|
+
context: "value",
|
|
40
|
+
onParseError: () => {
|
|
41
|
+
malformed = true;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
} catch {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (malformed || parsed.type !== "Value") return void 0;
|
|
48
|
+
const [node, ...rest] = parsed.children.toArray();
|
|
49
|
+
if (!node || rest.length > 0) return void 0;
|
|
50
|
+
switch (node.type) {
|
|
51
|
+
case "Dimension": {
|
|
52
|
+
if (!isLengthUnit(node.unit.toLowerCase())) return void 0;
|
|
53
|
+
const amount = Number(node.value);
|
|
54
|
+
const unit = node.unit.toLowerCase();
|
|
55
|
+
if (unit === "px") return {
|
|
56
|
+
css,
|
|
57
|
+
px: amount
|
|
58
|
+
};
|
|
59
|
+
if (unit === "em" || unit === "rem") return {
|
|
60
|
+
css,
|
|
61
|
+
px: amount * 16
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
css,
|
|
65
|
+
px: void 0
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
case "Percentage": return {
|
|
69
|
+
css,
|
|
70
|
+
px: Number(node.value) / 100 * 16
|
|
71
|
+
};
|
|
72
|
+
case "Number": return Number(node.value) === 0 ? {
|
|
73
|
+
css,
|
|
74
|
+
px: 0
|
|
75
|
+
} : void 0;
|
|
76
|
+
case "Identifier": return KEYWORDS.has(node.name.toLowerCase()) ? {
|
|
77
|
+
css,
|
|
78
|
+
px: void 0
|
|
79
|
+
} : void 0;
|
|
80
|
+
case "Function": return FUNCTIONS.has(node.name.toLowerCase()) ? {
|
|
81
|
+
css,
|
|
82
|
+
px: void 0
|
|
83
|
+
} : void 0;
|
|
84
|
+
default: return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function balanced(css) {
|
|
88
|
+
let depth = 0;
|
|
89
|
+
for (const char of css) {
|
|
90
|
+
if (char === "(") depth += 1;
|
|
91
|
+
if (char === ")") depth -= 1;
|
|
92
|
+
if (depth < 0) return false;
|
|
93
|
+
}
|
|
94
|
+
return depth === 0;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { parseRootFontSize };
|
package/dist/css/value-type.js
CHANGED
|
@@ -29,6 +29,10 @@ const EASING_FUNCTIONS = [
|
|
|
29
29
|
"linear("
|
|
30
30
|
];
|
|
31
31
|
/** Absolute and relative length units, longest-first so `rem` wins over `em`. */
|
|
32
|
+
/** Whether `unit` is a CSS length unit, `rem` and `vw` alike. */
|
|
33
|
+
function isLengthUnit(unit) {
|
|
34
|
+
return LENGTH_UNITS.includes(unit);
|
|
35
|
+
}
|
|
32
36
|
const LENGTH_UNITS = [
|
|
33
37
|
"ch",
|
|
34
38
|
"cm",
|
|
@@ -128,4 +132,4 @@ function resolveValueType({ type, value }) {
|
|
|
128
132
|
return sniffValueType(value);
|
|
129
133
|
}
|
|
130
134
|
//#endregion
|
|
131
|
-
export { resolveValueType, sniffValueType };
|
|
135
|
+
export { isLengthUnit, resolveValueType, sniffValueType };
|
|
@@ -14,8 +14,8 @@ declare const NativeModifier: EntityClass<z.ZodObject<{
|
|
|
14
14
|
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
15
15
|
}, z.core.$strip>, z.ZodObject<{
|
|
16
16
|
type: z.ZodOptional<z.ZodEnum<{
|
|
17
|
-
state: "state";
|
|
18
17
|
mode: "mode";
|
|
18
|
+
state: "state";
|
|
19
19
|
}>>;
|
|
20
20
|
default: z.ZodOptional<z.ZodObject<{
|
|
21
21
|
__ref: z.ZodString;
|
|
@@ -29,6 +29,7 @@ declare const NativeSettings: EntityClass<z.ZodObject<Omit<{
|
|
|
29
29
|
}, z.core.$strict>]>>;
|
|
30
30
|
scope: z.ZodOptional<z.ZodString>;
|
|
31
31
|
preflight: z.ZodOptional<z.ZodBoolean>;
|
|
32
|
+
rem: z.ZodOptional<z.ZodString>;
|
|
32
33
|
safelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
33
34
|
}, z.core.$strict>>;
|
|
34
35
|
playground: z.ZodOptional<z.ZodObject<{
|
|
@@ -10,7 +10,9 @@ import { StoredPreviewCondition } from "../../framework/preview-store.js";
|
|
|
10
10
|
import { listSlots, namedSlots, slotHostedBy, slotOf } from "../../spec/slot.js";
|
|
11
11
|
import { cellAt } from "../../spec/cell.js";
|
|
12
12
|
import { instanceAt } from "../../spec/instance.js";
|
|
13
|
+
import { ComposedValue } from "./composed-values.js";
|
|
13
14
|
import { StyleValue } from "./style-bag.js";
|
|
15
|
+
import { OverrideCondition, TokenRefPath, TokenReference } from "./Token.js";
|
|
14
16
|
import { AsInput, LayerMap, PhantomCallProps, PhantomReadProps, PropMap, RenderArgs, StyleRuleInput } from "./defineComponent.js";
|
|
15
17
|
import { z } from "zod";
|
|
16
18
|
|
|
@@ -718,6 +720,22 @@ declare const componentFields: z.ZodObject<{
|
|
|
718
720
|
__ref: z.ZodString;
|
|
719
721
|
}, z.core.$strip>>;
|
|
720
722
|
export: z.ZodOptional<z.ZodString>;
|
|
723
|
+
tokenRefs: z.ZodOptional<z.ZodArray<Omit<EntityClass<z.ZodObject<{
|
|
724
|
+
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
725
|
+
type: z.ZodOptional<z.ZodString>;
|
|
726
|
+
overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
|
|
727
|
+
on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
|
|
728
|
+
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
729
|
+
}, z.core.$strip>>>;
|
|
730
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
731
|
+
cssPrefix: z.ZodOptional<z.ZodString>;
|
|
732
|
+
}, z.core.$strip>, "token", {
|
|
733
|
+
readonly create: {
|
|
734
|
+
readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
|
|
735
|
+
};
|
|
736
|
+
}, Record<never, never>, false, Record<never, never>, Record<never, never>>, "ref"> & {
|
|
737
|
+
readonly ref: <const Path extends TokenRefPath>(path: Path) => TokenReference<Path>;
|
|
738
|
+
}>>;
|
|
721
739
|
}, z.core.$strict>;
|
|
722
740
|
/**
|
|
723
741
|
* A component's stored body — every field, typed off the schema above.
|
|
@@ -906,6 +924,22 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
|
|
|
906
924
|
__ref: z.ZodString;
|
|
907
925
|
}, z.core.$strip>>;
|
|
908
926
|
export: z.ZodOptional<z.ZodString>;
|
|
927
|
+
tokenRefs: z.ZodOptional<z.ZodArray<Omit<EntityClass<z.ZodObject<{
|
|
928
|
+
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
929
|
+
type: z.ZodOptional<z.ZodString>;
|
|
930
|
+
overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
|
|
931
|
+
on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
|
|
932
|
+
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
933
|
+
}, z.core.$strip>>>;
|
|
934
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
935
|
+
cssPrefix: z.ZodOptional<z.ZodString>;
|
|
936
|
+
}, z.core.$strip>, "token", {
|
|
937
|
+
readonly create: {
|
|
938
|
+
readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
|
|
939
|
+
};
|
|
940
|
+
}, Record<never, never>, false, Record<never, never>, Record<never, never>>, "ref"> & {
|
|
941
|
+
readonly ref: <const Path extends TokenRefPath>(path: Path) => TokenReference<Path>;
|
|
942
|
+
}>>;
|
|
909
943
|
}, z.core.$strict>, z.ZodObject<{
|
|
910
944
|
icon: z.ZodOptional<z.ZodString>;
|
|
911
945
|
}, z.core.$strip>, "component", {
|
|
@@ -1078,6 +1112,22 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
|
|
|
1078
1112
|
__ref: z.ZodString;
|
|
1079
1113
|
}, z.core.$strip>>;
|
|
1080
1114
|
export: z.ZodOptional<z.ZodString>;
|
|
1115
|
+
tokenRefs: z.ZodOptional<z.ZodArray<Omit<EntityClass<z.ZodObject<{
|
|
1116
|
+
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
1117
|
+
type: z.ZodOptional<z.ZodString>;
|
|
1118
|
+
overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
|
|
1119
|
+
on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
|
|
1120
|
+
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
1121
|
+
}, z.core.$strip>>>;
|
|
1122
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1123
|
+
cssPrefix: z.ZodOptional<z.ZodString>;
|
|
1124
|
+
}, z.core.$strip>, "token", {
|
|
1125
|
+
readonly create: {
|
|
1126
|
+
readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
|
|
1127
|
+
};
|
|
1128
|
+
}, Record<never, never>, false, Record<never, never>, Record<never, never>>, "ref"> & {
|
|
1129
|
+
readonly ref: <const Path extends TokenRefPath>(path: Path) => TokenReference<Path>;
|
|
1130
|
+
}>>;
|
|
1081
1131
|
}, z.core.$strict>;
|
|
1082
1132
|
readonly description: "Create a component at a new qualified `path`. For a component with nothing in it yet, one to design on the canvas before it has props or variants, use `create-blank` with only a `path` instead of authoring a minimal body here. When its complete body is known, include all layers, anatomy, props, defaults, and styles in this one operation. `data.layers` maps each stable layer name to the element it renders and must include `root` — a tag for a primitive (`{ \"layers\": { \"root\": \"div\" } }`), and for anything built on top, a ref to a primitive (`{ \"root\": { \"__ref\": \"component:Box\" }, \"label\": { \"__ref\": \"component:Text\" } }`), which is what lets the layer take that primitive's style props. A primitive exposes style props as `{ \"type\": \"styleProperty\", \"value\": { \"__ref\": \"style-property:bg\" } }` and declares `children: { \"type\": \"slot\", \"text\": true }` when it holds content — without the slot, nothing nested inside it renders. A composed component exposes a layer's props at its own top level through `forwards`, keyed by layer: `{ \"forwards\": { \"root\": \"*\" } }` passes every prop the root's element takes (so `<Button width=\"full\">` reaches the root's `width`), and a list of member refs (`[{ \"__ref\": \"component:Box#props/width\" }]`, each naming the component that declares the prop) passes only those. A prop a layer already exposes is forwarded, never redeclared in `props`; a `props` entry of `{ \"type\": \"forward\" }` is the reverse route, one of this component's props into a layer's. `anatomy` is the tree the build would read off a render: each node renders a layer (`{ \"__ref\": \"layer:<name>\" }`), an intrinsic tag, or `Slot` (`{ \"element\": \"Slot\", \"props\": { \"name\": \"children\" } }`, where a slot prop's content lands — but a declared slot fills `root` by default, so a primitive still declares its `children` slot prop yet needs no `Slot` node or `children` entry for it, and a `Slot` node is only for landing that content in a nested layer); `children` lists sibling node keys from the `root` node, never a prop name; `text` is a fixed string; `visible` is a condition on a prop (`{ \"$state\": \"<prop>\" }`, or with `eq`); content shown in a nested layer is a route, `children: { \"type\": \"forward\", \"target\": \"label/children\" }`. `$state` appears only in `visible` and in an `element` a prop chooses, never as a prop value. A glyph is a node of its own, `{ \"element\": { \"__ref\": \"icon:<library>/<Name>\" } }`, listed in the `children` of the layer node that holds it, never a prop value. Text a prop supplies to a text layer is never a node either: declare the prop `{ \"type\": \"forward\", \"target\": \"<layer>/children\" }` and the layer shows it; a `Slot` node goes only under a layer that takes nodes. A style rule is `\"styles\": { \"*\": { \"when\": null, \"layers\": { \"root\": { \"bg\": \"surface\" } } } }`, keyed by a name, with each layer's bag under `layers`. A composed component carries its own styles: give it an unconditional (`\"*\"`) rule that lays out and surfaces it through the system's style properties — spacing, alignment and gap on the container, a background and color where it has one — rather than leaning on the bare primitives, which paint flat. Style a layer only with the style props the primitive it renders exposes, set to the values those props accept: `component/get` on the primitive lists both, so read it and choose from that rather than reaching for a CSS name or value the system has no style property for. Set `preview` (`{ \"*\": { \"when\": null, \"props\": { \"children\": \"Example\" } } }`) or `defaultProps` so the component previews with content; a root that takes children renders empty otherwise. A style rule's layer bag is keyed by style-prop name (`fontWeight`, `color`), never by CSS property name, and each value is one of that prop's values.";
|
|
1083
1133
|
readonly example: ({
|
|
@@ -19,6 +19,7 @@ import { instanceAt } from "../../spec/instance.js";
|
|
|
19
19
|
import { Package } from "./Package.js";
|
|
20
20
|
import { File } from "./File.js";
|
|
21
21
|
import { iconDomainInvariants } from "./icon-domain-invariants.js";
|
|
22
|
+
import { Token } from "./Token.js";
|
|
22
23
|
import { intoName, routedPropIn, routesContent } from "../../framework/prop-surface.js";
|
|
23
24
|
import { z } from "zod";
|
|
24
25
|
//#region src/entities/system/Component.ts
|
|
@@ -1444,7 +1445,13 @@ const componentFields = z.strictObject({
|
|
|
1444
1445
|
* two components, and a borrower registers the publisher's `Card` under its own name. Nothing
|
|
1445
1446
|
* recovers that binding from a path.
|
|
1446
1447
|
*/
|
|
1447
|
-
export: z.string().optional()
|
|
1448
|
+
export: z.string().optional(),
|
|
1449
|
+
/**
|
|
1450
|
+
* The tokens the render reads as values — `Token.ref('spacing/2.5').px()` — baked from the source
|
|
1451
|
+
* like `file`. Each is an edge: a rename reaches the read, a delete names the component, and a
|
|
1452
|
+
* link's closure carries the token. A read whose path a prop completes names the group.
|
|
1453
|
+
*/
|
|
1454
|
+
tokenRefs: z.array(Token).optional()
|
|
1448
1455
|
}).check((ctx) => {
|
|
1449
1456
|
const body = ctx.value;
|
|
1450
1457
|
if (isPlainObject(body.props)) for (const [name, decl] of Object.entries(body.props)) {
|
|
@@ -27,8 +27,8 @@ type WebActivation = (typeof WEB_ACTIVATIONS)[number];
|
|
|
27
27
|
*/
|
|
28
28
|
declare const modifierGroupFields: z.ZodObject<{
|
|
29
29
|
type: z.ZodOptional<z.ZodEnum<{
|
|
30
|
-
state: "state";
|
|
31
30
|
mode: "mode";
|
|
31
|
+
state: "state";
|
|
32
32
|
}>>;
|
|
33
33
|
default: z.ZodOptional<z.ZodObject<{
|
|
34
34
|
__ref: z.ZodString;
|
|
@@ -49,8 +49,8 @@ declare const Modifier: EntityClass<z.ZodObject<{
|
|
|
49
49
|
}, z.core.$strip>, z.ZodObject<Record<never, never>, z.core.$strip>, "composite", Record<never, never>, Record<never, never>, false, Record<never, never>, Record<never, never>>>]>;
|
|
50
50
|
}, z.core.$strip>, z.ZodObject<{
|
|
51
51
|
type: z.ZodOptional<z.ZodEnum<{
|
|
52
|
-
state: "state";
|
|
53
52
|
mode: "mode";
|
|
53
|
+
state: "state";
|
|
54
54
|
}>>;
|
|
55
55
|
default: z.ZodOptional<z.ZodObject<{
|
|
56
56
|
__ref: z.ZodString;
|
|
@@ -29,6 +29,7 @@ type CssPrefix = z.output<typeof prefix>;
|
|
|
29
29
|
declare const CSS_DEFAULTS: {
|
|
30
30
|
readonly prefix: "uds";
|
|
31
31
|
readonly preflight: true;
|
|
32
|
+
readonly rem: "16px";
|
|
32
33
|
};
|
|
33
34
|
/** The `css` section — how a web system names what it emits and where its stylesheet applies. A
|
|
34
35
|
* native system declares no such section, so a native config setting one is refused. */
|
|
@@ -39,6 +40,7 @@ declare const CssSection: SubEntityClass<z.ZodObject<{
|
|
|
39
40
|
}, z.core.$strict>]>>;
|
|
40
41
|
scope: z.ZodOptional<z.ZodString>;
|
|
41
42
|
preflight: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
+
rem: z.ZodOptional<z.ZodString>;
|
|
42
44
|
safelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
45
|
}, z.core.$strict>>;
|
|
44
46
|
/** The two namespaces a prefix names, whichever form it was written in, defaulted when unset. */
|
|
@@ -83,6 +85,7 @@ declare const Settings: EntityClass<z.ZodObject<{
|
|
|
83
85
|
}, z.core.$strict>]>>;
|
|
84
86
|
scope: z.ZodOptional<z.ZodString>;
|
|
85
87
|
preflight: z.ZodOptional<z.ZodBoolean>;
|
|
88
|
+
rem: z.ZodOptional<z.ZodString>;
|
|
86
89
|
safelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
87
90
|
}, z.core.$strict>>;
|
|
88
91
|
playground: z.ZodOptional<z.ZodObject<{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildFields } from "../../configs/build-options.js";
|
|
2
|
+
import { parseRootFontSize } from "../../css/root-font-size.js";
|
|
2
3
|
import { defineEntity } from "../../framework/defineEntity.js";
|
|
3
4
|
import { defineSubEntity } from "../../framework/defineSubEntity.js";
|
|
4
5
|
import { z } from "zod";
|
|
@@ -61,7 +62,8 @@ const prefix = z.union([z.string(), z.strictObject({
|
|
|
61
62
|
/** The `css` section as a config that set nothing reads it, spelled once. */
|
|
62
63
|
const CSS_DEFAULTS = {
|
|
63
64
|
prefix: "uds",
|
|
64
|
-
preflight: true
|
|
65
|
+
preflight: true,
|
|
66
|
+
rem: "16px"
|
|
65
67
|
};
|
|
66
68
|
const cssFields = z.strictObject({
|
|
67
69
|
prefix: prefix.meta({
|
|
@@ -81,6 +83,15 @@ const cssFields = z.strictObject({
|
|
|
81
83
|
description: "Start the stylesheet with the element resets, so buttons, inputs and headings begin from the same baseline in every browser. Off, the page keeps its own base styles. Unset, on.",
|
|
82
84
|
default: CSS_DEFAULTS.preflight
|
|
83
85
|
}).optional(),
|
|
86
|
+
/** The root font size of the page this sheet is for, as CSS — what one `rem` is. Stated, the app's
|
|
87
|
+
* sheet sets it and a token's `px()` reads a `rem` value against it; a fixed size (`16px`, `62.5%`,
|
|
88
|
+
* `1em`) folds to pixels against the browser's 16px default, a fluid one (`clamp(…)`) leaves a
|
|
89
|
+
* rem with no pixel reading. A sheet confined to a `scope` cannot set it, and states it only. */
|
|
90
|
+
rem: z.string().refine((value) => parseRootFontSize(value) !== void 0, { error: "a root font size is one CSS `font-size` value: a length (`16px`, `1em`), a percentage (`62.5%`), a math or `var()` function (`clamp(14px, 1vw + 12px, 18px)`), or a font-size keyword." }).meta({
|
|
91
|
+
title: "Rem",
|
|
92
|
+
description: `The root font size of the page this stylesheet is for, as CSS: \`16px\`, \`62.5%\`, \`1em\`, or a fluid \`clamp(…)\`. Stated, the stylesheet sets \`html { font-size }\` to it unless it is confined to a \`scope\`, and a token's \`px()\` reads a rem value against it — a fixed size folds against the browser's 16px default, a fluid one leaves rem values with no pixel reading. Unset, ${CSS_DEFAULTS.rem} is assumed and the page's root is left alone.`,
|
|
93
|
+
default: CSS_DEFAULTS.rem
|
|
94
|
+
}).optional(),
|
|
84
95
|
/** Classes the stylesheet keeps whether or not a scanned file names them — for a class built at
|
|
85
96
|
* runtime, or one used by a file the build does not scan. */
|
|
86
97
|
safelist: z.array(z.string().min(1)).meta({
|
|
@@ -2,6 +2,7 @@ import { isRef, kindOf, pathOf, ref, refSchema, sourceOf, splitRef } from "../..
|
|
|
2
2
|
import { defineEntity } from "../../framework/defineEntity.js";
|
|
3
3
|
import { cssValueIssue, isCssProperty } from "../../css/values.js";
|
|
4
4
|
import { camelCase } from "./style-bag.js";
|
|
5
|
+
import { isIdentifier } from "../../framework/utils/identifier.js";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
//#region src/entities/system/StyleProperty.ts
|
|
7
8
|
/**
|
|
@@ -9,15 +10,13 @@ import { z } from "zod";
|
|
|
9
10
|
* The forms a `values` entry takes come first, each a zod schema built from its context, and the
|
|
10
11
|
* entity's fields read them.
|
|
11
12
|
*/
|
|
12
|
-
/** A style property's path is the JSX prop a component exposes, so each segment is an identifier. */
|
|
13
|
-
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
14
13
|
/** A token group's ref. Through the framework helper rather than `Token.ref`: `Token` reaches this
|
|
15
14
|
* module through `Modifier` and `Composite`, so importing it here is a cycle. */
|
|
16
15
|
const tokenGroup = (group) => ref("token", group);
|
|
17
16
|
/** A token ref as a model writes it, spelled from the ref itself so the prose can't drift. */
|
|
18
17
|
const spelled = (path) => JSON.stringify(tokenGroup(path));
|
|
19
18
|
const stylePropertyPath = z.string().check((ctx) => {
|
|
20
|
-
const bad = ctx.value.split("/").find((segment) => !
|
|
19
|
+
const bad = ctx.value.split("/").find((segment) => !isIdentifier(segment));
|
|
21
20
|
if (bad === void 0) return;
|
|
22
21
|
ctx.issues.push({
|
|
23
22
|
code: "custom",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Ref } from "../../framework/utils/ref-grammar.js";
|
|
2
2
|
import { Ref as Ref$1 } from "../../framework/utils/refs.js";
|
|
3
3
|
import { SubEntityClass } from "../../framework/defineSubEntity.js";
|
|
4
|
-
import {
|
|
4
|
+
import { PathOf } from "../../framework/registered.js";
|
|
5
|
+
import { EntityClass, KindRef } from "../../framework/defineEntity.js";
|
|
5
6
|
import { ComposedValue } from "./composed-values.js";
|
|
7
|
+
import { TokenReaders } from "./token-values.js";
|
|
6
8
|
import { z } from "zod";
|
|
7
9
|
|
|
8
10
|
//#region src/entities/system/Token.d.ts
|
|
@@ -50,7 +52,7 @@ declare const tokenFields: z.ZodObject<{
|
|
|
50
52
|
/** A token's stored body — what a `create` writes and what `Token.get` hands back typed. Exported
|
|
51
53
|
* so a consumer building one names the entity's own shape instead of restating it. */
|
|
52
54
|
type TokenBody = z.infer<typeof tokenFields>;
|
|
53
|
-
declare const
|
|
55
|
+
declare const TokenEntity: EntityClass<z.ZodObject<{
|
|
54
56
|
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
55
57
|
type: z.ZodOptional<z.ZodString>;
|
|
56
58
|
overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
|
|
@@ -64,5 +66,16 @@ declare const Token: EntityClass<z.ZodObject<{
|
|
|
64
66
|
readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
|
|
65
67
|
};
|
|
66
68
|
}, Record<never, never>, false, Record<never, never>, Record<never, never>>;
|
|
69
|
+
/** A token ref as an authoring file holds it: the marker, `extend`, and the value readers. */
|
|
70
|
+
type TokenReference<Path extends string> = KindRef<typeof tokenFields, 'token', Path> & TokenReaders<Path>;
|
|
71
|
+
/**
|
|
72
|
+
* What `Token.ref` takes in an authoring file: a registered path, or a path a prop completes under a
|
|
73
|
+
* registered group — `` `spacing/${gutter}` `` — whose readers stay open since no one token is named.
|
|
74
|
+
*/
|
|
75
|
+
type TokenRefPath = PathOf<'token'> | `${PathOf<'token'>}/${string}`;
|
|
76
|
+
declare function tokenRef<const Path extends TokenRefPath>(path: Path): TokenReference<Path>;
|
|
77
|
+
declare const Token: Omit<typeof TokenEntity, "ref"> & {
|
|
78
|
+
readonly ref: typeof tokenRef;
|
|
79
|
+
};
|
|
67
80
|
//#endregion
|
|
68
|
-
export { OverrideCondition, Token, TokenBody, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
|
|
81
|
+
export { OverrideCondition, Token, TokenBody, TokenRefPath, TokenReference, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
|
|
@@ -4,6 +4,7 @@ import { defineSubEntity } from "../../framework/defineSubEntity.js";
|
|
|
4
4
|
import { composedValueSchemas } from "./composed-values.js";
|
|
5
5
|
import { Font } from "./Font.js";
|
|
6
6
|
import { Modifier } from "./Modifier.js";
|
|
7
|
+
import { TOKEN_READERS } from "./token-values.js";
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
//#region src/entities/system/Token.ts
|
|
9
10
|
/**
|
|
@@ -112,7 +113,7 @@ function defineOverride(value) {
|
|
|
112
113
|
});
|
|
113
114
|
}
|
|
114
115
|
const Override = defineOverride(tokenValue);
|
|
115
|
-
const
|
|
116
|
+
const TokenEntity = defineEntity({
|
|
116
117
|
kind: "token",
|
|
117
118
|
fields: z.object({
|
|
118
119
|
value: tokenValue,
|
|
@@ -124,5 +125,24 @@ const Token = defineEntity({
|
|
|
124
125
|
subEntities: { overrides: Override },
|
|
125
126
|
schemas: { create: { description: `Create a token at \`<group>/<name>\` — \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${JSON.stringify(ref("token", "<group>"))}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.` } }
|
|
126
127
|
});
|
|
128
|
+
/**
|
|
129
|
+
* Token refs add the value readers a render calls — `Token.ref('spacing/2.5').px()`. Phantoms, like a
|
|
130
|
+
* component ref's `render`: the build resolves each read in place and the authoring file's own call is
|
|
131
|
+
* never made, so calling one anyway names what happened. Non-enumerable, so the marker a config
|
|
132
|
+
* evaluates to is the same small wire form a hydrated config receives.
|
|
133
|
+
*/
|
|
134
|
+
const baseTokenRef = TokenEntity.ref.bind(TokenEntity);
|
|
135
|
+
function tokenRef(path) {
|
|
136
|
+
const value = baseTokenRef(path);
|
|
137
|
+
for (const reader of TOKEN_READERS) Object.defineProperty(value, reader, {
|
|
138
|
+
enumerable: false,
|
|
139
|
+
configurable: false,
|
|
140
|
+
value: () => {
|
|
141
|
+
throw new Error(`[uds] Token.ref(${JSON.stringify(path)}).${reader}() was called from an authoring file. The build resolves a token read in place, and a render only ever runs from the module it emits.`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
const Token = Object.assign(TokenEntity, { ref: tokenRef });
|
|
127
147
|
//#endregion
|
|
128
148
|
export { Token, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isRef, splitRef } from "../../framework/utils/refs.js";
|
|
2
|
-
import { resolveTokenValueUnder } from "../../framework/projections.js";
|
|
3
2
|
import { resolveValueType } from "../../css/value-type.js";
|
|
3
|
+
import { resolveTokenValueUnder } from "../../framework/projections.js";
|
|
4
4
|
import { matchEntities, searchEntities } from "../../framework/entity-search.js";
|
|
5
5
|
//#region src/entities/system/token-index.ts
|
|
6
6
|
/**
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Ref } from "../../framework/utils/refs.js";
|
|
2
|
+
import { Config } from "../../framework/Config.js";
|
|
3
|
+
import { RootFontSize } from "../../css/root-font-size.js";
|
|
4
|
+
|
|
5
|
+
//#region src/entities/system/token-values.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A resolved value and the numbers a JS caller reads off it. `px` is present for a length in `px`,
|
|
8
|
+
* for a unitless number, and for a length in `rem` when the page's root font size fixes one; `ms` for
|
|
9
|
+
* a time in `s` or `ms`. Anything else — a color, a font stack, a percentage — is its CSS string only.
|
|
10
|
+
*/
|
|
11
|
+
interface TokenValueEntry {
|
|
12
|
+
readonly css: string;
|
|
13
|
+
readonly px?: number;
|
|
14
|
+
readonly ms?: number;
|
|
15
|
+
}
|
|
16
|
+
/** What a token resolved to, or why it did not. */
|
|
17
|
+
type TokenValueState = /** One value, the same under every mode and state. */{
|
|
18
|
+
readonly kind: 'value';
|
|
19
|
+
readonly value: TokenValueEntry;
|
|
20
|
+
}
|
|
21
|
+
/** A modifier decides it — an override on the token or on one it aliases — so it is only ever a
|
|
22
|
+
* `var()` on the page. */
|
|
23
|
+
| {
|
|
24
|
+
readonly kind: 'conditional';
|
|
25
|
+
}
|
|
26
|
+
/** Nothing to resolve to: an alias chain that never ends, a linked source this config did not
|
|
27
|
+
* reach, or a value that is not one scalar. */
|
|
28
|
+
| {
|
|
29
|
+
readonly kind: 'unresolved';
|
|
30
|
+
};
|
|
31
|
+
/** One token as the runtime module carries it. */
|
|
32
|
+
interface TokenValueRow {
|
|
33
|
+
readonly path: string;
|
|
34
|
+
/** The first path segment, which is the group a reader is addressed by. */
|
|
35
|
+
readonly group: string;
|
|
36
|
+
/** The rest of the path — `2.5` for `spacing/2.5`, `blue/3` for `spectrum/blue/3`. A path with no
|
|
37
|
+
* slash is its own group and alias. */
|
|
38
|
+
readonly alias: string;
|
|
39
|
+
/** The custom property the stylesheet declares it as, spelled by the config that owns it. */
|
|
40
|
+
readonly variable: string;
|
|
41
|
+
readonly state: TokenValueState;
|
|
42
|
+
}
|
|
43
|
+
/** The root font size of the page this config's sheet is for, as stated or the browser's own. A
|
|
44
|
+
* stored value already passed the setting's own check, so it parses. */
|
|
45
|
+
declare function rootFontSize(config: Config): RootFontSize;
|
|
46
|
+
/**
|
|
47
|
+
* Every token this config lists, in list order, as {@link TokenValueRow}s.
|
|
48
|
+
*
|
|
49
|
+
* Keyed on the token and settings slices, plus the attached sources every memo keys on: a token edit
|
|
50
|
+
* or a `rem` change recomputes, a component edit does not. A source's values reach here through the
|
|
51
|
+
* closure the artifact recorded whether or not the source is attached, so the two answer the same.
|
|
52
|
+
*/
|
|
53
|
+
declare const tokenValueRows: (config: Config) => readonly TokenValueRow[];
|
|
54
|
+
/**
|
|
55
|
+
* One token's row by ref or path, qualified or not. A qualified target is read in the source it
|
|
56
|
+
* names, so the row spells that source's variable and value; an unqualified one is this config's,
|
|
57
|
+
* including a row this config derived from a link.
|
|
58
|
+
*/
|
|
59
|
+
declare function tokenValueRow({
|
|
60
|
+
config,
|
|
61
|
+
target
|
|
62
|
+
}: {
|
|
63
|
+
config: Config;
|
|
64
|
+
target: string | Ref;
|
|
65
|
+
}): TokenValueRow | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Whether a `token:` target names something this config can answer for — an item, qualified or not,
|
|
68
|
+
* or a group. A group is what a read whose path a prop completes is an edge to.
|
|
69
|
+
*/
|
|
70
|
+
declare function tokenExists({
|
|
71
|
+
config,
|
|
72
|
+
target
|
|
73
|
+
}: {
|
|
74
|
+
config: Config;
|
|
75
|
+
target: string | Ref;
|
|
76
|
+
}): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Token path → what the build resolved it to, the augmentation the emitted `uds-env.d.ts` fills:
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* declare module '@yahoo/uds-create-config' {
|
|
82
|
+
* interface RegisteredTokenValues {
|
|
83
|
+
* 'spacing/1': { css: '0.25rem'; px: 4 };
|
|
84
|
+
* 'motion/fast': { css: '200ms'; ms: 200 };
|
|
85
|
+
* 'color/brand': {};
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* Every token is a key, so `var()` types against all of them; `css`, `px` and `ms` are present only
|
|
91
|
+
* where the token resolves to one value, which is what makes `px('brand')` on a colour an error.
|
|
92
|
+
*/
|
|
93
|
+
interface RegisteredTokenValues {}
|
|
94
|
+
/**
|
|
95
|
+
* One token group's readers. `var` is available for every token; the others only for the tokens the
|
|
96
|
+
* build resolved to one value, so a mode-dependent token is a type error everywhere but `var`.
|
|
97
|
+
*/
|
|
98
|
+
interface TokenGroupReader<All extends string = string, Static extends string = string, Px extends string = string, Ms extends string = string> {
|
|
99
|
+
/** `var(--uds-spacing-2_5)` — the reference the stylesheet's own rules use, so it follows modes. */
|
|
100
|
+
var(alias: All): string;
|
|
101
|
+
/** The resolved CSS value, `'0.625rem'`. */
|
|
102
|
+
value(alias: Static): string;
|
|
103
|
+
/** The value as CSS pixels: `rem` against the page's root font size (`settings.css.rem`), `px`
|
|
104
|
+
* as written, a unitless number as itself. */
|
|
105
|
+
px(alias: Px): number;
|
|
106
|
+
/** The value in milliseconds, for a duration or a delay. */
|
|
107
|
+
ms(alias: Ms): number;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The readers a token ref offers, by what the build resolved that path to. A path the augmentation
|
|
111
|
+
* knows narrows to the readers its value supports, so `.px()` on a colour is a compile error; a path it
|
|
112
|
+
* does not know — before a build, or a template a prop fills in — keeps all four.
|
|
113
|
+
*/
|
|
114
|
+
type TokenReaders<P extends string> = P extends keyof RegisteredTokenValues ? ReadersOf<RegisteredTokenValues[P]> : ReadersOf<{
|
|
115
|
+
css: string;
|
|
116
|
+
px: number;
|
|
117
|
+
ms: number;
|
|
118
|
+
}>;
|
|
119
|
+
type ReadersOf<V> = {
|
|
120
|
+
/** `var(--uds-spacing-2_5)` — the reference the stylesheet's own rules use, so it follows modes. */var(): string;
|
|
121
|
+
} & (V extends {
|
|
122
|
+
css: string;
|
|
123
|
+
} ? {
|
|
124
|
+
value(): string;
|
|
125
|
+
} : unknown) & (V extends {
|
|
126
|
+
px: number;
|
|
127
|
+
} ? {
|
|
128
|
+
px(): number;
|
|
129
|
+
} : unknown) & (V extends {
|
|
130
|
+
ms: number;
|
|
131
|
+
} ? {
|
|
132
|
+
ms(): number;
|
|
133
|
+
} : unknown);
|
|
134
|
+
/** The reader names, which is what the build recognizes on a `Token.ref(…)`. */
|
|
135
|
+
declare const TOKEN_READERS: readonly ["var", "value", "px", "ms"];
|
|
136
|
+
type TokenReader = (typeof TOKEN_READERS)[number];
|
|
137
|
+
//#endregion
|
|
138
|
+
export { RegisteredTokenValues, TOKEN_READERS, TokenGroupReader, TokenReader, TokenReaders, TokenValueEntry, TokenValueRow, TokenValueState, rootFontSize, tokenExists, tokenValueRow, tokenValueRows };
|