@yahoo/uds-create-config 5.15.2 → 5.16.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/capture/class-grammar.js +1 -1
- package/dist/configs/react-native-system.d.ts +31 -1
- package/dist/configs/system.js +2 -0
- package/dist/entities/native/NativeStyleProperty.d.ts +31 -1
- package/dist/entities/system/Component.js +7 -4
- package/dist/entities/system/StyleProperty.d.ts +43 -2
- package/dist/entities/system/StyleProperty.js +86 -6
- package/dist/framework/Config.d.ts +12 -1
- package/dist/framework/Config.js +46 -3
- package/dist/framework/class-names.d.ts +16 -2
- package/dist/framework/class-names.js +45 -9
- package/dist/framework/projections.d.ts +85 -2
- package/dist/framework/projections.js +280 -8
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/value-domain.js +9 -8
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/migrations/20261007210309_style_numbers_name_numeric_values.d.ts +23 -0
- package/dist/migrations/20261007210309_style_numbers_name_numeric_values.js +217 -0
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +2 -2
- package/dist/spec/rewrite-refs.d.ts +31 -1
- package/dist/spec/rewrite-refs.js +34 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -54,7 +54,7 @@ function classGrammar(config) {
|
|
|
54
54
|
const list = byCss.get(property) ?? [];
|
|
55
55
|
list.push(matchFor({
|
|
56
56
|
path: entity.path,
|
|
57
|
-
arbitrary: body.arbitrary
|
|
57
|
+
arbitrary: Boolean(body.arbitrary),
|
|
58
58
|
negates: body.negates === true
|
|
59
59
|
}));
|
|
60
60
|
byCss.set(property, list);
|
|
@@ -95,7 +95,37 @@ declare const ReactNativeSystemDefinition: ConfigClass<{
|
|
|
95
95
|
values: _$zod.ZodOptional<_$zod.ZodArray<_$zod.ZodType<StylePropValue, unknown, _$zod_v4_core0.$ZodTypeInternals<StylePropValue, unknown>>>>;
|
|
96
96
|
responsive: _$zod.ZodDefault<_$zod.ZodBoolean>;
|
|
97
97
|
classPrefix: _$zod.ZodOptional<_$zod.ZodString>;
|
|
98
|
-
arbitrary: _$zod.ZodDefault<_$zod.ZodBoolean
|
|
98
|
+
arbitrary: _$zod.ZodDefault<_$zod.ZodUnion<readonly [_$zod.ZodBoolean, _$zod.ZodEnum<{
|
|
99
|
+
ch: "ch";
|
|
100
|
+
dvh: "dvh";
|
|
101
|
+
dvw: "dvw";
|
|
102
|
+
em: "em";
|
|
103
|
+
ex: "ex";
|
|
104
|
+
lvh: "lvh";
|
|
105
|
+
lvw: "lvw";
|
|
106
|
+
px: "px";
|
|
107
|
+
rem: "rem";
|
|
108
|
+
svh: "svh";
|
|
109
|
+
svw: "svw";
|
|
110
|
+
vh: "vh";
|
|
111
|
+
vmax: "vmax";
|
|
112
|
+
vmin: "vmin";
|
|
113
|
+
vw: "vw";
|
|
114
|
+
"%": "%";
|
|
115
|
+
deg: "deg";
|
|
116
|
+
rad: "rad";
|
|
117
|
+
turn: "turn";
|
|
118
|
+
s: "s";
|
|
119
|
+
ms: "ms";
|
|
120
|
+
fr: "fr";
|
|
121
|
+
unitless: "unitless";
|
|
122
|
+
lh: "lh";
|
|
123
|
+
rlh: "rlh";
|
|
124
|
+
cqw: "cqw";
|
|
125
|
+
cqh: "cqh";
|
|
126
|
+
cqi: "cqi";
|
|
127
|
+
cqb: "cqb";
|
|
128
|
+
}>]>>;
|
|
99
129
|
negates: _$zod.ZodDefault<_$zod.ZodBoolean>;
|
|
100
130
|
selector: _$zod.ZodOptional<_$zod.ZodString>;
|
|
101
131
|
alpha: _$zod.ZodOptional<_$zod.ZodString>;
|
package/dist/configs/system.js
CHANGED
|
@@ -59,6 +59,7 @@ import { addAndSubtractMigration } from "../migrations/20261005220312_add_and_su
|
|
|
59
59
|
import { systemIncludeMigration } from "../migrations/20261005225949_system_include.js";
|
|
60
60
|
import { motionVariantsAndLayoutMigration } from "../migrations/20261006125609_motion_variants_and_layout.js";
|
|
61
61
|
import { systemScriptsMigration } from "../migrations/20261007182932_system_scripts.js";
|
|
62
|
+
import { styleNumbersNameNumericValuesMigration } from "../migrations/20261007210309_style_numbers_name_numeric_values.js";
|
|
62
63
|
const System = defineConfig({
|
|
63
64
|
name: WEB_CONFIG_NAME,
|
|
64
65
|
description: "A design system: the values, primitives and compositions an app is built from. A ref is written `{ \"__ref\": \"<kind>:<path>\" }`, so a token is `{ \"__ref\": \"token:<group>/<name>\" }` and a token group is `{ \"__ref\": \"token:<group>\" }`. Build it in this order. Tokens first, in groups by purpose (colors, spacing, radii, type sizes), since everything else names them. Then style properties, each with `values` holding one ref to the token group whose members it accepts (`{ \"__ref\": \"token:color\" }`), so a prop takes token names rather than raw CSS; add a string keyword only for a value no token holds. A style rule's bag holds a prop's values (`\"padding\": \"4\"`, `\"bg\": \"brand\"`), never raw CSS. Then a few primitive components, one layer each, whose props are style properties (a box for layout and surface, a text for type, a pressable for interaction). Then every other component composed from those primitives: its layers name a primitive or an element, its anatomy places them (a node renders a layer, a tag or `Slot` where a slot prop's content lands; `children` is the edge list; `visible` is `{ \"$state\": \"<prop>\" }`; a prop shown in a nested layer is a `forward` route, and `$state` never sits in a node's props), and its style rules set style-property values per layer, keyed by prop values when a variant changes them. Modes (a dark mode is `{ type: \"selector\", value: \".dark\" }`) change token values through a token's `overrides`, so a component never restyles for a mode; modifiers (hover, focus, disabled) name a state, and a style rule applies under one as a `_hover` block inside a layer bag. Composites bundle style values that recur. Last, register canvas roles: the canvas draws with them, one entity per tool (`frame` for the layout primitive, `text` for the text primitive, `image`) whose entries name the components that tool inserts, and a tool whose role is unregistered is disabled. Give each component a `preview` entry under `*` so it shows content, and never write a CSS property name where a style-property name belongs.",
|
|
@@ -129,5 +130,6 @@ if (!registeredSchemaMigrations().has(20261005220312)) registerSchemaMigrations(
|
|
|
129
130
|
if (!registeredSchemaMigrations().has(20261005225949)) registerSchemaMigrations(systemIncludeMigration);
|
|
130
131
|
if (!registeredSchemaMigrations().has(20261006125609)) registerSchemaMigrations(motionVariantsAndLayoutMigration);
|
|
131
132
|
if (!registeredSchemaMigrations().has(20261007182932)) registerSchemaMigrations(systemScriptsMigration);
|
|
133
|
+
if (!registeredSchemaMigrations().has(20261007210309)) registerSchemaMigrations(styleNumbersNameNumericValuesMigration);
|
|
132
134
|
//#endregion
|
|
133
135
|
export { System };
|
|
@@ -168,7 +168,37 @@ declare const NativeStyleProperty: EntityClass<z.ZodObject<Omit<{
|
|
|
168
168
|
values: z.ZodOptional<z.ZodArray<z.ZodType<StylePropValue, unknown, z.core.$ZodTypeInternals<StylePropValue, unknown>>>>;
|
|
169
169
|
responsive: z.ZodDefault<z.ZodBoolean>;
|
|
170
170
|
classPrefix: z.ZodOptional<z.ZodString>;
|
|
171
|
-
arbitrary: z.ZodDefault<z.ZodBoolean
|
|
171
|
+
arbitrary: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodEnum<{
|
|
172
|
+
ch: "ch";
|
|
173
|
+
dvh: "dvh";
|
|
174
|
+
dvw: "dvw";
|
|
175
|
+
em: "em";
|
|
176
|
+
ex: "ex";
|
|
177
|
+
lvh: "lvh";
|
|
178
|
+
lvw: "lvw";
|
|
179
|
+
px: "px";
|
|
180
|
+
rem: "rem";
|
|
181
|
+
svh: "svh";
|
|
182
|
+
svw: "svw";
|
|
183
|
+
vh: "vh";
|
|
184
|
+
vmax: "vmax";
|
|
185
|
+
vmin: "vmin";
|
|
186
|
+
vw: "vw";
|
|
187
|
+
"%": "%";
|
|
188
|
+
deg: "deg";
|
|
189
|
+
rad: "rad";
|
|
190
|
+
turn: "turn";
|
|
191
|
+
s: "s";
|
|
192
|
+
ms: "ms";
|
|
193
|
+
fr: "fr";
|
|
194
|
+
unitless: "unitless";
|
|
195
|
+
lh: "lh";
|
|
196
|
+
rlh: "rlh";
|
|
197
|
+
cqw: "cqw";
|
|
198
|
+
cqh: "cqh";
|
|
199
|
+
cqi: "cqi";
|
|
200
|
+
cqb: "cqb";
|
|
201
|
+
}>]>>;
|
|
172
202
|
negates: z.ZodDefault<z.ZodBoolean>;
|
|
173
203
|
selector: z.ZodOptional<z.ZodString>;
|
|
174
204
|
alpha: z.ZodOptional<z.ZodString>;
|
|
@@ -13,7 +13,7 @@ import { Package } from "./Package.js";
|
|
|
13
13
|
import { bindPreviewGlyphs, glyphLeafOf } from "./glyph-identity.js";
|
|
14
14
|
import { Style, addBagKeyIssues, anatomyPropValue, bagNameEdges, bagRewriteName, bindBagTokens, collectBag, isPlainRecord, openBag, styleValue } from "./style-bag.js";
|
|
15
15
|
import { storedPreview } from "../../framework/preview-store.js";
|
|
16
|
-
import { glyphPropsOf, propOwner, propValueDomain, refListOf, resolveComponentProps, stylePropValueLeaves, validateComponentProps } from "../../framework/projections.js";
|
|
16
|
+
import { glyphPropsOf, propOwner, propValueDomain, refListOf, resolveComponentProps, stylePropNumberUnit, stylePropValueLeaves, validateComponentProps } from "../../framework/projections.js";
|
|
17
17
|
import { carriesContent, intoName, isValueSlot, renderBoundProp, routedPropIn, routesContent } from "../../framework/prop-surface.js";
|
|
18
18
|
import { inlineJsMotion } from "./Motion.js";
|
|
19
19
|
import { slotAnatomyPropOf, slotTargetsOf, withoutTruthyTerm } from "../../framework/render-spec.js";
|
|
@@ -668,12 +668,15 @@ function styleBagValue({ config, declaration, name = "<prop>", glyphLibraries })
|
|
|
668
668
|
gradient,
|
|
669
669
|
shadowValue,
|
|
670
670
|
refSchema
|
|
671
|
-
], { error: () => {
|
|
671
|
+
], { error: (issue) => {
|
|
672
672
|
const leaves = stylePropValueLeaves(config, property);
|
|
673
|
+
if (typeof issue.input === "number" && leaves.includes(String(issue.input))) return `\`${name}\`: \`${issue.input}\` names a value as a string, so write \`'${issue.input}'\`. A number names only a value the style property declares as a number.`;
|
|
673
674
|
const shown = leaves.slice(0, 12).map((leaf) => `\`${leaf}\``);
|
|
674
675
|
const more = leaves.length > 12 ? `, … (${leaves.length} in all)` : "";
|
|
675
|
-
const arbitrary = (config.resolve("style-property", property)?.toJSON())?.arbitrary
|
|
676
|
-
|
|
676
|
+
const arbitrary = (config.resolve("style-property", property)?.toJSON())?.arbitrary ? ", or a bracketed literal such as `[13px]`" : "";
|
|
677
|
+
const unit = stylePropNumberUnit(config, property);
|
|
678
|
+
const number = unit === void 0 ? "" : unit === "unitless" ? ", or a number" : `, or a number of \`${unit}\` such as \`600\``;
|
|
679
|
+
return `\`${name}\` takes one of ${shown.join(", ")}${more}${arbitrary}${number}, or a token ref. A value the system has no token for is added to the style property's \`values\` first.`;
|
|
677
680
|
} });
|
|
678
681
|
}
|
|
679
682
|
/**
|
|
@@ -82,6 +82,10 @@ interface StylePropLeaf {
|
|
|
82
82
|
readonly atRules?: Record<string, Record<string, string>>;
|
|
83
83
|
/** The class the value is written as, when it names its own. */
|
|
84
84
|
readonly className?: string;
|
|
85
|
+
/** The value was declared as a NUMBER — a numeric literal (`values: [1, 2]`) or alias
|
|
86
|
+
* (`{ alias: 3, … }`) — so it is written as one (`columns={3}`). A leaf without it is a string
|
|
87
|
+
* however it reads: a token named `4` is `spacing="4"`. */
|
|
88
|
+
readonly numeric?: true;
|
|
85
89
|
}
|
|
86
90
|
/** One entry a ref names: the leaf it is called and the qualified path it stores. */
|
|
87
91
|
interface RefMember {
|
|
@@ -129,6 +133,13 @@ interface TokenVariableAsText {
|
|
|
129
133
|
* follows it. A variable no token declares, such as a third-party library's, isn't one.
|
|
130
134
|
*/
|
|
131
135
|
declare function tokenVariablesAsText(config: Config): TokenVariableAsText[];
|
|
136
|
+
/**
|
|
137
|
+
* The units a style property can read a bare number in (`arbitrary: 'px'`), and `unitless` for a
|
|
138
|
+
* property whose CSS takes a plain number (`line-height`, `opacity`). A closed list, so
|
|
139
|
+
* `arbitrary: 'pxx'` is refused rather than written into every declaration the prop makes.
|
|
140
|
+
*/
|
|
141
|
+
declare const STYLE_PROP_NUMBER_UNITS: readonly ["unitless", "px", "rem", "em", "%", "ch", "ex", "lh", "rlh", "vw", "vh", "vmin", "vmax", "svw", "svh", "lvw", "lvh", "dvw", "dvh", "cqw", "cqh", "cqi", "cqb", "fr", "ms", "s", "deg", "rad", "turn"];
|
|
142
|
+
type StylePropNumberUnit = (typeof STYLE_PROP_NUMBER_UNITS)[number];
|
|
132
143
|
/** StyleProperty (`flex/direction`) — a prop a component can expose, mapped to a CSS
|
|
133
144
|
* property with an allowed value set. Grouping (`flex`) is emergent from the path. */
|
|
134
145
|
declare const StylePropertyEntity: EntityClass<z.ZodObject<{
|
|
@@ -136,7 +147,37 @@ declare const StylePropertyEntity: EntityClass<z.ZodObject<{
|
|
|
136
147
|
values: z.ZodOptional<z.ZodArray<z.ZodType<StylePropValue, unknown, z.core.$ZodTypeInternals<StylePropValue, unknown>>>>;
|
|
137
148
|
responsive: z.ZodDefault<z.ZodBoolean>;
|
|
138
149
|
classPrefix: z.ZodOptional<z.ZodString>;
|
|
139
|
-
arbitrary: z.ZodDefault<z.ZodBoolean
|
|
150
|
+
arbitrary: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodEnum<{
|
|
151
|
+
ch: "ch";
|
|
152
|
+
dvh: "dvh";
|
|
153
|
+
dvw: "dvw";
|
|
154
|
+
em: "em";
|
|
155
|
+
ex: "ex";
|
|
156
|
+
lvh: "lvh";
|
|
157
|
+
lvw: "lvw";
|
|
158
|
+
px: "px";
|
|
159
|
+
rem: "rem";
|
|
160
|
+
svh: "svh";
|
|
161
|
+
svw: "svw";
|
|
162
|
+
vh: "vh";
|
|
163
|
+
vmax: "vmax";
|
|
164
|
+
vmin: "vmin";
|
|
165
|
+
vw: "vw";
|
|
166
|
+
"%": "%";
|
|
167
|
+
deg: "deg";
|
|
168
|
+
rad: "rad";
|
|
169
|
+
turn: "turn";
|
|
170
|
+
s: "s";
|
|
171
|
+
ms: "ms";
|
|
172
|
+
fr: "fr";
|
|
173
|
+
unitless: "unitless";
|
|
174
|
+
lh: "lh";
|
|
175
|
+
rlh: "rlh";
|
|
176
|
+
cqw: "cqw";
|
|
177
|
+
cqh: "cqh";
|
|
178
|
+
cqi: "cqi";
|
|
179
|
+
cqb: "cqb";
|
|
180
|
+
}>]>>;
|
|
140
181
|
negates: z.ZodDefault<z.ZodBoolean>;
|
|
141
182
|
selector: z.ZodOptional<z.ZodString>;
|
|
142
183
|
alpha: z.ZodOptional<z.ZodString>;
|
|
@@ -168,4 +209,4 @@ declare function cssPropertyNames(styleProp: {
|
|
|
168
209
|
readonly properties: readonly string[];
|
|
169
210
|
}): string[];
|
|
170
211
|
//#endregion
|
|
171
|
-
export { RefMember, STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StylePropLeaf, StylePropValue, StylePropValueCtx, StylePropValueKind, StyleProperty, StylePropertyClass, TokenVariableAsText, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName, tokenVariablesAsText };
|
|
212
|
+
export { RefMember, STYLE_PROP_NUMBER_UNITS, STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StylePropLeaf, StylePropNumberUnit, StylePropValue, StylePropValueCtx, StylePropValueKind, StyleProperty, StylePropertyClass, TokenVariableAsText, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName, tokenVariablesAsText };
|
|
@@ -158,6 +158,10 @@ const STYLE_PROP_VALUE_FORMS = {
|
|
|
158
158
|
})
|
|
159
159
|
};
|
|
160
160
|
const KINDS = Object.keys(STYLE_PROP_VALUE_FORMS);
|
|
161
|
+
/** `{ numeric: true }` for an alias declared as a number. */
|
|
162
|
+
function numericOf(alias) {
|
|
163
|
+
return typeof alias === "number" ? { numeric: true } : {};
|
|
164
|
+
}
|
|
161
165
|
/** The value half of a flag as leaves carry it. */
|
|
162
166
|
function flagValue(value, members) {
|
|
163
167
|
if (typeof value === "string") return [{ value }];
|
|
@@ -180,7 +184,8 @@ const STYLE_PROP_VALUE_LEAVES = {
|
|
|
180
184
|
}],
|
|
181
185
|
number: (entry) => [{
|
|
182
186
|
leaf: String(entry),
|
|
183
|
-
value: String(entry)
|
|
187
|
+
value: String(entry),
|
|
188
|
+
numeric: true
|
|
184
189
|
}],
|
|
185
190
|
token: (entry, members) => members(entry).map(({ leaf, path }) => ({
|
|
186
191
|
leaf,
|
|
@@ -202,18 +207,21 @@ const STYLE_PROP_VALUE_LEAVES = {
|
|
|
202
207
|
aliasToken: (entry, members) => members(entry.value).map(({ path }) => ({
|
|
203
208
|
leaf: String(entry.alias),
|
|
204
209
|
value: path,
|
|
205
|
-
...classNameOf(entry)
|
|
210
|
+
...classNameOf(entry),
|
|
211
|
+
...numericOf(entry.alias)
|
|
206
212
|
})),
|
|
207
213
|
aliasLiteral: (entry) => [{
|
|
208
214
|
leaf: String(entry.alias),
|
|
209
215
|
value: entry.value,
|
|
210
|
-
...classNameOf(entry)
|
|
216
|
+
...classNameOf(entry),
|
|
217
|
+
...numericOf(entry.alias)
|
|
211
218
|
}],
|
|
212
219
|
aliasDeclarations: (entry) => [{
|
|
213
220
|
leaf: String(entry.alias),
|
|
214
221
|
value: "",
|
|
215
222
|
...splitDeclarations(entry.value),
|
|
216
|
-
...classNameOf(entry)
|
|
223
|
+
...classNameOf(entry),
|
|
224
|
+
...numericOf(entry.alias)
|
|
217
225
|
}]
|
|
218
226
|
};
|
|
219
227
|
/** A bag's declarations as text, each ref as its marker. */
|
|
@@ -434,6 +442,66 @@ const tokenPathsByValue = createSliceMemo({
|
|
|
434
442
|
return tokens;
|
|
435
443
|
}
|
|
436
444
|
});
|
|
445
|
+
/**
|
|
446
|
+
* The units a style property can read a bare number in (`arbitrary: 'px'`), and `unitless` for a
|
|
447
|
+
* property whose CSS takes a plain number (`line-height`, `opacity`). A closed list, so
|
|
448
|
+
* `arbitrary: 'pxx'` is refused rather than written into every declaration the prop makes.
|
|
449
|
+
*/
|
|
450
|
+
const STYLE_PROP_NUMBER_UNITS = [
|
|
451
|
+
"unitless",
|
|
452
|
+
"px",
|
|
453
|
+
"rem",
|
|
454
|
+
"em",
|
|
455
|
+
"%",
|
|
456
|
+
"ch",
|
|
457
|
+
"ex",
|
|
458
|
+
"lh",
|
|
459
|
+
"rlh",
|
|
460
|
+
"vw",
|
|
461
|
+
"vh",
|
|
462
|
+
"vmin",
|
|
463
|
+
"vmax",
|
|
464
|
+
"svw",
|
|
465
|
+
"svh",
|
|
466
|
+
"lvw",
|
|
467
|
+
"lvh",
|
|
468
|
+
"dvw",
|
|
469
|
+
"dvh",
|
|
470
|
+
"cqw",
|
|
471
|
+
"cqh",
|
|
472
|
+
"cqi",
|
|
473
|
+
"cqb",
|
|
474
|
+
"fr",
|
|
475
|
+
"ms",
|
|
476
|
+
"s",
|
|
477
|
+
"deg",
|
|
478
|
+
"rad",
|
|
479
|
+
"turn"
|
|
480
|
+
];
|
|
481
|
+
/**
|
|
482
|
+
* Why a prop can't read a bare number in a unit, or `undefined` when it can. A bare number is written
|
|
483
|
+
* as an inline declaration on each of the prop's `properties`, so a prop needs some, and one with a
|
|
484
|
+
* `selector` or `declarations` of its own would paint wrong that way. A field left out of `body` isn't
|
|
485
|
+
* judged, so an update is checked by only the fields it carries.
|
|
486
|
+
*/
|
|
487
|
+
function numberUnitRefusal(body) {
|
|
488
|
+
if (body.properties !== void 0 && body.properties.length === 0) return "A prop that takes a number writes it to its `properties`, and this one sets none.";
|
|
489
|
+
if (body.selector || Object.keys(body.declarations ?? {}).length > 0) return "A number is written inline on the element, so a prop with a `selector` or its own `declarations` cannot take one. Use `arbitrary: true` for its off-scale values.";
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* A check refusing an `arbitrary` unit the rest of the body can't honour ({@link numberUnitRefusal}).
|
|
493
|
+
* An update carries only the fields it sets, so a field it leaves out is judged by the stored body,
|
|
494
|
+
* which the emitters guard themselves (`bodyNumberUnit`).
|
|
495
|
+
*/
|
|
496
|
+
const numberFitsBody = (body) => (ctx) => {
|
|
497
|
+
if (typeof ctx.value !== "string") return;
|
|
498
|
+
const refusal = numberUnitRefusal(body ?? {});
|
|
499
|
+
if (refusal) ctx.issues.push({
|
|
500
|
+
code: "custom",
|
|
501
|
+
input: ctx.value,
|
|
502
|
+
message: refusal
|
|
503
|
+
});
|
|
504
|
+
};
|
|
437
505
|
/** A check refusing a ref to a token or group nothing declares: it offers no values, and the prop
|
|
438
506
|
* paints nothing. */
|
|
439
507
|
const namesDeclaredTokens = (config) => (ctx) => {
|
|
@@ -493,7 +561,19 @@ const styleFields = ({ config, body, linked } = {}) => z.object({
|
|
|
493
561
|
* to the prop's own path, which is the common case.
|
|
494
562
|
*/
|
|
495
563
|
classPrefix: z.string().optional().describe("The class-name segment the prop contributes. Defaults to the prop's own path; `''` for self-describing values."),
|
|
496
|
-
|
|
564
|
+
/**
|
|
565
|
+
* The prop's open side: what it takes beside the fixed list in `values`. `true` takes a bracketed
|
|
566
|
+
* literal (`[13px]`, `[#abc]`). A unit also takes a bare number, read in that unit: `'px'` makes
|
|
567
|
+
* `maxWidth={600}` paint `max-width: 600px` as `[600px]` would, and `'unitless'` writes the
|
|
568
|
+
* number as it is (`lineHeight={1.5}`). Any other unit is still a literal (`[40rem]`).
|
|
569
|
+
*
|
|
570
|
+
* One field, since a bare number is one more way to write an off-list value. Which unit a number
|
|
571
|
+
* means is the prop's API, so it is declared rather than read off the CSS grammar. On a prop with
|
|
572
|
+
* a unit the type decides: a number is the unit, and a value named by a number is the string
|
|
573
|
+
* (`spacing="4"` is the scale's step, `spacing={4}` is `4px`). A number the prop declares as a
|
|
574
|
+
* value (`values: [1, 2]`, `{ alias: 3, … }`) is still that value.
|
|
575
|
+
*/
|
|
576
|
+
arbitrary: (config ? z.union([z.boolean(), z.enum(STYLE_PROP_NUMBER_UNITS)]).check(numberFitsBody(body)) : z.union([z.boolean(), z.enum(STYLE_PROP_NUMBER_UNITS)])).default(false).describe("What the prop takes beside its `values`: `true` takes a bracketed literal (`[13px]`); a unit also takes a bare number in it (`'px'` makes `600` paint `600px`, `'unitless'` writes `1.5` as it is). A number the prop declares as a value (`{ alias: 3, … }`) is still that value, and a token's name is a string: `spacing=\"4\"` is the step, `spacing={4}` a length. Defaults to `false`."),
|
|
497
577
|
/**
|
|
498
578
|
* The prop applies the NEGATION of its value — `offsetX="3"` paints `margin-inline: calc(var(…) *
|
|
499
579
|
* -1)`.
|
|
@@ -578,4 +658,4 @@ function cssPropertyNames(styleProp) {
|
|
|
578
658
|
return styleProp.properties.map(toCssPropertyName);
|
|
579
659
|
}
|
|
580
660
|
//#endregion
|
|
581
|
-
export { STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StyleProperty, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName, tokenVariablesAsText };
|
|
661
|
+
export { STYLE_PROP_NUMBER_UNITS, STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StyleProperty, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, numberUnitRefusal, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName, tokenVariablesAsText };
|
|
@@ -73,7 +73,7 @@ interface RecordedSource {
|
|
|
73
73
|
* The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
|
|
74
74
|
* that replaced it (see `detectedWireVersion`).
|
|
75
75
|
*/
|
|
76
|
-
declare const SERIALIZED_CONFIG_VERSION =
|
|
76
|
+
declare const SERIALIZED_CONFIG_VERSION = 20261007210309;
|
|
77
77
|
interface SerializedConfig {
|
|
78
78
|
/** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
|
|
79
79
|
readonly version: number;
|
|
@@ -1245,6 +1245,17 @@ declare class Config {
|
|
|
1245
1245
|
* the whole config; the index already knows the answer.
|
|
1246
1246
|
*/
|
|
1247
1247
|
private cascadeRename;
|
|
1248
|
+
/**
|
|
1249
|
+
* A style property's unit changed: every stored bag that sets it a bare number is rewritten as what it
|
|
1250
|
+
* painted before (`pinBodyStyleNumbers`), so the change doesn't reread what's already written. `gap: 6`
|
|
1251
|
+
* named the token `'6'` until `gap` took `px`, and `maxWidth: 4` was `4px` until it lost its unit. The
|
|
1252
|
+
* rewritten form — the value's name, or its bracket literal — paints the same under either reading,
|
|
1253
|
+
* so a replay lands on the same bodies.
|
|
1254
|
+
*
|
|
1255
|
+
* Like {@link cascadeRename}, only the components the ref index says set the property are visited;
|
|
1256
|
+
* canvas roles hold props by name, so each is read.
|
|
1257
|
+
*/
|
|
1258
|
+
private pinNumberReading;
|
|
1248
1259
|
/**
|
|
1249
1260
|
* Rename one key inside every record that is keyed by the renamed collection — a style rule's
|
|
1250
1261
|
* `when`, which names the props it conditions on.
|
package/dist/framework/Config.js
CHANGED
|
@@ -9,7 +9,7 @@ import { isComposedValue } from "../entities/system/composed-values.js";
|
|
|
9
9
|
import { borrowedSlug, classifyOverlay } from "./source-integrity.js";
|
|
10
10
|
import { kindBorrowable, titleCase } from "./registry.js";
|
|
11
11
|
import { isGroupBody } from "./utils/group.js";
|
|
12
|
-
import { authoredBag } from "./projections.js";
|
|
12
|
+
import { authoredBag, pinBodyStyleNumbers, stylePropNumberUnit } from "./projections.js";
|
|
13
13
|
import { isAuthoringPhantom, phantomsIn } from "./utils/authoring-phantom.js";
|
|
14
14
|
import { addressOf, editOp, inputOf, opVerb } from "./config-op.js";
|
|
15
15
|
import { configSchemas } from "./config-ops.js";
|
|
@@ -1617,7 +1617,12 @@ var Config = class Config {
|
|
|
1617
1617
|
}
|
|
1618
1618
|
/** The next snapshot for one patch, validated. Pure in `snapshot`; reads metadata off `this`. */
|
|
1619
1619
|
computeApply(snapshot, incoming, mode = "write", checked = true) {
|
|
1620
|
-
const
|
|
1620
|
+
const unitChange = incoming.kind === "style-property" && incoming.operation === "update" ? {
|
|
1621
|
+
path: incoming.path,
|
|
1622
|
+
was: stylePropNumberUnit(this.viewOf(snapshot), incoming.path)
|
|
1623
|
+
} : void 0;
|
|
1624
|
+
let next = this.computeApplyPatch(snapshot, incoming, mode);
|
|
1625
|
+
if (unitChange) next = this.pinNumberReading(next, unitChange.path, unitChange.was);
|
|
1621
1626
|
const view = this.viewOf(next);
|
|
1622
1627
|
if (checked) view.assertInvariants(incoming.kind);
|
|
1623
1628
|
else view.assertUniqueLeaves(incoming.kind);
|
|
@@ -2272,6 +2277,44 @@ var Config = class Config {
|
|
|
2272
2277
|
return snapshot.withChanges(changes);
|
|
2273
2278
|
}
|
|
2274
2279
|
/**
|
|
2280
|
+
* A style property's unit changed: every stored bag that sets it a bare number is rewritten as what it
|
|
2281
|
+
* painted before (`pinBodyStyleNumbers`), so the change doesn't reread what's already written. `gap: 6`
|
|
2282
|
+
* named the token `'6'` until `gap` took `px`, and `maxWidth: 4` was `4px` until it lost its unit. The
|
|
2283
|
+
* rewritten form — the value's name, or its bracket literal — paints the same under either reading,
|
|
2284
|
+
* so a replay lands on the same bodies.
|
|
2285
|
+
*
|
|
2286
|
+
* Like {@link cascadeRename}, only the components the ref index says set the property are visited;
|
|
2287
|
+
* canvas roles hold props by name, so each is read.
|
|
2288
|
+
*/
|
|
2289
|
+
pinNumberReading(after, path, was) {
|
|
2290
|
+
const view = this.viewOf(after);
|
|
2291
|
+
if (was === stylePropNumberUnit(view, path)) return after;
|
|
2292
|
+
const reading = new Map([[path, was]]);
|
|
2293
|
+
const changes = [];
|
|
2294
|
+
const visit = (kind, itemPath) => {
|
|
2295
|
+
const entry = this.entityOf(kind);
|
|
2296
|
+
const target = after.slice(kind).items.get(itemPath);
|
|
2297
|
+
if (!entry || !target) return;
|
|
2298
|
+
const body = target.toJSON();
|
|
2299
|
+
const pinned = pinBodyStyleNumbers(view, kind, body, reading, itemPath);
|
|
2300
|
+
if (pinned === body) return;
|
|
2301
|
+
let keys = isExtendedRef(pinned) ? ["__ref", "extend"] : entry.fieldKeys;
|
|
2302
|
+
if (isMintedRow(pinned)) keys = [...keys, MINTED];
|
|
2303
|
+
changes.push({
|
|
2304
|
+
kind,
|
|
2305
|
+
target: "item",
|
|
2306
|
+
path: itemPath,
|
|
2307
|
+
value: new Entity(itemPath, pinned, keys)
|
|
2308
|
+
});
|
|
2309
|
+
};
|
|
2310
|
+
for (const dependent of this.referrersOf(after, "style-property", path)) {
|
|
2311
|
+
const { kind, qualifiedPath } = splitRef(dependent);
|
|
2312
|
+
if (kind === "component") visit(kind, qualifiedPath);
|
|
2313
|
+
}
|
|
2314
|
+
if (this.entityOf("canvas-role")) for (const rolePath of after.slice("canvas-role").items.keys()) visit("canvas-role", rolePath);
|
|
2315
|
+
return changes.length > 0 ? after.withChanges(changes) : after;
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2275
2318
|
* Rename one key inside every record that is keyed by the renamed collection — a style rule's
|
|
2276
2319
|
* `when`, which names the props it conditions on.
|
|
2277
2320
|
*
|
|
@@ -5382,7 +5425,7 @@ var Config = class Config {
|
|
|
5382
5425
|
* links from the provided sources. A declared `linkedKind` with no provided source throws. */
|
|
5383
5426
|
hydrateFrom(stored, options) {
|
|
5384
5427
|
const claimed = schemaVersionOf(stored);
|
|
5385
|
-
if (claimed >
|
|
5428
|
+
if (claimed > 20261007210309) throw new SchemaVersionTooNew(claimed);
|
|
5386
5429
|
const wire = upgradeSerializedConfig(stored);
|
|
5387
5430
|
if (!Array.isArray(wire.ownedKinds) || typeof wire.items !== "object" || wire.items === null) throw new ConfigFormatError(`Config "${stored.name ?? "system-config"}" cannot be hydrated: this is not a serialized config-v2 config (no "ownedKinds"/"items"). A pre-cutover build artifact converts through the registered cutover migration — import the config type's module (e.g. \`configs/system\`) before hydrating, or port the repo once with \`uds migrate\`.`);
|
|
5388
5431
|
const leftover = wire.options;
|
|
@@ -281,6 +281,16 @@ declare function arbitraryClassName({
|
|
|
281
281
|
readonly path: string; /** The authored value, brackets included. */
|
|
282
282
|
readonly value: string;
|
|
283
283
|
}): string;
|
|
284
|
+
/**
|
|
285
|
+
* The bracket literal an off-scale value is written as where it needs a class, not an inline style:
|
|
286
|
+
* under a modifier, whose condition an inline style can't hold. A bracket literal on a prop that takes
|
|
287
|
+
* one is itself, and a bare number in the prop's unit is its literal (`600` with `arbitrary: 'px'` is
|
|
288
|
+
* `[600px]`). `undefined` for a value a declared class paints, or one nothing does.
|
|
289
|
+
*
|
|
290
|
+
* The canvas split, the build's class extractor and the emitted runtime all name the class this gives,
|
|
291
|
+
* so the element wears the class the sheet has a rule for.
|
|
292
|
+
*/
|
|
293
|
+
declare function stylePropOffScaleLiteral(config: Config, path: string, value: unknown): string | undefined;
|
|
284
294
|
/** What a prop bag amounts to once styling is taken out of it. */
|
|
285
295
|
interface SplitStyleProps {
|
|
286
296
|
/** The classes the bag's style props produce, space-joined. */
|
|
@@ -304,10 +314,14 @@ interface SplitStyleProps {
|
|
|
304
314
|
declare function splitStyleProps({
|
|
305
315
|
config,
|
|
306
316
|
bag,
|
|
307
|
-
through
|
|
317
|
+
through,
|
|
318
|
+
underModifier
|
|
308
319
|
}: {
|
|
309
320
|
readonly config: Config;
|
|
310
321
|
readonly bag: Record<string, unknown>;
|
|
322
|
+
/** The bag is a modifier's block. An inline style can't hold its condition, so an off-scale value
|
|
323
|
+
* is its arbitrary class (see {@link stylePropOffScaleLiteral}) rather than a declaration. */
|
|
324
|
+
readonly underModifier?: boolean;
|
|
311
325
|
/** The definitions this bag's keys resolve against, in precedence order — see
|
|
312
326
|
* {@link stylePropertyPathFor}. `<VStack gap="4">` wears `uds-gap-y-4`. Empty for a bag with no
|
|
313
327
|
* owning component, where a key names its own property. */
|
|
@@ -347,4 +361,4 @@ declare function modifierUtilitiesUsed(config: Config): Set<string>;
|
|
|
347
361
|
*/
|
|
348
362
|
declare function splitVariants(cls: string): string[];
|
|
349
363
|
//#endregion
|
|
350
|
-
export { ComponentRuleClass, PickedComposite, SplitStyleProps, arbitraryClassName, arbitraryNeedsClass, classLeaf, classSelector, componentClassBase, componentCompositeClasses, componentLayerClass, componentMotionClasses, componentPropClasses, componentPropComposites, componentRuleClasses, forceModeAttribute, forceModeAttributeOf, forceModeProp, forceModePropGroup, forceModePropValue, forceModeProps, forceStateAttribute, inlineRuleComposites, isForceModeProp, kebabComponent, layerJsMotionPresets, layerRoutedProps, modifierUtilitiesUsed, motionClassName, ruleCondition, splitStyleProps, splitVariants, stylePropClassBase, stylePropClassName, stylePropClasses, stylePropOwnClasses, styleRuleMotionClasses };
|
|
364
|
+
export { ComponentRuleClass, PickedComposite, SplitStyleProps, arbitraryClassName, arbitraryNeedsClass, classLeaf, classSelector, componentClassBase, componentCompositeClasses, componentLayerClass, componentMotionClasses, componentPropClasses, componentPropComposites, componentRuleClasses, forceModeAttribute, forceModeAttributeOf, forceModeProp, forceModePropGroup, forceModePropValue, forceModeProps, forceStateAttribute, inlineRuleComposites, isForceModeProp, kebabComponent, layerJsMotionPresets, layerRoutedProps, modifierUtilitiesUsed, motionClassName, ruleCondition, splitStyleProps, splitVariants, stylePropClassBase, stylePropClassName, stylePropClasses, stylePropOffScaleLiteral, stylePropOwnClasses, styleRuleMotionClasses };
|
|
@@ -3,7 +3,7 @@ import { forwardsOf, rootLayerOf } from "../entities/system/element.js";
|
|
|
3
3
|
import { createSliceMemo } from "./memo.js";
|
|
4
4
|
import { classPrefixOf, prefixed, varPrefixOf } from "../entities/system/Settings.js";
|
|
5
5
|
import { storedPreview } from "./preview-store.js";
|
|
6
|
-
import { authoredValue, className, isCustomPropertyKey, negatedCssValue, renderStyleValue, resolveComponentProps, stylePropEntries, stylePropLeafForToken, stylePropValueLeaves, stylePropertyPathFor } from "./projections.js";
|
|
6
|
+
import { authoredValue, className, isCustomPropertyKey, negatedCssValue, renderStyleValue, resolveComponentProps, stylePropEntries, stylePropLeafForToken, stylePropNumberLeaf, stylePropNumberUnit, stylePropValueLeaves, stylePropertyPathFor } from "./projections.js";
|
|
7
7
|
import { inlineMotionId, isInlineMotion, jsMotionBody } from "../entities/system/motion-variants.js";
|
|
8
8
|
import { ruleApplies } from "./layer-styles.js";
|
|
9
9
|
//#region src/framework/class-names.ts
|
|
@@ -725,6 +725,25 @@ function arbitraryClassName({ config, path, value }) {
|
|
|
725
725
|
return `${stylePropClassBase(config, path)}-[${inner}]`;
|
|
726
726
|
}
|
|
727
727
|
/**
|
|
728
|
+
* The bracket literal an off-scale value is written as where it needs a class, not an inline style:
|
|
729
|
+
* under a modifier, whose condition an inline style can't hold. A bracket literal on a prop that takes
|
|
730
|
+
* one is itself, and a bare number in the prop's unit is its literal (`600` with `arbitrary: 'px'` is
|
|
731
|
+
* `[600px]`). `undefined` for a value a declared class paints, or one nothing does.
|
|
732
|
+
*
|
|
733
|
+
* The canvas split, the build's class extractor and the emitted runtime all name the class this gives,
|
|
734
|
+
* so the element wears the class the sheet has a rule for.
|
|
735
|
+
*/
|
|
736
|
+
function stylePropOffScaleLiteral(config, path, value) {
|
|
737
|
+
const { arbitrary } = config.resolve("style-property", path)?.toJSON() ?? {};
|
|
738
|
+
if (!arbitrary) return void 0;
|
|
739
|
+
if (typeof value === "string") return offScale(value) === void 0 ? void 0 : value;
|
|
740
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
741
|
+
if (stylePropNumberLeaf(config, path, value) !== void 0) return void 0;
|
|
742
|
+
const unit = stylePropNumberUnit(config, path);
|
|
743
|
+
if (unit === void 0) return void 0;
|
|
744
|
+
return `[${value}${unit === "unitless" ? "" : unit}]`;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
728
747
|
* Split a prop bag into a class string and the props that aren't styling.
|
|
729
748
|
*
|
|
730
749
|
* This is the same dispatch the emitted `_style-props` runtime performs, over a live config instead
|
|
@@ -737,19 +756,20 @@ function arbitraryClassName({ config, path, value }) {
|
|
|
737
756
|
* ({@link arbitraryNeedsClass}); an explicit `style` the caller passed still wins, since that's the
|
|
738
757
|
* escape hatch of last resort.
|
|
739
758
|
*/
|
|
740
|
-
function splitStyleProps({ config, bag, through }) {
|
|
759
|
+
function splitStyleProps({ config, bag, through, underModifier = false }) {
|
|
741
760
|
const classes = [];
|
|
742
761
|
const rest = {};
|
|
743
762
|
const style = {};
|
|
744
763
|
const modifiers = new Map(config.list("modifier").map((modifier) => [`_${modifier.leaf}`, modifier.leaf]));
|
|
745
|
-
for (const [key,
|
|
764
|
+
for (const [key, authored] of Object.entries(bag)) {
|
|
746
765
|
const modifier = modifiers.get(key);
|
|
747
766
|
if (modifier) {
|
|
748
|
-
if (
|
|
767
|
+
if (authored && typeof authored === "object" && !Array.isArray(authored)) {
|
|
749
768
|
const inner = splitStyleProps({
|
|
750
769
|
config,
|
|
751
|
-
bag:
|
|
752
|
-
through
|
|
770
|
+
bag: authored,
|
|
771
|
+
through,
|
|
772
|
+
underModifier: true
|
|
753
773
|
});
|
|
754
774
|
for (const cls of inner.className.split(" ").filter(Boolean)) classes.push(`${modifier}:${cls}`);
|
|
755
775
|
}
|
|
@@ -763,11 +783,27 @@ function splitStyleProps({ config, bag, through }) {
|
|
|
763
783
|
}) ?? key;
|
|
764
784
|
const prop = config.resolve("style-property", name);
|
|
765
785
|
if (!prop) {
|
|
766
|
-
rest[key] =
|
|
786
|
+
rest[key] = authored;
|
|
767
787
|
continue;
|
|
768
788
|
}
|
|
769
|
-
if (
|
|
789
|
+
if (authored === void 0 || authored === null) continue;
|
|
770
790
|
const { arbitrary, properties = [], negates } = prop.toJSON();
|
|
791
|
+
const value = typeof authored === "number" ? stylePropOffScaleLiteral(config, name, authored) ?? authored : authored;
|
|
792
|
+
if (underModifier && typeof value === "string" && arbitrary) {
|
|
793
|
+
if (offScale(value) !== void 0) {
|
|
794
|
+
classes.push(arbitraryClassName({
|
|
795
|
+
config,
|
|
796
|
+
path: name,
|
|
797
|
+
value
|
|
798
|
+
}));
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (typeof value === "number") {
|
|
803
|
+
const leaf = stylePropNumberLeaf(config, name, value);
|
|
804
|
+
if (leaf !== void 0) classes.push(stylePropClassName(config, name, leaf));
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
771
807
|
const off = arbitrary ? offScale(value) : void 0;
|
|
772
808
|
if (off !== void 0 && arbitraryNeedsClass(config, name)) {
|
|
773
809
|
classes.push(arbitraryClassName({
|
|
@@ -970,4 +1006,4 @@ function splitVariants(cls) {
|
|
|
970
1006
|
return segments;
|
|
971
1007
|
}
|
|
972
1008
|
//#endregion
|
|
973
|
-
export { arbitraryClassName, arbitraryNeedsClass, classLeaf, classSelector, componentClassBase, componentCompositeClasses, componentLayerClass, componentMotionClasses, componentPropClasses, componentPropComposites, componentRuleClasses, forceModeAttribute, forceModeAttributeOf, forceModeProp, forceModePropGroup, forceModePropValue, forceModeProps, forceStateAttribute, inlineRuleComposites, isForceModeProp, kebabComponent, layerJsMotionPresets, layerRoutedProps, modifierUtilitiesUsed, motionClassName, ruleCondition, splitStyleProps, splitVariants, stylePropClassBase, stylePropClassName, stylePropClasses, stylePropOwnClasses, styleRuleMotionClasses };
|
|
1009
|
+
export { arbitraryClassName, arbitraryNeedsClass, classLeaf, classSelector, componentClassBase, componentCompositeClasses, componentLayerClass, componentMotionClasses, componentPropClasses, componentPropComposites, componentRuleClasses, forceModeAttribute, forceModeAttributeOf, forceModeProp, forceModePropGroup, forceModePropValue, forceModeProps, forceStateAttribute, inlineRuleComposites, isForceModeProp, kebabComponent, layerJsMotionPresets, layerRoutedProps, modifierUtilitiesUsed, motionClassName, ruleCondition, splitStyleProps, splitVariants, stylePropClassBase, stylePropClassName, stylePropClasses, stylePropOffScaleLiteral, stylePropOwnClasses, styleRuleMotionClasses };
|