@unpunnyfuns/swatchbook-blocks 0.7.0 → 0.9.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 +4 -2
- package/dist/index.mjs +104 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,11 +6,13 @@ Published as `@unpunnyfuns/swatchbook-blocks`. Storybook MDX doc blocks for DTCG
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
Most consumers pick this up transitively via `@unpunnyfuns/swatchbook-addon` — the addon re-exports the full blocks API, so `import { TokenTable } from '@unpunnyfuns/swatchbook-addon'` works and you don't need a second install line. Reach for this package directly when you want blocks *without* the Storybook addon (unit tests, a standalone React app wrapping tokens in a custom surface):
|
|
10
|
+
|
|
9
11
|
```sh
|
|
10
12
|
npm install @unpunnyfuns/swatchbook-blocks
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
Blocks read the token graph from a `SwatchbookProvider`. Inside Storybook,
|
|
15
|
+
Blocks read the token graph from a `SwatchbookProvider`. Inside Storybook, the addon's preview decorator mounts the provider automatically. Outside Storybook, wrap your tree in `SwatchbookProvider` and pass a `ProjectSnapshot` directly (see [Outside Storybook](#outside-storybook) below).
|
|
14
16
|
|
|
15
17
|
## Blocks
|
|
16
18
|
|
|
@@ -46,7 +48,7 @@ Shared: every block accepts a `caption` override and renders against the addon's
|
|
|
46
48
|
The addon's preview decorator wraps every story in a `SwatchbookProvider` for you, so MDX and story authors drop blocks in directly:
|
|
47
49
|
|
|
48
50
|
```mdx
|
|
49
|
-
import { TokenTable, ColorPalette, TokenDetail } from '@unpunnyfuns/swatchbook-
|
|
51
|
+
import { TokenTable, ColorPalette, TokenDetail } from '@unpunnyfuns/swatchbook-addon';
|
|
50
52
|
|
|
51
53
|
# Color tokens
|
|
52
54
|
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import Color from "colorjs.io";
|
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { addons } from "storybook/preview-api";
|
|
6
|
-
import { axes, css, cssVarPrefix, defaultTheme, diagnostics, themes, themesResolved } from "virtual:swatchbook/tokens";
|
|
6
|
+
import { axes, css, cssVarPrefix, defaultTheme, diagnostics, presets, themes, themesResolved } from "virtual:swatchbook/tokens";
|
|
7
7
|
import cx from "clsx";
|
|
8
8
|
//#region src/format-color.ts
|
|
9
9
|
const COLOR_FORMATS = [
|
|
@@ -202,24 +202,24 @@ function EmptyState({ children }) {
|
|
|
202
202
|
const AXES_GLOBAL_KEY = "swatchbookAxes";
|
|
203
203
|
const THEME_GLOBAL_KEY = "swatchbookTheme";
|
|
204
204
|
const COLOR_FORMAT_GLOBAL_KEY = "swatchbookColorFormat";
|
|
205
|
-
let snapshot = {
|
|
205
|
+
let snapshot$1 = {
|
|
206
206
|
axes: null,
|
|
207
207
|
theme: null,
|
|
208
208
|
format: null
|
|
209
209
|
};
|
|
210
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
211
|
-
let subscribed = false;
|
|
210
|
+
const listeners$1 = /* @__PURE__ */ new Set();
|
|
211
|
+
let subscribed$1 = false;
|
|
212
212
|
function isColorFormat(value) {
|
|
213
213
|
return typeof value === "string" && COLOR_FORMATS.includes(value);
|
|
214
214
|
}
|
|
215
|
-
function ensureSubscribed() {
|
|
216
|
-
if (subscribed || typeof window === "undefined") return;
|
|
217
|
-
subscribed = true;
|
|
215
|
+
function ensureSubscribed$1() {
|
|
216
|
+
if (subscribed$1 || typeof window === "undefined") return;
|
|
217
|
+
subscribed$1 = true;
|
|
218
218
|
const channel = addons.getChannel();
|
|
219
219
|
const onGlobals = (payload) => {
|
|
220
220
|
const globals = payload.globals;
|
|
221
221
|
if (!globals) return;
|
|
222
|
-
let next = snapshot;
|
|
222
|
+
let next = snapshot$1;
|
|
223
223
|
const nextAxes = globals[AXES_GLOBAL_KEY];
|
|
224
224
|
if (nextAxes && typeof nextAxes === "object") next = {
|
|
225
225
|
...next,
|
|
@@ -235,9 +235,9 @@ function ensureSubscribed() {
|
|
|
235
235
|
...next,
|
|
236
236
|
format: nextFormat
|
|
237
237
|
};
|
|
238
|
-
if (next !== snapshot) {
|
|
239
|
-
snapshot = next;
|
|
240
|
-
for (const cb of listeners) cb();
|
|
238
|
+
if (next !== snapshot$1) {
|
|
239
|
+
snapshot$1 = next;
|
|
240
|
+
for (const cb of listeners$1) cb();
|
|
241
241
|
}
|
|
242
242
|
};
|
|
243
243
|
/**
|
|
@@ -257,22 +257,22 @@ function ensureSubscribed() {
|
|
|
257
257
|
* lands in our snapshot before any block renders. Running `useSyncExternalStore`'s
|
|
258
258
|
* `subscribe` lazily on first hook call would miss the event in most cases.
|
|
259
259
|
*/
|
|
260
|
-
ensureSubscribed();
|
|
261
|
-
function subscribe(cb) {
|
|
262
|
-
ensureSubscribed();
|
|
263
|
-
listeners.add(cb);
|
|
260
|
+
ensureSubscribed$1();
|
|
261
|
+
function subscribe$1(cb) {
|
|
262
|
+
ensureSubscribed$1();
|
|
263
|
+
listeners$1.add(cb);
|
|
264
264
|
return () => {
|
|
265
|
-
listeners.delete(cb);
|
|
265
|
+
listeners$1.delete(cb);
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
|
-
function getSnapshot() {
|
|
269
|
-
return snapshot;
|
|
268
|
+
function getSnapshot$1() {
|
|
269
|
+
return snapshot$1;
|
|
270
270
|
}
|
|
271
271
|
function getServerSnapshot() {
|
|
272
|
-
return snapshot;
|
|
272
|
+
return snapshot$1;
|
|
273
273
|
}
|
|
274
274
|
function useChannelGlobals() {
|
|
275
|
-
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
275
|
+
return useSyncExternalStore(subscribe$1, getSnapshot$1, getServerSnapshot);
|
|
276
276
|
}
|
|
277
277
|
//#endregion
|
|
278
278
|
//#region src/contexts.ts
|
|
@@ -326,6 +326,70 @@ function useColorFormat() {
|
|
|
326
326
|
return contextValue ?? channelGlobals.format ?? "hex";
|
|
327
327
|
}
|
|
328
328
|
//#endregion
|
|
329
|
+
//#region src/internal/channel-tokens.ts
|
|
330
|
+
/**
|
|
331
|
+
* Live token snapshot backed by the addon's preview dev-time HMR event.
|
|
332
|
+
*
|
|
333
|
+
* Blocks read the virtual module at module load; without a way to notice
|
|
334
|
+
* changes, edits to the source token files would flow into the addon's
|
|
335
|
+
* in-memory project but nowhere else — the React tree would keep
|
|
336
|
+
* rendering the old values until a full preview reload. This module
|
|
337
|
+
* subscribes to `TOKENS_UPDATED_EVENT` on Storybook's channel (which the
|
|
338
|
+
* addon preview re-broadcasts from its own HMR listener) and exposes
|
|
339
|
+
* the latest snapshot via `useSyncExternalStore`, so hooks that read
|
|
340
|
+
* through this module re-render in place on each token save.
|
|
341
|
+
*
|
|
342
|
+
* Outside the preview iframe (the docs-site path, unit tests) the
|
|
343
|
+
* channel never receives anything, and consumers keep seeing the
|
|
344
|
+
* initial values baked into the virtual module at build time.
|
|
345
|
+
*/
|
|
346
|
+
const TOKENS_UPDATED_EVENT = "swatchbook/tokens-updated";
|
|
347
|
+
let snapshot = {
|
|
348
|
+
axes,
|
|
349
|
+
presets,
|
|
350
|
+
themes,
|
|
351
|
+
defaultTheme,
|
|
352
|
+
themesResolved,
|
|
353
|
+
diagnostics,
|
|
354
|
+
css,
|
|
355
|
+
cssVarPrefix,
|
|
356
|
+
version: 0
|
|
357
|
+
};
|
|
358
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
359
|
+
let subscribed = false;
|
|
360
|
+
function ensureSubscribed() {
|
|
361
|
+
if (subscribed || typeof window === "undefined") return;
|
|
362
|
+
subscribed = true;
|
|
363
|
+
addons.getChannel().on(TOKENS_UPDATED_EVENT, (payload) => {
|
|
364
|
+
snapshot = {
|
|
365
|
+
axes: payload.axes ?? snapshot.axes,
|
|
366
|
+
presets: payload.presets ?? snapshot.presets,
|
|
367
|
+
themes: payload.themes ?? snapshot.themes,
|
|
368
|
+
defaultTheme: payload.defaultTheme ?? snapshot.defaultTheme,
|
|
369
|
+
themesResolved: payload.themesResolved ?? snapshot.themesResolved,
|
|
370
|
+
diagnostics: payload.diagnostics ?? snapshot.diagnostics,
|
|
371
|
+
css: payload.css ?? snapshot.css,
|
|
372
|
+
cssVarPrefix: payload.cssVarPrefix ?? snapshot.cssVarPrefix,
|
|
373
|
+
version: snapshot.version + 1
|
|
374
|
+
};
|
|
375
|
+
for (const cb of listeners) cb();
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
ensureSubscribed();
|
|
379
|
+
function subscribe(cb) {
|
|
380
|
+
ensureSubscribed();
|
|
381
|
+
listeners.add(cb);
|
|
382
|
+
return () => {
|
|
383
|
+
listeners.delete(cb);
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function getSnapshot() {
|
|
387
|
+
return snapshot;
|
|
388
|
+
}
|
|
389
|
+
function useTokenSnapshot() {
|
|
390
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
329
393
|
//#region src/internal/use-project.ts
|
|
330
394
|
const STYLE_ELEMENT_ID = "swatchbook-tokens";
|
|
331
395
|
function ensureStylesheet(css) {
|
|
@@ -387,24 +451,32 @@ function useVirtualModuleFallback(enabled) {
|
|
|
387
451
|
const contextTheme = useActiveTheme();
|
|
388
452
|
const contextAxes = useActiveAxes();
|
|
389
453
|
const channelGlobals = useChannelGlobals();
|
|
454
|
+
/**
|
|
455
|
+
* Subscribe to the live token snapshot rather than reading the virtual
|
|
456
|
+
* module's module-level exports directly. Initial values come from
|
|
457
|
+
* `virtual:swatchbook/tokens` at load time; subsequent dev-time edits
|
|
458
|
+
* flow through the addon's HMR channel and update this snapshot in
|
|
459
|
+
* place so blocks re-render without a full preview reload.
|
|
460
|
+
*/
|
|
461
|
+
const tokens = useTokenSnapshot();
|
|
390
462
|
useEffect(() => {
|
|
391
463
|
if (!enabled) return;
|
|
392
|
-
ensureStylesheet(css);
|
|
393
|
-
}, [enabled]);
|
|
394
|
-
const activeAxes = Object.keys(contextAxes).length > 0 ? { ...contextAxes } : channelGlobals.axes ?? defaultTuple(axes);
|
|
395
|
-
const derivedName = nameForTuple(themes, activeAxes);
|
|
464
|
+
ensureStylesheet(tokens.css);
|
|
465
|
+
}, [enabled, tokens.css]);
|
|
466
|
+
const activeAxes = Object.keys(contextAxes).length > 0 ? { ...contextAxes } : channelGlobals.axes ?? defaultTuple(tokens.axes);
|
|
467
|
+
const derivedName = nameForTuple(tokens.themes, activeAxes);
|
|
396
468
|
const channelTheme = channelGlobals.theme;
|
|
397
|
-
const fallbackTupleName = channelTheme && tupleForName(themes, channelTheme) ? channelTheme : null;
|
|
398
|
-
const activeTheme = contextTheme || derivedName || fallbackTupleName || channelTheme || defaultTheme || themes[0]?.name || "";
|
|
469
|
+
const fallbackTupleName = channelTheme && tupleForName(tokens.themes, channelTheme) ? channelTheme : null;
|
|
470
|
+
const activeTheme = contextTheme || derivedName || fallbackTupleName || channelTheme || tokens.defaultTheme || tokens.themes[0]?.name || "";
|
|
399
471
|
return {
|
|
400
472
|
activeTheme,
|
|
401
473
|
activeAxes,
|
|
402
|
-
axes,
|
|
403
|
-
themes,
|
|
404
|
-
themesResolved,
|
|
405
|
-
resolved: themesResolved[activeTheme] ?? {},
|
|
406
|
-
diagnostics,
|
|
407
|
-
cssVarPrefix
|
|
474
|
+
axes: tokens.axes,
|
|
475
|
+
themes: tokens.themes,
|
|
476
|
+
themesResolved: tokens.themesResolved,
|
|
477
|
+
resolved: tokens.themesResolved[activeTheme] ?? {},
|
|
478
|
+
diagnostics: tokens.diagnostics,
|
|
479
|
+
cssVarPrefix: tokens.cssVarPrefix
|
|
408
480
|
};
|
|
409
481
|
}
|
|
410
482
|
function makeCssVar(path, prefix) {
|