@unpunnyfuns/swatchbook-blocks 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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) {