@unpunnyfuns/swatchbook-switcher 0.60.9 → 0.61.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.d.mts CHANGED
@@ -1,12 +1,6 @@
1
1
  import { KeyboardEvent, ReactElement } from "react";
2
2
 
3
3
  //#region src/types.d.ts
4
- /**
5
- * Structural types the switcher consumes. Mirror the shapes produced by
6
- * `@unpunnyfuns/swatchbook-core` and by the addon's preview INIT payload —
7
- * re-declared here so the switcher stays framework-agnostic and doesn't pull
8
- * core / addon as runtime deps.
9
- */
10
4
  interface SwitcherAxis {
11
5
  name: string;
12
6
  contexts: readonly string[];
package/dist/index.mjs CHANGED
@@ -98,12 +98,6 @@ function AxisSection({ axis, active, onSelect }) {
98
98
  onClick: () => onSelect(ctx)
99
99
  }))));
100
100
  }
101
- /**
102
- * Treat the `{ name: 'theme', source: 'synthetic' }` axis — the one core
103
- * fabricates for single-theme projects with no resolver — as a special case
104
- * that reads as "Permutation". Authored single-axis resolvers keep their real
105
- * name (e.g. `mode`, `brand`).
106
- */
107
101
  function displayLabelFor(axis) {
108
102
  if (axis.source === "synthetic" && axis.name === "theme") return "Permutation";
109
103
  return axis.name;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/preset-tuple.ts","../src/ThemeSwitcher.tsx"],"sourcesContent":["import type { SwitcherAxis, SwitcherPreset } from '#/types.ts';\n\n/**\n * Compose a preset's sanitized partial tuple with the axis defaults, so\n * applying a preset that only names some axes leaves the omitted ones at\n * their defaults (not blank). Mirrors the addon preview decorator's own\n * fallback logic so what a host sends out is what the decorator honors.\n */\nexport function presetTuple(\n preset: SwitcherPreset,\n axes: readonly SwitcherAxis[],\n defaults: Readonly<Record<string, string>>,\n): Record<string, string> {\n const out: Record<string, string> = { ...defaults };\n for (const axis of axes) {\n const candidate = preset.axes[axis.name];\n if (candidate !== undefined && axis.contexts.includes(candidate)) {\n out[axis.name] = candidate;\n }\n }\n return out;\n}\n","import cx from 'clsx';\nimport React from 'react';\nimport type { KeyboardEvent, ReactElement } from 'react';\nimport './ThemeSwitcher.css';\nimport { presetTuple } from '#/preset-tuple.ts';\nimport type { SwitcherAxis, SwitcherPreset } from '#/types.ts';\n\nexport interface ThemeSwitcherProps {\n /** Project axes the UI renders one section per. */\n axes: readonly SwitcherAxis[];\n /** Saved preset snapshots rendered above the axes, if any. */\n presets?: readonly SwitcherPreset[];\n /** Active axis tuple, keyed by axis name. */\n activeTuple: Readonly<Record<string, string>>;\n /** Default tuple used to fill in omitted preset axes. */\n defaults: Readonly<Record<string, string>>;\n /** Name of the most recently applied preset, or null. Drives the \"modified\" dot. */\n lastApplied: string | null;\n /** Receives an axis name + new context. */\n onAxisChange(axisName: string, next: string): void;\n /** Called with the full preset object when the user clicks a preset pill. */\n onPresetApply(preset: SwitcherPreset): void;\n /** Optional key handler, usually used by consumers to close a popover on Escape. */\n onKeyDown?(event: KeyboardEvent<HTMLDivElement>): void;\n /** Host-specific content rendered after the axes (e.g. the Storybook addon's color-format picker). */\n footer?: ReactElement | null;\n}\n\n/**\n * Popover body for the swatchbook theme switcher. Renders preset pills\n * (when the project ships any) and one row per axis. Color-format\n * selection is specific to the Storybook addon (it toggles how blocks\n * stringify colors); hosts that need it slot\n * `<ColorFormatSelector>` into the `footer` prop rather than it being\n * baked into every consumer.\n *\n * Consumers own the trigger + positioning — the switcher just draws\n * the menu. Uses classic JSX so it survives embedding in Storybook's\n * manager bundle (which doesn't expose `react/jsx-runtime`).\n */\nexport function ThemeSwitcher({\n axes,\n presets = [],\n activeTuple,\n defaults,\n lastApplied,\n onAxisChange,\n onPresetApply,\n onKeyDown,\n footer,\n}: ThemeSwitcherProps): ReactElement {\n return (\n // `role=\"group\"` + `aria-label` — the switcher is a settings panel\n // (presets, per-axis selectors, color-format pills), not a command\n // menu. WAI-ARIA `menu` would require `menuitem`-rolled children plus\n // a roving tabindex; the actual content is a panel of independent\n // controls each of which already exposes its own role + state.\n <div\n role=\"group\"\n aria-label=\"Swatchbook controls\"\n tabIndex={-1}\n className=\"sb-switcher\"\n onKeyDown={onKeyDown}\n data-testid=\"swatchbook-switcher\"\n >\n {presets.length > 0 && (\n <>\n <PresetsSection\n presets={presets}\n axes={axes}\n defaults={defaults}\n activeTuple={activeTuple}\n lastApplied={lastApplied}\n onApply={onPresetApply}\n />\n <div className=\"sb-switcher__divider\" />\n </>\n )}\n\n {axes.map((axis) => (\n <AxisSection\n key={`axis-${axis.name}`}\n axis={axis}\n active={activeTuple[axis.name] ?? axis.default}\n onSelect={(next) => onAxisChange(axis.name, next)}\n />\n ))}\n\n {footer && (\n <>\n <div className=\"sb-switcher__divider\" />\n {footer}\n </>\n )}\n </div>\n );\n}\n\ninterface OptionPillProps {\n label: string;\n active: boolean;\n title?: string;\n onClick(): void;\n trailing?: ReactElement | null;\n /**\n * Optional accessible-label prefix — disambiguates pills that share a\n * label across sections (e.g. `Default` appears on both `mode` and\n * `brand`). The full accessible name becomes `\"<label> <ariaLabelSuffix>\"`.\n */\n ariaLabelSuffix?: string;\n}\n\nfunction OptionPill({\n label,\n active,\n title,\n onClick,\n trailing,\n ariaLabelSuffix,\n}: OptionPillProps): ReactElement {\n return (\n <button\n type=\"button\"\n title={title}\n aria-pressed={active}\n aria-label={ariaLabelSuffix ? `${label} (${ariaLabelSuffix})` : undefined}\n onClick={onClick}\n className={cx('sb-switcher__pill', active && 'sb-switcher__pill--active')}\n >\n {label}\n {trailing ?? null}\n </button>\n );\n}\n\ninterface PresetsSectionProps {\n presets: readonly SwitcherPreset[];\n axes: readonly SwitcherAxis[];\n defaults: Readonly<Record<string, string>>;\n activeTuple: Readonly<Record<string, string>>;\n lastApplied: string | null;\n onApply(preset: SwitcherPreset): void;\n}\n\nfunction PresetsSection({\n presets,\n axes,\n defaults,\n activeTuple,\n lastApplied,\n onApply,\n}: PresetsSectionProps): ReactElement {\n return (\n <div>\n <div className=\"sb-switcher__section-label\">Presets</div>\n <div className=\"sb-switcher__section-body\">\n {presets.map((preset) => {\n const tuple = presetTuple(preset, axes, defaults);\n const matches = tuplesEqual(tuple, activeTuple, axes);\n const modified = !matches && preset.name === lastApplied;\n const title = preset.description ? `${preset.name} — ${preset.description}` : preset.name;\n return (\n <OptionPill\n key={`preset/${preset.name}`}\n label={preset.name}\n active={matches}\n title={title}\n onClick={() => onApply(preset)}\n trailing={\n modified ? <span aria-hidden className=\"sb-switcher__pill-modified\" /> : null\n }\n />\n );\n })}\n </div>\n </div>\n );\n}\n\ninterface AxisSectionProps {\n axis: SwitcherAxis;\n active: string;\n onSelect(next: string): void;\n}\n\nfunction AxisSection({ axis, active, onSelect }: AxisSectionProps): ReactElement {\n const label = displayLabelFor(axis);\n return (\n <div className=\"sb-switcher__axis-row\">\n <div\n className=\"sb-switcher__axis-label\"\n title={axis.description}\n id={`sb-axis-${axis.name}-label`}\n >\n {label}\n </div>\n <div\n className=\"sb-switcher__axis-pills\"\n role=\"group\"\n aria-labelledby={`sb-axis-${axis.name}-label`}\n >\n {axis.contexts.map((ctx) => (\n <OptionPill\n key={`${axis.name}/${ctx}`}\n label={ctx}\n ariaLabelSuffix={label}\n active={ctx === active}\n onClick={() => onSelect(ctx)}\n />\n ))}\n </div>\n </div>\n );\n}\n\n/**\n * Treat the `{ name: 'theme', source: 'synthetic' }` axis — the one core\n * fabricates for single-theme projects with no resolver — as a special case\n * that reads as \"Permutation\". Authored single-axis resolvers keep their real\n * name (e.g. `mode`, `brand`).\n */\nfunction displayLabelFor(axis: SwitcherAxis): string {\n if (axis.source === 'synthetic' && axis.name === 'theme') return 'Permutation';\n return axis.name;\n}\n\nfunction tuplesEqual(\n a: Readonly<Record<string, string>>,\n b: Readonly<Record<string, string>>,\n axes: readonly SwitcherAxis[],\n): boolean {\n for (const axis of axes) {\n if (a[axis.name] !== b[axis.name]) return false;\n }\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/preset-tuple.ts","../src/ThemeSwitcher.tsx"],"sourcesContent":["import type { SwitcherAxis, SwitcherPreset } from '#/types.ts';\n\n/**\n * Compose a preset's sanitized partial tuple with the axis defaults, so\n * applying a preset that only names some axes leaves the omitted ones at\n * their defaults (not blank). Mirrors the addon preview decorator's own\n * fallback logic so what a host sends out is what the decorator honors.\n */\nexport function presetTuple(\n preset: SwitcherPreset,\n axes: readonly SwitcherAxis[],\n defaults: Readonly<Record<string, string>>,\n): Record<string, string> {\n const out: Record<string, string> = { ...defaults };\n for (const axis of axes) {\n const candidate = preset.axes[axis.name];\n if (candidate !== undefined && axis.contexts.includes(candidate)) {\n out[axis.name] = candidate;\n }\n }\n return out;\n}\n","import cx from 'clsx';\nimport React from 'react';\nimport type { KeyboardEvent, ReactElement } from 'react';\nimport './ThemeSwitcher.css';\nimport { presetTuple } from '#/preset-tuple.ts';\nimport type { SwitcherAxis, SwitcherPreset } from '#/types.ts';\n\nexport interface ThemeSwitcherProps {\n /** Project axes the UI renders one section per. */\n axes: readonly SwitcherAxis[];\n /** Saved preset snapshots rendered above the axes, if any. */\n presets?: readonly SwitcherPreset[];\n /** Active axis tuple, keyed by axis name. */\n activeTuple: Readonly<Record<string, string>>;\n /** Default tuple used to fill in omitted preset axes. */\n defaults: Readonly<Record<string, string>>;\n /** Name of the most recently applied preset, or null. Drives the \"modified\" dot. */\n lastApplied: string | null;\n /** Receives an axis name + new context. */\n onAxisChange(axisName: string, next: string): void;\n /** Called with the full preset object when the user clicks a preset pill. */\n onPresetApply(preset: SwitcherPreset): void;\n /** Optional key handler, usually used by consumers to close a popover on Escape. */\n onKeyDown?(event: KeyboardEvent<HTMLDivElement>): void;\n /** Host-specific content rendered after the axes (e.g. the Storybook addon's color-format picker). */\n footer?: ReactElement | null;\n}\n\n/**\n * Popover body for the swatchbook theme switcher. Renders preset pills\n * (when the project ships any) and one row per axis. Color-format\n * selection is specific to the Storybook addon (it toggles how blocks\n * stringify colors); hosts that need it slot\n * `<ColorFormatSelector>` into the `footer` prop rather than it being\n * baked into every consumer.\n *\n * Consumers own the trigger + positioning — the switcher just draws\n * the menu. Uses classic JSX so it survives embedding in Storybook's\n * manager bundle (which doesn't expose `react/jsx-runtime`).\n */\nexport function ThemeSwitcher({\n axes,\n presets = [],\n activeTuple,\n defaults,\n lastApplied,\n onAxisChange,\n onPresetApply,\n onKeyDown,\n footer,\n}: ThemeSwitcherProps): ReactElement {\n return (\n // `role=\"group\"` + `aria-label` — the switcher is a settings panel\n // (presets, per-axis selectors, color-format pills), not a command\n // menu. WAI-ARIA `menu` would require `menuitem`-rolled children plus\n // a roving tabindex; the actual content is a panel of independent\n // controls each of which already exposes its own role + state.\n <div\n role=\"group\"\n aria-label=\"Swatchbook controls\"\n tabIndex={-1}\n className=\"sb-switcher\"\n onKeyDown={onKeyDown}\n data-testid=\"swatchbook-switcher\"\n >\n {presets.length > 0 && (\n <>\n <PresetsSection\n presets={presets}\n axes={axes}\n defaults={defaults}\n activeTuple={activeTuple}\n lastApplied={lastApplied}\n onApply={onPresetApply}\n />\n <div className=\"sb-switcher__divider\" />\n </>\n )}\n\n {axes.map((axis) => (\n <AxisSection\n key={`axis-${axis.name}`}\n axis={axis}\n active={activeTuple[axis.name] ?? axis.default}\n onSelect={(next) => onAxisChange(axis.name, next)}\n />\n ))}\n\n {footer && (\n <>\n <div className=\"sb-switcher__divider\" />\n {footer}\n </>\n )}\n </div>\n );\n}\n\ninterface OptionPillProps {\n label: string;\n active: boolean;\n title?: string;\n onClick(): void;\n trailing?: ReactElement | null;\n // Optional accessible-label prefix — disambiguates pills that share a\n // label across sections (e.g. `Default` appears on both `mode` and\n // `brand`). The full accessible name becomes `\"<label> <ariaLabelSuffix>\"`.\n ariaLabelSuffix?: string;\n}\n\nfunction OptionPill({\n label,\n active,\n title,\n onClick,\n trailing,\n ariaLabelSuffix,\n}: OptionPillProps): ReactElement {\n return (\n <button\n type=\"button\"\n title={title}\n aria-pressed={active}\n aria-label={ariaLabelSuffix ? `${label} (${ariaLabelSuffix})` : undefined}\n onClick={onClick}\n className={cx('sb-switcher__pill', active && 'sb-switcher__pill--active')}\n >\n {label}\n {trailing ?? null}\n </button>\n );\n}\n\ninterface PresetsSectionProps {\n presets: readonly SwitcherPreset[];\n axes: readonly SwitcherAxis[];\n defaults: Readonly<Record<string, string>>;\n activeTuple: Readonly<Record<string, string>>;\n lastApplied: string | null;\n onApply(preset: SwitcherPreset): void;\n}\n\nfunction PresetsSection({\n presets,\n axes,\n defaults,\n activeTuple,\n lastApplied,\n onApply,\n}: PresetsSectionProps): ReactElement {\n return (\n <div>\n <div className=\"sb-switcher__section-label\">Presets</div>\n <div className=\"sb-switcher__section-body\">\n {presets.map((preset) => {\n const tuple = presetTuple(preset, axes, defaults);\n const matches = tuplesEqual(tuple, activeTuple, axes);\n const modified = !matches && preset.name === lastApplied;\n const title = preset.description ? `${preset.name} — ${preset.description}` : preset.name;\n return (\n <OptionPill\n key={`preset/${preset.name}`}\n label={preset.name}\n active={matches}\n title={title}\n onClick={() => onApply(preset)}\n trailing={\n modified ? <span aria-hidden className=\"sb-switcher__pill-modified\" /> : null\n }\n />\n );\n })}\n </div>\n </div>\n );\n}\n\ninterface AxisSectionProps {\n axis: SwitcherAxis;\n active: string;\n onSelect(next: string): void;\n}\n\nfunction AxisSection({ axis, active, onSelect }: AxisSectionProps): ReactElement {\n const label = displayLabelFor(axis);\n return (\n <div className=\"sb-switcher__axis-row\">\n <div\n className=\"sb-switcher__axis-label\"\n title={axis.description}\n id={`sb-axis-${axis.name}-label`}\n >\n {label}\n </div>\n <div\n className=\"sb-switcher__axis-pills\"\n role=\"group\"\n aria-labelledby={`sb-axis-${axis.name}-label`}\n >\n {axis.contexts.map((ctx) => (\n <OptionPill\n key={`${axis.name}/${ctx}`}\n label={ctx}\n ariaLabelSuffix={label}\n active={ctx === active}\n onClick={() => onSelect(ctx)}\n />\n ))}\n </div>\n </div>\n );\n}\n\n// Treat the `{ name: 'theme', source: 'synthetic' }` axis — the one core\n// fabricates for single-theme projects with no resolver — as a special case\n// that reads as \"Permutation\". Authored single-axis resolvers keep their real\n// name (e.g. `mode`, `brand`).\nfunction displayLabelFor(axis: SwitcherAxis): string {\n if (axis.source === 'synthetic' && axis.name === 'theme') return 'Permutation';\n return axis.name;\n}\n\nfunction tuplesEqual(\n a: Readonly<Record<string, string>>,\n b: Readonly<Record<string, string>>,\n axes: readonly SwitcherAxis[],\n): boolean {\n for (const axis of axes) {\n if (a[axis.name] !== b[axis.name]) return false;\n }\n return true;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unpunnyfuns/swatchbook-switcher",
3
- "version": "0.60.9",
3
+ "version": "0.61.0",
4
4
  "description": "Framework-agnostic theme-switcher UI for swatchbook — axis pills, preset pills, color-format selector. Consumed by the Storybook addon toolbar and any React host that knows how to set data-attributes on the document.",
5
5
  "license": "MIT",
6
6
  "author": "unpunnyfuns <unpunnyfuns@gmail.com>",
@@ -52,14 +52,14 @@
52
52
  "@tsdown/css": "^0.21.9",
53
53
  "@types/react": "^19.2.14",
54
54
  "@vitejs/plugin-react": "^6.0.1",
55
- "@vitest/browser": "^4.1.4",
56
- "@vitest/browser-playwright": "^4.1.4",
55
+ "@vitest/browser": "^4.1.7",
56
+ "@vitest/browser-playwright": "^4.1.7",
57
57
  "playwright": "^1.59.1",
58
58
  "react": "^19.2.4",
59
59
  "react-dom": "^19.2.4",
60
60
  "tsdown": "^0.21.9",
61
61
  "typescript": "^6.0.0",
62
- "vitest": "^4.1.4"
62
+ "vitest": "^4.1.7"
63
63
  },
64
64
  "dependencies": {
65
65
  "clsx": "^2.1.1"