@unpunnyfuns/swatchbook-blocks 1.1.1 → 2.0.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/README.md +5 -3
- package/dist/host-e2nSv-sA.mjs +791 -0
- package/dist/host-e2nSv-sA.mjs.map +1 -0
- package/dist/host.d.mts +57 -0
- package/dist/host.mjs +2 -0
- package/dist/index.d.mts +273 -328
- package/dist/index.mjs +536 -1112
- package/dist/index.mjs.map +1 -1
- package/dist/registry-Dm25Go94.d.mts +244 -0
- package/dist/style.css +237 -240
- package/package.json +6 -2
package/dist/index.mjs
CHANGED
|
@@ -1,100 +1,18 @@
|
|
|
1
1
|
import './style.css';
|
|
2
|
-
import { COLOR_FORMATS } from "
|
|
3
|
-
import {
|
|
4
|
-
import { createContext, memo, useCallback, useContext, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState
|
|
2
|
+
import { C as COLOR_FORMATS, S as BorderSample, _ as cssVarAsNumber, a as mergePresenters, b as useRootFontSize, c as ShadowSample, d as OpacitySwatch, f as MotionSample, g as FontWeightSpecimen, h as GradientSwatch, i as PresenterContext, l as TypeSpecimen, m as usePrefersReducedMotion, n as useProjectSource, p as resolveMotionSpec, r as DEFAULT_PRESENTERS, s as usePresenter, u as StrokeSample, v as FontFamilySpecimen, w as formatColor, x as ColorSwatch, y as DimensionSample } from "./host-e2nSv-sA.mjs";
|
|
3
|
+
import { parseColor } from "@unpunnyfuns/swatchbook-core/format-color";
|
|
4
|
+
import { createContext, memo, useCallback, useContext, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { formatDimension, formatSubColor, formatTokenValue, formatTokenValue as formatTokenValue$2 } from "@unpunnyfuns/swatchbook-core/token-value-css";
|
|
7
|
+
import cx from "clsx";
|
|
8
|
+
import { dataAttr } from "@unpunnyfuns/swatchbook-core/data-attr";
|
|
5
9
|
import { getVariance, listPaths, resolveAllWithProvenanceAt } from "@unpunnyfuns/swatchbook-core/graph";
|
|
6
10
|
import { cssVarRef } from "@unpunnyfuns/swatchbook-core/css-var";
|
|
7
11
|
import { SWATCHBOOK_STYLE_ELEMENT_ID, ensureStyleElement } from "@unpunnyfuns/swatchbook-core/style-element";
|
|
8
12
|
import { tupleToName } from "@unpunnyfuns/swatchbook-core/themes";
|
|
9
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
-
import { dataAttr } from "@unpunnyfuns/swatchbook-core/data-attr";
|
|
11
13
|
import { matchPath } from "@unpunnyfuns/swatchbook-core/match-path";
|
|
12
14
|
import { fuzzyFilter } from "@unpunnyfuns/swatchbook-core/fuzzy";
|
|
13
|
-
import cx from "clsx";
|
|
14
15
|
import { useWindowVirtualizer } from "@tanstack/react-virtual";
|
|
15
|
-
//#region src/internal/channel.ts
|
|
16
|
-
let channel = null;
|
|
17
|
-
const pending = /* @__PURE__ */ new Set();
|
|
18
|
-
/**
|
|
19
|
-
* Host adapter API. The single most load-bearing function in the host
|
|
20
|
-
* contract — the addon calls it once at preview init.
|
|
21
|
-
*
|
|
22
|
-
* Inject the host channel. Runs any subscribers that queued before the
|
|
23
|
-
* channel existed, then clears the queue. The host contract does NOT dedupe:
|
|
24
|
-
* on HMR a subscriber's module re-runs {@link onChannel}, which re-attaches
|
|
25
|
-
* immediately once the channel is set, so each subscriber MUST guard its own
|
|
26
|
-
* attach to stay single-wired.
|
|
27
|
-
*/
|
|
28
|
-
function registerChannel(next) {
|
|
29
|
-
channel = next;
|
|
30
|
-
for (const attach of pending) attach(next);
|
|
31
|
-
pending.clear();
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Host adapter API — see {@link registerChannel}.
|
|
35
|
-
*
|
|
36
|
-
* Attach a subscriber to the host channel now if one is registered, else queue
|
|
37
|
-
* it to run the moment {@link registerChannel} fires. Blocks call this at
|
|
38
|
-
* module load; the queue absorbs the gap until the addon injects the channel.
|
|
39
|
-
*/
|
|
40
|
-
function onChannel(attach) {
|
|
41
|
-
if (channel) attach(channel);
|
|
42
|
-
else pending.add(attach);
|
|
43
|
-
}
|
|
44
|
-
//#endregion
|
|
45
|
-
//#region src/internal/channel-globals.ts
|
|
46
|
-
const AXES_GLOBAL_KEY = "swatchbookAxes";
|
|
47
|
-
const COLOR_FORMAT_GLOBAL_KEY = "swatchbookColorFormat";
|
|
48
|
-
let snapshot$1 = {
|
|
49
|
-
axes: null,
|
|
50
|
-
format: null
|
|
51
|
-
};
|
|
52
|
-
const listeners$1 = /* @__PURE__ */ new Set();
|
|
53
|
-
let subscribed$1 = false;
|
|
54
|
-
function isColorFormat(value) {
|
|
55
|
-
return typeof value === "string" && COLOR_FORMATS.includes(value);
|
|
56
|
-
}
|
|
57
|
-
function attach$1(channel) {
|
|
58
|
-
if (subscribed$1) return;
|
|
59
|
-
subscribed$1 = true;
|
|
60
|
-
let lastFingerprint = "";
|
|
61
|
-
const onGlobals = (payload) => {
|
|
62
|
-
const globals = payload.globals;
|
|
63
|
-
if (!globals) return;
|
|
64
|
-
const incomingAxes = globals[AXES_GLOBAL_KEY];
|
|
65
|
-
const incomingFormat = globals[COLOR_FORMAT_GLOBAL_KEY];
|
|
66
|
-
const nextAxes = incomingAxes && typeof incomingAxes === "object" ? incomingAxes : snapshot$1.axes;
|
|
67
|
-
const nextFormat = isColorFormat(incomingFormat) ? incomingFormat : snapshot$1.format;
|
|
68
|
-
const fingerprint = `${nextFormat ?? ""}|${nextAxes ? JSON.stringify(nextAxes) : ""}`;
|
|
69
|
-
if (fingerprint === lastFingerprint) return;
|
|
70
|
-
lastFingerprint = fingerprint;
|
|
71
|
-
snapshot$1 = {
|
|
72
|
-
axes: nextAxes,
|
|
73
|
-
format: nextFormat
|
|
74
|
-
};
|
|
75
|
-
for (const cb of listeners$1) cb();
|
|
76
|
-
};
|
|
77
|
-
channel.on("globalsUpdated", onGlobals);
|
|
78
|
-
channel.on("updateGlobals", onGlobals);
|
|
79
|
-
channel.on("setGlobals", onGlobals);
|
|
80
|
-
}
|
|
81
|
-
onChannel(attach$1);
|
|
82
|
-
function subscribe$2(cb) {
|
|
83
|
-
listeners$1.add(cb);
|
|
84
|
-
return () => {
|
|
85
|
-
listeners$1.delete(cb);
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
function getSnapshot$1() {
|
|
89
|
-
return snapshot$1;
|
|
90
|
-
}
|
|
91
|
-
function getServerSnapshot$1() {
|
|
92
|
-
return snapshot$1;
|
|
93
|
-
}
|
|
94
|
-
function useChannelGlobals() {
|
|
95
|
-
return useSyncExternalStore(subscribe$2, getSnapshot$1, getServerSnapshot$1);
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
16
|
//#region src/contexts.ts
|
|
99
17
|
/**
|
|
100
18
|
* Context carrying the full {@link ProjectSnapshot}. `null` sentinel lets
|
|
@@ -129,10 +47,18 @@ function useActiveAxes() {
|
|
|
129
47
|
return useContext(AxesContext);
|
|
130
48
|
}
|
|
131
49
|
/**
|
|
132
|
-
* Active color-display format for the current story/docs render
|
|
133
|
-
* by
|
|
134
|
-
*
|
|
135
|
-
*
|
|
50
|
+
* Active color-display format for the current story/docs render, consumed
|
|
51
|
+
* by blocks that render color-token values. Emitted CSS is unaffected.
|
|
52
|
+
*
|
|
53
|
+
* Sits in the middle of the precedence chain a block resolves via
|
|
54
|
+
* `colorFormat ?? useColorFormat()`: a block's own `colorFormat` prop wins
|
|
55
|
+
* over this context, which wins over the active source's
|
|
56
|
+
* `defaultColorFormat` (from `Config.defaultColorFormat`). "Active
|
|
57
|
+
* source" is whichever one is actually feeding the render:
|
|
58
|
+
* `SwatchbookProvider`'s snapshot when a story decorator mounted one,
|
|
59
|
+
* otherwise the ambient `ProjectSource` (`#/host.ts`) that MDX-embedded
|
|
60
|
+
* blocks (no `<Story/>`, no provider) read through `useProject()`'s
|
|
61
|
+
* fallback path.
|
|
136
62
|
*
|
|
137
63
|
* Runs through plain React context rather than Storybook's `useGlobals` so
|
|
138
64
|
* per-story seeded globals flow through on first render and the same hook
|
|
@@ -140,314 +66,18 @@ function useActiveAxes() {
|
|
|
140
66
|
* isn't available).
|
|
141
67
|
*/
|
|
142
68
|
const ColorFormatContext = createContext(null);
|
|
143
|
-
function useColorFormat() {
|
|
144
|
-
const contextValue = useContext(ColorFormatContext);
|
|
145
|
-
const channelGlobals = useChannelGlobals();
|
|
146
|
-
return contextValue ?? channelGlobals.format ?? "hex";
|
|
147
|
-
}
|
|
148
|
-
//#endregion
|
|
149
|
-
//#region src/internal/channel-tokens.ts
|
|
150
|
-
/**
|
|
151
|
-
* Host adapter API — same integration tier as {@link BlockChannel} /
|
|
152
|
-
* {@link registerChannel} / {@link onChannel}, distinct from the
|
|
153
|
-
* components/hooks/`SwatchbookProvider` surface MDX/story authors use.
|
|
154
|
-
* Most consumers never touch this; it exists for whoever builds the next
|
|
155
|
-
* host integration.
|
|
156
|
-
*
|
|
157
|
-
* Live token snapshot backed by the addon's preview dev-time HMR event.
|
|
158
|
-
*
|
|
159
|
-
* The initial snapshot is *injected* by the addon preview via
|
|
160
|
-
* {@link registerTokenSource} rather than imported from the addon's
|
|
161
|
-
* `virtual:swatchbook/tokens` build artifact — so blocks carries no
|
|
162
|
-
* dependency on that module and imports cleanly standalone (outside
|
|
163
|
-
* Storybook, in unit tests, in the docs site). Until something registers
|
|
164
|
-
* a source, blocks render from empty defaults.
|
|
165
|
-
*
|
|
166
|
-
* For dev-time updates this module subscribes to `TOKENS_UPDATED_EVENT`
|
|
167
|
-
* on Storybook's channel (which the addon preview re-broadcasts from its
|
|
168
|
-
* own HMR listener) and exposes the latest snapshot via
|
|
169
|
-
* `useSyncExternalStore`, so hooks re-render in place on each token save.
|
|
170
|
-
*/
|
|
171
|
-
const TOKENS_UPDATED_EVENT = "swatchbook/tokens-updated";
|
|
172
|
-
let snapshot = {
|
|
173
|
-
axes: [],
|
|
174
|
-
presets: [],
|
|
175
|
-
diagnostics: [],
|
|
176
|
-
css: "",
|
|
177
|
-
cssVarPrefix: "",
|
|
178
|
-
indicators: {},
|
|
179
|
-
listing: {},
|
|
180
|
-
tokenGraph: {
|
|
181
|
-
nodes: {},
|
|
182
|
-
axes: [],
|
|
183
|
-
axisDefaults: {},
|
|
184
|
-
axisContexts: {}
|
|
185
|
-
},
|
|
186
|
-
defaultTuple: {},
|
|
187
|
-
version: 0
|
|
188
|
-
};
|
|
189
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
190
|
-
let subscribed = false;
|
|
191
|
-
function applyPatch(patch) {
|
|
192
|
-
snapshot = {
|
|
193
|
-
axes: patch.axes ?? snapshot.axes,
|
|
194
|
-
presets: patch.presets ?? snapshot.presets,
|
|
195
|
-
diagnostics: patch.diagnostics ?? snapshot.diagnostics,
|
|
196
|
-
css: patch.css ?? snapshot.css,
|
|
197
|
-
cssVarPrefix: patch.cssVarPrefix ?? snapshot.cssVarPrefix,
|
|
198
|
-
indicators: patch.indicators ?? snapshot.indicators,
|
|
199
|
-
listing: patch.listing ?? snapshot.listing,
|
|
200
|
-
tokenGraph: patch.tokenGraph ?? snapshot.tokenGraph,
|
|
201
|
-
defaultTuple: patch.defaultTuple ?? snapshot.defaultTuple,
|
|
202
|
-
version: snapshot.version + 1
|
|
203
|
-
};
|
|
204
|
-
for (const cb of listeners) cb();
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Host adapter API — same integration tier as {@link registerChannel}.
|
|
208
|
-
*
|
|
209
|
-
* Seed the initial token snapshot. The addon preview calls this once at
|
|
210
|
-
* init with the build-time `virtual:swatchbook/tokens` data. Keeping the
|
|
211
|
-
* virtual-module read on the addon side (the package that owns it) lets
|
|
212
|
-
* blocks import cleanly without it. No-op fields fall back to the current
|
|
213
|
-
* snapshot, so a partial source is safe.
|
|
214
|
-
*/
|
|
215
|
-
function registerTokenSource(source) {
|
|
216
|
-
applyPatch(source);
|
|
217
|
-
}
|
|
218
|
-
function attach(channel) {
|
|
219
|
-
if (subscribed) return;
|
|
220
|
-
subscribed = true;
|
|
221
|
-
channel.on(TOKENS_UPDATED_EVENT, (payload) => {
|
|
222
|
-
applyPatch(payload);
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
onChannel(attach);
|
|
226
|
-
function subscribe$1(cb) {
|
|
227
|
-
listeners.add(cb);
|
|
228
|
-
return () => {
|
|
229
|
-
listeners.delete(cb);
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
function getSnapshot() {
|
|
233
|
-
return snapshot;
|
|
234
|
-
}
|
|
235
|
-
function getServerSnapshot() {
|
|
236
|
-
return snapshot;
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Host adapter API — same integration tier as {@link registerChannel}. Most
|
|
240
|
-
* consumers read project data through `useSwatchbookData()` /
|
|
241
|
-
* `SwatchbookProvider` instead; this is the internal subscription
|
|
242
|
-
* `useProject()`'s fallback path uses to read the live channel-fed snapshot.
|
|
243
|
-
*/
|
|
244
|
-
function useTokenSnapshot() {
|
|
245
|
-
return useSyncExternalStore(subscribe$1, getSnapshot, getServerSnapshot);
|
|
246
|
-
}
|
|
247
|
-
//#endregion
|
|
248
|
-
//#region src/internal/use-project.ts
|
|
249
|
-
function computeVarianceByPath(graph) {
|
|
250
|
-
if (!graph) return {};
|
|
251
|
-
const out = {};
|
|
252
|
-
for (const path of listPaths(graph)) out[path] = getVariance(graph, path);
|
|
253
|
-
return out;
|
|
254
|
-
}
|
|
255
|
-
function ensureStylesheet(css) {
|
|
256
|
-
ensureStyleElement(SWATCHBOOK_STYLE_ELEMENT_ID, css);
|
|
257
|
-
}
|
|
258
|
-
function defaultTuple(axes) {
|
|
259
|
-
const out = {};
|
|
260
|
-
for (const axis of axes) out[axis.name] = axis.default;
|
|
261
|
-
return out;
|
|
262
|
-
}
|
|
263
|
-
function makeResolveAt(graph) {
|
|
264
|
-
if (!graph) return () => ({});
|
|
265
|
-
return (tuple) => resolveAllWithProvenanceAt(graph, tuple);
|
|
266
|
-
}
|
|
267
|
-
function snapshotResolveAt(snapshot) {
|
|
268
|
-
if (snapshot.resolveAt) return snapshot.resolveAt;
|
|
269
|
-
return makeResolveAt(snapshot.tokenGraph);
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Reads project data either from a mounted {@link SwatchbookProvider}
|
|
273
|
-
* (preferred — the addon's preview decorator installs one around every
|
|
274
|
-
* story) or, when no provider is present, from the virtual module plus
|
|
275
|
-
* Storybook globals directly.
|
|
276
|
-
*
|
|
277
|
-
* The provider-less path is what makes the hook safe to call from MDX
|
|
278
|
-
* doc blocks and autodocs renders where no story is active. It
|
|
279
|
-
* self-mounts the injected snapshot's per-theme CSS and tracks the active
|
|
280
|
-
* tuple via the injected channel's `globalsUpdated` event, since a
|
|
281
|
-
* story-scoped globals hook only works while a story is rendering.
|
|
282
|
-
*/
|
|
283
|
-
function useProject() {
|
|
284
|
-
const snapshot = useOptionalSwatchbookData();
|
|
285
|
-
const axes = snapshot?.axes;
|
|
286
|
-
const activeAxes = snapshot?.activeAxes;
|
|
287
|
-
const activeTheme = snapshot?.activeTheme;
|
|
288
|
-
const diagnostics = snapshot?.diagnostics;
|
|
289
|
-
const cssVarPrefix = snapshot?.cssVarPrefix;
|
|
290
|
-
const indicators = snapshot?.indicators;
|
|
291
|
-
const listing = snapshot?.listing;
|
|
292
|
-
const tokenGraph = snapshot?.tokenGraph;
|
|
293
|
-
const resolveAt = useMemo(() => {
|
|
294
|
-
if (!snapshot) return null;
|
|
295
|
-
return snapshotResolveAt(snapshot);
|
|
296
|
-
}, [tokenGraph, activeTheme]);
|
|
297
|
-
const derivedVarianceByPath = useMemo(() => computeVarianceByPath(tokenGraph), [tokenGraph]);
|
|
298
|
-
const providerData = useMemo(() => {
|
|
299
|
-
if (!snapshot || !resolveAt || !axes || !activeAxes) return null;
|
|
300
|
-
return {
|
|
301
|
-
activeTheme: activeTheme ?? "",
|
|
302
|
-
activeAxes,
|
|
303
|
-
axes,
|
|
304
|
-
resolved: resolveAt(activeAxes),
|
|
305
|
-
diagnostics: diagnostics ?? [],
|
|
306
|
-
cssVarPrefix: cssVarPrefix ?? "",
|
|
307
|
-
indicators: indicators ?? {},
|
|
308
|
-
listing: listing ?? {},
|
|
309
|
-
varianceByPath: derivedVarianceByPath,
|
|
310
|
-
resolveAt
|
|
311
|
-
};
|
|
312
|
-
}, [
|
|
313
|
-
snapshot,
|
|
314
|
-
resolveAt,
|
|
315
|
-
axes,
|
|
316
|
-
activeTheme,
|
|
317
|
-
activeAxes,
|
|
318
|
-
diagnostics,
|
|
319
|
-
cssVarPrefix,
|
|
320
|
-
indicators,
|
|
321
|
-
listing,
|
|
322
|
-
derivedVarianceByPath,
|
|
323
|
-
tokenGraph
|
|
324
|
-
]);
|
|
325
|
-
const fallback = useVirtualModuleFallback(snapshot === null);
|
|
326
|
-
return providerData ?? fallback;
|
|
327
|
-
}
|
|
328
|
-
function useVirtualModuleFallback(enabled) {
|
|
329
|
-
const contextThemeName = useActiveTheme();
|
|
330
|
-
const contextAxes = useActiveAxes();
|
|
331
|
-
const channelGlobals = useChannelGlobals();
|
|
332
|
-
const tokens = useTokenSnapshot();
|
|
333
|
-
useEffect(() => {
|
|
334
|
-
if (!enabled) return;
|
|
335
|
-
ensureStylesheet(tokens.css);
|
|
336
|
-
}, [enabled, tokens.css]);
|
|
337
|
-
const activeAxes = useMemo(() => {
|
|
338
|
-
return Object.keys(contextAxes).length > 0 ? { ...contextAxes } : channelGlobals.axes ?? defaultTuple(tokens.axes);
|
|
339
|
-
}, [
|
|
340
|
-
contextAxes,
|
|
341
|
-
channelGlobals.axes,
|
|
342
|
-
tokens.axes
|
|
343
|
-
]);
|
|
344
|
-
const activeTheme = contextThemeName || tupleToName(tokens.axes, activeAxes);
|
|
345
|
-
const resolveAt = useMemo(() => makeResolveAt(tokens.tokenGraph), [tokens.tokenGraph]);
|
|
346
|
-
const fallbackVarianceByPath = useMemo(() => computeVarianceByPath(tokens.tokenGraph), [tokens.tokenGraph]);
|
|
347
|
-
return useMemo(() => ({
|
|
348
|
-
activeTheme,
|
|
349
|
-
activeAxes,
|
|
350
|
-
axes: tokens.axes,
|
|
351
|
-
resolved: resolveAt(activeAxes),
|
|
352
|
-
diagnostics: tokens.diagnostics,
|
|
353
|
-
cssVarPrefix: tokens.cssVarPrefix,
|
|
354
|
-
indicators: tokens.indicators,
|
|
355
|
-
listing: tokens.listing,
|
|
356
|
-
varianceByPath: fallbackVarianceByPath,
|
|
357
|
-
resolveAt
|
|
358
|
-
}), [
|
|
359
|
-
activeTheme,
|
|
360
|
-
activeAxes,
|
|
361
|
-
tokens.axes,
|
|
362
|
-
tokens.diagnostics,
|
|
363
|
-
tokens.cssVarPrefix,
|
|
364
|
-
tokens.indicators,
|
|
365
|
-
tokens.listing,
|
|
366
|
-
fallbackVarianceByPath,
|
|
367
|
-
resolveAt
|
|
368
|
-
]);
|
|
369
|
-
}
|
|
370
69
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
* listing-plugin miss.
|
|
70
|
+
* Resolves the color-display format from the context/snapshot/source
|
|
71
|
+
* chain: `ColorFormatContext` → provider snapshot's `defaultColorFormat`
|
|
72
|
+
* → ambient source's `defaultColorFormat` → `'hex'`. Composing blocks
|
|
73
|
+
* read `colorFormat ?? useColorFormat()` to give their own `colorFormat`
|
|
74
|
+
* prop top precedence over this chain.
|
|
377
75
|
*/
|
|
378
|
-
function
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Resolve a color value's display string + gamut flag, preferring the
|
|
385
|
-
* listing's `previewValue` when the user's active color-format matches
|
|
386
|
-
* plugin-css's output (hex). For any other format we fall back to
|
|
387
|
-
* `formatColor` so the toolbar's inspection modes (rgb / hsl / oklch /
|
|
388
|
-
* raw) keep working — the listing has only one canonical format.
|
|
389
|
-
*
|
|
390
|
-
* Pass `path === undefined` when resolving a sub-color inside a composite
|
|
391
|
-
* (shadow / border / gradient stop): composites' `previewValue` covers
|
|
392
|
-
* the whole token's rendering, not the individual channel, so there's no
|
|
393
|
-
* listing entry to key against.
|
|
394
|
-
*/
|
|
395
|
-
function resolveColorValue(path, raw, colorFormat, project) {
|
|
396
|
-
if (path !== void 0 && colorFormat === "hex") {
|
|
397
|
-
const listed = project.listing[path]?.previewValue;
|
|
398
|
-
if (typeof listed === "string") return {
|
|
399
|
-
value: listed,
|
|
400
|
-
outOfGamut: false
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
return formatColor(raw, colorFormat);
|
|
404
|
-
}
|
|
405
|
-
//#endregion
|
|
406
|
-
//#region src/border-preview/BorderSample.tsx
|
|
407
|
-
/**
|
|
408
|
-
* Pure derivation of a single border token's sample data from resolved
|
|
409
|
-
* project data. Extracted so it is unit-testable without React or a store.
|
|
410
|
-
*/
|
|
411
|
-
function deriveBorderSample(path, project) {
|
|
412
|
-
return { cssVar: resolveCssVar(path, project) };
|
|
413
|
-
}
|
|
414
|
-
/** Pure presentation for a single border token's sample. Renders from plain props. */
|
|
415
|
-
function BorderSampleView({ cssVar }) {
|
|
416
|
-
return /* @__PURE__ */ jsx("div", {
|
|
417
|
-
className: "sb-border-sample",
|
|
418
|
-
style: { border: cssVar },
|
|
419
|
-
"aria-hidden": true
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
function BorderSample({ path }) {
|
|
423
|
-
const { cssVar } = deriveBorderSample(path, useProject());
|
|
424
|
-
return /* @__PURE__ */ jsx(BorderSampleView, { cssVar });
|
|
425
|
-
}
|
|
426
|
-
//#endregion
|
|
427
|
-
//#region src/internal/composite-sample-format.ts
|
|
428
|
-
/**
|
|
429
|
-
* Display a composite sub-field dimension (shadow offset / blur / spread,
|
|
430
|
-
* border width, …) in the preview tables. Renders `—` for a missing
|
|
431
|
-
* sub-field and falls back to JSON for shapes it doesn't recognize.
|
|
432
|
-
*
|
|
433
|
-
* Distinct from `format-token-value`'s internal `formatDimension`, which
|
|
434
|
-
* formats a token's top-level value and has no `—` placeholder — these are
|
|
435
|
-
* the per-layer sample formatters shared by `ShadowPreview` + `BorderPreview`.
|
|
436
|
-
*/
|
|
437
|
-
function formatDimension$1(raw) {
|
|
438
|
-
if (raw == null) return "—";
|
|
439
|
-
if (typeof raw === "number") return String(raw);
|
|
440
|
-
if (typeof raw === "string") return raw;
|
|
441
|
-
if (typeof raw === "object") {
|
|
442
|
-
const v = raw;
|
|
443
|
-
if (typeof v.value === "number" && typeof v.unit === "string") return `${v.value}${v.unit}`;
|
|
444
|
-
}
|
|
445
|
-
return JSON.stringify(raw);
|
|
446
|
-
}
|
|
447
|
-
/** Display a composite sub-field color via the active format; `—` when absent. */
|
|
448
|
-
function formatSubColor(raw, format) {
|
|
449
|
-
if (raw == null) return "—";
|
|
450
|
-
return formatColor(raw, format).value;
|
|
76
|
+
function useColorFormat() {
|
|
77
|
+
const contextValue = useContext(ColorFormatContext);
|
|
78
|
+
const snapshot = useContext(SwatchbookContext);
|
|
79
|
+
const source = useProjectSource();
|
|
80
|
+
return contextValue ?? snapshot?.defaultColorFormat ?? source.defaultColorFormat ?? "hex";
|
|
451
81
|
}
|
|
452
82
|
//#endregion
|
|
453
83
|
//#region src/internal/data-attr.ts
|
|
@@ -604,6 +234,163 @@ function toDisplayable(v) {
|
|
|
604
234
|
return String(v ?? "");
|
|
605
235
|
}
|
|
606
236
|
//#endregion
|
|
237
|
+
//#region src/internal/use-project.ts
|
|
238
|
+
function computeVarianceByPath(graph) {
|
|
239
|
+
if (!graph) return {};
|
|
240
|
+
const out = {};
|
|
241
|
+
for (const path of listPaths(graph)) out[path] = getVariance(graph, path);
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
function ensureStylesheet(css) {
|
|
245
|
+
ensureStyleElement(SWATCHBOOK_STYLE_ELEMENT_ID, css);
|
|
246
|
+
}
|
|
247
|
+
function defaultTuple(axes) {
|
|
248
|
+
const out = {};
|
|
249
|
+
for (const axis of axes) out[axis.name] = axis.default;
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
252
|
+
function makeResolveAt(graph) {
|
|
253
|
+
if (!graph) return () => ({});
|
|
254
|
+
return (tuple) => resolveAllWithProvenanceAt(graph, tuple);
|
|
255
|
+
}
|
|
256
|
+
function snapshotResolveAt(snapshot) {
|
|
257
|
+
if (snapshot.resolveAt) return snapshot.resolveAt;
|
|
258
|
+
return makeResolveAt(snapshot.tokenGraph);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Reads project data either from a mounted {@link SwatchbookProvider}
|
|
262
|
+
* (preferred: the addon's preview decorator installs one around every
|
|
263
|
+
* story) or, when no provider is present, from the ambient project source
|
|
264
|
+
* a host pushes into via `registerProjectSource` (`#/host.ts`).
|
|
265
|
+
*
|
|
266
|
+
* The provider-less path is what makes the hook safe to call from MDX
|
|
267
|
+
* doc blocks and autodocs renders where no story is active. It
|
|
268
|
+
* self-mounts the source's per-theme CSS and tracks the active tuple via
|
|
269
|
+
* the source's `activeAxes`, since a story-scoped globals hook only works
|
|
270
|
+
* while a story is rendering.
|
|
271
|
+
*/
|
|
272
|
+
function useProject() {
|
|
273
|
+
const snapshot = useOptionalSwatchbookData();
|
|
274
|
+
const axes = snapshot?.axes;
|
|
275
|
+
const activeAxes = snapshot?.activeAxes;
|
|
276
|
+
const activeTheme = snapshot?.activeTheme;
|
|
277
|
+
const diagnostics = snapshot?.diagnostics;
|
|
278
|
+
const cssVarPrefix = snapshot?.cssVarPrefix;
|
|
279
|
+
const indicators = snapshot?.indicators;
|
|
280
|
+
const listing = snapshot?.listing;
|
|
281
|
+
const tokenGraph = snapshot?.tokenGraph;
|
|
282
|
+
const resolveAt = useMemo(() => {
|
|
283
|
+
if (!snapshot) return null;
|
|
284
|
+
return snapshotResolveAt(snapshot);
|
|
285
|
+
}, [tokenGraph, activeTheme]);
|
|
286
|
+
const derivedVarianceByPath = useMemo(() => computeVarianceByPath(tokenGraph), [tokenGraph]);
|
|
287
|
+
const providerData = useMemo(() => {
|
|
288
|
+
if (!snapshot || !resolveAt || !axes || !activeAxes) return null;
|
|
289
|
+
return {
|
|
290
|
+
activeTheme: activeTheme ?? "",
|
|
291
|
+
activeAxes,
|
|
292
|
+
axes,
|
|
293
|
+
resolved: resolveAt(activeAxes),
|
|
294
|
+
diagnostics: diagnostics ?? [],
|
|
295
|
+
cssVarPrefix: cssVarPrefix ?? "",
|
|
296
|
+
indicators: indicators ?? {},
|
|
297
|
+
listing: listing ?? {},
|
|
298
|
+
varianceByPath: derivedVarianceByPath,
|
|
299
|
+
resolveAt
|
|
300
|
+
};
|
|
301
|
+
}, [
|
|
302
|
+
snapshot,
|
|
303
|
+
resolveAt,
|
|
304
|
+
axes,
|
|
305
|
+
activeTheme,
|
|
306
|
+
activeAxes,
|
|
307
|
+
diagnostics,
|
|
308
|
+
cssVarPrefix,
|
|
309
|
+
indicators,
|
|
310
|
+
listing,
|
|
311
|
+
derivedVarianceByPath,
|
|
312
|
+
tokenGraph
|
|
313
|
+
]);
|
|
314
|
+
const fallback = useVirtualModuleFallback(snapshot === null);
|
|
315
|
+
return providerData ?? fallback;
|
|
316
|
+
}
|
|
317
|
+
function useVirtualModuleFallback(enabled) {
|
|
318
|
+
const contextThemeName = useActiveTheme();
|
|
319
|
+
const contextAxes = useActiveAxes();
|
|
320
|
+
const source = useProjectSource();
|
|
321
|
+
useEffect(() => {
|
|
322
|
+
if (!enabled) return;
|
|
323
|
+
ensureStylesheet(source.css);
|
|
324
|
+
}, [enabled, source.css]);
|
|
325
|
+
const activeAxes = useMemo(() => {
|
|
326
|
+
return Object.keys(contextAxes).length > 0 ? { ...contextAxes } : source.activeAxes ?? defaultTuple(source.axes);
|
|
327
|
+
}, [
|
|
328
|
+
contextAxes,
|
|
329
|
+
source.activeAxes,
|
|
330
|
+
source.axes
|
|
331
|
+
]);
|
|
332
|
+
const activeTheme = contextThemeName || tupleToName(source.axes, activeAxes);
|
|
333
|
+
const resolveAt = useMemo(() => makeResolveAt(source.tokenGraph), [source.tokenGraph]);
|
|
334
|
+
const fallbackVarianceByPath = useMemo(() => computeVarianceByPath(source.tokenGraph), [source.tokenGraph]);
|
|
335
|
+
return useMemo(() => ({
|
|
336
|
+
activeTheme,
|
|
337
|
+
activeAxes,
|
|
338
|
+
axes: source.axes,
|
|
339
|
+
resolved: resolveAt(activeAxes),
|
|
340
|
+
diagnostics: source.diagnostics,
|
|
341
|
+
cssVarPrefix: source.cssVarPrefix,
|
|
342
|
+
indicators: source.indicators,
|
|
343
|
+
listing: source.listing,
|
|
344
|
+
varianceByPath: fallbackVarianceByPath,
|
|
345
|
+
resolveAt
|
|
346
|
+
}), [
|
|
347
|
+
activeTheme,
|
|
348
|
+
activeAxes,
|
|
349
|
+
source.axes,
|
|
350
|
+
source.diagnostics,
|
|
351
|
+
source.cssVarPrefix,
|
|
352
|
+
source.indicators,
|
|
353
|
+
source.listing,
|
|
354
|
+
fallbackVarianceByPath,
|
|
355
|
+
resolveAt
|
|
356
|
+
]);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Resolve a token's CSS var reference, preferring the authoritative name
|
|
360
|
+
* emitted by `@terrazzo/plugin-css` (as recorded by
|
|
361
|
+
* `@terrazzo/plugin-token-listing` in the snapshot's `listing` field).
|
|
362
|
+
* Falls back to `cssVarRef` when the listing lacks an entry for this
|
|
363
|
+
* path — covers non-resolver projects, hand-built snapshots, and any
|
|
364
|
+
* listing-plugin miss.
|
|
365
|
+
*/
|
|
366
|
+
function resolveCssVar(path, project) {
|
|
367
|
+
const listed = project.listing[path]?.names?.["css"];
|
|
368
|
+
if (listed) return `var(${listed})`;
|
|
369
|
+
return cssVarRef(path, project.cssVarPrefix);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Resolve a color value's display string + gamut flag, preferring the
|
|
373
|
+
* listing's `previewValue` when the user's active color-format matches
|
|
374
|
+
* plugin-css's output (hex). For any other format we fall back to
|
|
375
|
+
* `formatColor` so the toolbar's inspection modes (rgb / hsl / oklch /
|
|
376
|
+
* raw) keep working — the listing has only one canonical format.
|
|
377
|
+
*
|
|
378
|
+
* Pass `path === undefined` when resolving a sub-color inside a composite
|
|
379
|
+
* (shadow / border / gradient stop): composites' `previewValue` covers
|
|
380
|
+
* the whole token's rendering, not the individual channel, so there's no
|
|
381
|
+
* listing entry to key against.
|
|
382
|
+
*/
|
|
383
|
+
function resolveColorValue(path, raw, colorFormat, project) {
|
|
384
|
+
if (path !== void 0 && colorFormat === "hex") {
|
|
385
|
+
const listed = project.listing[path]?.previewValue;
|
|
386
|
+
if (typeof listed === "string") return {
|
|
387
|
+
value: listed,
|
|
388
|
+
outOfGamut: false
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
return formatColor(raw, colorFormat);
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
607
394
|
//#region src/BorderPreview.tsx
|
|
608
395
|
/**
|
|
609
396
|
* Pure derivation of the preview's display rows from resolved project data.
|
|
@@ -621,7 +408,8 @@ function deriveBorderRows(resolved, project, { filter, sortBy, sortDir, colorFor
|
|
|
621
408
|
return {
|
|
622
409
|
path,
|
|
623
410
|
cssVar: resolveCssVar(path, project),
|
|
624
|
-
|
|
411
|
+
token,
|
|
412
|
+
width: formatDimension(value.width),
|
|
625
413
|
style: value.style != null ? String(value.style) : "—",
|
|
626
414
|
color: formatSubColor(value.color, colorFormat)
|
|
627
415
|
};
|
|
@@ -629,10 +417,11 @@ function deriveBorderRows(resolved, project, { filter, sortBy, sortDir, colorFor
|
|
|
629
417
|
}
|
|
630
418
|
/**
|
|
631
419
|
* Pure presentation for the border preview. Renders from plain props;
|
|
632
|
-
* composes the connected `BorderSample` as a child
|
|
633
|
-
*
|
|
420
|
+
* composes the connected `BorderSample` as a child, feeding it this row's
|
|
421
|
+
* already-resolved `token`/`cssVar` per the presenter contract.
|
|
634
422
|
*/
|
|
635
|
-
function BorderPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter, caption }) {
|
|
423
|
+
function BorderPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, colorFormat, filter, caption }) {
|
|
424
|
+
const Sample = usePresenter("border");
|
|
636
425
|
const captionText = caption ?? `${rows.length} border${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
637
426
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
638
427
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -661,7 +450,12 @@ function BorderPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter
|
|
|
661
450
|
}),
|
|
662
451
|
/* @__PURE__ */ jsx("div", {
|
|
663
452
|
className: "sb-border-preview__sample-cell",
|
|
664
|
-
children: /* @__PURE__ */ jsx(
|
|
453
|
+
children: Sample && /* @__PURE__ */ jsx(Sample, {
|
|
454
|
+
path: row.path,
|
|
455
|
+
token: row.token,
|
|
456
|
+
cssVar: row.cssVar,
|
|
457
|
+
colorFormat
|
|
458
|
+
})
|
|
665
459
|
}),
|
|
666
460
|
/* @__PURE__ */ jsxs("div", {
|
|
667
461
|
className: "sb-border-preview__breakdown",
|
|
@@ -687,27 +481,29 @@ function BorderPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter
|
|
|
687
481
|
}, row.path))]
|
|
688
482
|
});
|
|
689
483
|
}
|
|
690
|
-
function BorderPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
|
|
484
|
+
function BorderPreview({ filter, caption, sortBy = "path", sortDir = "asc", colorFormat }) {
|
|
691
485
|
const project = useProject();
|
|
692
486
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
693
|
-
const
|
|
487
|
+
const contextColorFormat = useColorFormat();
|
|
488
|
+
const format = colorFormat ?? contextColorFormat;
|
|
694
489
|
return /* @__PURE__ */ jsx(BorderPreviewView, {
|
|
695
490
|
rows: useMemo(() => deriveBorderRows(resolved, project, {
|
|
696
491
|
filter,
|
|
697
492
|
sortBy,
|
|
698
493
|
sortDir,
|
|
699
|
-
colorFormat
|
|
494
|
+
colorFormat: format
|
|
700
495
|
}), [
|
|
701
496
|
resolved,
|
|
702
497
|
project,
|
|
703
498
|
filter,
|
|
704
499
|
sortBy,
|
|
705
500
|
sortDir,
|
|
706
|
-
|
|
501
|
+
format
|
|
707
502
|
]),
|
|
708
503
|
activeTheme,
|
|
709
504
|
cssVarPrefix,
|
|
710
505
|
activeAxes,
|
|
506
|
+
colorFormat: format,
|
|
711
507
|
filter,
|
|
712
508
|
caption
|
|
713
509
|
});
|
|
@@ -728,7 +524,7 @@ function fixedPrefixLength(filter) {
|
|
|
728
524
|
* Pure derivation of the palette's grouped swatch rows from resolved project
|
|
729
525
|
* data. Extracted so it is unit-testable without React or a store.
|
|
730
526
|
*/
|
|
731
|
-
function deriveColorPaletteGroups(resolved, listing, cssVarPrefix, { filter, groupBy, sortBy, sortDir
|
|
527
|
+
function deriveColorPaletteGroups(resolved, listing, cssVarPrefix, { filter, groupBy, sortBy, sortDir }) {
|
|
732
528
|
const projectFields = {
|
|
733
529
|
listing,
|
|
734
530
|
cssVarPrefix
|
|
@@ -748,13 +544,11 @@ function deriveColorPaletteGroups(resolved, listing, cssVarPrefix, { filter, gro
|
|
|
748
544
|
const groupKey = segments.slice(0, effectiveGroupBy).join(".");
|
|
749
545
|
const leaf = segments.slice(effectiveGroupBy).join(".") || segments.at(-1) || path;
|
|
750
546
|
const list = bucket.get(groupKey) ?? [];
|
|
751
|
-
const formatted = resolveColorValue(path, token.$value, colorFormat, projectFields);
|
|
752
547
|
list.push({
|
|
753
548
|
path,
|
|
754
|
-
leaf,
|
|
755
549
|
cssVar: resolveCssVar(path, projectFields),
|
|
756
|
-
|
|
757
|
-
|
|
550
|
+
leaf,
|
|
551
|
+
token
|
|
758
552
|
});
|
|
759
553
|
bucket.set(groupKey, list);
|
|
760
554
|
}
|
|
@@ -763,8 +557,13 @@ function deriveColorPaletteGroups(resolved, listing, cssVarPrefix, { filter, gro
|
|
|
763
557
|
swatches
|
|
764
558
|
}));
|
|
765
559
|
}
|
|
766
|
-
/**
|
|
767
|
-
|
|
560
|
+
/**
|
|
561
|
+
* Pure presentation for the color palette. Renders from plain props;
|
|
562
|
+
* composes the registry's `color` presenter per swatch, feeding it this
|
|
563
|
+
* row's already-resolved `token`/`cssVar` per the presenter contract.
|
|
564
|
+
*/
|
|
565
|
+
function ColorPaletteView({ groups, activeTheme, cssVarPrefix, activeAxes, colorFormat, filter, caption }) {
|
|
566
|
+
const Swatch = usePresenter("color");
|
|
768
567
|
const totalCount = groups.reduce((acc, { swatches }) => acc + swatches.length, 0);
|
|
769
568
|
const captionText = caption ?? `${totalCount} color${totalCount === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
770
569
|
if (totalCount === 0) return /* @__PURE__ */ jsx("div", {
|
|
@@ -786,42 +585,27 @@ function ColorPaletteView({ groups, activeTheme, cssVarPrefix, activeAxes, filte
|
|
|
786
585
|
children: group
|
|
787
586
|
}), /* @__PURE__ */ jsx("div", {
|
|
788
587
|
className: "sb-color-palette__grid",
|
|
789
|
-
children: swatches.map((swatch) => /* @__PURE__ */
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
796
|
-
className: "sb-color-palette__meta",
|
|
797
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
798
|
-
className: "sb-color-palette__leaf",
|
|
799
|
-
children: swatch.leaf
|
|
800
|
-
}), /* @__PURE__ */ jsxs("span", {
|
|
801
|
-
className: "sb-color-palette__value",
|
|
802
|
-
children: [swatch.value, swatch.outOfGamut && /* @__PURE__ */ jsxs("span", {
|
|
803
|
-
title: "Out of sRGB gamut for this format",
|
|
804
|
-
"aria-label": "out of gamut",
|
|
805
|
-
className: "sb-color-palette__gamut-warn",
|
|
806
|
-
children: [" ", "⚠"]
|
|
807
|
-
})]
|
|
808
|
-
})]
|
|
809
|
-
})]
|
|
588
|
+
children: swatches.map((swatch) => Swatch && /* @__PURE__ */ jsx(Swatch, {
|
|
589
|
+
path: swatch.path,
|
|
590
|
+
token: swatch.token,
|
|
591
|
+
cssVar: swatch.cssVar,
|
|
592
|
+
colorFormat,
|
|
593
|
+
options: { label: swatch.leaf }
|
|
810
594
|
}, swatch.path))
|
|
811
595
|
})]
|
|
812
596
|
}, group))]
|
|
813
597
|
});
|
|
814
598
|
}
|
|
815
|
-
function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "asc" }) {
|
|
599
|
+
function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "asc", colorFormat }) {
|
|
816
600
|
const { resolved, activeTheme, activeAxes, cssVarPrefix, listing } = useProject();
|
|
817
|
-
const
|
|
601
|
+
const contextColorFormat = useColorFormat();
|
|
602
|
+
const format = colorFormat ?? contextColorFormat;
|
|
818
603
|
return /* @__PURE__ */ jsx(ColorPaletteView, {
|
|
819
604
|
groups: useMemo(() => deriveColorPaletteGroups(resolved, listing, cssVarPrefix, {
|
|
820
605
|
filter,
|
|
821
606
|
groupBy,
|
|
822
607
|
sortBy,
|
|
823
|
-
sortDir
|
|
824
|
-
colorFormat
|
|
608
|
+
sortDir
|
|
825
609
|
}), [
|
|
826
610
|
resolved,
|
|
827
611
|
listing,
|
|
@@ -829,12 +613,12 @@ function ColorPalette({ filter, groupBy, caption, sortBy = "path", sortDir = "as
|
|
|
829
613
|
filter,
|
|
830
614
|
groupBy,
|
|
831
615
|
sortBy,
|
|
832
|
-
sortDir
|
|
833
|
-
colorFormat
|
|
616
|
+
sortDir
|
|
834
617
|
]),
|
|
835
618
|
activeTheme,
|
|
836
619
|
cssVarPrefix,
|
|
837
620
|
activeAxes,
|
|
621
|
+
colorFormat: format,
|
|
838
622
|
filter,
|
|
839
623
|
caption
|
|
840
624
|
});
|
|
@@ -1157,11 +941,11 @@ function CopyButton$1({ value, label, variant = "icon", className }) {
|
|
|
1157
941
|
/**
|
|
1158
942
|
* Block UI state that survives a docs-mode remount.
|
|
1159
943
|
*
|
|
1160
|
-
* In MDX docs mode Storybook re-renders the docs container on every
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
944
|
+
* In MDX docs mode Storybook re-renders the docs container on every axis
|
|
945
|
+
* flip, which unmounts and remounts the embedded blocks: destroying any
|
|
946
|
+
* plain `useState` (expand/collapse, selection, search). This is the same
|
|
947
|
+
* problem the ambient project source (`#/host.ts`) solves for token/axis
|
|
948
|
+
* data: lift the value out of React into module state so it persists
|
|
1165
949
|
* across the remount, and re-seed component state from it on mount.
|
|
1166
950
|
*
|
|
1167
951
|
* `usePersistedState` is a drop-in `useState` whose value is mirrored to a
|
|
@@ -1402,9 +1186,10 @@ function ColorTableView({ groups, activeTheme, cssVarPrefix, activeAxes, colorFo
|
|
|
1402
1186
|
* selector; clicking a row expands it in place to show the description,
|
|
1403
1187
|
* alias chain, and (for grouped rows) every variant's hex/HSL/OKLCH values.
|
|
1404
1188
|
*/
|
|
1405
|
-
function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, variants, id, indicators }) {
|
|
1189
|
+
function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, variants, id, indicators, colorFormat }) {
|
|
1406
1190
|
const { resolved, activeTheme, activeAxes, cssVarPrefix, listing, varianceByPath } = useProject();
|
|
1407
|
-
const
|
|
1191
|
+
const contextColorFormat = useColorFormat();
|
|
1192
|
+
const format = colorFormat ?? contextColorFormat;
|
|
1408
1193
|
const enabledIndicators = useMemo(() => ({
|
|
1409
1194
|
...resolveIndicators(indicators),
|
|
1410
1195
|
gamut: false
|
|
@@ -1420,7 +1205,7 @@ function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searcha
|
|
|
1420
1205
|
variants,
|
|
1421
1206
|
sortBy,
|
|
1422
1207
|
sortDir,
|
|
1423
|
-
colorFormat
|
|
1208
|
+
colorFormat: format
|
|
1424
1209
|
}), [
|
|
1425
1210
|
resolved,
|
|
1426
1211
|
listing,
|
|
@@ -1430,12 +1215,12 @@ function ColorTable({ filter, caption, sortBy = "path", sortDir = "asc", searcha
|
|
|
1430
1215
|
variants,
|
|
1431
1216
|
sortBy,
|
|
1432
1217
|
sortDir,
|
|
1433
|
-
|
|
1218
|
+
format
|
|
1434
1219
|
]),
|
|
1435
1220
|
activeTheme,
|
|
1436
1221
|
cssVarPrefix,
|
|
1437
1222
|
activeAxes,
|
|
1438
|
-
colorFormat,
|
|
1223
|
+
colorFormat: format,
|
|
1439
1224
|
enabledIndicators,
|
|
1440
1225
|
blockKey,
|
|
1441
1226
|
filter,
|
|
@@ -1796,244 +1581,27 @@ function DiagnosticsView({ caption, diagnostics, cssVarPrefix, activeAxes }) {
|
|
|
1796
1581
|
})] })]
|
|
1797
1582
|
}, diagnosticKey(d, i)))
|
|
1798
1583
|
})]
|
|
1799
|
-
})
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
|
-
/**
|
|
1803
|
-
* Render the project's load diagnostics — parser errors, resolver warnings,
|
|
1804
|
-
* disabled-axes validation issues, etc. — as a collapsible list. Auto-opens
|
|
1805
|
-
* when the project carries errors or warnings; stays collapsed for clean
|
|
1806
|
-
* loads and info-only loads.
|
|
1807
|
-
*
|
|
1808
|
-
* Replaces the diagnostics section from the addon's (now-retired) Design
|
|
1809
|
-
* Tokens panel. Consumers compose it alongside TokenNavigator / TokenTable
|
|
1810
|
-
* on their own MDX pages.
|
|
1811
|
-
*/
|
|
1812
|
-
function Diagnostics({ caption } = {}) {
|
|
1813
|
-
const { activeAxes, cssVarPrefix, diagnostics } = useProject();
|
|
1814
|
-
return /* @__PURE__ */ jsx(DiagnosticsView, {
|
|
1815
|
-
caption,
|
|
1816
|
-
diagnostics,
|
|
1817
|
-
cssVarPrefix,
|
|
1818
|
-
activeAxes
|
|
1819
|
-
});
|
|
1820
|
-
}
|
|
1821
|
-
//#endregion
|
|
1822
|
-
//#region src/dimension-scale/dimension-px.ts
|
|
1823
|
-
/**
|
|
1824
|
-
* Convert a DTCG dimension `$value` (`{ value, unit }`) to pixels for the
|
|
1825
|
-
* purpose of deciding whether to cap the rendered size. `rootFontSizePx`
|
|
1826
|
-
* scales `rem` against the rendering context's actual root font-size
|
|
1827
|
-
* (default 16 for the no-DOM / SSR path); the bar paints at the same
|
|
1828
|
-
* context's `var()`, so passing the measured root keeps the cap decision
|
|
1829
|
-
* aligned with what's drawn. Returns `NaN` for anything other than `px` /
|
|
1830
|
-
* `rem` — `ex` / `ch` / `%`, and the non-DTCG `em` (the `dimension` type
|
|
1831
|
-
* permits only `px | rem`) — which the caller treats as "render at cssVar
|
|
1832
|
-
* but don't cap".
|
|
1833
|
-
*/
|
|
1834
|
-
function toPixels(raw, rootFontSizePx = 16) {
|
|
1835
|
-
if (raw == null || typeof raw !== "object") return NaN;
|
|
1836
|
-
const v = raw;
|
|
1837
|
-
if (typeof v.value !== "number" || typeof v.unit !== "string") return NaN;
|
|
1838
|
-
switch (v.unit) {
|
|
1839
|
-
case "px": return v.value;
|
|
1840
|
-
case "rem": return v.value * rootFontSizePx;
|
|
1841
|
-
default: return NaN;
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
//#endregion
|
|
1845
|
-
//#region src/internal/use-root-font-size.ts
|
|
1846
|
-
function readRootFontSize() {
|
|
1847
|
-
if (typeof document === "undefined") return 16;
|
|
1848
|
-
const fontSize = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
1849
|
-
return Number.isFinite(fontSize) && fontSize > 0 ? fontSize : 16;
|
|
1850
|
-
}
|
|
1851
|
-
function subscribe(onChange) {
|
|
1852
|
-
if (typeof window === "undefined") return () => {};
|
|
1853
|
-
window.addEventListener("resize", onChange);
|
|
1854
|
-
return () => window.removeEventListener("resize", onChange);
|
|
1855
|
-
}
|
|
1856
|
-
/**
|
|
1857
|
-
* Root font-size (px) of the context a block renders in, tracked across
|
|
1858
|
-
* viewport changes. `rem` dimension values resolve their `var()` against this
|
|
1859
|
-
* root, so the cap math (`toPixels`) and value sort (`sortTokens`) must scale
|
|
1860
|
-
* `rem` by it rather than a literal 16: a Storybook Docs page, or a responsive
|
|
1861
|
-
* desktop/tablet/mobile breakpoint, can apply a different root to the same
|
|
1862
|
-
* token. Falls back to 16 with no DOM.
|
|
1863
|
-
*/
|
|
1864
|
-
function useRootFontSize() {
|
|
1865
|
-
return useSyncExternalStore(subscribe, readRootFontSize, () => 16);
|
|
1866
|
-
}
|
|
1867
|
-
//#endregion
|
|
1868
|
-
//#region src/dimension-scale/DimensionSample.tsx
|
|
1869
|
-
/**
|
|
1870
|
-
* Pure derivation of a single dimension token's sample geometry from
|
|
1871
|
-
* resolved project data. Extracted so it is unit-testable without React or
|
|
1872
|
-
* a store.
|
|
1873
|
-
*/
|
|
1874
|
-
function deriveDimensionSample(path, project, rootFontSizePx) {
|
|
1875
|
-
const cssVar = resolveCssVar(path, project);
|
|
1876
|
-
const token = project.resolved[path];
|
|
1877
|
-
const pxValue = toPixels(token?.$value, rootFontSizePx);
|
|
1878
|
-
const capped = Number.isFinite(pxValue) && pxValue > 480;
|
|
1879
|
-
return {
|
|
1880
|
-
cssVar,
|
|
1881
|
-
pxValue,
|
|
1882
|
-
capped,
|
|
1883
|
-
cappedValue: capped ? `480px` : cssVar
|
|
1884
|
-
};
|
|
1885
|
-
}
|
|
1886
|
-
function withCap(visual) {
|
|
1887
|
-
return /* @__PURE__ */ jsxs("span", {
|
|
1888
|
-
className: "sb-dimension-sample sb-dimension-sample--capped",
|
|
1889
|
-
title: `capped at 480px`,
|
|
1890
|
-
children: [visual, /* @__PURE__ */ jsx("span", {
|
|
1891
|
-
className: "sb-dimension-sample__cap",
|
|
1892
|
-
"aria-hidden": true,
|
|
1893
|
-
children: "…"
|
|
1894
|
-
})]
|
|
1895
|
-
});
|
|
1896
|
-
}
|
|
1897
|
-
/** Pure presentation for a single dimension token's bar/sample. Renders from plain props. */
|
|
1898
|
-
function DimensionSampleView({ cssVar, capped, cappedValue, visual }) {
|
|
1899
|
-
switch (visual) {
|
|
1900
|
-
case "radius": return /* @__PURE__ */ jsx("div", {
|
|
1901
|
-
className: "sb-dimension-sample__radius-sample",
|
|
1902
|
-
style: { borderRadius: cssVar },
|
|
1903
|
-
"aria-hidden": true
|
|
1904
|
-
});
|
|
1905
|
-
case "size": {
|
|
1906
|
-
const sample = /* @__PURE__ */ jsx("div", {
|
|
1907
|
-
className: "sb-dimension-sample__size-sample",
|
|
1908
|
-
style: {
|
|
1909
|
-
width: cappedValue,
|
|
1910
|
-
height: cappedValue
|
|
1911
|
-
},
|
|
1912
|
-
"aria-hidden": true
|
|
1913
|
-
});
|
|
1914
|
-
return capped ? withCap(sample) : sample;
|
|
1915
|
-
}
|
|
1916
|
-
default: {
|
|
1917
|
-
const bar = /* @__PURE__ */ jsx("div", {
|
|
1918
|
-
className: "sb-dimension-sample__bar",
|
|
1919
|
-
style: { width: cappedValue },
|
|
1920
|
-
"aria-hidden": true
|
|
1921
|
-
});
|
|
1922
|
-
return capped ? withCap(bar) : bar;
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1926
|
-
/** Connected block: resolves `path` against the active project and renders its dimension sample. */
|
|
1927
|
-
function DimensionSample({ path, visual = "length" }) {
|
|
1928
|
-
const { cssVar, capped, cappedValue } = deriveDimensionSample(path, useProject(), useRootFontSize());
|
|
1929
|
-
return /* @__PURE__ */ jsx(DimensionSampleView, {
|
|
1930
|
-
cssVar,
|
|
1931
|
-
capped,
|
|
1932
|
-
cappedValue,
|
|
1933
|
-
visual
|
|
1584
|
+
})
|
|
1934
1585
|
});
|
|
1935
1586
|
}
|
|
1936
|
-
//#endregion
|
|
1937
|
-
//#region src/internal/format-token-value.ts
|
|
1938
1587
|
/**
|
|
1939
|
-
*
|
|
1940
|
-
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1588
|
+
* Render the project's load diagnostics — parser errors, resolver warnings,
|
|
1589
|
+
* disabled-axes validation issues, etc. — as a collapsible list. Auto-opens
|
|
1590
|
+
* when the project carries errors or warnings; stays collapsed for clean
|
|
1591
|
+
* loads and info-only loads.
|
|
1592
|
+
*
|
|
1593
|
+
* Replaces the diagnostics section from the addon's (now-retired) Design
|
|
1594
|
+
* Tokens panel. Consumers compose it alongside TokenNavigator / TokenTable
|
|
1595
|
+
* on their own MDX pages.
|
|
1943
1596
|
*/
|
|
1944
|
-
function
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
}
|
|
1953
|
-
switch ($type) {
|
|
1954
|
-
case "color": return formatColor(value, colorFormat).value;
|
|
1955
|
-
case "dimension":
|
|
1956
|
-
case "duration": return formatDimension(value);
|
|
1957
|
-
case "fontFamily": return formatFontFamily$1(value);
|
|
1958
|
-
case "fontWeight":
|
|
1959
|
-
case "lineHeight":
|
|
1960
|
-
case "letterSpacing":
|
|
1961
|
-
case "opacity":
|
|
1962
|
-
case "number": return formatPrimitive$1(value);
|
|
1963
|
-
case "cubicBezier": return formatCubicBezier(value);
|
|
1964
|
-
case "strokeStyle": return formatStrokeStyle(value);
|
|
1965
|
-
case "shadow": return formatShadow(value, colorFormat);
|
|
1966
|
-
case "border": return formatBorder(value, colorFormat);
|
|
1967
|
-
default: return formatUnknown(value);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
function formatDimension(v) {
|
|
1971
|
-
if (typeof v === "string" || typeof v === "number") return String(v);
|
|
1972
|
-
if (v && typeof v === "object") {
|
|
1973
|
-
const d = v;
|
|
1974
|
-
if (typeof d.value === "number" && typeof d.unit === "string") return `${d.value}${d.unit}`;
|
|
1975
|
-
}
|
|
1976
|
-
return formatUnknown(v);
|
|
1977
|
-
}
|
|
1978
|
-
function cleanFloatNoise(s) {
|
|
1979
|
-
return s.replace(/-?\d+\.\d{8,}/g, (m) => `${+Number(m).toFixed(3)}`);
|
|
1980
|
-
}
|
|
1981
|
-
function formatFontFamily$1(v) {
|
|
1982
|
-
if (typeof v === "string") return v;
|
|
1983
|
-
if (Array.isArray(v)) return v.map(String).join(", ");
|
|
1984
|
-
return formatUnknown(v);
|
|
1985
|
-
}
|
|
1986
|
-
function formatPrimitive$1(v) {
|
|
1987
|
-
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return String(v);
|
|
1988
|
-
return formatUnknown(v);
|
|
1989
|
-
}
|
|
1990
|
-
function formatCubicBezier(v) {
|
|
1991
|
-
if (Array.isArray(v) && v.length === 4) return `cubic-bezier(${v.map((n) => typeof n === "number" ? n : 0).join(", ")})`;
|
|
1992
|
-
return formatUnknown(v);
|
|
1993
|
-
}
|
|
1994
|
-
function formatStrokeStyle(v) {
|
|
1995
|
-
if (typeof v === "string") return v;
|
|
1996
|
-
if (v && typeof v === "object") {
|
|
1997
|
-
const s = v;
|
|
1998
|
-
const parts = ["dashed"];
|
|
1999
|
-
if (Array.isArray(s.dashArray)) parts.push(s.dashArray.map((n) => formatDimension(n)).join(" "));
|
|
2000
|
-
if (typeof s.lineCap === "string") parts.push(s.lineCap);
|
|
2001
|
-
return parts.join(" · ");
|
|
2002
|
-
}
|
|
2003
|
-
return formatUnknown(v);
|
|
2004
|
-
}
|
|
2005
|
-
function formatShadow(v, colorFormat) {
|
|
2006
|
-
return (Array.isArray(v) ? v : [v]).map((layer) => {
|
|
2007
|
-
if (!layer || typeof layer !== "object") return formatUnknown(layer);
|
|
2008
|
-
const s = layer;
|
|
2009
|
-
const pieces = [
|
|
2010
|
-
formatDimension(s.offsetX),
|
|
2011
|
-
formatDimension(s.offsetY),
|
|
2012
|
-
formatDimension(s.blur),
|
|
2013
|
-
formatDimension(s.spread),
|
|
2014
|
-
formatColor(s.color, colorFormat).value
|
|
2015
|
-
].filter((p) => p !== "");
|
|
2016
|
-
if (s.inset) pieces.push("inset");
|
|
2017
|
-
return pieces.join(" ");
|
|
2018
|
-
}).join(", ");
|
|
2019
|
-
}
|
|
2020
|
-
function formatBorder(v, colorFormat) {
|
|
2021
|
-
if (!v || typeof v !== "object") return formatUnknown(v);
|
|
2022
|
-
const b = v;
|
|
2023
|
-
return [
|
|
2024
|
-
formatDimension(b.width),
|
|
2025
|
-
formatPrimitive$1(b.style),
|
|
2026
|
-
formatColor(b.color, colorFormat).value
|
|
2027
|
-
].filter((p) => p !== "").join(" ");
|
|
2028
|
-
}
|
|
2029
|
-
function formatUnknown(v) {
|
|
2030
|
-
if (v == null) return "";
|
|
2031
|
-
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return String(v);
|
|
2032
|
-
try {
|
|
2033
|
-
return JSON.stringify(v).slice(0, 120);
|
|
2034
|
-
} catch {
|
|
2035
|
-
return String(v);
|
|
2036
|
-
}
|
|
1597
|
+
function Diagnostics({ caption } = {}) {
|
|
1598
|
+
const { activeAxes, cssVarPrefix, diagnostics } = useProject();
|
|
1599
|
+
return /* @__PURE__ */ jsx(DiagnosticsView, {
|
|
1600
|
+
caption,
|
|
1601
|
+
diagnostics,
|
|
1602
|
+
cssVarPrefix,
|
|
1603
|
+
activeAxes
|
|
1604
|
+
});
|
|
2037
1605
|
}
|
|
2038
1606
|
//#endregion
|
|
2039
1607
|
//#region src/DimensionScale.tsx
|
|
@@ -2052,15 +1620,17 @@ function deriveDimensionRows(resolved, project, { filter, sortBy, sortDir, rootF
|
|
|
2052
1620
|
}).map(([path, token]) => ({
|
|
2053
1621
|
path,
|
|
2054
1622
|
cssVar: resolveCssVar(path, project),
|
|
2055
|
-
|
|
1623
|
+
token,
|
|
1624
|
+
displayValue: formatTokenValue$2(token.$value, token.$type, "raw", project.listing[path])
|
|
2056
1625
|
}));
|
|
2057
1626
|
}
|
|
2058
1627
|
/**
|
|
2059
1628
|
* Pure presentation for the dimension scale. Renders from plain props;
|
|
2060
|
-
* composes the connected `DimensionSample` as a child
|
|
2061
|
-
*
|
|
1629
|
+
* composes the connected `DimensionSample` as a child, feeding it this row's
|
|
1630
|
+
* already-resolved `token`/`cssVar` per the presenter contract.
|
|
2062
1631
|
*/
|
|
2063
|
-
function DimensionScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, visual, filter, caption }) {
|
|
1632
|
+
function DimensionScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, visual, colorFormat, filter, caption }) {
|
|
1633
|
+
const Sample = usePresenter("dimension");
|
|
2064
1634
|
const captionText = caption ?? `${rows.length} dimension${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
2065
1635
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
2066
1636
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -2089,9 +1659,12 @@ function DimensionScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, visua
|
|
|
2089
1659
|
}),
|
|
2090
1660
|
/* @__PURE__ */ jsx("div", {
|
|
2091
1661
|
className: "sb-dimension-scale__visual-cell",
|
|
2092
|
-
children: /* @__PURE__ */ jsx(
|
|
1662
|
+
children: Sample && /* @__PURE__ */ jsx(Sample, {
|
|
2093
1663
|
path: row.path,
|
|
2094
|
-
|
|
1664
|
+
token: row.token,
|
|
1665
|
+
cssVar: row.cssVar,
|
|
1666
|
+
colorFormat,
|
|
1667
|
+
options: { visual }
|
|
2095
1668
|
})
|
|
2096
1669
|
}),
|
|
2097
1670
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -2106,6 +1679,7 @@ function DimensionScale({ filter, visual = "length", caption, sortBy = "value",
|
|
|
2106
1679
|
const project = useProject();
|
|
2107
1680
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
2108
1681
|
const rootFontSize = useRootFontSize();
|
|
1682
|
+
const colorFormat = useColorFormat();
|
|
2109
1683
|
return /* @__PURE__ */ jsx(DimensionScaleView, {
|
|
2110
1684
|
rows: useMemo(() => deriveDimensionRows(resolved, project, {
|
|
2111
1685
|
filter,
|
|
@@ -2124,17 +1698,13 @@ function DimensionScale({ filter, visual = "length", caption, sortBy = "value",
|
|
|
2124
1698
|
cssVarPrefix,
|
|
2125
1699
|
activeAxes,
|
|
2126
1700
|
visual,
|
|
1701
|
+
colorFormat,
|
|
2127
1702
|
filter,
|
|
2128
1703
|
caption
|
|
2129
1704
|
});
|
|
2130
1705
|
}
|
|
2131
1706
|
//#endregion
|
|
2132
1707
|
//#region src/FontFamilyPreview.tsx
|
|
2133
|
-
function stackString(raw) {
|
|
2134
|
-
if (typeof raw === "string") return raw;
|
|
2135
|
-
if (Array.isArray(raw)) return raw.map(String).join(", ");
|
|
2136
|
-
return "";
|
|
2137
|
-
}
|
|
2138
1708
|
/**
|
|
2139
1709
|
* Pure derivation of the preview's display rows from resolved project data.
|
|
2140
1710
|
* Extracted so it is unit-testable without React or a store.
|
|
@@ -2149,11 +1719,16 @@ function deriveFontFamilyRows(resolved, project, { filter, sortBy, sortDir }) {
|
|
|
2149
1719
|
}).map(([path, token]) => ({
|
|
2150
1720
|
path,
|
|
2151
1721
|
cssVar: resolveCssVar(path, project),
|
|
2152
|
-
|
|
1722
|
+
token
|
|
2153
1723
|
}));
|
|
2154
1724
|
}
|
|
2155
|
-
/**
|
|
2156
|
-
|
|
1725
|
+
/**
|
|
1726
|
+
* Pure presentation for the font-family preview. Renders from plain props;
|
|
1727
|
+
* composes the connected `FontFamilySpecimen` as a child, feeding it this
|
|
1728
|
+
* row's already-resolved `token`/`cssVar` per the presenter contract.
|
|
1729
|
+
*/
|
|
1730
|
+
function FontFamilyPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, sample, colorFormat, filter, caption }) {
|
|
1731
|
+
const Specimen = usePresenter("fontFamily");
|
|
2157
1732
|
const captionText = caption ?? `${rows.length} fontFamily token${rows.length === 1 ? "" : "s"}${filter && filter !== "fontFamily" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
2158
1733
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
2159
1734
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -2167,35 +1742,19 @@ function FontFamilyPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, sa
|
|
|
2167
1742
|
children: [/* @__PURE__ */ jsx("div", {
|
|
2168
1743
|
className: "sb-block__caption",
|
|
2169
1744
|
children: captionText
|
|
2170
|
-
}), rows.map((row) => /* @__PURE__ */
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
className: "sb-font-family-sample__path",
|
|
2177
|
-
children: row.path
|
|
2178
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
2179
|
-
className: "sb-font-family-sample__stack",
|
|
2180
|
-
children: row.stack
|
|
2181
|
-
})]
|
|
2182
|
-
}),
|
|
2183
|
-
/* @__PURE__ */ jsx("div", {
|
|
2184
|
-
className: "sb-font-family-sample__sample",
|
|
2185
|
-
style: { fontFamily: row.cssVar },
|
|
2186
|
-
children: sample
|
|
2187
|
-
}),
|
|
2188
|
-
/* @__PURE__ */ jsx("span", {
|
|
2189
|
-
className: "sb-font-family-sample__css-var",
|
|
2190
|
-
children: row.cssVar
|
|
2191
|
-
})
|
|
2192
|
-
]
|
|
1745
|
+
}), rows.map((row) => Specimen && /* @__PURE__ */ jsx(Specimen, {
|
|
1746
|
+
path: row.path,
|
|
1747
|
+
token: row.token,
|
|
1748
|
+
cssVar: row.cssVar,
|
|
1749
|
+
colorFormat,
|
|
1750
|
+
options: { sample }
|
|
2193
1751
|
}, row.path))]
|
|
2194
1752
|
});
|
|
2195
1753
|
}
|
|
2196
1754
|
function FontFamilyPreview({ filter, sample = "The quick brown fox jumps over the lazy dog.", caption, sortBy = "path", sortDir = "asc" }) {
|
|
2197
1755
|
const project = useProject();
|
|
2198
1756
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
1757
|
+
const colorFormat = useColorFormat();
|
|
2199
1758
|
return /* @__PURE__ */ jsx(FontFamilyPreviewView, {
|
|
2200
1759
|
rows: useMemo(() => deriveFontFamilyRows(resolved, project, {
|
|
2201
1760
|
filter,
|
|
@@ -2212,35 +1771,13 @@ function FontFamilyPreview({ filter, sample = "The quick brown fox jumps over th
|
|
|
2212
1771
|
cssVarPrefix,
|
|
2213
1772
|
activeAxes,
|
|
2214
1773
|
sample,
|
|
1774
|
+
colorFormat,
|
|
2215
1775
|
filter,
|
|
2216
1776
|
caption
|
|
2217
1777
|
});
|
|
2218
1778
|
}
|
|
2219
1779
|
//#endregion
|
|
2220
|
-
//#region src/internal/css-var-style.ts
|
|
2221
|
-
/**
|
|
2222
|
-
* Coerce a CSS `var(--…)` reference into the numeric slot of a React
|
|
2223
|
-
* inline-style property.
|
|
2224
|
-
*
|
|
2225
|
-
* React's `CSSProperties` types unitless properties (`fontWeight`,
|
|
2226
|
-
* `lineHeight`, `zIndex`, …) as `number`. The DOM accepts a string at
|
|
2227
|
-
* runtime — the rendered stylesheet just receives whatever React passes —
|
|
2228
|
-
* so a `var(--font-weight)` reference works functionally. TypeScript still
|
|
2229
|
-
* complains. Centralising the type assertion in one named helper keeps
|
|
2230
|
-
* the gap visible (and greppable) instead of scattering casts across
|
|
2231
|
-
* block components that want CSS-var-driven typography or layout values.
|
|
2232
|
-
*/
|
|
2233
|
-
const cssVarAsNumber = (varRef) => varRef;
|
|
2234
|
-
//#endregion
|
|
2235
1780
|
//#region src/FontWeightScale.tsx
|
|
2236
|
-
function toWeight(raw) {
|
|
2237
|
-
if (typeof raw === "number") return raw;
|
|
2238
|
-
if (typeof raw === "string") {
|
|
2239
|
-
const n = Number.parseInt(raw, 10);
|
|
2240
|
-
return Number.isFinite(n) ? n : NaN;
|
|
2241
|
-
}
|
|
2242
|
-
return NaN;
|
|
2243
|
-
}
|
|
2244
1781
|
/**
|
|
2245
1782
|
* Pure derivation of the scale's display rows from resolved project data.
|
|
2246
1783
|
* Extracted so it is unit-testable without React or a store.
|
|
@@ -2255,12 +1792,16 @@ function deriveFontWeightRows(resolved, project, { filter, sortBy, sortDir }) {
|
|
|
2255
1792
|
}).map(([path, token]) => ({
|
|
2256
1793
|
path,
|
|
2257
1794
|
cssVar: resolveCssVar(path, project),
|
|
2258
|
-
|
|
2259
|
-
weight: toWeight(token.$value)
|
|
1795
|
+
token
|
|
2260
1796
|
}));
|
|
2261
1797
|
}
|
|
2262
|
-
/**
|
|
2263
|
-
|
|
1798
|
+
/**
|
|
1799
|
+
* Pure presentation for the font-weight scale. Renders from plain props;
|
|
1800
|
+
* composes the connected `FontWeightSpecimen` as a child, feeding it this
|
|
1801
|
+
* row's already-resolved `token`/`cssVar` per the presenter contract.
|
|
1802
|
+
*/
|
|
1803
|
+
function FontWeightScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, sample, colorFormat, filter, caption }) {
|
|
1804
|
+
const Specimen = usePresenter("fontWeight");
|
|
2264
1805
|
const captionText = caption ?? `${rows.length} fontWeight token${rows.length === 1 ? "" : "s"}${filter && filter !== "fontWeight" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
2265
1806
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
2266
1807
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -2274,35 +1815,19 @@ function FontWeightScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, samp
|
|
|
2274
1815
|
children: [/* @__PURE__ */ jsx("div", {
|
|
2275
1816
|
className: "sb-block__caption",
|
|
2276
1817
|
children: captionText
|
|
2277
|
-
}), rows.map((row) => /* @__PURE__ */
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
className: "sb-font-weight-scale__path",
|
|
2284
|
-
children: row.path
|
|
2285
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
2286
|
-
className: "sb-font-weight-scale__value",
|
|
2287
|
-
children: row.display
|
|
2288
|
-
})]
|
|
2289
|
-
}),
|
|
2290
|
-
/* @__PURE__ */ jsx("div", {
|
|
2291
|
-
className: "sb-font-weight-scale__sample",
|
|
2292
|
-
style: { fontWeight: cssVarAsNumber(row.cssVar) },
|
|
2293
|
-
children: sample
|
|
2294
|
-
}),
|
|
2295
|
-
/* @__PURE__ */ jsx("span", {
|
|
2296
|
-
className: "sb-font-weight-scale__css-var",
|
|
2297
|
-
children: row.cssVar
|
|
2298
|
-
})
|
|
2299
|
-
]
|
|
1818
|
+
}), rows.map((row) => Specimen && /* @__PURE__ */ jsx(Specimen, {
|
|
1819
|
+
path: row.path,
|
|
1820
|
+
token: row.token,
|
|
1821
|
+
cssVar: row.cssVar,
|
|
1822
|
+
colorFormat,
|
|
1823
|
+
options: { sample }
|
|
2300
1824
|
}, row.path))]
|
|
2301
1825
|
});
|
|
2302
1826
|
}
|
|
2303
1827
|
function FontWeightScale({ filter, sample = "Aa", caption, sortBy = "value", sortDir = "asc" }) {
|
|
2304
1828
|
const project = useProject();
|
|
2305
1829
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
1830
|
+
const colorFormat = useColorFormat();
|
|
2306
1831
|
return /* @__PURE__ */ jsx(FontWeightScaleView, {
|
|
2307
1832
|
rows: useMemo(() => deriveFontWeightRows(resolved, project, {
|
|
2308
1833
|
filter,
|
|
@@ -2319,6 +1844,7 @@ function FontWeightScale({ filter, sample = "Aa", caption, sortBy = "value", sor
|
|
|
2319
1844
|
cssVarPrefix,
|
|
2320
1845
|
activeAxes,
|
|
2321
1846
|
sample,
|
|
1847
|
+
colorFormat,
|
|
2322
1848
|
filter,
|
|
2323
1849
|
caption
|
|
2324
1850
|
});
|
|
@@ -2357,6 +1883,7 @@ function deriveGradientRows(resolved, listing, cssVarPrefix, { filter, sortBy, s
|
|
|
2357
1883
|
return {
|
|
2358
1884
|
path,
|
|
2359
1885
|
cssVar: resolveCssVar(path, projectFields),
|
|
1886
|
+
token,
|
|
2360
1887
|
stops: stops.map((stop, i) => ({
|
|
2361
1888
|
key: stopKey(path, stop, i),
|
|
2362
1889
|
cssColor: stopCssColor(stop),
|
|
@@ -2366,8 +1893,13 @@ function deriveGradientRows(resolved, listing, cssVarPrefix, { filter, sortBy, s
|
|
|
2366
1893
|
};
|
|
2367
1894
|
});
|
|
2368
1895
|
}
|
|
2369
|
-
/**
|
|
2370
|
-
|
|
1896
|
+
/**
|
|
1897
|
+
* Pure presentation for the gradient palette. Renders from plain props;
|
|
1898
|
+
* composes the registry's `gradient` presenter per row, feeding it this
|
|
1899
|
+
* row's already-resolved `token`/`cssVar` per the presenter contract.
|
|
1900
|
+
*/
|
|
1901
|
+
function GradientPaletteView({ rows, activeTheme, cssVarPrefix, activeAxes, colorFormat, filter, caption }) {
|
|
1902
|
+
const Swatch = usePresenter("gradient");
|
|
2371
1903
|
const captionText = caption ?? `${rows.length} gradient${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
2372
1904
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
2373
1905
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -2394,10 +1926,11 @@ function GradientPaletteView({ rows, activeTheme, cssVarPrefix, activeAxes, filt
|
|
|
2394
1926
|
children: row.cssVar
|
|
2395
1927
|
})]
|
|
2396
1928
|
}),
|
|
2397
|
-
/* @__PURE__ */ jsx(
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
1929
|
+
Swatch && /* @__PURE__ */ jsx(Swatch, {
|
|
1930
|
+
path: row.path,
|
|
1931
|
+
token: row.token,
|
|
1932
|
+
cssVar: row.cssVar,
|
|
1933
|
+
colorFormat
|
|
2401
1934
|
}),
|
|
2402
1935
|
/* @__PURE__ */ jsx("div", {
|
|
2403
1936
|
className: "sb-gradient-palette__stops",
|
|
@@ -2425,15 +1958,16 @@ function GradientPaletteView({ rows, activeTheme, cssVarPrefix, activeAxes, filt
|
|
|
2425
1958
|
}, row.path))]
|
|
2426
1959
|
});
|
|
2427
1960
|
}
|
|
2428
|
-
function GradientPalette({ filter, caption, sortBy = "path", sortDir = "asc" }) {
|
|
1961
|
+
function GradientPalette({ filter, caption, sortBy = "path", sortDir = "asc", colorFormat }) {
|
|
2429
1962
|
const { resolved, activeTheme, activeAxes, cssVarPrefix, listing } = useProject();
|
|
2430
|
-
const
|
|
1963
|
+
const contextColorFormat = useColorFormat();
|
|
1964
|
+
const format = colorFormat ?? contextColorFormat;
|
|
2431
1965
|
return /* @__PURE__ */ jsx(GradientPaletteView, {
|
|
2432
1966
|
rows: useMemo(() => deriveGradientRows(resolved, listing, cssVarPrefix, {
|
|
2433
1967
|
filter,
|
|
2434
1968
|
sortBy,
|
|
2435
1969
|
sortDir,
|
|
2436
|
-
colorFormat
|
|
1970
|
+
colorFormat: format
|
|
2437
1971
|
}), [
|
|
2438
1972
|
resolved,
|
|
2439
1973
|
listing,
|
|
@@ -2441,172 +1975,17 @@ function GradientPalette({ filter, caption, sortBy = "path", sortDir = "asc" })
|
|
|
2441
1975
|
filter,
|
|
2442
1976
|
sortBy,
|
|
2443
1977
|
sortDir,
|
|
2444
|
-
|
|
1978
|
+
format
|
|
2445
1979
|
]),
|
|
2446
1980
|
activeTheme,
|
|
2447
1981
|
cssVarPrefix,
|
|
2448
1982
|
activeAxes,
|
|
1983
|
+
colorFormat: format,
|
|
2449
1984
|
filter,
|
|
2450
1985
|
caption
|
|
2451
1986
|
});
|
|
2452
1987
|
}
|
|
2453
1988
|
//#endregion
|
|
2454
|
-
//#region src/internal/prefers-reduced-motion.ts
|
|
2455
|
-
function isChromatic() {
|
|
2456
|
-
if (typeof navigator === "undefined") return false;
|
|
2457
|
-
return navigator.userAgent.includes("Chromatic");
|
|
2458
|
-
}
|
|
2459
|
-
/**
|
|
2460
|
-
* Reactive `prefers-reduced-motion: reduce` detector. Returns the current
|
|
2461
|
-
* match and updates if the user toggles the OS-level preference. Also
|
|
2462
|
-
* returns `true` under Chromatic to keep animated samples deterministic
|
|
2463
|
-
* during visual regression capture.
|
|
2464
|
-
*/
|
|
2465
|
-
function usePrefersReducedMotion() {
|
|
2466
|
-
const [reduced, setReduced] = useState(false);
|
|
2467
|
-
useEffect(() => {
|
|
2468
|
-
if (typeof window === "undefined") return;
|
|
2469
|
-
if (isChromatic()) {
|
|
2470
|
-
setReduced(true);
|
|
2471
|
-
return;
|
|
2472
|
-
}
|
|
2473
|
-
const query = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
2474
|
-
setReduced(query.matches);
|
|
2475
|
-
const onChange = (e) => setReduced(e.matches);
|
|
2476
|
-
query.addEventListener("change", onChange);
|
|
2477
|
-
return () => query.removeEventListener("change", onChange);
|
|
2478
|
-
}, []);
|
|
2479
|
-
return reduced;
|
|
2480
|
-
}
|
|
2481
|
-
//#endregion
|
|
2482
|
-
//#region src/motion-preview/MotionSample.tsx
|
|
2483
|
-
const DEFAULT_DURATION_MS = 300;
|
|
2484
|
-
const DEFAULT_EASING = "cubic-bezier(0.2, 0, 0, 1)";
|
|
2485
|
-
function extractDurationMs(raw) {
|
|
2486
|
-
if (raw == null) return NaN;
|
|
2487
|
-
if (typeof raw === "object") {
|
|
2488
|
-
const v = raw;
|
|
2489
|
-
if (typeof v.value === "number" && typeof v.unit === "string") {
|
|
2490
|
-
if (v.unit === "ms") return v.value;
|
|
2491
|
-
if (v.unit === "s") return v.value * 1e3;
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
return NaN;
|
|
2495
|
-
}
|
|
2496
|
-
function extractCubicBezier(raw) {
|
|
2497
|
-
if (Array.isArray(raw) && raw.length === 4 && raw.every((n) => typeof n === "number")) return `cubic-bezier(${raw.map((n) => Number(n).toFixed(3)).join(", ")})`;
|
|
2498
|
-
return null;
|
|
2499
|
-
}
|
|
2500
|
-
function asDuration(raw, themeTokens, fallback) {
|
|
2501
|
-
const direct = extractDurationMs(raw);
|
|
2502
|
-
if (Number.isFinite(direct)) return direct;
|
|
2503
|
-
if (typeof raw === "string") {
|
|
2504
|
-
const match = raw.match(/^\{([^}]+)\}$/);
|
|
2505
|
-
if (match && match[1]) {
|
|
2506
|
-
const referenced = themeTokens[match[1]];
|
|
2507
|
-
const resolved = extractDurationMs(referenced?.$value);
|
|
2508
|
-
if (Number.isFinite(resolved)) return resolved;
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
2511
|
-
return fallback;
|
|
2512
|
-
}
|
|
2513
|
-
function asEasing(raw, themeTokens, fallback) {
|
|
2514
|
-
const direct = extractCubicBezier(raw);
|
|
2515
|
-
if (direct) return direct;
|
|
2516
|
-
if (typeof raw === "string") {
|
|
2517
|
-
const match = raw.match(/^\{([^}]+)\}$/);
|
|
2518
|
-
if (match && match[1]) {
|
|
2519
|
-
const referenced = themeTokens[match[1]];
|
|
2520
|
-
const resolved = extractCubicBezier(referenced?.$value);
|
|
2521
|
-
if (resolved) return resolved;
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
|
-
return fallback;
|
|
2525
|
-
}
|
|
2526
|
-
function resolveMotionSpec(token, themeTokens) {
|
|
2527
|
-
if (!token) return null;
|
|
2528
|
-
const type = token.$type;
|
|
2529
|
-
if (type === "transition") {
|
|
2530
|
-
const v = token.$value ?? {};
|
|
2531
|
-
return {
|
|
2532
|
-
durationMs: asDuration(v.duration, themeTokens, DEFAULT_DURATION_MS),
|
|
2533
|
-
easing: asEasing(v.timingFunction, themeTokens, DEFAULT_EASING)
|
|
2534
|
-
};
|
|
2535
|
-
}
|
|
2536
|
-
if (type === "duration") {
|
|
2537
|
-
const durationMs = extractDurationMs(token.$value);
|
|
2538
|
-
if (!Number.isFinite(durationMs)) return null;
|
|
2539
|
-
return {
|
|
2540
|
-
durationMs,
|
|
2541
|
-
easing: DEFAULT_EASING
|
|
2542
|
-
};
|
|
2543
|
-
}
|
|
2544
|
-
if (type === "cubicBezier") {
|
|
2545
|
-
const easing = extractCubicBezier(token.$value);
|
|
2546
|
-
if (!easing) return null;
|
|
2547
|
-
return {
|
|
2548
|
-
durationMs: DEFAULT_DURATION_MS,
|
|
2549
|
-
easing
|
|
2550
|
-
};
|
|
2551
|
-
}
|
|
2552
|
-
return null;
|
|
2553
|
-
}
|
|
2554
|
-
/**
|
|
2555
|
-
* Pure derivation of a single motion token's animation spec from resolved
|
|
2556
|
-
* project data. Extracted so it is unit-testable without React or a store.
|
|
2557
|
-
*/
|
|
2558
|
-
function deriveMotionSample(path, project) {
|
|
2559
|
-
return { spec: resolveMotionSpec(project.resolved[path], project.resolved) };
|
|
2560
|
-
}
|
|
2561
|
-
/** Pure presentation + animation for a single motion token's sample. Renders from plain props. */
|
|
2562
|
-
function MotionSampleView({ spec, speed, runKey }) {
|
|
2563
|
-
const reducedMotion = usePrefersReducedMotion();
|
|
2564
|
-
const durationMs = spec?.durationMs ?? DEFAULT_DURATION_MS;
|
|
2565
|
-
const easing = spec?.easing ?? DEFAULT_EASING;
|
|
2566
|
-
const scaledDuration = Math.max(1, durationMs / speed);
|
|
2567
|
-
const [phase, setPhase] = useState(0);
|
|
2568
|
-
useEffect(() => {
|
|
2569
|
-
if (reducedMotion) return;
|
|
2570
|
-
setPhase(0);
|
|
2571
|
-
const id = requestAnimationFrame(() => setPhase(1));
|
|
2572
|
-
const loop = window.setInterval(() => {
|
|
2573
|
-
setPhase((p) => p === 0 ? 1 : 0);
|
|
2574
|
-
}, scaledDuration * 2);
|
|
2575
|
-
return () => {
|
|
2576
|
-
cancelAnimationFrame(id);
|
|
2577
|
-
window.clearInterval(loop);
|
|
2578
|
-
};
|
|
2579
|
-
}, [
|
|
2580
|
-
scaledDuration,
|
|
2581
|
-
runKey,
|
|
2582
|
-
reducedMotion
|
|
2583
|
-
]);
|
|
2584
|
-
if (reducedMotion) return /* @__PURE__ */ jsxs("div", {
|
|
2585
|
-
className: "sb-motion-sample__reduced-motion",
|
|
2586
|
-
children: [
|
|
2587
|
-
"Animation suppressed by ",
|
|
2588
|
-
/* @__PURE__ */ jsx("code", { children: "prefers-reduced-motion: reduce" }),
|
|
2589
|
-
"."
|
|
2590
|
-
]
|
|
2591
|
-
});
|
|
2592
|
-
return /* @__PURE__ */ jsx("div", {
|
|
2593
|
-
className: "sb-motion-sample__track",
|
|
2594
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
2595
|
-
className: cx("sb-motion-sample__ball", phase === 1 ? "sb-motion-sample__ball--end" : "sb-motion-sample__ball--start"),
|
|
2596
|
-
style: { transition: `left ${scaledDuration}ms ${easing}` },
|
|
2597
|
-
"aria-hidden": true
|
|
2598
|
-
})
|
|
2599
|
-
});
|
|
2600
|
-
}
|
|
2601
|
-
function MotionSample({ path, speed = 1, runKey = 0 }) {
|
|
2602
|
-
const { spec } = deriveMotionSample(path, useProject());
|
|
2603
|
-
return /* @__PURE__ */ jsx(MotionSampleView, {
|
|
2604
|
-
spec,
|
|
2605
|
-
speed,
|
|
2606
|
-
runKey
|
|
2607
|
-
});
|
|
2608
|
-
}
|
|
2609
|
-
//#endregion
|
|
2610
1989
|
//#region src/MotionPreview.tsx
|
|
2611
1990
|
const SPEEDS = [
|
|
2612
1991
|
.25,
|
|
@@ -2641,6 +2020,7 @@ function deriveMotionRows(resolved, project, { filter }) {
|
|
|
2641
2020
|
collected.push({
|
|
2642
2021
|
path,
|
|
2643
2022
|
cssVar: resolveCssVar(path, project),
|
|
2023
|
+
token,
|
|
2644
2024
|
durationMs: spec.durationMs,
|
|
2645
2025
|
easing: spec.easing,
|
|
2646
2026
|
kind
|
|
@@ -2656,10 +2036,11 @@ function deriveMotionRows(resolved, project, { filter }) {
|
|
|
2656
2036
|
* Pure presentation for the motion preview. Owns the speed/replay controls'
|
|
2657
2037
|
* local UI state and the `prefers-reduced-motion` read (a browser-environment
|
|
2658
2038
|
* concern, not project data); renders from the derived `rows` view-model.
|
|
2659
|
-
* Composes the connected `MotionSample` as a child
|
|
2660
|
-
*
|
|
2039
|
+
* Composes the connected `MotionSample` as a child, feeding it this row's
|
|
2040
|
+
* already-resolved `token`/`cssVar` per the presenter contract.
|
|
2661
2041
|
*/
|
|
2662
|
-
function MotionPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter, caption }) {
|
|
2042
|
+
function MotionPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, colorFormat, filter, caption }) {
|
|
2043
|
+
const Sample = usePresenter("transition");
|
|
2663
2044
|
const [speed, setSpeed] = useState(1);
|
|
2664
2045
|
const [run, setRun] = useState(0);
|
|
2665
2046
|
const reducedMotion = usePrefersReducedMotion();
|
|
@@ -2714,10 +2095,15 @@ function MotionPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter
|
|
|
2714
2095
|
children: formatSpec(row)
|
|
2715
2096
|
})]
|
|
2716
2097
|
}),
|
|
2717
|
-
/* @__PURE__ */ jsx(
|
|
2098
|
+
Sample && /* @__PURE__ */ jsx(Sample, {
|
|
2718
2099
|
path: row.path,
|
|
2719
|
-
|
|
2720
|
-
|
|
2100
|
+
token: row.token,
|
|
2101
|
+
cssVar: row.cssVar,
|
|
2102
|
+
colorFormat,
|
|
2103
|
+
options: {
|
|
2104
|
+
speed,
|
|
2105
|
+
runKey: run
|
|
2106
|
+
}
|
|
2721
2107
|
}),
|
|
2722
2108
|
/* @__PURE__ */ jsx("span", {
|
|
2723
2109
|
className: "sb-motion-preview__css-var",
|
|
@@ -2731,6 +2117,7 @@ function MotionPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter
|
|
|
2731
2117
|
function MotionPreview({ filter, caption }) {
|
|
2732
2118
|
const project = useProject();
|
|
2733
2119
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
2120
|
+
const colorFormat = useColorFormat();
|
|
2734
2121
|
return /* @__PURE__ */ jsx(MotionPreviewView, {
|
|
2735
2122
|
rows: useMemo(() => deriveMotionRows(resolved, project, { filter }), [
|
|
2736
2123
|
resolved,
|
|
@@ -2740,6 +2127,7 @@ function MotionPreview({ filter, caption }) {
|
|
|
2740
2127
|
activeTheme,
|
|
2741
2128
|
cssVarPrefix,
|
|
2742
2129
|
activeAxes,
|
|
2130
|
+
colorFormat,
|
|
2743
2131
|
filter,
|
|
2744
2132
|
caption
|
|
2745
2133
|
});
|
|
@@ -2772,12 +2160,18 @@ function deriveOpacityRows(resolved, project, { filter, type, sortBy, sortDir })
|
|
|
2772
2160
|
path,
|
|
2773
2161
|
cssVar: resolveCssVar(path, project),
|
|
2774
2162
|
opacity,
|
|
2775
|
-
displayValue: String(opacity)
|
|
2163
|
+
displayValue: String(opacity),
|
|
2164
|
+
token
|
|
2776
2165
|
};
|
|
2777
2166
|
});
|
|
2778
2167
|
}
|
|
2779
|
-
/**
|
|
2780
|
-
|
|
2168
|
+
/**
|
|
2169
|
+
* Pure presentation for the opacity scale. Renders from plain props;
|
|
2170
|
+
* composes the connected `OpacitySwatch` as a child, feeding it this row's
|
|
2171
|
+
* already-resolved `token`/`cssVar` per the presenter contract.
|
|
2172
|
+
*/
|
|
2173
|
+
function OpacityScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, sampleColorVar, colorFormat, filter, caption }) {
|
|
2174
|
+
const Swatch = usePresenter("number");
|
|
2781
2175
|
const captionText = caption ?? `${rows.length} opacity token${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
2782
2176
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
2783
2177
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -2795,13 +2189,12 @@ function OpacityScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, sampleC
|
|
|
2795
2189
|
className: "sb-opacity-scale__grid",
|
|
2796
2190
|
children: rows.map((row) => /* @__PURE__ */ jsxs("div", {
|
|
2797
2191
|
className: "sb-opacity-scale__card",
|
|
2798
|
-
children: [/* @__PURE__ */ jsx(
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
}
|
|
2804
|
-
"aria-hidden": true
|
|
2192
|
+
children: [Swatch && /* @__PURE__ */ jsx(Swatch, {
|
|
2193
|
+
path: row.path,
|
|
2194
|
+
token: row.token,
|
|
2195
|
+
cssVar: row.cssVar,
|
|
2196
|
+
colorFormat,
|
|
2197
|
+
options: { sampleColorVar }
|
|
2805
2198
|
}), /* @__PURE__ */ jsxs("div", {
|
|
2806
2199
|
className: "sb-opacity-scale__meta",
|
|
2807
2200
|
children: [
|
|
@@ -2832,6 +2225,7 @@ function OpacityScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, sampleC
|
|
|
2832
2225
|
function OpacityScale({ filter, type = "number", sampleColor = "color.accent.bg", caption, sortBy = "value", sortDir = "asc" }) {
|
|
2833
2226
|
const project = useProject();
|
|
2834
2227
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
2228
|
+
const colorFormat = useColorFormat();
|
|
2835
2229
|
return /* @__PURE__ */ jsx(OpacityScaleView, {
|
|
2836
2230
|
rows: useMemo(() => deriveOpacityRows(resolved, project, {
|
|
2837
2231
|
filter,
|
|
@@ -2850,59 +2244,87 @@ function OpacityScale({ filter, type = "number", sampleColor = "color.accent.bg"
|
|
|
2850
2244
|
cssVarPrefix,
|
|
2851
2245
|
activeAxes,
|
|
2852
2246
|
sampleColorVar: resolveCssVar(sampleColor, project),
|
|
2247
|
+
colorFormat,
|
|
2853
2248
|
filter,
|
|
2854
2249
|
caption
|
|
2855
2250
|
});
|
|
2856
2251
|
}
|
|
2857
2252
|
//#endregion
|
|
2858
2253
|
//#region src/provider.tsx
|
|
2254
|
+
const SetAxesContext = createContext(null);
|
|
2255
|
+
function assemble(snapshot, activeAxes) {
|
|
2256
|
+
return {
|
|
2257
|
+
axes: snapshot.axes,
|
|
2258
|
+
activeAxes,
|
|
2259
|
+
activeTheme: tupleToName(snapshot.axes, activeAxes),
|
|
2260
|
+
cssVarPrefix: snapshot.cssVarPrefix,
|
|
2261
|
+
indicators: snapshot.indicators,
|
|
2262
|
+
diagnostics: snapshot.diagnostics,
|
|
2263
|
+
css: snapshot.css,
|
|
2264
|
+
listing: snapshot.listing,
|
|
2265
|
+
tokenGraph: snapshot.tokenGraph,
|
|
2266
|
+
defaultTuple: snapshot.defaultTuple,
|
|
2267
|
+
defaultColorFormat: snapshot.defaultColorFormat
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2859
2270
|
/**
|
|
2860
|
-
* Wraps a tree of blocks with the token data they need to render
|
|
2271
|
+
* Wraps a tree of blocks with the token data they need to render, assembled
|
|
2272
|
+
* from core's wire snapshot plus an active axis tuple.
|
|
2861
2273
|
*
|
|
2862
|
-
* The Storybook addon's preview decorator mounts this automatically
|
|
2863
|
-
* story/MDX authors typically never
|
|
2864
|
-
* tests, custom React apps, non-Storybook
|
|
2865
|
-
* a {@link
|
|
2866
|
-
*
|
|
2274
|
+
* The Storybook addon's preview decorator mounts this automatically
|
|
2275
|
+
* (`axes` controlled by the toolbar), so story/MDX authors typically never
|
|
2276
|
+
* see it. Outside Storybook: unit tests, custom React apps, non-Storybook
|
|
2277
|
+
* doc sites. Consumers pass a {@link SnapshotForWire} (often imported from
|
|
2278
|
+
* a JSON file) and either drive `axes` themselves or let the provider own
|
|
2279
|
+
* the tuple via `defaultAxes` + {@link useSetAxes}.
|
|
2867
2280
|
*/
|
|
2868
|
-
function SwatchbookProvider({
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2281
|
+
function SwatchbookProvider({ snapshot, axes, defaultAxes, presenters, mountCss = true, children }) {
|
|
2282
|
+
const [internalAxes, setInternalAxes] = useState(() => defaultAxes ?? snapshot.defaultTuple);
|
|
2283
|
+
useEffect(() => {
|
|
2284
|
+
if (!mountCss) return;
|
|
2285
|
+
ensureStyleElement(SWATCHBOOK_STYLE_ELEMENT_ID, snapshot.css);
|
|
2286
|
+
}, [mountCss, snapshot.css]);
|
|
2287
|
+
const controlled = axes !== void 0;
|
|
2288
|
+
const activeAxes = controlled ? axes : internalAxes;
|
|
2289
|
+
const value = useMemo(() => assemble(snapshot, activeAxes), [snapshot, activeAxes]);
|
|
2290
|
+
const merged = useMemo(() => mergePresenters(presenters), [presenters]);
|
|
2291
|
+
const setAxes = controlled ? null : setInternalAxes;
|
|
2292
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2293
|
+
...perAxisAttrs(snapshot.cssVarPrefix, activeAxes),
|
|
2294
|
+
children: /* @__PURE__ */ jsx(SwatchbookContext.Provider, {
|
|
2295
|
+
value,
|
|
2296
|
+
children: /* @__PURE__ */ jsx(PresenterContext.Provider, {
|
|
2297
|
+
value: merged,
|
|
2298
|
+
children: /* @__PURE__ */ jsx(SetAxesContext.Provider, {
|
|
2299
|
+
value: setAxes,
|
|
2300
|
+
children
|
|
2301
|
+
})
|
|
2302
|
+
})
|
|
2303
|
+
})
|
|
2872
2304
|
});
|
|
2873
2305
|
}
|
|
2874
2306
|
/**
|
|
2307
|
+
* Flip the active tuple on an uncontrolled {@link SwatchbookProvider}
|
|
2308
|
+
* (mounted with `defaultAxes`, not `axes`). Throws when the nearest
|
|
2309
|
+
* provider is controlled (the host owns `axes`) or absent, since there's
|
|
2310
|
+
* no internal state to flip in either case.
|
|
2311
|
+
*/
|
|
2312
|
+
function useSetAxes() {
|
|
2313
|
+
const setAxes = useContext(SetAxesContext);
|
|
2314
|
+
if (!setAxes) throw new Error("[swatchbook-blocks] useSetAxes() requires an uncontrolled <SwatchbookProvider defaultAxes={…}> (not one given an `axes` prop, and not no provider at all).");
|
|
2315
|
+
return setAxes;
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2875
2318
|
* Read the current {@link ProjectSnapshot}. Throws if called outside a
|
|
2876
2319
|
* {@link SwatchbookProvider}; blocks that need to fall back to the
|
|
2877
2320
|
* virtual module go through the internal `useProject()` hook instead.
|
|
2878
2321
|
*/
|
|
2879
2322
|
function useSwatchbookData() {
|
|
2880
2323
|
const value = useOptionalSwatchbookData();
|
|
2881
|
-
if (!value) throw new Error("[swatchbook-blocks] useSwatchbookData() called outside <SwatchbookProvider>. Wrap your tree in <SwatchbookProvider
|
|
2324
|
+
if (!value) throw new Error("[swatchbook-blocks] useSwatchbookData() called outside <SwatchbookProvider>. Wrap your tree in <SwatchbookProvider snapshot={wire}> or render inside a Storybook story.");
|
|
2882
2325
|
return value;
|
|
2883
2326
|
}
|
|
2884
2327
|
//#endregion
|
|
2885
|
-
//#region src/shadow-preview/ShadowSample.tsx
|
|
2886
|
-
/**
|
|
2887
|
-
* Pure derivation of a single shadow token's sample data from resolved
|
|
2888
|
-
* project data. Extracted so it is unit-testable without React or a store.
|
|
2889
|
-
*/
|
|
2890
|
-
function deriveShadowSample(path, project) {
|
|
2891
|
-
return { cssVar: resolveCssVar(path, project) };
|
|
2892
|
-
}
|
|
2893
|
-
/** Pure presentation for a single shadow token's sample. Renders from plain props. */
|
|
2894
|
-
function ShadowSampleView({ cssVar }) {
|
|
2895
|
-
return /* @__PURE__ */ jsx("div", {
|
|
2896
|
-
className: "sb-shadow-sample",
|
|
2897
|
-
style: { boxShadow: cssVar },
|
|
2898
|
-
"aria-hidden": true
|
|
2899
|
-
});
|
|
2900
|
-
}
|
|
2901
|
-
function ShadowSample({ path }) {
|
|
2902
|
-
const { cssVar } = deriveShadowSample(path, useProject());
|
|
2903
|
-
return /* @__PURE__ */ jsx(ShadowSampleView, { cssVar });
|
|
2904
|
-
}
|
|
2905
|
-
//#endregion
|
|
2906
2328
|
//#region src/ShadowPreview.tsx
|
|
2907
2329
|
function layerKey(path, layer, fallback) {
|
|
2908
2330
|
return `${path}|${layer.offset}|${layer.blur}|${layer.spread}|${fallback}`;
|
|
@@ -2914,9 +2336,9 @@ function asLayers(raw) {
|
|
|
2914
2336
|
}
|
|
2915
2337
|
function formatLayer(layer, colorFormat) {
|
|
2916
2338
|
return {
|
|
2917
|
-
offset: `${formatDimension
|
|
2918
|
-
blur: formatDimension
|
|
2919
|
-
spread: formatDimension
|
|
2339
|
+
offset: `${formatDimension(layer.offsetX)} ${formatDimension(layer.offsetY)}`,
|
|
2340
|
+
blur: formatDimension(layer.blur),
|
|
2341
|
+
spread: formatDimension(layer.spread),
|
|
2920
2342
|
color: formatSubColor(layer.color, colorFormat),
|
|
2921
2343
|
inset: layer.inset ? String(layer.inset) : void 0
|
|
2922
2344
|
};
|
|
@@ -2935,15 +2357,17 @@ function deriveShadowRows(resolved, project, { filter, sortBy, sortDir, colorFor
|
|
|
2935
2357
|
}).map(([path, token]) => ({
|
|
2936
2358
|
path,
|
|
2937
2359
|
cssVar: resolveCssVar(path, project),
|
|
2360
|
+
token,
|
|
2938
2361
|
layers: asLayers(token.$value).map((layer) => formatLayer(layer, colorFormat))
|
|
2939
2362
|
}));
|
|
2940
2363
|
}
|
|
2941
2364
|
/**
|
|
2942
2365
|
* Pure presentation for the shadow preview. Renders from plain props;
|
|
2943
|
-
* composes the connected `ShadowSample` as a child
|
|
2944
|
-
*
|
|
2366
|
+
* composes the connected `ShadowSample` as a child, feeding it this row's
|
|
2367
|
+
* already-resolved `token`/`cssVar` per the presenter contract.
|
|
2945
2368
|
*/
|
|
2946
|
-
function ShadowPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter, caption }) {
|
|
2369
|
+
function ShadowPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, colorFormat, filter, caption }) {
|
|
2370
|
+
const Sample = usePresenter("shadow");
|
|
2947
2371
|
const captionText = caption ?? `${rows.length} shadow${rows.length === 1 ? "" : "s"}${filter ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
2948
2372
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
2949
2373
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -2972,7 +2396,12 @@ function ShadowPreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, filter
|
|
|
2972
2396
|
}),
|
|
2973
2397
|
/* @__PURE__ */ jsx("div", {
|
|
2974
2398
|
className: "sb-shadow-preview__sample-cell",
|
|
2975
|
-
children: /* @__PURE__ */ jsx(
|
|
2399
|
+
children: Sample && /* @__PURE__ */ jsx(Sample, {
|
|
2400
|
+
path: row.path,
|
|
2401
|
+
token: row.token,
|
|
2402
|
+
cssVar: row.cssVar,
|
|
2403
|
+
colorFormat
|
|
2404
|
+
})
|
|
2976
2405
|
}),
|
|
2977
2406
|
/* @__PURE__ */ jsx("div", {
|
|
2978
2407
|
className: "sb-shadow-preview__breakdown",
|
|
@@ -3017,47 +2446,35 @@ function Layer({ layer, index, total }) {
|
|
|
3017
2446
|
})]
|
|
3018
2447
|
});
|
|
3019
2448
|
}
|
|
3020
|
-
function ShadowPreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
|
|
2449
|
+
function ShadowPreview({ filter, caption, sortBy = "path", sortDir = "asc", colorFormat }) {
|
|
3021
2450
|
const project = useProject();
|
|
3022
2451
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
3023
|
-
const
|
|
2452
|
+
const contextColorFormat = useColorFormat();
|
|
2453
|
+
const format = colorFormat ?? contextColorFormat;
|
|
3024
2454
|
return /* @__PURE__ */ jsx(ShadowPreviewView, {
|
|
3025
2455
|
rows: useMemo(() => deriveShadowRows(resolved, project, {
|
|
3026
2456
|
filter,
|
|
3027
2457
|
sortBy,
|
|
3028
2458
|
sortDir,
|
|
3029
|
-
colorFormat
|
|
2459
|
+
colorFormat: format
|
|
3030
2460
|
}), [
|
|
3031
2461
|
resolved,
|
|
3032
2462
|
project,
|
|
3033
2463
|
filter,
|
|
3034
2464
|
sortBy,
|
|
3035
2465
|
sortDir,
|
|
3036
|
-
|
|
2466
|
+
format
|
|
3037
2467
|
]),
|
|
3038
2468
|
activeTheme,
|
|
3039
2469
|
cssVarPrefix,
|
|
3040
2470
|
activeAxes,
|
|
2471
|
+
colorFormat: format,
|
|
3041
2472
|
filter,
|
|
3042
2473
|
caption
|
|
3043
2474
|
});
|
|
3044
2475
|
}
|
|
3045
2476
|
//#endregion
|
|
3046
2477
|
//#region src/StrokeStylePreview.tsx
|
|
3047
|
-
const STRING_STYLES = new Set([
|
|
3048
|
-
"solid",
|
|
3049
|
-
"dashed",
|
|
3050
|
-
"dotted",
|
|
3051
|
-
"double",
|
|
3052
|
-
"groove",
|
|
3053
|
-
"ridge",
|
|
3054
|
-
"outset",
|
|
3055
|
-
"inset"
|
|
3056
|
-
]);
|
|
3057
|
-
function extractCssStyle(value) {
|
|
3058
|
-
if (typeof value === "string" && STRING_STYLES.has(value)) return value;
|
|
3059
|
-
return null;
|
|
3060
|
-
}
|
|
3061
2478
|
/**
|
|
3062
2479
|
* Pure derivation of the preview's display rows from resolved project data.
|
|
3063
2480
|
* Extracted so it is unit-testable without React or a store.
|
|
@@ -3072,12 +2489,17 @@ function deriveStrokeStyleRows(resolved, project, { filter, sortBy, sortDir }) {
|
|
|
3072
2489
|
}).map(([path, token]) => ({
|
|
3073
2490
|
path,
|
|
3074
2491
|
cssVar: resolveCssVar(path, project),
|
|
3075
|
-
displayValue: formatTokenValue(token.$value, token.$type, "raw", project.listing[path]),
|
|
3076
|
-
|
|
2492
|
+
displayValue: formatTokenValue$2(token.$value, token.$type, "raw", project.listing[path]),
|
|
2493
|
+
token
|
|
3077
2494
|
}));
|
|
3078
2495
|
}
|
|
3079
|
-
/**
|
|
3080
|
-
|
|
2496
|
+
/**
|
|
2497
|
+
* Pure presentation for the stroke-style preview. Renders from plain props;
|
|
2498
|
+
* composes the connected `StrokeSample` as a child, feeding it this row's
|
|
2499
|
+
* already-resolved `token`/`cssVar` per the presenter contract.
|
|
2500
|
+
*/
|
|
2501
|
+
function StrokeStylePreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, colorFormat, filter, caption }) {
|
|
2502
|
+
const Sample = usePresenter("strokeStyle");
|
|
3081
2503
|
const captionText = caption ?? `${rows.length} strokeStyle token${rows.length === 1 ? "" : "s"}${filter && filter !== "strokeStyle" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
3082
2504
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
3083
2505
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -3104,13 +2526,11 @@ function StrokeStylePreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, f
|
|
|
3104
2526
|
children: row.displayValue
|
|
3105
2527
|
})]
|
|
3106
2528
|
}),
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
className: "sb-stroke-style-sample__object-fallback",
|
|
3113
|
-
children: "Object-form (dashArray + lineCap) — no pure CSS `border-style` equivalent."
|
|
2529
|
+
Sample && /* @__PURE__ */ jsx(Sample, {
|
|
2530
|
+
path: row.path,
|
|
2531
|
+
token: row.token,
|
|
2532
|
+
cssVar: row.cssVar,
|
|
2533
|
+
colorFormat
|
|
3114
2534
|
}),
|
|
3115
2535
|
/* @__PURE__ */ jsx("span", {
|
|
3116
2536
|
className: "sb-stroke-style-sample__css-var",
|
|
@@ -3123,6 +2543,7 @@ function StrokeStylePreviewView({ rows, activeTheme, cssVarPrefix, activeAxes, f
|
|
|
3123
2543
|
function StrokeStylePreview({ filter, caption, sortBy = "path", sortDir = "asc" }) {
|
|
3124
2544
|
const project = useProject();
|
|
3125
2545
|
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
2546
|
+
const colorFormat = useColorFormat();
|
|
3126
2547
|
return /* @__PURE__ */ jsx(StrokeStylePreviewView, {
|
|
3127
2548
|
rows: useMemo(() => deriveStrokeStyleRows(resolved, project, {
|
|
3128
2549
|
filter,
|
|
@@ -3138,6 +2559,7 @@ function StrokeStylePreview({ filter, caption, sortBy = "path", sortDir = "asc"
|
|
|
3138
2559
|
activeTheme,
|
|
3139
2560
|
cssVarPrefix,
|
|
3140
2561
|
activeAxes,
|
|
2562
|
+
colorFormat,
|
|
3141
2563
|
filter,
|
|
3142
2564
|
caption
|
|
3143
2565
|
});
|
|
@@ -3280,12 +2702,13 @@ function treeHasTruncation(nodes) {
|
|
|
3280
2702
|
}
|
|
3281
2703
|
//#endregion
|
|
3282
2704
|
//#region src/token-detail/AxisVariance.tsx
|
|
3283
|
-
function AxisVariance({ path }) {
|
|
2705
|
+
function AxisVariance({ path, colorFormat }) {
|
|
3284
2706
|
const { token, cssVar, axes, activeAxes, cssVarPrefix, varianceByPath, resolveAt } = useTokenDetailData(path);
|
|
3285
|
-
const
|
|
2707
|
+
const contextColorFormat = useColorFormat();
|
|
2708
|
+
const format = colorFormat ?? contextColorFormat;
|
|
3286
2709
|
const tokenType = token?.$type;
|
|
3287
2710
|
const isColor = tokenType === "color";
|
|
3288
|
-
const formatFn = (t) => valueFor(t, tokenType,
|
|
2711
|
+
const formatFn = (t) => valueFor(t, tokenType, format);
|
|
3289
2712
|
const variance = useMemo(() => {
|
|
3290
2713
|
const result = varianceByPath[path];
|
|
3291
2714
|
if (!result) return { kind: "constant" };
|
|
@@ -3434,20 +2857,21 @@ function AxisVariance({ path }) {
|
|
|
3434
2857
|
}
|
|
3435
2858
|
function valueFor(token, $type, format) {
|
|
3436
2859
|
if (!token) return "—";
|
|
3437
|
-
return formatTokenValue(token.$value, $type, format);
|
|
2860
|
+
return formatTokenValue$2(token.$value, $type, format);
|
|
3438
2861
|
}
|
|
3439
2862
|
//#endregion
|
|
3440
2863
|
//#region src/token-detail/CompositeBreakdown.tsx
|
|
3441
|
-
function CompositeBreakdown({ path }) {
|
|
2864
|
+
function CompositeBreakdown({ path, colorFormat }) {
|
|
3442
2865
|
const { token, resolved } = useTokenDetailData(path);
|
|
3443
|
-
const
|
|
2866
|
+
const contextColorFormat = useColorFormat();
|
|
2867
|
+
const format = colorFormat ?? contextColorFormat;
|
|
3444
2868
|
if (!token) return null;
|
|
3445
2869
|
return /* @__PURE__ */ jsx(CompositeBreakdownContent, {
|
|
3446
2870
|
type: token.$type,
|
|
3447
2871
|
rawValue: token.$value,
|
|
3448
2872
|
partialAliasOf: token.partialAliasOf,
|
|
3449
2873
|
resolved,
|
|
3450
|
-
colorFormat
|
|
2874
|
+
colorFormat: format
|
|
3451
2875
|
});
|
|
3452
2876
|
}
|
|
3453
2877
|
function CompositeBreakdownContent({ type, rawValue, partialAliasOf, resolved, colorFormat }) {
|
|
@@ -4076,7 +3500,7 @@ function deriveTokenDetail(path, token, listing, colorFormat) {
|
|
|
4076
3500
|
if (!token) return EMPTY_DERIVED;
|
|
4077
3501
|
const isColor = token.$type === "color";
|
|
4078
3502
|
const gamut = isColor ? formatColor(token.$value, colorFormat) : null;
|
|
4079
|
-
const value = formatTokenValue(token.$value, token.$type, colorFormat, listing[path]);
|
|
3503
|
+
const value = formatTokenValue$2(token.$value, token.$type, colorFormat, listing[path]);
|
|
4080
3504
|
const outOfGamut = gamut?.outOfGamut ?? false;
|
|
4081
3505
|
const dep = token.$deprecated;
|
|
4082
3506
|
return {
|
|
@@ -4166,12 +3590,13 @@ function TokenDetailView({ path, heading, token, cssVar, activeTheme, activeAxes
|
|
|
4166
3590
|
]
|
|
4167
3591
|
});
|
|
4168
3592
|
}
|
|
4169
|
-
function TokenDetail({ path, heading }) {
|
|
3593
|
+
function TokenDetail({ path, heading, colorFormat }) {
|
|
4170
3594
|
const { token, cssVar, activeTheme, activeAxes, cssVarPrefix } = useTokenDetailData(path);
|
|
4171
|
-
const
|
|
3595
|
+
const contextColorFormat = useColorFormat();
|
|
3596
|
+
const format = colorFormat ?? contextColorFormat;
|
|
4172
3597
|
const { listing } = useProject();
|
|
4173
|
-
const derived = deriveTokenDetail(path, token, listing,
|
|
4174
|
-
|
|
3598
|
+
const derived = deriveTokenDetail(path, token, listing, format);
|
|
3599
|
+
const view = /* @__PURE__ */ jsx(TokenDetailView, {
|
|
4175
3600
|
path,
|
|
4176
3601
|
...heading !== void 0 && { heading },
|
|
4177
3602
|
token,
|
|
@@ -4181,6 +3606,10 @@ function TokenDetail({ path, heading }) {
|
|
|
4181
3606
|
cssVarPrefix,
|
|
4182
3607
|
...derived
|
|
4183
3608
|
});
|
|
3609
|
+
return colorFormat !== void 0 ? /* @__PURE__ */ jsx(ColorFormatContext.Provider, {
|
|
3610
|
+
value: colorFormat,
|
|
3611
|
+
children: view
|
|
3612
|
+
}) : view;
|
|
4184
3613
|
}
|
|
4185
3614
|
//#endregion
|
|
4186
3615
|
//#region src/internal/DetailOverlay.tsx
|
|
@@ -4730,14 +4159,14 @@ function TokenNavigatorView({ resolved, root, enabledIndicators, cssVarPrefix, a
|
|
|
4730
4159
|
* navigation. Click a leaf to inspect it in a slide-over (unless `onSelect`
|
|
4731
4160
|
* is provided, which hands the follow-up to the consumer).
|
|
4732
4161
|
*/
|
|
4733
|
-
function TokenNavigator({ root, type, initiallyExpanded = 1, searchable = true, onSelect, id, indicators }) {
|
|
4162
|
+
function TokenNavigator({ root, type, initiallyExpanded = 1, searchable = true, onSelect, id, indicators, colorFormat }) {
|
|
4734
4163
|
const { resolved, activeTheme, activeAxes, cssVarPrefix, indicators: indicatorBaseline } = useProject();
|
|
4735
4164
|
const blockKey = useBlockKey("TokenNavigator", [
|
|
4736
4165
|
root,
|
|
4737
4166
|
type === void 0 ? "" : typeof type === "string" ? type : type.join(","),
|
|
4738
4167
|
id
|
|
4739
4168
|
]);
|
|
4740
|
-
|
|
4169
|
+
const view = /* @__PURE__ */ jsx(TokenNavigatorView, {
|
|
4741
4170
|
resolved,
|
|
4742
4171
|
root,
|
|
4743
4172
|
enabledIndicators: useMemo(() => resolveIndicators(indicators, indicatorBaseline), [indicators, indicatorBaseline]),
|
|
@@ -4750,6 +4179,10 @@ function TokenNavigator({ root, type, initiallyExpanded = 1, searchable = true,
|
|
|
4750
4179
|
searchable,
|
|
4751
4180
|
onSelect
|
|
4752
4181
|
});
|
|
4182
|
+
return colorFormat !== void 0 ? /* @__PURE__ */ jsx(ColorFormatContext.Provider, {
|
|
4183
|
+
value: colorFormat,
|
|
4184
|
+
children: view
|
|
4185
|
+
}) : view;
|
|
4753
4186
|
}
|
|
4754
4187
|
function TreeNodeRow({ node, expanded, focusedPath, registerTreeItem, onToggle, onFocusPath, onLeafClick, root, resolveInView, onNavigate, enabled, level, setsize, posinset }) {
|
|
4755
4188
|
if (node.kind === "leaf") return /* @__PURE__ */ jsx(LeafRow, {
|
|
@@ -4887,7 +4320,7 @@ const LeafPreview = memo(function LeafPreview({ path, token }) {
|
|
|
4887
4320
|
className: "sb-token-navigator__preview-box",
|
|
4888
4321
|
children: [/* @__PURE__ */ jsx("span", {
|
|
4889
4322
|
className: "sb-token-navigator__value",
|
|
4890
|
-
children: formatTokenValue(token.$value, type, colorFormat, project.listing[path])
|
|
4323
|
+
children: formatTokenValue$2(token.$value, type, colorFormat, project.listing[path])
|
|
4891
4324
|
}), /* @__PURE__ */ jsx("span", {
|
|
4892
4325
|
className: "sb-token-navigator__color-swatch",
|
|
4893
4326
|
style: { background: cssVar },
|
|
@@ -4899,12 +4332,15 @@ const LeafPreview = memo(function LeafPreview({ path, token }) {
|
|
|
4899
4332
|
className: "sb-token-navigator__preview-box",
|
|
4900
4333
|
children: [/* @__PURE__ */ jsx("span", {
|
|
4901
4334
|
className: "sb-token-navigator__value",
|
|
4902
|
-
children: formatTokenValue(token.$value, type, colorFormat, project.listing[path])
|
|
4335
|
+
children: formatTokenValue$2(token.$value, type, colorFormat, project.listing[path])
|
|
4903
4336
|
}), /* @__PURE__ */ jsx("span", {
|
|
4904
4337
|
className: "sb-token-navigator__preview-dimension",
|
|
4905
4338
|
children: /* @__PURE__ */ jsx(DimensionSample, {
|
|
4906
4339
|
path,
|
|
4907
|
-
|
|
4340
|
+
token,
|
|
4341
|
+
cssVar: resolveCssVar(path, project),
|
|
4342
|
+
colorFormat,
|
|
4343
|
+
options: { visual: "length" }
|
|
4908
4344
|
})
|
|
4909
4345
|
})]
|
|
4910
4346
|
});
|
|
@@ -4912,28 +4348,43 @@ const LeafPreview = memo(function LeafPreview({ path, token }) {
|
|
|
4912
4348
|
className: "sb-token-navigator__preview-box",
|
|
4913
4349
|
children: /* @__PURE__ */ jsx("span", {
|
|
4914
4350
|
className: "sb-token-navigator__preview-scaled",
|
|
4915
|
-
children: /* @__PURE__ */ jsx(ShadowSample, {
|
|
4351
|
+
children: /* @__PURE__ */ jsx(ShadowSample, {
|
|
4352
|
+
path,
|
|
4353
|
+
token,
|
|
4354
|
+
cssVar: resolveCssVar(path, project),
|
|
4355
|
+
colorFormat
|
|
4356
|
+
})
|
|
4916
4357
|
})
|
|
4917
4358
|
});
|
|
4918
4359
|
if (type === "border") return /* @__PURE__ */ jsx("span", {
|
|
4919
4360
|
className: "sb-token-navigator__preview-box",
|
|
4920
4361
|
children: /* @__PURE__ */ jsx("span", {
|
|
4921
4362
|
className: "sb-token-navigator__preview-scaled",
|
|
4922
|
-
children: /* @__PURE__ */ jsx(BorderSample, {
|
|
4363
|
+
children: /* @__PURE__ */ jsx(BorderSample, {
|
|
4364
|
+
path,
|
|
4365
|
+
token,
|
|
4366
|
+
cssVar: resolveCssVar(path, project),
|
|
4367
|
+
colorFormat
|
|
4368
|
+
})
|
|
4923
4369
|
})
|
|
4924
4370
|
});
|
|
4925
4371
|
if (type === "transition" || type === "duration" || type === "cubicBezier") return /* @__PURE__ */ jsx("span", {
|
|
4926
4372
|
className: "sb-token-navigator__preview-box",
|
|
4927
4373
|
children: /* @__PURE__ */ jsx("span", {
|
|
4928
4374
|
className: "sb-token-navigator__preview-motion",
|
|
4929
|
-
children: /* @__PURE__ */ jsx(MotionSample, {
|
|
4375
|
+
children: /* @__PURE__ */ jsx(MotionSample, {
|
|
4376
|
+
path,
|
|
4377
|
+
token,
|
|
4378
|
+
cssVar: resolveCssVar(path, project),
|
|
4379
|
+
colorFormat
|
|
4380
|
+
})
|
|
4930
4381
|
})
|
|
4931
4382
|
});
|
|
4932
4383
|
return /* @__PURE__ */ jsx("span", {
|
|
4933
4384
|
className: "sb-token-navigator__preview-box",
|
|
4934
4385
|
children: /* @__PURE__ */ jsx("span", {
|
|
4935
4386
|
className: "sb-token-navigator__value",
|
|
4936
|
-
children: formatTokenValue(token.$value, type, colorFormat, project.listing[path])
|
|
4387
|
+
children: formatTokenValue$2(token.$value, type, colorFormat, project.listing[path])
|
|
4937
4388
|
})
|
|
4938
4389
|
});
|
|
4939
4390
|
});
|
|
@@ -4965,7 +4416,7 @@ function deriveTokenRows(resolved, listing, cssVarPrefix, varianceByPath, { filt
|
|
|
4965
4416
|
return {
|
|
4966
4417
|
path,
|
|
4967
4418
|
type: token.$type ?? "",
|
|
4968
|
-
value: formatTokenValue(token.$value, token.$type, colorFormat, listing[path]),
|
|
4419
|
+
value: formatTokenValue$2(token.$value, token.$type, colorFormat, listing[path]),
|
|
4969
4420
|
outOfGamut: color?.outOfGamut ?? false,
|
|
4970
4421
|
cssVar: resolveCssVar(path, projectFields),
|
|
4971
4422
|
isColor,
|
|
@@ -5207,9 +4658,10 @@ function TokenTableView({ rows, activeTheme, cssVarPrefix, activeAxes, colorForm
|
|
|
5207
4658
|
* slide-over (unless `onSelect` is provided, which hands the follow-up to the
|
|
5208
4659
|
* consumer).
|
|
5209
4660
|
*/
|
|
5210
|
-
function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, id, indicators }) {
|
|
4661
|
+
function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", searchable = true, onSelect, id, indicators, colorFormat }) {
|
|
5211
4662
|
const { resolved, activeTheme, activeAxes, cssVarPrefix, listing, varianceByPath, indicators: indicatorBaseline } = useProject();
|
|
5212
|
-
const
|
|
4663
|
+
const contextColorFormat = useColorFormat();
|
|
4664
|
+
const format = colorFormat ?? contextColorFormat;
|
|
5213
4665
|
const rootFontSize = useRootFontSize();
|
|
5214
4666
|
const blockKey = useBlockKey("TokenTable", [
|
|
5215
4667
|
filter,
|
|
@@ -5218,13 +4670,13 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
|
|
|
5218
4670
|
id
|
|
5219
4671
|
]);
|
|
5220
4672
|
const enabledIndicators = useMemo(() => resolveIndicators(indicators, indicatorBaseline), [indicators, indicatorBaseline]);
|
|
5221
|
-
|
|
4673
|
+
const view = /* @__PURE__ */ jsx(TokenTableView, {
|
|
5222
4674
|
rows: useMemo(() => deriveTokenRows(resolved, listing, cssVarPrefix, varianceByPath, {
|
|
5223
4675
|
filter,
|
|
5224
4676
|
type,
|
|
5225
4677
|
sortBy,
|
|
5226
4678
|
sortDir,
|
|
5227
|
-
colorFormat,
|
|
4679
|
+
colorFormat: format,
|
|
5228
4680
|
rootFontSizePx: rootFontSize
|
|
5229
4681
|
}), [
|
|
5230
4682
|
resolved,
|
|
@@ -5235,13 +4687,13 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
|
|
|
5235
4687
|
type,
|
|
5236
4688
|
sortBy,
|
|
5237
4689
|
sortDir,
|
|
5238
|
-
|
|
4690
|
+
format,
|
|
5239
4691
|
rootFontSize
|
|
5240
4692
|
]),
|
|
5241
4693
|
activeTheme,
|
|
5242
4694
|
cssVarPrefix,
|
|
5243
4695
|
activeAxes,
|
|
5244
|
-
colorFormat,
|
|
4696
|
+
colorFormat: format,
|
|
5245
4697
|
enabledIndicators,
|
|
5246
4698
|
validPaths: useMemo(() => new Set(Object.keys(resolved)), [resolved]),
|
|
5247
4699
|
blockKey,
|
|
@@ -5251,66 +4703,37 @@ function TokenTable({ filter, type, caption, sortBy = "path", sortDir = "asc", s
|
|
|
5251
4703
|
searchable,
|
|
5252
4704
|
onSelect
|
|
5253
4705
|
});
|
|
4706
|
+
return colorFormat !== void 0 ? /* @__PURE__ */ jsx(ColorFormatContext.Provider, {
|
|
4707
|
+
value: colorFormat,
|
|
4708
|
+
children: view
|
|
4709
|
+
}) : view;
|
|
5254
4710
|
}
|
|
5255
4711
|
//#endregion
|
|
5256
4712
|
//#region src/TypographyScale.tsx
|
|
5257
|
-
function asDimension(raw) {
|
|
5258
|
-
if (raw == null) return void 0;
|
|
5259
|
-
if (typeof raw === "string" || typeof raw === "number") return String(raw);
|
|
5260
|
-
if (typeof raw === "object") {
|
|
5261
|
-
const v = raw;
|
|
5262
|
-
if (v.value !== void 0 && v.unit !== void 0) return `${String(v.value)}${String(v.unit)}`;
|
|
5263
|
-
}
|
|
5264
|
-
}
|
|
5265
|
-
function asFontFamily(raw) {
|
|
5266
|
-
if (typeof raw === "string") return raw;
|
|
5267
|
-
if (Array.isArray(raw)) return raw.map(String).join(", ");
|
|
5268
|
-
}
|
|
5269
|
-
function buildRow(path, composite) {
|
|
5270
|
-
const fontFamily = asFontFamily(composite.fontFamily);
|
|
5271
|
-
const fontSize = asDimension(composite.fontSize);
|
|
5272
|
-
const fontWeight = composite.fontWeight == null ? void 0 : String(composite.fontWeight);
|
|
5273
|
-
const lineHeight = composite.lineHeight == null ? void 0 : String(composite.lineHeight);
|
|
5274
|
-
const letterSpacing = asDimension(composite.letterSpacing);
|
|
5275
|
-
const sampleStyle = {};
|
|
5276
|
-
if (fontFamily) sampleStyle.fontFamily = fontFamily;
|
|
5277
|
-
if (fontSize) sampleStyle.fontSize = fontSize;
|
|
5278
|
-
if (fontWeight) sampleStyle.fontWeight = fontWeight;
|
|
5279
|
-
if (lineHeight) sampleStyle.lineHeight = lineHeight;
|
|
5280
|
-
if (letterSpacing) sampleStyle.letterSpacing = letterSpacing;
|
|
5281
|
-
return {
|
|
5282
|
-
path,
|
|
5283
|
-
sampleStyle,
|
|
5284
|
-
specs: [
|
|
5285
|
-
fontSize,
|
|
5286
|
-
fontWeight ? `w${fontWeight}` : void 0,
|
|
5287
|
-
lineHeight ? `lh ${lineHeight}` : void 0
|
|
5288
|
-
].filter(Boolean).join(" · ")
|
|
5289
|
-
};
|
|
5290
|
-
}
|
|
5291
4713
|
/**
|
|
5292
4714
|
* Pure derivation of the scale's display rows from resolved project data.
|
|
5293
4715
|
* Extracted so it is unit-testable without React or a store.
|
|
5294
4716
|
*/
|
|
5295
|
-
function deriveTypographyRows(resolved, { filter, sortBy, sortDir }) {
|
|
4717
|
+
function deriveTypographyRows(resolved, project, { filter, sortBy, sortDir }) {
|
|
5296
4718
|
return sortTokens(Object.entries(resolved).filter(([path, token]) => {
|
|
5297
4719
|
if (token.$type !== "typography") return false;
|
|
5298
4720
|
return matchPath(path, filter);
|
|
5299
4721
|
}), {
|
|
5300
4722
|
by: sortBy,
|
|
5301
4723
|
dir: sortDir
|
|
5302
|
-
}).map(([path, token]) => {
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
specs: ""
|
|
5308
|
-
};
|
|
5309
|
-
return buildRow(path, value);
|
|
5310
|
-
});
|
|
4724
|
+
}).map(([path, token]) => ({
|
|
4725
|
+
path,
|
|
4726
|
+
cssVar: resolveCssVar(path, project),
|
|
4727
|
+
token
|
|
4728
|
+
}));
|
|
5311
4729
|
}
|
|
5312
|
-
/**
|
|
5313
|
-
|
|
4730
|
+
/**
|
|
4731
|
+
* Pure presentation for the typography scale. Renders from plain props;
|
|
4732
|
+
* composes the connected `TypeSpecimen` as a child, feeding it this row's
|
|
4733
|
+
* already-resolved `token`/`cssVar` per the presenter contract.
|
|
4734
|
+
*/
|
|
4735
|
+
function TypographyScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, sample, colorFormat, filter, caption }) {
|
|
4736
|
+
const Specimen = usePresenter("typography");
|
|
5314
4737
|
const captionText = caption ?? `${rows.length} typography token${rows.length === 1 ? "" : "s"}${filter && filter !== "typography" ? ` matching \`${filter}\`` : ""} · ${activeTheme}`;
|
|
5315
4738
|
if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
5316
4739
|
...blockWrapperAttrs(cssVarPrefix, activeAxes),
|
|
@@ -5326,31 +4749,31 @@ function TypographyScaleView({ rows, activeTheme, cssVarPrefix, activeAxes, samp
|
|
|
5326
4749
|
children: captionText
|
|
5327
4750
|
}), rows.map((row) => /* @__PURE__ */ jsxs("div", {
|
|
5328
4751
|
className: "sb-typography-scale__row",
|
|
5329
|
-
children: [/* @__PURE__ */
|
|
5330
|
-
className: "sb-typography-
|
|
5331
|
-
children:
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
}
|
|
5338
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
5339
|
-
style: row.sampleStyle,
|
|
5340
|
-
children: sample
|
|
4752
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4753
|
+
className: "sb-typography-scale__path",
|
|
4754
|
+
children: row.path
|
|
4755
|
+
}), Specimen && /* @__PURE__ */ jsx(Specimen, {
|
|
4756
|
+
path: row.path,
|
|
4757
|
+
token: row.token,
|
|
4758
|
+
cssVar: row.cssVar,
|
|
4759
|
+
colorFormat,
|
|
4760
|
+
options: { sample }
|
|
5341
4761
|
})]
|
|
5342
4762
|
}, row.path))]
|
|
5343
4763
|
});
|
|
5344
4764
|
}
|
|
5345
4765
|
function TypographyScale({ filter, sample = "The quick brown fox jumps over the lazy dog.", caption, sortBy = "path", sortDir = "asc" }) {
|
|
5346
|
-
const
|
|
4766
|
+
const project = useProject();
|
|
4767
|
+
const { resolved, activeTheme, activeAxes, cssVarPrefix } = project;
|
|
4768
|
+
const colorFormat = useColorFormat();
|
|
5347
4769
|
return /* @__PURE__ */ jsx(TypographyScaleView, {
|
|
5348
|
-
rows: useMemo(() => deriveTypographyRows(resolved, {
|
|
4770
|
+
rows: useMemo(() => deriveTypographyRows(resolved, project, {
|
|
5349
4771
|
filter,
|
|
5350
4772
|
sortBy,
|
|
5351
4773
|
sortDir
|
|
5352
4774
|
}), [
|
|
5353
4775
|
resolved,
|
|
4776
|
+
project,
|
|
5354
4777
|
filter,
|
|
5355
4778
|
sortBy,
|
|
5356
4779
|
sortDir
|
|
@@ -5359,11 +4782,12 @@ function TypographyScale({ filter, sample = "The quick brown fox jumps over the
|
|
|
5359
4782
|
cssVarPrefix,
|
|
5360
4783
|
activeAxes,
|
|
5361
4784
|
sample,
|
|
4785
|
+
colorFormat,
|
|
5362
4786
|
filter,
|
|
5363
4787
|
caption
|
|
5364
4788
|
});
|
|
5365
4789
|
}
|
|
5366
4790
|
//#endregion
|
|
5367
|
-
export { AliasChain, AliasedBy, AxesContext, AxisVariance, BorderPreview, BorderSample, COLOR_FORMATS, ColorFormatContext, ColorPalette, ColorTable, CompositeBreakdown, CompositePreview, ConsumerOutput, Diagnostics, DimensionSample, DimensionScale, FontFamilyPreview, FontWeightScale, GradientPalette, MotionPreview, MotionSample, OpacityScale, ShadowPreview, ShadowSample, StrokeStylePreview,
|
|
4791
|
+
export { AliasChain, AliasedBy, AxesContext, AxisVariance, BorderPreview, BorderSample, COLOR_FORMATS, ColorFormatContext, ColorPalette, ColorSwatch, ColorTable, CompositeBreakdown, CompositePreview, ConsumerOutput, DEFAULT_PRESENTERS, Diagnostics, DimensionSample, DimensionScale, FontFamilyPreview, FontFamilySpecimen, FontWeightScale, FontWeightSpecimen, GradientPalette, GradientSwatch, MotionPreview, MotionSample, OpacityScale, OpacitySwatch, PresenterContext, ShadowPreview, ShadowSample, StrokeSample, StrokeStylePreview, SwatchbookContext, SwatchbookProvider, ThemeContext, TokenDetail, TokenHeader, TokenNavigator, TokenTable, TokenUsageSnippet, TypeSpecimen, TypographyScale, formatColor, formatTokenValue, mergePresenters, useActiveAxes, useActiveTheme, useColorFormat, useOptionalSwatchbookData, usePresenter, useSetAxes, useSwatchbookData };
|
|
5368
4792
|
|
|
5369
4793
|
//# sourceMappingURL=index.mjs.map
|