@yahoo/uds-create-config 4.54.0 → 4.55.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -16,9 +16,59 @@ import { z } from "zod";
|
|
|
16
16
|
* That's also why this isn't a separate entity: a derived colour is a value, and making it its own
|
|
17
17
|
* kind would split the token store and break every `token:` ref that points into it.
|
|
18
18
|
*/
|
|
19
|
+
const oklchColor = z.strictObject({ oklch: z.strictObject({
|
|
20
|
+
l: z.number().min(0).max(100),
|
|
21
|
+
c: z.number().min(0),
|
|
22
|
+
h: z.number().optional(),
|
|
23
|
+
alpha: z.number().min(0).max(1).optional()
|
|
24
|
+
}) });
|
|
25
|
+
/** Whether a value is an OKLCH colour. */
|
|
26
|
+
function isOklchColor(value) {
|
|
27
|
+
const channels = value?.oklch;
|
|
28
|
+
return typeof value === "object" && value !== null && typeof channels === "object" && channels !== null && typeof channels.l === "number" && typeof channels.c === "number";
|
|
29
|
+
}
|
|
30
|
+
/** `oklch(63.7, 0.237, 25.331)` — lightness as a percentage, chroma, and hue in degrees. A grey
|
|
31
|
+
* leaves the hue out. */
|
|
32
|
+
function oklch(l, c, h, alpha) {
|
|
33
|
+
return { oklch: {
|
|
34
|
+
l,
|
|
35
|
+
c,
|
|
36
|
+
...h === void 0 ? {} : { h },
|
|
37
|
+
...alpha === void 0 ? {} : { alpha }
|
|
38
|
+
} };
|
|
39
|
+
}
|
|
40
|
+
/** An OKLCH colour as CSS: `oklch(63.7% 0.237 25.331)`, `oklch(98.5% 0 none)`, `oklch(… / 0.5)`. */
|
|
41
|
+
function renderOklch(value) {
|
|
42
|
+
const { l, c, h, alpha: opacity } = value.oklch;
|
|
43
|
+
const channels = `${l}% ${c} ${h ?? "none"}`;
|
|
44
|
+
return opacity === void 0 ? `oklch(${channels})` : `oklch(${channels} / ${opacity})`;
|
|
45
|
+
}
|
|
46
|
+
/** The `oklch(…)` call that authors a stored OKLCH colour. One with an alpha and no hue prints as
|
|
47
|
+
* the object it is, since the helper takes the hue before the alpha. */
|
|
48
|
+
function oklchAuthoring(value) {
|
|
49
|
+
const { l, c, h, alpha: opacity } = value.oklch;
|
|
50
|
+
if (h === void 0) return opacity === void 0 ? {
|
|
51
|
+
helper: "oklch",
|
|
52
|
+
args: [l, c]
|
|
53
|
+
} : void 0;
|
|
54
|
+
return {
|
|
55
|
+
helper: "oklch",
|
|
56
|
+
args: opacity === void 0 ? [
|
|
57
|
+
l,
|
|
58
|
+
c,
|
|
59
|
+
h
|
|
60
|
+
] : [
|
|
61
|
+
l,
|
|
62
|
+
c,
|
|
63
|
+
h,
|
|
64
|
+
opacity
|
|
65
|
+
]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
19
68
|
const colorValue = z.lazy(() => z.union([
|
|
20
69
|
z.string(),
|
|
21
70
|
refSchema,
|
|
71
|
+
oklchColor,
|
|
22
72
|
derivedColor,
|
|
23
73
|
gradient
|
|
24
74
|
]));
|
|
@@ -71,6 +121,8 @@ const DERIVED_COLOR_SPACE = {
|
|
|
71
121
|
alpha: "srgb",
|
|
72
122
|
mix: "oklch"
|
|
73
123
|
};
|
|
124
|
+
/** The space a derived colour with no `space` of its own mixes in, on every platform. */
|
|
125
|
+
const DERIVED_COLOR_DEFAULT_SPACE = "oklch";
|
|
74
126
|
/** Blend toward black in light mode and white in dark — so a hover state darkens in both. */
|
|
75
127
|
function darken(color, amount) {
|
|
76
128
|
return {
|
|
@@ -89,12 +141,13 @@ function lighten(color, amount) {
|
|
|
89
141
|
space: DERIVED_COLOR_SPACE.lighten
|
|
90
142
|
};
|
|
91
143
|
}
|
|
92
|
-
/** `alpha(color, 0.2)` — the colour at 20% opacity. Takes a 0–1 opacity, like CSS
|
|
144
|
+
/** `alpha(color, 0.2)` — the colour at 20% opacity. Takes a 0–1 opacity, like CSS, kept to a tenth
|
|
145
|
+
* of a percent. */
|
|
93
146
|
function alpha(color, opacity) {
|
|
94
147
|
return {
|
|
95
148
|
op: "alpha",
|
|
96
149
|
color,
|
|
97
|
-
amount: Math.round(Math.max(0, Math.min(1, opacity)) *
|
|
150
|
+
amount: Math.round(Math.max(0, Math.min(1, opacity)) * 1e3) / 10,
|
|
98
151
|
space: DERIVED_COLOR_SPACE.alpha
|
|
99
152
|
};
|
|
100
153
|
}
|
|
@@ -110,7 +163,7 @@ function mix(color, towards, amount, space = DERIVED_COLOR_SPACE.mix) {
|
|
|
110
163
|
}
|
|
111
164
|
/**
|
|
112
165
|
* The helper call that authors a stored derived colour, or `undefined` for one no helper produces (a
|
|
113
|
-
* `darken` in another space, an `alpha`
|
|
166
|
+
* `darken` in another space, an `alpha` finer than a tenth of a percent). `alpha` stores a 0–100 percent and
|
|
114
167
|
* takes a 0–1 opacity, so the argument is the percent scaled back. A `darken`, `lighten` or `mix`
|
|
115
168
|
* with no `space` renders in `oklch`, so it is the helper's own value; an `alpha` with none renders
|
|
116
169
|
* in `oklch` too, which is not what `alpha()` writes, so it stays an object.
|
|
@@ -124,12 +177,12 @@ function derivedColorAuthoring(value) {
|
|
|
124
177
|
};
|
|
125
178
|
}
|
|
126
179
|
if (value.op === "alpha") {
|
|
127
|
-
const whole = Number.isInteger(value.amount);
|
|
180
|
+
const whole = Number.isInteger(value.amount * 10);
|
|
128
181
|
const inRange = value.amount >= 0 && value.amount <= 100;
|
|
129
182
|
if (value.space !== DERIVED_COLOR_SPACE.alpha || value.with !== void 0 || !whole || !inRange) return;
|
|
130
183
|
return {
|
|
131
184
|
helper: "alpha",
|
|
132
|
-
args: [value.color, value.amount /
|
|
185
|
+
args: [value.color, Math.round(value.amount * 10) / 1e3]
|
|
133
186
|
};
|
|
134
187
|
}
|
|
135
188
|
if (value.with === void 0) return void 0;
|
|
@@ -169,9 +222,9 @@ function renderDerivedColor(value, render) {
|
|
|
169
222
|
}
|
|
170
223
|
/** A gradient as CSS — `linear-gradient(to right, transparent 0%, var(--uds-bg-primary) 50%)`. */
|
|
171
224
|
function renderGradient(value, render) {
|
|
172
|
-
const stops = value.stops.map((stop) => [render(stop.color), stop.at].filter(Boolean).join(" ")).join(", ");
|
|
173
|
-
const head = [value.angle, value.space ? `in ${value.space}` : void 0].filter(Boolean);
|
|
225
|
+
const stops = value.stops.map((stop) => [render(stop.color), stop.at === void 0 ? void 0 : render(stop.at)].filter(Boolean).join(" ")).join(", ");
|
|
226
|
+
const head = [value.angle === void 0 ? void 0 : render(value.angle), value.space ? `in ${value.space}` : void 0].filter(Boolean);
|
|
174
227
|
return `${value.repeating ? "repeating-" : ""}${value.gradient}-gradient(${[...head, stops].join(", ")})`;
|
|
175
228
|
}
|
|
176
229
|
//#endregion
|
|
177
|
-
export { DERIVED_COLOR_SPACE, alpha, darken, derivedColor, derivedColorAuthoring, gradient, gradientAuthoring, isDerivedColor, isGradient, lighten, linearGradient, mix, renderDerivedColor, renderGradient };
|
|
230
|
+
export { DERIVED_COLOR_DEFAULT_SPACE, DERIVED_COLOR_SPACE, alpha, darken, derivedColor, derivedColorAuthoring, gradient, gradientAuthoring, isDerivedColor, isGradient, isOklchColor, lighten, linearGradient, mix, oklch, oklchAuthoring, oklchColor, renderDerivedColor, renderGradient, renderOklch };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ref, splitRef } from "../../framework/utils/refs.js";
|
|
2
2
|
import { createSliceMemo } from "../../framework/memo.js";
|
|
3
|
-
import { Settings, classPrefixOf, varPrefixOf } from "./Settings.js";
|
|
3
|
+
import { Settings, classPrefixOf, prefixed, varPrefixOf } from "./Settings.js";
|
|
4
4
|
import { File } from "./File.js";
|
|
5
|
+
import { isNativeConfig } from "../../configs/platform.js";
|
|
5
6
|
import { Component } from "./Component.js";
|
|
6
7
|
import { contentKey } from "../../framework/utils/content-key.js";
|
|
7
|
-
import { isNativeConfig } from "../../configs/platform.js";
|
|
8
8
|
//#region src/entities/system/component-module.ts
|
|
9
9
|
/**
|
|
10
10
|
* What names a compiled component module: a content key over the module's config-visible build
|
|
@@ -278,7 +278,10 @@ function componentModuleSource(config, componentPathOrRef) {
|
|
|
278
278
|
* three name the same classes and hash the same config.
|
|
279
279
|
*/
|
|
280
280
|
function linkedModuleView({ consumer, owner, slug, shadowed }) {
|
|
281
|
-
const classNames = shadowed ?
|
|
281
|
+
const classNames = shadowed ? prefixed({
|
|
282
|
+
prefix: classPrefixOf(consumer),
|
|
283
|
+
name: slug
|
|
284
|
+
}) : classPrefixOf(consumer);
|
|
282
285
|
return owner.apply({
|
|
283
286
|
kind: "settings",
|
|
284
287
|
operation: "update",
|
|
@@ -353,7 +356,7 @@ function linkedComponentModule(config, componentPathOrRef) {
|
|
|
353
356
|
moduleKey,
|
|
354
357
|
exportName: componentModuleExportName(authored?.path ?? component.path),
|
|
355
358
|
linked: {
|
|
356
|
-
slug,
|
|
359
|
+
slug: slug.split("~").at(-1) ?? slug,
|
|
357
360
|
...pin
|
|
358
361
|
}
|
|
359
362
|
},
|
|
@@ -365,6 +368,14 @@ function linkedComponentModule(config, componentPathOrRef) {
|
|
|
365
368
|
* bridge this config links, not by this config.
|
|
366
369
|
*/
|
|
367
370
|
function publishedPin(config, slug) {
|
|
371
|
+
if (slug.includes("~")) {
|
|
372
|
+
const segments = slug.split("~");
|
|
373
|
+
const { systemId, version } = (config.sourceFor(segments.slice(0, -1).join("~"))?.resolve("linked-system", segments.at(-1) ?? ""))?.toJSON() ?? {};
|
|
374
|
+
return systemId && version ? {
|
|
375
|
+
systemId,
|
|
376
|
+
version
|
|
377
|
+
} : void 0;
|
|
378
|
+
}
|
|
368
379
|
const seen = /* @__PURE__ */ new Set();
|
|
369
380
|
const queue = [config];
|
|
370
381
|
for (let at = queue.shift(); at; at = queue.shift()) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Divide, Multiply } from "./calc.js";
|
|
2
|
+
import { DerivedColor, Gradient, OklchColor } from "./color.js";
|
|
2
3
|
import { Shadow } from "./shadow.js";
|
|
3
4
|
import { Transition } from "./transition.js";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
|
|
6
7
|
//#region src/entities/system/composed-values.d.ts
|
|
7
|
-
type ComposedValue = DerivedColor | Gradient | Transition | Shadow;
|
|
8
|
+
type ComposedValue = Multiply | Divide | OklchColor | DerivedColor | Gradient | Transition | Shadow;
|
|
8
9
|
/** How the write engine spells a stored value: the helper's name and its positional arguments. */
|
|
9
10
|
interface ComposedValueAuthoring {
|
|
10
11
|
readonly helper: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { divideAuthoring, divideValue, isDivide, isMultiply, multiplyAuthoring, multiplyValue, renderDivide, renderMultiply } from "./calc.js";
|
|
2
|
+
import { derivedColor, derivedColorAuthoring, gradient, gradientAuthoring, isDerivedColor, isGradient, isOklchColor, oklchAuthoring, oklchColor, renderDerivedColor, renderGradient, renderOklch } from "./color.js";
|
|
2
3
|
import { isShadow, renderShadow, shadowAuthoring, shadowValue } from "./shadow.js";
|
|
3
4
|
import { isTransition, renderTransition, transitionAuthoring, transitionValue } from "./transition.js";
|
|
4
5
|
//#region src/entities/system/composed-values.ts
|
|
@@ -8,6 +9,24 @@ function form(spec) {
|
|
|
8
9
|
return spec;
|
|
9
10
|
}
|
|
10
11
|
const composedValueForms = [
|
|
12
|
+
form({
|
|
13
|
+
schema: multiplyValue,
|
|
14
|
+
is: isMultiply,
|
|
15
|
+
render: renderMultiply,
|
|
16
|
+
authoring: multiplyAuthoring
|
|
17
|
+
}),
|
|
18
|
+
form({
|
|
19
|
+
schema: divideValue,
|
|
20
|
+
is: isDivide,
|
|
21
|
+
render: renderDivide,
|
|
22
|
+
authoring: divideAuthoring
|
|
23
|
+
}),
|
|
24
|
+
form({
|
|
25
|
+
schema: oklchColor,
|
|
26
|
+
is: isOklchColor,
|
|
27
|
+
render: renderOklch,
|
|
28
|
+
authoring: oklchAuthoring
|
|
29
|
+
}),
|
|
11
30
|
form({
|
|
12
31
|
schema: derivedColor,
|
|
13
32
|
is: isDerivedColor,
|
|
@@ -499,10 +499,10 @@ type Registered = [keyof RegisteredStyleProps] extends [never] ? false : true;
|
|
|
499
499
|
/** Every style prop the system registers, typed by the values it registers. */
|
|
500
500
|
type StyleProps = { [K in keyof RegisteredStyleProps]?: RegisteredStyleProps[K] };
|
|
501
501
|
/**
|
|
502
|
-
* A modifier's block: the same style props, applied under its condition.
|
|
503
|
-
*
|
|
502
|
+
* A modifier's block: the same style props, applied under its condition. A block inside a block
|
|
503
|
+
* stacks onto it in the order written, so `_md={{ _hover: { … } }}` is `md:hover:`.
|
|
504
504
|
*/
|
|
505
|
-
type ModifierProps = { [K in keyof RegisteredModifiers]?: StyleProps };
|
|
505
|
+
type ModifierProps = { [K in keyof RegisteredModifiers]?: StyleProps & ModifierProps };
|
|
506
506
|
/** The per-layer override channel the emitted module reads, keyed by this component's layers. */
|
|
507
507
|
type LayerProps<L extends LayerMap> = {
|
|
508
508
|
layerProps?: Partial<Record<keyof L & string, Record<string, unknown>>>;
|
|
@@ -168,7 +168,16 @@ function planLink({ config, input }) {
|
|
|
168
168
|
if (path === "*") continue;
|
|
169
169
|
rejectData(`Cannot link "${path}" through "${slug}" — the source has no ${kind} there.`, ["data", accessor]);
|
|
170
170
|
}
|
|
171
|
-
const taken = entries.filter((entry) =>
|
|
171
|
+
const taken = entries.filter((entry) => {
|
|
172
|
+
const fit = config.canBorrow({
|
|
173
|
+
kind,
|
|
174
|
+
path: entry.from,
|
|
175
|
+
slug
|
|
176
|
+
});
|
|
177
|
+
if (fit.ok) return true;
|
|
178
|
+
if (source.resolve(kind, path)) rejectData(fit.error, ["data", accessor]);
|
|
179
|
+
return false;
|
|
180
|
+
}).filter((entry) => shadow(kind, entry.from));
|
|
172
181
|
if (taken.length === entries.length) {
|
|
173
182
|
if (path !== "*") gained.push(path);
|
|
174
183
|
continue;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Config } from "../../framework/Config.js";
|
|
2
|
+
import { NATIVE_CONFIG_NAME, WEB_CONFIG_NAME } from "../../configs/platform.js";
|
|
3
|
+
|
|
4
|
+
//#region src/entities/system/linked-token-values.d.ts
|
|
5
|
+
type Platform = typeof WEB_CONFIG_NAME | typeof NATIVE_CONFIG_NAME;
|
|
6
|
+
declare function convertLinkedTokenValue({
|
|
7
|
+
path,
|
|
8
|
+
value,
|
|
9
|
+
sourcePlatform,
|
|
10
|
+
destinationPlatform,
|
|
11
|
+
remBase
|
|
12
|
+
}: {
|
|
13
|
+
path: string;
|
|
14
|
+
value: string | number | boolean;
|
|
15
|
+
sourcePlatform: string;
|
|
16
|
+
destinationPlatform: Platform; /** The pinned web source's fixed root size, only needed for web rem → native points. */
|
|
17
|
+
remBase?: number;
|
|
18
|
+
}): string | number | boolean;
|
|
19
|
+
/** The one adapter for base and mode values before a linked body is parsed by its destination. */
|
|
20
|
+
declare function prepareLinkedTokenBody({
|
|
21
|
+
path,
|
|
22
|
+
body,
|
|
23
|
+
source,
|
|
24
|
+
config,
|
|
25
|
+
destinationPlatform
|
|
26
|
+
}: {
|
|
27
|
+
path: string;
|
|
28
|
+
body: Record<string, unknown>;
|
|
29
|
+
source: Config; /** The config whose refs the body spells; a source's own body spells them in the source. */
|
|
30
|
+
config: Config;
|
|
31
|
+
destinationPlatform: Platform;
|
|
32
|
+
}): Record<string, unknown>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { convertLinkedTokenValue, prepareLinkedTokenBody };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { hasRefMarker } from "../../framework/utils/refs.js";
|
|
2
|
+
import { isDivide, isMultiply } from "./calc.js";
|
|
3
|
+
import { isOklchColor } from "./color.js";
|
|
4
|
+
import { literalValueOf } from "../../framework/projections.js";
|
|
5
|
+
import "../../configs/platform.js";
|
|
6
|
+
import { rootFontSize } from "./token-values.js";
|
|
7
|
+
//#region src/entities/system/linked-token-values.ts
|
|
8
|
+
/**
|
|
9
|
+
* Adapt a linked token for the system reading it. The source keeps its authored value; only the
|
|
10
|
+
* destination's resolved view changes. Length groups are explicit because a bare native number
|
|
11
|
+
* does not say whether CSS should spell it as px or as a unitless value.
|
|
12
|
+
*/
|
|
13
|
+
const LENGTH_GROUPS = new Set([
|
|
14
|
+
"spacing",
|
|
15
|
+
"radius",
|
|
16
|
+
"borderRadius",
|
|
17
|
+
"iconSize",
|
|
18
|
+
"borderWidth"
|
|
19
|
+
]);
|
|
20
|
+
const NUMBER_GROUPS = new Set(["opacity", "zIndex"]);
|
|
21
|
+
const LENGTH = /^(-?(?:\d+(?:\.\d+)?|\.\d+))(px|rem)$/;
|
|
22
|
+
const NUMBER = /^-?(?:\d+(?:\.\d+)?|\.\d+)$/;
|
|
23
|
+
/** `oklch(L C H / A)`, the colour notation a config writes and React Native's colour parser
|
|
24
|
+
* doesn't read. Lightness and alpha may be percentages, the hue `none`. */
|
|
25
|
+
const OKLCH = /^oklch\(\s*(-?[\d.]+)(%?)\s+(-?[\d.]+)(%?)\s+(-?[\d.]+|none)(?:deg)?\s*(?:\/\s*(-?[\d.]+)(%?)\s*)?\)$/i;
|
|
26
|
+
function convertLinkedTokenValue({ path, value, sourcePlatform, destinationPlatform, remBase }) {
|
|
27
|
+
if (sourcePlatform === destinationPlatform) return value;
|
|
28
|
+
const group = path.split("/")[0];
|
|
29
|
+
if (destinationPlatform === "react-native-system") {
|
|
30
|
+
if (LENGTH_GROUPS.has(group)) {
|
|
31
|
+
if (typeof value === "number") return value;
|
|
32
|
+
const match = typeof value === "string" ? LENGTH.exec(value) : null;
|
|
33
|
+
if (!match) throw unsupported("native", path, value, "a px or rem length");
|
|
34
|
+
if (match[2] === "rem" && remBase === void 0) throw new Error(`[uds] Linked native token "${path}" has value ${JSON.stringify(value)}, but its source has no fixed root font size for rem conversion.`);
|
|
35
|
+
return Number(match[1]) * (match[2] === "rem" ? remBase : 1);
|
|
36
|
+
}
|
|
37
|
+
if (NUMBER_GROUPS.has(group)) {
|
|
38
|
+
if (typeof value === "number") return value;
|
|
39
|
+
if (typeof value !== "string" || !NUMBER.test(value)) throw unsupported("native", path, value, "a unitless number");
|
|
40
|
+
const number = Number(value);
|
|
41
|
+
if (group === "opacity" && (number < 0 || number > 1)) throw unsupported("native", path, value, "an opacity between 0 and 1");
|
|
42
|
+
if (group === "zIndex" && !Number.isInteger(number)) throw unsupported("native", path, value, "an integer z-index");
|
|
43
|
+
return number;
|
|
44
|
+
}
|
|
45
|
+
if (typeof value === "string") return oklchAsHex(value.trim()) ?? value;
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
if (sourcePlatform === "react-native-system" && typeof value === "number") {
|
|
49
|
+
if (LENGTH_GROUPS.has(group)) return `${value}px`;
|
|
50
|
+
if (NUMBER_GROUPS.has(group)) return value;
|
|
51
|
+
throw unsupported("web", path, value, "a token with a known CSS unit");
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
/** The one adapter for base and mode values before a linked body is parsed by its destination. */
|
|
56
|
+
function prepareLinkedTokenBody({ path, body, source, config, destinationPlatform }) {
|
|
57
|
+
if (source.name === destinationPlatform) return body;
|
|
58
|
+
const remBase = source.name === "system-config" ? rootFontSize(source).px : void 0;
|
|
59
|
+
const convert = (value) => {
|
|
60
|
+
const literal = destinationPlatform === "react-native-system" ? nativeLiteral({
|
|
61
|
+
config,
|
|
62
|
+
value,
|
|
63
|
+
remBase
|
|
64
|
+
}) : value;
|
|
65
|
+
return typeof literal === "string" || typeof literal === "number" || typeof literal === "boolean" ? convertLinkedTokenValue({
|
|
66
|
+
path,
|
|
67
|
+
value: literal,
|
|
68
|
+
sourcePlatform: source.name,
|
|
69
|
+
destinationPlatform,
|
|
70
|
+
remBase
|
|
71
|
+
}) : literal;
|
|
72
|
+
};
|
|
73
|
+
const overrides = body.overrides;
|
|
74
|
+
return {
|
|
75
|
+
...body,
|
|
76
|
+
value: convert(body.value),
|
|
77
|
+
...overrides && typeof overrides === "object" && !Array.isArray(overrides) ? { overrides: Object.fromEntries(Object.entries(overrides).map(([key, raw]) => [key, raw && typeof raw === "object" && !Array.isArray(raw) ? {
|
|
78
|
+
...raw,
|
|
79
|
+
value: convert(raw.value)
|
|
80
|
+
} : raw])) } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A composed web value as a literal: an OKLCH colour as its CSS (which the conversion turns into a
|
|
85
|
+
* hex), arithmetic evaluated (a length in points where it comes to one), and a string's ref markers
|
|
86
|
+
* filled in. Refs and derived colours stay as they are, since the native theme resolves both. A value
|
|
87
|
+
* with no literal is returned unchanged for the native schema to refuse.
|
|
88
|
+
*/
|
|
89
|
+
function nativeLiteral({ config, value, remBase }) {
|
|
90
|
+
const marked = typeof value === "string" && hasRefMarker(value);
|
|
91
|
+
const calc = isMultiply(value) || isDivide(value);
|
|
92
|
+
if (!marked && !calc && !isOklchColor(value)) return value;
|
|
93
|
+
const literal = literalValueOf(config, value);
|
|
94
|
+
if (literal === void 0) return value;
|
|
95
|
+
if (typeof literal !== "string") return literal;
|
|
96
|
+
const length = calc ? LENGTH.exec(literal) : null;
|
|
97
|
+
if (length && (length[2] === "px" || remBase !== void 0)) return Number(length[1]) * (length[2] === "rem" ? remBase : 1);
|
|
98
|
+
return literal;
|
|
99
|
+
}
|
|
100
|
+
/** An `oklch()` colour as the hex a native token holds, each channel clipped to sRGB as a browser
|
|
101
|
+
* paints it. Anything else answers `undefined`. */
|
|
102
|
+
function oklchAsHex(value) {
|
|
103
|
+
const match = OKLCH.exec(value);
|
|
104
|
+
if (!match) return void 0;
|
|
105
|
+
const [, l, lPercent, c, cPercent, h, a, aPercent] = match;
|
|
106
|
+
const lightness = Number(l) / (lPercent ? 100 : 1);
|
|
107
|
+
const chroma = Number(c) * (cPercent ? .004 : 1);
|
|
108
|
+
const hue = h === "none" ? 0 : Number(h) * Math.PI / 180;
|
|
109
|
+
const opacity = a === void 0 ? 1 : Number(a) / (aPercent ? 100 : 1);
|
|
110
|
+
const okA = chroma * Math.cos(hue);
|
|
111
|
+
const okB = chroma * Math.sin(hue);
|
|
112
|
+
const [lc, mc, sc] = [
|
|
113
|
+
lightness + .3963377774 * okA + .2158037573 * okB,
|
|
114
|
+
lightness - .1055613458 * okA - .0638541728 * okB,
|
|
115
|
+
lightness - .0894841775 * okA - 1.291485548 * okB
|
|
116
|
+
].map((channel) => channel ** 3);
|
|
117
|
+
const linear = [
|
|
118
|
+
4.0767416621 * lc - 3.3077115913 * mc + .2309699292 * sc,
|
|
119
|
+
-1.2684380046 * lc + 2.6097574011 * mc - .3413193965 * sc,
|
|
120
|
+
-.0041960863 * lc - .7034186147 * mc + 1.707614701 * sc
|
|
121
|
+
];
|
|
122
|
+
const byte = (unit) => Math.round(Math.min(1, Math.max(0, unit)) * 255).toString(16).padStart(2, "0");
|
|
123
|
+
const encode = (channel) => {
|
|
124
|
+
const magnitude = Math.abs(channel);
|
|
125
|
+
const encoded = magnitude <= .0031308 ? 12.92 * magnitude : 1.055 * magnitude ** (1 / 2.4) - .055;
|
|
126
|
+
return Math.sign(channel) * encoded;
|
|
127
|
+
};
|
|
128
|
+
const hex = `#${linear.map((channel) => byte(encode(channel))).join("")}`;
|
|
129
|
+
return opacity < 1 ? `${hex}${byte(opacity)}` : hex;
|
|
130
|
+
}
|
|
131
|
+
function unsupported(destination, path, value, expected) {
|
|
132
|
+
return /* @__PURE__ */ new Error(`[uds] Linked ${destination} token "${path}" has value ${JSON.stringify(value)}; expected ${expected}.`);
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { convertLinkedTokenValue, prepareLinkedTokenBody };
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { Ref } from "../../framework/utils/refs.js";
|
|
2
|
-
import { DerivedColor } from "./color.js";
|
|
2
|
+
import { DerivedColor, OklchColor } from "./color.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
//#region src/entities/system/shadow.d.ts
|
|
6
|
-
/** A shadow layer's colour: a literal, a token ref, or a colour derived from one. */
|
|
7
|
-
type ShadowColor = string | Ref | DerivedColor;
|
|
8
|
-
/** One shadow spelled as its parts. The lengths are CSS lengths, as `box-shadow` takes them.
|
|
6
|
+
/** A shadow layer's colour: a literal, a token ref, an OKLCH colour, or a colour derived from one. */
|
|
7
|
+
type ShadowColor = string | Ref | OklchColor | DerivedColor;
|
|
8
|
+
/** One shadow spelled as its parts. The lengths are CSS lengths, as `box-shadow` takes them. A shadow
|
|
9
|
+
* for `text-shadow` or `drop-shadow()`, which take no spread, leaves `spread` out, and one with no
|
|
10
|
+
* blur (`0 1px #000`) leaves `blur` out. */
|
|
9
11
|
interface ShadowParts {
|
|
10
12
|
readonly inset?: boolean;
|
|
11
13
|
readonly offsetX: string;
|
|
12
14
|
readonly offsetY: string;
|
|
13
|
-
readonly blur
|
|
14
|
-
readonly spread
|
|
15
|
+
readonly blur?: string;
|
|
16
|
+
readonly spread?: string;
|
|
15
17
|
readonly color: ShadowColor;
|
|
16
18
|
}
|
|
17
19
|
/** A layer of the stack: a ref to a shadow token (all of its layers), or one shadow as parts. */
|
|
@@ -34,6 +36,6 @@ declare function shadow(...layers: readonly ShadowLayer[]): Shadow;
|
|
|
34
36
|
* expression. It is passed in rather than imported because resolving a ref is the projection layer's
|
|
35
37
|
* job and this module is the value's shape.
|
|
36
38
|
*/
|
|
37
|
-
declare function renderShadow(value: Shadow, render: (part:
|
|
39
|
+
declare function renderShadow(value: Shadow, render: (part: unknown) => string): string;
|
|
38
40
|
//#endregion
|
|
39
41
|
export { Shadow, ShadowColor, ShadowLayer, ShadowParts, isShadow, renderShadow, shadow };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isRef, refSchema } from "../../framework/utils/refs.js";
|
|
2
|
-
import { derivedColor } from "./color.js";
|
|
2
|
+
import { derivedColor, oklchColor } from "./color.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/entities/system/shadow.ts
|
|
5
5
|
/**
|
|
@@ -20,11 +20,12 @@ const shadowParts = z.strictObject({
|
|
|
20
20
|
inset: z.boolean().optional(),
|
|
21
21
|
offsetX: z.string(),
|
|
22
22
|
offsetY: z.string(),
|
|
23
|
-
blur: z.string(),
|
|
24
|
-
spread: z.string(),
|
|
23
|
+
blur: z.string().optional(),
|
|
24
|
+
spread: z.string().optional(),
|
|
25
25
|
color: z.union([
|
|
26
26
|
z.string(),
|
|
27
27
|
refSchema,
|
|
28
|
+
oklchColor,
|
|
28
29
|
derivedColor
|
|
29
30
|
])
|
|
30
31
|
});
|
|
@@ -64,10 +65,10 @@ function renderShadow(value, render) {
|
|
|
64
65
|
const { inset, offsetX, offsetY, blur, spread, color } = layer;
|
|
65
66
|
return [
|
|
66
67
|
inset ? "inset" : void 0,
|
|
67
|
-
offsetX,
|
|
68
|
-
offsetY,
|
|
69
|
-
blur,
|
|
70
|
-
spread,
|
|
68
|
+
render(offsetX),
|
|
69
|
+
render(offsetY),
|
|
70
|
+
blur === void 0 ? spread === void 0 ? void 0 : "0" : render(blur),
|
|
71
|
+
spread === void 0 ? void 0 : render(spread),
|
|
71
72
|
render(color)
|
|
72
73
|
].filter((part) => part !== void 0).join(" ");
|
|
73
74
|
}).join(", ");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SubEntityClass } from "../../framework/defineSubEntity.js";
|
|
2
2
|
import { RegisteredModifiers, RegisteredStyleProps } from "../../framework/registered.js";
|
|
3
|
-
import { Named } from "../../framework/named.js";
|
|
4
3
|
import { ComposedValue } from "./composed-values.js";
|
|
4
|
+
import { Named } from "../../framework/named.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
7
|
//#region src/entities/system/style-bag.d.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRef, splitRef } from "../../framework/utils/refs.js";
|
|
1
|
+
import { isRef, kindOf, pathOf, splitRef } from "../../framework/utils/refs.js";
|
|
2
2
|
import { resolveValueType } from "../../css/value-type.js";
|
|
3
3
|
import { resolveTokenValueUnder } from "../../framework/projections.js";
|
|
4
4
|
import { matchEntities, searchEntities } from "../../framework/entity-search.js";
|
|
@@ -41,13 +41,56 @@ function bindingFor(config, path, label = path, source, under = null) {
|
|
|
41
41
|
path: label,
|
|
42
42
|
source,
|
|
43
43
|
type,
|
|
44
|
-
valueType:
|
|
44
|
+
valueType: valueTypeOf({
|
|
45
|
+
config,
|
|
46
|
+
path,
|
|
45
47
|
type,
|
|
46
48
|
value
|
|
47
49
|
}),
|
|
48
50
|
value
|
|
49
51
|
};
|
|
50
52
|
}
|
|
53
|
+
/** A binding's visual type: its own declared type, else the first one declared along its ref chain,
|
|
54
|
+
* else what its literal (or, with none, its stored form at the chain's end) looks like. */
|
|
55
|
+
function valueTypeOf({ config, path, type, value }) {
|
|
56
|
+
const end = chainEnd(config, path);
|
|
57
|
+
const declared = type ?? end.type;
|
|
58
|
+
return resolveValueType({
|
|
59
|
+
type: declared,
|
|
60
|
+
value
|
|
61
|
+
}) ?? resolveValueType({
|
|
62
|
+
type: declared,
|
|
63
|
+
value: end.value
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** Along a token's ref chain: the first declared type, and the value stored at its end — the first
|
|
67
|
+
* token whose value is no ref to another. */
|
|
68
|
+
function chainEnd(config, path) {
|
|
69
|
+
const seen = /* @__PURE__ */ new Set();
|
|
70
|
+
let holder = config;
|
|
71
|
+
let at = path;
|
|
72
|
+
let type;
|
|
73
|
+
while (!seen.has(at)) {
|
|
74
|
+
seen.add(at);
|
|
75
|
+
const body = holder.resolve("token", at)?.toJSON();
|
|
76
|
+
type ??= body?.type;
|
|
77
|
+
if (!isRef(body?.value) || kindOf(body.value) !== "token") return {
|
|
78
|
+
type,
|
|
79
|
+
value: body?.value
|
|
80
|
+
};
|
|
81
|
+
const next = holder.configFor(body.value);
|
|
82
|
+
if (!next) return {
|
|
83
|
+
type,
|
|
84
|
+
value: void 0
|
|
85
|
+
};
|
|
86
|
+
holder = next;
|
|
87
|
+
at = pathOf(body.value);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
type,
|
|
91
|
+
value: void 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
51
94
|
/**
|
|
52
95
|
* What `value` binds to, or `undefined` when it is a literal.
|
|
53
96
|
*
|
|
@@ -44,8 +44,9 @@ interface TokenValueRow {
|
|
|
44
44
|
/** The rest of the path — `2.5` for `spacing/2.5`, `blue/3` for `spectrum/blue/3`. A path with no
|
|
45
45
|
* slash is its own group and alias. */
|
|
46
46
|
readonly alias: string;
|
|
47
|
-
/** The custom property the stylesheet declares it as, spelled by the config that owns it.
|
|
48
|
-
|
|
47
|
+
/** The custom property the stylesheet declares it as, spelled by the config that owns it. A token
|
|
48
|
+
* that declares none has none. */
|
|
49
|
+
readonly variable?: string;
|
|
49
50
|
readonly state: TokenValueState;
|
|
50
51
|
}
|
|
51
52
|
/** The root font size of the page this config's sheet is for, as stated or the browser's own. A
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectRefs, kindOf, pathOf, ref, splitRef } from "../../framework/utils/refs.js";
|
|
2
2
|
import { parseRootFontSize } from "../../css/root-font-size.js";
|
|
3
3
|
import { createSliceMemo } from "../../framework/memo.js";
|
|
4
4
|
import { CSS_DEFAULTS, Settings } from "./Settings.js";
|
|
5
|
-
import { cssVar, resolveTokenValue } from "../../framework/projections.js";
|
|
5
|
+
import { cssVar, declaresCssVariable, resolveTokenValue } from "../../framework/projections.js";
|
|
6
6
|
//#region src/entities/system/token-values.ts
|
|
7
7
|
/**
|
|
8
8
|
* A token as a VALUE a module can compute with.
|
|
@@ -78,7 +78,7 @@ const tokenValueRows = createSliceMemo({
|
|
|
78
78
|
path,
|
|
79
79
|
group: slash > 0 ? path.slice(0, slash) : path,
|
|
80
80
|
alias: slash > 0 ? path.slice(slash + 1) : path,
|
|
81
|
-
variable: cssVar(owner, "token", path),
|
|
81
|
+
...declaresCssVariable(owner, path) ? { variable: cssVar(owner, "token", path) } : {},
|
|
82
82
|
state: stateOf({
|
|
83
83
|
config,
|
|
84
84
|
path,
|
|
@@ -137,12 +137,12 @@ function modeDecides(config, path, seen = /* @__PURE__ */ new Set()) {
|
|
|
137
137
|
const body = config.resolve("token", path)?.toJSON();
|
|
138
138
|
if (!body) return false;
|
|
139
139
|
if (body.overrides && Object.keys(body.overrides).length > 0) return true;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
return collectRefs(body.value).some((target) => {
|
|
141
|
+
if (kindOf(target) !== "token" || seen.has(target)) return false;
|
|
142
|
+
seen.add(target);
|
|
143
|
+
const source = config.configFor({ __ref: target });
|
|
144
|
+
return source ? modeDecides(source, pathOf(target), seen) : false;
|
|
145
|
+
});
|
|
146
146
|
}
|
|
147
147
|
/** The reader names, which is what the build recognizes on a `Token.ref(…)`. */
|
|
148
148
|
const TOKEN_READERS = [
|