@yahoo/uds-create-config 4.54.0 → 4.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capture/class-grammar.js +2 -3
- package/dist/configs/platform.d.ts +8 -1
- package/dist/configs/react-native-system.d.ts +21 -9
- package/dist/configs/react-native-system.js +1 -1
- package/dist/configs/system.js +3 -1
- package/dist/css/value-type.js +24 -0
- package/dist/entities/documentation/Documentation.d.ts +1 -1
- package/dist/entities/native/NativeStyleProperty.d.ts +14 -9
- package/dist/entities/native/NativeStyleProperty.js +23 -2
- package/dist/entities/native/NativeToken.d.ts +10 -3
- package/dist/entities/native/NativeToken.js +39 -4
- package/dist/entities/system/Component.js +2 -1
- package/dist/entities/system/GlobalStyle.d.ts +2 -2
- package/dist/entities/system/GlobalStyle.js +2 -1
- package/dist/entities/system/Modifier.d.ts +3 -1
- package/dist/entities/system/Modifier.js +3 -1
- package/dist/entities/system/Settings.d.ts +9 -1
- package/dist/entities/system/Settings.js +6 -2
- package/dist/entities/system/StyleProperty.d.ts +39 -3
- package/dist/entities/system/StyleProperty.js +141 -18
- package/dist/entities/system/Token.d.ts +14 -1
- package/dist/entities/system/Token.js +21 -3
- package/dist/entities/system/calc.d.ts +28 -0
- package/dist/entities/system/calc.js +121 -0
- package/dist/entities/system/color.d.ts +29 -4
- package/dist/entities/system/color.js +61 -8
- package/dist/entities/system/component-module.js +15 -4
- package/dist/entities/system/composed-values.d.ts +3 -2
- package/dist/entities/system/composed-values.js +20 -1
- package/dist/entities/system/defineComponent.d.ts +3 -3
- package/dist/entities/system/link-plan.js +10 -1
- package/dist/entities/system/linked-token-values.d.ts +34 -0
- package/dist/entities/system/linked-token-values.js +135 -0
- package/dist/entities/system/shadow.d.ts +9 -7
- package/dist/entities/system/shadow.js +8 -7
- package/dist/entities/system/style-bag.d.ts +1 -1
- package/dist/entities/system/token-index.js +45 -2
- package/dist/entities/system/token-values.d.ts +3 -2
- package/dist/entities/system/token-values.js +9 -9
- package/dist/framework/Config.d.ts +14 -18
- package/dist/framework/Config.js +105 -55
- package/dist/framework/class-names.d.ts +47 -7
- package/dist/framework/class-names.js +115 -14
- package/dist/framework/defineEntity.d.ts +3 -1
- package/dist/framework/defineEntity.js +3 -1
- package/dist/framework/inline-sources.d.ts +3 -3
- package/dist/framework/inline-sources.js +1 -1
- package/dist/framework/projections.d.ts +84 -3
- package/dist/framework/projections.js +315 -20
- package/dist/framework/ref-graph.js +1 -1
- package/dist/framework/registry.d.ts +7 -0
- package/dist/framework/registry.js +2 -1
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/utils/group.d.ts +2 -1
- package/dist/framework/utils/refs.d.ts +19 -2
- package/dist/framework/utils/refs.js +100 -12
- package/dist/index.d.ts +12 -10
- package/dist/index.js +10 -8
- package/dist/migrations/20261001162046_oklch_and_emit_fields.d.ts +25 -0
- package/dist/migrations/20261001162046_oklch_and_emit_fields.js +10 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { isRef, kindOf, pathOf, ref, refSchema, sourceOf, splitRef } from "../../framework/utils/refs.js";
|
|
1
|
+
import { declarationText, isRef, kindOf, pathOf, ref, refOfKey, refSchema, replaceRefMarkers, sourceOf, splitRef } from "../../framework/utils/refs.js";
|
|
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
5
|
import { createSliceMemo } from "../../framework/memo.js";
|
|
6
6
|
import { isIdentifier } from "../../framework/utils/identifier.js";
|
|
7
|
+
import { cssVar, declaresCssVariable } from "../../framework/projections.js";
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
//#region src/entities/system/StyleProperty.ts
|
|
9
10
|
/**
|
|
@@ -31,10 +32,11 @@ const aliasName = z.union([z.string(), z.number()]);
|
|
|
31
32
|
/** The first property among `properties` that refuses `text`, as the refusal. A property the table
|
|
32
33
|
* can't judge says nothing. */
|
|
33
34
|
function cssIssue(properties, text, grammar) {
|
|
35
|
+
const written = replaceRefMarkers(text, () => "var(--ref)");
|
|
34
36
|
for (const property of properties ?? []) {
|
|
35
37
|
const issue = cssValueIssue({
|
|
36
38
|
property,
|
|
37
|
-
value:
|
|
39
|
+
value: written,
|
|
38
40
|
grammar
|
|
39
41
|
});
|
|
40
42
|
if (issue !== void 0 && issue !== "unknown") return issue;
|
|
@@ -76,6 +78,14 @@ const namesItem = (config, wanted) => (ctx) => {
|
|
|
76
78
|
/** A declaration bag's key. A ref is a record of one string too, so the ref marker is not a key here,
|
|
77
79
|
* or a ref no ref form admits would read as a bag. */
|
|
78
80
|
const declarationKey = z.string().regex(/^(?!__ref$)/);
|
|
81
|
+
/** A declaration's value: CSS text, or a ref to the token it is, `'border-style':
|
|
82
|
+
* Token.ref('tw/border-style')`. */
|
|
83
|
+
const declarationValue = z.union([z.string(), refSchema]);
|
|
84
|
+
/** The class an alias or a flag is written as, in place of `<classPrefix>-<alias>`. */
|
|
85
|
+
const valueClassName = z.string().regex(/^[^\s]+$/).meta({
|
|
86
|
+
title: "Class name",
|
|
87
|
+
description: "The class this value is written as, in place of the prop's `<classPrefix>-<leaf>`: `{ alias: 'auto', value: 'auto', className: 'isolation-auto' }`. The system's class prefix still applies."
|
|
88
|
+
}).optional();
|
|
79
89
|
const STYLE_PROP_VALUE_FORMS = {
|
|
80
90
|
keyword: (ctx = {}) => z.string().meta({
|
|
81
91
|
title: "Keyword",
|
|
@@ -105,32 +115,46 @@ const STYLE_PROP_VALUE_FORMS = {
|
|
|
105
115
|
value: z.union([
|
|
106
116
|
z.string(),
|
|
107
117
|
refSchema,
|
|
108
|
-
z.record(z.string(),
|
|
109
|
-
])
|
|
118
|
+
z.record(z.string(), declarationValue)
|
|
119
|
+
]),
|
|
120
|
+
className: valueClassName
|
|
110
121
|
}).meta({
|
|
111
122
|
title: "Flag",
|
|
112
123
|
description: "A boolean alias for a boolean-shaped prop: `<Box border />` sets `border-width: 1px`."
|
|
113
124
|
}),
|
|
114
125
|
aliasToken: ({ config } = {}) => z.strictObject({
|
|
115
126
|
alias: aliasName,
|
|
116
|
-
value: refSchema.check(namesItem(config, true))
|
|
127
|
+
value: refSchema.check(namesItem(config, true)),
|
|
128
|
+
className: valueClassName
|
|
117
129
|
}).meta({
|
|
118
130
|
title: "Token alias",
|
|
119
131
|
description: "One token under a name of the prop's own, still emitted as its `var()`."
|
|
120
132
|
}),
|
|
121
133
|
aliasLiteral: (ctx = {}) => z.strictObject({
|
|
122
134
|
alias: aliasName,
|
|
123
|
-
value: z.string()
|
|
135
|
+
value: z.string(),
|
|
136
|
+
className: valueClassName
|
|
124
137
|
}).meta({
|
|
125
138
|
title: "Alias",
|
|
126
139
|
description: "The authored key and the CSS value it resolves to: `{ alias: \"col\", value: \"column\" }`."
|
|
127
140
|
}).check(cssText(ctx, (value) => value.value)),
|
|
128
141
|
aliasDeclarations: (_ctx = {}) => z.strictObject({
|
|
129
142
|
alias: aliasName,
|
|
130
|
-
value: z.record(declarationKey, z.
|
|
143
|
+
value: z.record(declarationKey, z.union([declarationValue, z.record(declarationKey, declarationValue)])),
|
|
144
|
+
className: valueClassName
|
|
145
|
+
}).check((ctx) => {
|
|
146
|
+
for (const [key, value] of Object.entries(ctx.value.value)) {
|
|
147
|
+
if (typeof value === "string" || isRef(value) || key.startsWith("@")) continue;
|
|
148
|
+
ctx.issues.push({
|
|
149
|
+
code: "custom",
|
|
150
|
+
input: value,
|
|
151
|
+
path: ["value", key],
|
|
152
|
+
message: `\`${key}\` holds a block, and only an at-rule (\`@supports …\`, \`@media …\`) holds declarations of its own.`
|
|
153
|
+
});
|
|
154
|
+
}
|
|
131
155
|
}).meta({
|
|
132
156
|
title: "Declarations",
|
|
133
|
-
description: "A leaf that stands for a whole declaration bag: `truncate` sets `overflow`, `text-overflow` and `white-space`."
|
|
157
|
+
description: "A leaf that stands for a whole declaration bag: `truncate` sets `overflow`, `text-overflow` and `white-space`. A key that is an at-rule (`@media (forced-colors: active)`) holds the declarations that apply under it."
|
|
134
158
|
})
|
|
135
159
|
};
|
|
136
160
|
const KINDS = Object.keys(STYLE_PROP_VALUE_FORMS);
|
|
@@ -140,7 +164,7 @@ function flagValue(value, members) {
|
|
|
140
164
|
if ("__ref" in value && typeof value.__ref === "string") return members(value).map(({ path }) => ({ value: path }));
|
|
141
165
|
return [{
|
|
142
166
|
value: "",
|
|
143
|
-
declarations: value
|
|
167
|
+
declarations: textOf(value)
|
|
144
168
|
}];
|
|
145
169
|
}
|
|
146
170
|
/**
|
|
@@ -172,22 +196,45 @@ const STYLE_PROP_VALUE_LEAVES = {
|
|
|
172
196
|
})),
|
|
173
197
|
flag: (entry, members) => entry.alias ? flagValue(entry.value, members).map((rest) => ({
|
|
174
198
|
leaf: "",
|
|
175
|
-
...rest
|
|
199
|
+
...rest,
|
|
200
|
+
...classNameOf(entry)
|
|
176
201
|
})) : [],
|
|
177
202
|
aliasToken: (entry, members) => members(entry.value).map(({ path }) => ({
|
|
178
203
|
leaf: String(entry.alias),
|
|
179
|
-
value: path
|
|
204
|
+
value: path,
|
|
205
|
+
...classNameOf(entry)
|
|
180
206
|
})),
|
|
181
207
|
aliasLiteral: (entry) => [{
|
|
182
208
|
leaf: String(entry.alias),
|
|
183
|
-
value: entry.value
|
|
209
|
+
value: entry.value,
|
|
210
|
+
...classNameOf(entry)
|
|
184
211
|
}],
|
|
185
212
|
aliasDeclarations: (entry) => [{
|
|
186
213
|
leaf: String(entry.alias),
|
|
187
214
|
value: "",
|
|
188
|
-
|
|
215
|
+
...splitDeclarations(entry.value),
|
|
216
|
+
...classNameOf(entry)
|
|
189
217
|
}]
|
|
190
218
|
};
|
|
219
|
+
/** A bag's declarations as text, each ref as its marker. */
|
|
220
|
+
function textOf(bag) {
|
|
221
|
+
return Object.fromEntries(Object.entries(bag).map(([key, value]) => [key, declarationText(value)]));
|
|
222
|
+
}
|
|
223
|
+
/** A declaration bag as a leaf holds it: the plain declarations, and the blocks under at-rules. */
|
|
224
|
+
function splitDeclarations(bag) {
|
|
225
|
+
const declarations = {};
|
|
226
|
+
const atRules = {};
|
|
227
|
+
for (const [key, value] of Object.entries(bag)) if (typeof value === "string" || isRef(value)) declarations[key] = declarationText(value);
|
|
228
|
+
else atRules[key] = textOf(value);
|
|
229
|
+
return Object.keys(atRules).length > 0 ? {
|
|
230
|
+
declarations,
|
|
231
|
+
atRules
|
|
232
|
+
} : { declarations };
|
|
233
|
+
}
|
|
234
|
+
/** An entry's own class, spread into its leaf when it names one. */
|
|
235
|
+
function classNameOf(entry) {
|
|
236
|
+
return entry.className === void 0 ? {} : { className: entry.className };
|
|
237
|
+
}
|
|
191
238
|
/** The leaves one stored entry stands for; none for a value no form admits. */
|
|
192
239
|
function leavesOfStylePropValue(entry, members) {
|
|
193
240
|
const kind = stylePropValueFormOf(entry);
|
|
@@ -221,7 +268,7 @@ function classifyStylePropValue({ config, entry }) {
|
|
|
221
268
|
*/
|
|
222
269
|
const cssPropertyName = z.string().check((ctx) => {
|
|
223
270
|
const name = ctx.value;
|
|
224
|
-
if (name.startsWith("-")) return;
|
|
271
|
+
if (name.startsWith("-") || refOfKey(name)) return;
|
|
225
272
|
if (/[A-Z]/.test(name)) {
|
|
226
273
|
const css = name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
227
274
|
ctx.issues.push({
|
|
@@ -263,6 +310,8 @@ function eachValueOnce(ctx) {
|
|
|
263
310
|
}
|
|
264
311
|
/** One comparable form for a CSS value, so `#FFFFFF` and `#ffffff ` read as the same value. */
|
|
265
312
|
const comparable = (value) => typeof value === "string" || typeof value === "number" ? String(value).trim().toLowerCase() : void 0;
|
|
313
|
+
/** A colour spelled as a hex or a colour function. */
|
|
314
|
+
const COLOR_LITERAL = /^(?:#[0-9a-f]{3,8}|(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\(.*\))$/;
|
|
266
315
|
/** Whether `values` reaches the token at `path` — through the token itself, an alias naming it, or
|
|
267
316
|
* a group above it. */
|
|
268
317
|
function reaches(values, path) {
|
|
@@ -307,15 +356,80 @@ const copiesNoToken = (config) => (ctx) => {
|
|
|
307
356
|
});
|
|
308
357
|
});
|
|
309
358
|
};
|
|
310
|
-
/** Each token
|
|
311
|
-
|
|
359
|
+
/** Each variable a token declares, by name, and the token declaring it. */
|
|
360
|
+
const tokenPathsByVariable = createSliceMemo({
|
|
361
|
+
kinds: ["token", "settings"],
|
|
362
|
+
compute: (config) => {
|
|
363
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
364
|
+
for (const token of config.list("token")) {
|
|
365
|
+
if (!declaresCssVariable(config, token.path)) continue;
|
|
366
|
+
tokens.set(cssVar(config, "token", token.path), token.path);
|
|
367
|
+
}
|
|
368
|
+
return tokens;
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
/**
|
|
372
|
+
* This config's style properties whose values or declarations spell a token's variable as text. The
|
|
373
|
+
* text stops naming the token on a rename or a prefix change, where an interpolated `Token.ref`
|
|
374
|
+
* follows it. A variable no token declares, such as a third-party library's, isn't one.
|
|
375
|
+
*/
|
|
376
|
+
function tokenVariablesAsText(config) {
|
|
377
|
+
const tokens = tokenPathsByVariable(config);
|
|
378
|
+
const found = [];
|
|
379
|
+
const visit = ({ styleProperty, value, at }) => {
|
|
380
|
+
if (typeof value === "string") for (const [, variable = ""] of value.matchAll(/var\((--[\w-]+)/g)) {
|
|
381
|
+
const token = tokens.get(variable);
|
|
382
|
+
if (token) found.push({
|
|
383
|
+
styleProperty,
|
|
384
|
+
at: at.join("."),
|
|
385
|
+
variable,
|
|
386
|
+
token
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
else if (Array.isArray(value)) for (const [index, item] of value.entries()) visit({
|
|
390
|
+
styleProperty,
|
|
391
|
+
value: item,
|
|
392
|
+
at: [...at, index]
|
|
393
|
+
});
|
|
394
|
+
else if (value && typeof value === "object" && !("__ref" in value)) for (const [key, nested] of Object.entries(value)) visit({
|
|
395
|
+
styleProperty,
|
|
396
|
+
value: nested,
|
|
397
|
+
at: [...at, key]
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
for (const prop of config.list("style-property")) {
|
|
401
|
+
if (config.configFor(ref("style-property", prop.path)) !== config) continue;
|
|
402
|
+
const { values, declarations } = prop.toJSON();
|
|
403
|
+
visit({
|
|
404
|
+
styleProperty: prop.path,
|
|
405
|
+
value: values,
|
|
406
|
+
at: ["values"]
|
|
407
|
+
});
|
|
408
|
+
visit({
|
|
409
|
+
styleProperty: prop.path,
|
|
410
|
+
value: declarations,
|
|
411
|
+
at: ["declarations"]
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
return found;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Each colour token's value, as `comparable` spells it, and the first token path that holds it. Kept
|
|
418
|
+
* per token slice, since a batch of style-property writes would otherwise list every token once per
|
|
419
|
+
* write.
|
|
420
|
+
*
|
|
421
|
+
* Only colours: a colour literal that equals a token is that token written out, while a length or a
|
|
422
|
+
* number coincides across unrelated scales — `4px` is a blur radius and a stroke width, `1` a line
|
|
423
|
+
* height and `flex: 1`.
|
|
424
|
+
*/
|
|
312
425
|
const tokenPathsByValue = createSliceMemo({
|
|
313
426
|
kinds: ["token"],
|
|
314
427
|
compute: (config) => {
|
|
315
428
|
const tokens = /* @__PURE__ */ new Map();
|
|
316
429
|
for (const token of config.list("token")) {
|
|
317
430
|
const value = comparable(token.body.value);
|
|
318
|
-
if (value
|
|
431
|
+
if (value === void 0 || !COLOR_LITERAL.test(value)) continue;
|
|
432
|
+
if (!tokens.has(value)) tokens.set(value, token.path);
|
|
319
433
|
}
|
|
320
434
|
return tokens;
|
|
321
435
|
}
|
|
@@ -397,6 +511,15 @@ const styleFields = ({ config, body, linked } = {}) => z.object({
|
|
|
397
511
|
*/
|
|
398
512
|
negates: z.boolean().default(false).describe("Apply the negation of the value, so one scale works in both directions. Defaults to `false`."),
|
|
399
513
|
/**
|
|
514
|
+
* Declarations the prop sets beside every value. A prop whose value is one part of a composed
|
|
515
|
+
* property writes its part to a custom property and the whole here: `translateX` sets
|
|
516
|
+
* `--tw-translate-x` and `translate: var(--tw-translate-x) var(--tw-translate-y)`, so the X and Y
|
|
517
|
+
* props compose on one element.
|
|
518
|
+
*/
|
|
519
|
+
selector: z.string().regex(/&/).optional().describe("The selector the prop's rules are written under, with `&` for its class: `:where(& > :not(:last-child))` styles the element's children, as `space-x-4` does."),
|
|
520
|
+
alpha: z.string().refine((name) => name.startsWith("--") || refOfKey(name) !== void 0).optional().describe("The custom property holding the alpha the prop's colour values are mixed at, as `--tw-shadow-alpha` for `shadow-red-500`, or a token's key for the variable it declares. A colour value is written as its literal, and under `@supports` as its variable mixed at that alpha."),
|
|
521
|
+
declarations: z.record(declarationKey, declarationValue).optional().describe("Declarations the prop sets with every value, as `translate: var(--tw-translate-x) var(--tw-translate-y)` beside a prop writing `--tw-translate-x`. `{value}` stands for the value the prop was given, so a prop with no `properties` of its own can still place it: `margin-inline-start: calc({value} * var(--tw-space-x-reverse))`."),
|
|
522
|
+
/**
|
|
400
523
|
* The style prop whose alpha this one controls — `bgOpacity` is the opacity of `bg`.
|
|
401
524
|
*
|
|
402
525
|
* The pairing is modelled from the SIBLING's side, and the sibling is a real entity. The old model
|
|
@@ -455,4 +578,4 @@ function cssPropertyNames(styleProp) {
|
|
|
455
578
|
return styleProp.properties.map(toCssPropertyName);
|
|
456
579
|
}
|
|
457
580
|
//#endregion
|
|
458
|
-
export { STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StyleProperty, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName };
|
|
581
|
+
export { STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StyleProperty, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName, tokenVariablesAsText };
|
|
@@ -2,8 +2,8 @@ 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
4
|
import { PathOf } from "../../framework/registered.js";
|
|
5
|
-
import { EntityClass, KindRef } from "../../framework/defineEntity.js";
|
|
6
5
|
import { ComposedValue } from "./composed-values.js";
|
|
6
|
+
import { EntityClass, KindRef } from "../../framework/defineEntity.js";
|
|
7
7
|
import { TokenReaders } from "./token-values.js";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
@@ -48,6 +48,12 @@ declare const tokenFields: z.ZodObject<{
|
|
|
48
48
|
on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
|
|
49
49
|
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
50
50
|
}, z.core.$strip>>>;
|
|
51
|
+
utility: z.ZodOptional<z.ZodBoolean>;
|
|
52
|
+
property: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
syntax: z.ZodOptional<z.ZodString>;
|
|
54
|
+
inherits: z.ZodOptional<z.ZodBoolean>;
|
|
55
|
+
}, z.core.$strict>>;
|
|
56
|
+
cssVariable: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
51
57
|
}, z.core.$strip>;
|
|
52
58
|
/** A token's stored body — what a `create` writes and what `Token.get` hands back typed. Exported
|
|
53
59
|
* so a consumer building one names the entity's own shape instead of restating it. */
|
|
@@ -59,8 +65,15 @@ declare const TokenEntity: EntityClass<z.ZodObject<{
|
|
|
59
65
|
on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
|
|
60
66
|
value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
|
|
61
67
|
}, z.core.$strip>>>;
|
|
68
|
+
utility: z.ZodOptional<z.ZodBoolean>;
|
|
69
|
+
property: z.ZodOptional<z.ZodObject<{
|
|
70
|
+
syntax: z.ZodOptional<z.ZodString>;
|
|
71
|
+
inherits: z.ZodOptional<z.ZodBoolean>;
|
|
72
|
+
}, z.core.$strict>>;
|
|
73
|
+
cssVariable: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
62
74
|
}, z.core.$strip>, z.ZodObject<{
|
|
63
75
|
cssPrefix: z.ZodOptional<z.ZodString>;
|
|
76
|
+
cssVariables: z.ZodOptional<z.ZodBoolean>;
|
|
64
77
|
}, z.core.$strip>, "token", {}, Record<never, never>, false, Record<never, never>, Record<never, never>>;
|
|
65
78
|
/** A token ref as an authoring file holds it: the marker, `extend`, and the value readers. */
|
|
66
79
|
type TokenReference<Path extends string> = KindRef<typeof TokenEntity, 'token', Path> & TokenReaders<Path>;
|
|
@@ -4,8 +4,10 @@ import { defineEntity } from "../../framework/defineEntity.js";
|
|
|
4
4
|
import { defineSubEntity } from "../../framework/defineSubEntity.js";
|
|
5
5
|
import { Font } from "./Font.js";
|
|
6
6
|
import { Modifier } from "./Modifier.js";
|
|
7
|
+
import { WEB_CONFIG_NAME } from "../../configs/platform.js";
|
|
7
8
|
import { authoringPhantom } from "../../framework/utils/authoring-phantom.js";
|
|
8
9
|
import { TOKEN_READERS, spellTokenRead } from "./token-values.js";
|
|
10
|
+
import { prepareLinkedTokenBody } from "./linked-token-values.js";
|
|
9
11
|
import { z } from "zod";
|
|
10
12
|
//#region src/entities/system/Token.ts
|
|
11
13
|
/**
|
|
@@ -116,13 +118,29 @@ function defineOverride(value) {
|
|
|
116
118
|
const Override = defineOverride(tokenValue);
|
|
117
119
|
const TokenEntity = defineEntity({
|
|
118
120
|
kind: "token",
|
|
121
|
+
prepareLinkedBody: ({ path, body, source, config }) => prepareLinkedTokenBody({
|
|
122
|
+
path,
|
|
123
|
+
body,
|
|
124
|
+
source,
|
|
125
|
+
config,
|
|
126
|
+
destinationPlatform: WEB_CONFIG_NAME
|
|
127
|
+
}),
|
|
119
128
|
fields: z.object({
|
|
120
129
|
value: tokenValue.describe("The base value: a literal, a ref to another token, or a composed value such as a gradient."),
|
|
121
130
|
type: z.string().optional().describe("The CSS value type, when the value alone does not say it."),
|
|
122
|
-
overrides: Override.optional().describe("Per-mode values, keyed by the condition they apply under.")
|
|
131
|
+
overrides: Override.optional().describe("Per-mode values, keyed by the condition they apply under."),
|
|
132
|
+
utility: z.boolean().optional().describe("`false` keeps the token out of its group's values, so a style prop that takes the group writes no class for it — `spacing/scale` powers `--spacing` without becoming a `p-scale`. A ref naming the token still resolves."),
|
|
133
|
+
property: z.strictObject({
|
|
134
|
+
syntax: z.string().optional().describe("The `@property` syntax, as `<length>`. Defaults to `*`."),
|
|
135
|
+
inherits: z.boolean().optional().describe("Whether the property inherits. Defaults to `false`.")
|
|
136
|
+
}).optional().describe("Registers the token's custom property with `@property` instead of declaring it on `:root`, with the token's value as its initial value, or none for `initial`. For a variable utilities set on an element, as Tailwind's `--tw-border-style`."),
|
|
137
|
+
cssVariable: z.union([z.string().regex(/^(?:--)?[A-Za-z0-9_][A-Za-z0-9_-]*$/), z.literal(false)]).optional().describe("The custom property this token is declared as. A name is placed under the system's prefix: `'spacing'` is `--uds-spacing`, or `--spacing` with an empty prefix. A name starting with `--` is used exactly, whatever the prefix, as for a third-party library's variable: `'--mantine-color-primary'`. `false` declares none and writes the token into the values that use it. Unset, its name comes from its path and the nearest group's `cssPrefix`, and whether it is declared from the nearest group's `cssVariables`.")
|
|
138
|
+
}),
|
|
139
|
+
groupFields: z.object({
|
|
140
|
+
cssPrefix: z.string().optional().describe("The prefix the group emits its custom properties under. Local to the config that holds the group."),
|
|
141
|
+
cssVariables: z.boolean().optional().describe("`false` writes the group's tokens into the values that use them instead of declaring a custom property for each, as Tailwind's `@theme inline` does. A token's own `cssVariable` wins. Local to the config that holds the group.")
|
|
123
142
|
}),
|
|
124
|
-
|
|
125
|
-
localGroupFields: ["cssPrefix"],
|
|
143
|
+
localGroupFields: ["cssPrefix", "cssVariables"],
|
|
126
144
|
subEntities: { overrides: Override },
|
|
127
145
|
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.` } }
|
|
128
146
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Ref } from "../../framework/utils/refs.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/entities/system/calc.d.ts
|
|
5
|
+
/** One side of the arithmetic: a number, a CSS length such as `0.25rem`, or a ref to a token. */
|
|
6
|
+
type CalcOperand = number | string | Ref;
|
|
7
|
+
interface Multiply {
|
|
8
|
+
readonly multiply: readonly [CalcOperand, CalcOperand];
|
|
9
|
+
}
|
|
10
|
+
interface Divide {
|
|
11
|
+
readonly divide: readonly [CalcOperand, CalcOperand];
|
|
12
|
+
}
|
|
13
|
+
/** Whether a value is a `multiply(…)`. */
|
|
14
|
+
declare function isMultiply(value: unknown): value is Multiply;
|
|
15
|
+
/** Whether a value is a `divide(…)`. */
|
|
16
|
+
declare function isDivide(value: unknown): value is Divide;
|
|
17
|
+
/** `multiply(Token.ref('spacing'), 4)` — the first operand times the second. */
|
|
18
|
+
declare function multiply(left: CalcOperand, right: CalcOperand): Multiply;
|
|
19
|
+
/** `divide(1.25, 0.875)` — the first operand over the second. */
|
|
20
|
+
declare function divide(left: CalcOperand, right: CalcOperand): Divide;
|
|
21
|
+
/**
|
|
22
|
+
* What the arithmetic comes to, given each operand's literal — `1rem` for `0.25rem` times 4,
|
|
23
|
+
* `1.4286` for `1.25` over `0.875` — or `undefined` when the operands are not plain quantities or
|
|
24
|
+
* their units don't combine. `literal` resolves an operand (a ref to the token's value).
|
|
25
|
+
*/
|
|
26
|
+
declare function evaluateCalc(value: Multiply | Divide, literal: (part: CalcOperand) => string | number | undefined): string | number | undefined;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { CalcOperand, Divide, Multiply, divide, evaluateCalc, isDivide, isMultiply, multiply };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { refSchema } from "../../framework/utils/refs.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/entities/system/calc.ts
|
|
4
|
+
/**
|
|
5
|
+
* A value that is arithmetic on others — `multiply(Token.ref('spacing'), 4)` is
|
|
6
|
+
* `calc(var(--spacing) * 4)`, and `divide(1.25, 0.875)` is `calc(1.25 / 0.875)`.
|
|
7
|
+
*
|
|
8
|
+
* Held as its operands rather than as the `calc()` string so a ref in it stays a ref: a rename
|
|
9
|
+
* follows into it, the dependency graph sees it, and a reader can work out what it comes to
|
|
10
|
+
* (`spacing` at `0.25rem` times 4 is `1rem`) where a string would only be text.
|
|
11
|
+
*/
|
|
12
|
+
const operand = z.union([
|
|
13
|
+
z.number(),
|
|
14
|
+
z.string().min(1),
|
|
15
|
+
refSchema
|
|
16
|
+
]);
|
|
17
|
+
const operands = z.tuple([operand, operand]);
|
|
18
|
+
const multiplyValue = z.strictObject({ multiply: operands });
|
|
19
|
+
const divideValue = z.strictObject({ divide: operands });
|
|
20
|
+
const isPair = (value) => Array.isArray(value) && value.length === 2;
|
|
21
|
+
/** Whether a value is a `multiply(…)`. */
|
|
22
|
+
function isMultiply(value) {
|
|
23
|
+
return typeof value === "object" && value !== null && isPair(value.multiply);
|
|
24
|
+
}
|
|
25
|
+
/** Whether a value is a `divide(…)`. */
|
|
26
|
+
function isDivide(value) {
|
|
27
|
+
return typeof value === "object" && value !== null && isPair(value.divide);
|
|
28
|
+
}
|
|
29
|
+
/** `multiply(Token.ref('spacing'), 4)` — the first operand times the second. */
|
|
30
|
+
function multiply(left, right) {
|
|
31
|
+
return { multiply: [left, right] };
|
|
32
|
+
}
|
|
33
|
+
/** `divide(1.25, 0.875)` — the first operand over the second. */
|
|
34
|
+
function divide(left, right) {
|
|
35
|
+
return { divide: [left, right] };
|
|
36
|
+
}
|
|
37
|
+
/** A `multiply(…)` as CSS — `calc(var(--spacing) * 4)` — with `render` resolving each operand. */
|
|
38
|
+
function renderMultiply(value, render) {
|
|
39
|
+
const [left, right] = value.multiply;
|
|
40
|
+
return `calc(${render(left)} * ${render(right)})`;
|
|
41
|
+
}
|
|
42
|
+
/** A `divide(…)` as CSS — `calc(1.25 / 0.875)`. */
|
|
43
|
+
function renderDivide(value, render) {
|
|
44
|
+
const [left, right] = value.divide;
|
|
45
|
+
return `calc(${render(left)} / ${render(right)})`;
|
|
46
|
+
}
|
|
47
|
+
function multiplyAuthoring(value) {
|
|
48
|
+
return {
|
|
49
|
+
helper: "multiply",
|
|
50
|
+
args: [...value.multiply]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function divideAuthoring(value) {
|
|
54
|
+
return {
|
|
55
|
+
helper: "divide",
|
|
56
|
+
args: [...value.divide]
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const isDigit = (char) => char !== void 0 && char >= "0" && char <= "9";
|
|
60
|
+
/**
|
|
61
|
+
* The plain number a string starts with — an optional minus, digits, and an optional fraction
|
|
62
|
+
* (`-1`, `0.25`, `.5`) — split from what follows it, or `undefined` when it starts with none.
|
|
63
|
+
* A scan rather than a regex, so its cost stays linear on any input.
|
|
64
|
+
*/
|
|
65
|
+
function leadingNumber(text) {
|
|
66
|
+
let at = text.startsWith("-") ? 1 : 0;
|
|
67
|
+
const start = at;
|
|
68
|
+
while (isDigit(text[at])) at++;
|
|
69
|
+
const whole = at > start;
|
|
70
|
+
if (text[at] === "." && isDigit(text[at + 1])) {
|
|
71
|
+
at++;
|
|
72
|
+
while (isDigit(text[at])) at++;
|
|
73
|
+
} else if (!whole) return;
|
|
74
|
+
return {
|
|
75
|
+
sign: start === 1 ? "-" : "",
|
|
76
|
+
digits: text.slice(start, at),
|
|
77
|
+
rest: text.slice(at)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function quantityOf(value) {
|
|
81
|
+
if (typeof value === "number") return {
|
|
82
|
+
amount: value,
|
|
83
|
+
unit: ""
|
|
84
|
+
};
|
|
85
|
+
if (value === void 0) return void 0;
|
|
86
|
+
const number = leadingNumber(value.trim());
|
|
87
|
+
if (!number || !/^[a-z%]*$/i.test(number.rest)) return void 0;
|
|
88
|
+
return {
|
|
89
|
+
amount: Number(`${number.sign}${number.digits}`),
|
|
90
|
+
unit: number.rest
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* What the arithmetic comes to, given each operand's literal — `1rem` for `0.25rem` times 4,
|
|
95
|
+
* `1.4286` for `1.25` over `0.875` — or `undefined` when the operands are not plain quantities or
|
|
96
|
+
* their units don't combine. `literal` resolves an operand (a ref to the token's value).
|
|
97
|
+
*/
|
|
98
|
+
function evaluateCalc(value, literal) {
|
|
99
|
+
const [left, right] = isMultiply(value) ? value.multiply : value.divide;
|
|
100
|
+
const a = quantityOf(literal(left));
|
|
101
|
+
const b = quantityOf(literal(right));
|
|
102
|
+
if (!a || !b) return void 0;
|
|
103
|
+
let result;
|
|
104
|
+
if (isMultiply(value)) {
|
|
105
|
+
if (a.unit && b.unit) return void 0;
|
|
106
|
+
result = {
|
|
107
|
+
amount: a.amount * b.amount,
|
|
108
|
+
unit: a.unit || b.unit
|
|
109
|
+
};
|
|
110
|
+
} else {
|
|
111
|
+
if (b.amount === 0 || b.unit && b.unit !== a.unit) return void 0;
|
|
112
|
+
result = {
|
|
113
|
+
amount: a.amount / b.amount,
|
|
114
|
+
unit: b.unit ? "" : a.unit
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const amount = Math.round(result.amount * 1e4) / 1e4;
|
|
118
|
+
return result.unit ? `${amount}${result.unit}` : amount;
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { divide, divideAuthoring, divideValue, evaluateCalc, isDivide, isMultiply, leadingNumber, multiply, multiplyAuthoring, multiplyValue, renderDivide, renderMultiply };
|
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
//#region src/entities/system/color.d.ts
|
|
4
|
-
/** A colour: a literal, a token ref, a derived colour, or a gradient. */
|
|
4
|
+
/** A colour: a literal, a token ref, an OKLCH colour, a derived colour, or a gradient. */
|
|
5
5
|
type ColorValue = string | {
|
|
6
6
|
readonly __ref: string;
|
|
7
|
-
} | DerivedColor | Gradient;
|
|
7
|
+
} | OklchColor | DerivedColor | Gradient;
|
|
8
|
+
/**
|
|
9
|
+
* A colour as its OKLCH channels — `oklch(63.7, 0.237, 25.331)` is CSS `oklch(63.7% 0.237 25.331)`.
|
|
10
|
+
*
|
|
11
|
+
* Held as numbers rather than as the CSS string so an editor reads and adjusts the channels
|
|
12
|
+
* themselves: lightness and chroma move without the hue drifting, which is what makes a ramp of them
|
|
13
|
+
* a scale. It renders to the CSS function, so the browser paints it in its own gamut.
|
|
14
|
+
*/
|
|
15
|
+
interface OklchColor {
|
|
16
|
+
readonly oklch: {
|
|
17
|
+
/** Lightness, as a percentage (0–100). */readonly l: number; /** Chroma, from 0 (grey) up; sRGB colours stay under about 0.37. */
|
|
18
|
+
readonly c: number; /** Hue in degrees. Absent for a grey, whose hue CSS spells `none`. */
|
|
19
|
+
readonly h?: number; /** Opacity, 0–1. Absent is opaque. */
|
|
20
|
+
readonly alpha?: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Whether a value is an OKLCH colour. */
|
|
24
|
+
declare function isOklchColor(value: unknown): value is OklchColor;
|
|
25
|
+
/** `oklch(63.7, 0.237, 25.331)` — lightness as a percentage, chroma, and hue in degrees. A grey
|
|
26
|
+
* leaves the hue out. */
|
|
27
|
+
declare function oklch(l: number, c: number, h?: number, alpha?: number): OklchColor;
|
|
28
|
+
/** An OKLCH colour as CSS: `oklch(63.7% 0.237 25.331)`, `oklch(98.5% 0 none)`, `oklch(… / 0.5)`. */
|
|
29
|
+
declare function renderOklch(value: OklchColor): string;
|
|
8
30
|
interface DerivedColor {
|
|
9
31
|
readonly op: 'darken' | 'lighten' | 'alpha' | 'mix';
|
|
10
32
|
readonly color: ColorValue;
|
|
@@ -46,11 +68,14 @@ declare function isDerivedColor(value: unknown): value is DerivedColor;
|
|
|
46
68
|
/** The interpolation space each op is authored in: `oklch` for shading (perceptually even), `srgb`
|
|
47
69
|
* for alpha. */
|
|
48
70
|
declare const DERIVED_COLOR_SPACE: Record<DerivedColor['op'], string>;
|
|
71
|
+
/** The space a derived colour with no `space` of its own mixes in, on every platform. */
|
|
72
|
+
declare const DERIVED_COLOR_DEFAULT_SPACE = "oklch";
|
|
49
73
|
/** Blend toward black in light mode and white in dark — so a hover state darkens in both. */
|
|
50
74
|
declare function darken(color: ColorValue, amount: number): DerivedColor;
|
|
51
75
|
/** Mirror of {@link darken}: toward white in light mode, black in dark. */
|
|
52
76
|
declare function lighten(color: ColorValue, amount: number): DerivedColor;
|
|
53
|
-
/** `alpha(color, 0.2)` — the colour at 20% opacity. Takes a 0–1 opacity, like CSS
|
|
77
|
+
/** `alpha(color, 0.2)` — the colour at 20% opacity. Takes a 0–1 opacity, like CSS, kept to a tenth
|
|
78
|
+
* of a percent. */
|
|
54
79
|
declare function alpha(color: ColorValue, opacity: number): DerivedColor;
|
|
55
80
|
/** `mix(a, b, 30)` — 30% of the way from `a` to `b`. */
|
|
56
81
|
declare function mix(color: ColorValue, towards: ColorValue, amount: number, space?: string): DerivedColor;
|
|
@@ -64,4 +89,4 @@ declare function renderDerivedColor(value: DerivedColor, render: (color: ColorVa
|
|
|
64
89
|
/** A gradient as CSS — `linear-gradient(to right, transparent 0%, var(--uds-bg-primary) 50%)`. */
|
|
65
90
|
declare function renderGradient(value: Gradient, render: (color: ColorValue) => string): string;
|
|
66
91
|
//#endregion
|
|
67
|
-
export { ColorValue, DERIVED_COLOR_SPACE, DerivedColor, Gradient, alpha, darken, isDerivedColor, isGradient, lighten, linearGradient, mix, renderDerivedColor, renderGradient };
|
|
92
|
+
export { ColorValue, DERIVED_COLOR_DEFAULT_SPACE, DERIVED_COLOR_SPACE, DerivedColor, Gradient, OklchColor, alpha, darken, isDerivedColor, isGradient, isOklchColor, lighten, linearGradient, mix, oklch, renderDerivedColor, renderGradient, renderOklch };
|