@unpunnyfuns/swatchbook-addon 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as preview_exports } from "./preview-
|
|
1
|
+
import { i as preview_exports } from "./preview-AtzpsqFq.mjs";
|
|
2
2
|
import { c as PARAM_KEY, i as GLOBAL_KEY, n as AXES_GLOBAL_KEY, p as VIRTUAL_MODULE_ID, t as ADDON_ID } from "./constants-CBXL7TOk.mjs";
|
|
3
3
|
import { definePreviewAddon } from "storybook/internal/csf";
|
|
4
4
|
//#region src/index.ts
|
package/dist/manager.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.mjs","names":["h"],"sources":["../src/panel.tsx","../src/manager.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactElement,\n} from 'react';\nimport { Placeholder, ScrollArea } from 'storybook/internal/components';\nimport { addons, useGlobals } from 'storybook/manager-api';\nimport { AXES_GLOBAL_KEY, GLOBAL_KEY, INIT_EVENT } from '#/constants.ts';\n\n/** `React.createElement` alias so the manager bundle avoids `react/jsx-runtime`. */\nconst h = React.createElement;\n\ninterface VirtualToken {\n $type?: string;\n $value?: unknown;\n $description?: string;\n}\n\ninterface VirtualTheme {\n name: string;\n input: Record<string, string>;\n sources: string[];\n}\n\ninterface VirtualAxis {\n name: string;\n contexts: readonly string[];\n default: string;\n description?: string;\n source: 'resolver' | 'synthetic';\n}\n\ntype DiagnosticSeverity = 'error' | 'warn' | 'info';\n\ninterface VirtualDiagnostic {\n severity: DiagnosticSeverity;\n group: string;\n message: string;\n filename?: string;\n line?: number;\n column?: number;\n}\n\ninterface InitPayload {\n axes: VirtualAxis[];\n disabledAxes: readonly string[];\n themes: VirtualTheme[];\n defaultTheme: string | null;\n themesResolved: Record<string, Record<string, VirtualToken>>;\n diagnostics: VirtualDiagnostic[];\n cssVarPrefix: string;\n}\n\nfunction usePayload(): InitPayload | null {\n const [payload, setPayload] = useState<InitPayload | null>(null);\n useEffect(() => {\n const channel = addons.getChannel();\n const onInit = (next: InitPayload): void => setPayload(next);\n channel.on(INIT_EVENT, onInit);\n return () => {\n channel.off(INIT_EVENT, onInit);\n };\n }, []);\n return payload;\n}\n\nfunction makeCssVarName(path: string, prefix: string): string {\n const tail = path.replaceAll('.', '-');\n return prefix ? `--${prefix}-${tail}` : `--${tail}`;\n}\n\nasync function copy(text: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n /* clipboard access denied — no fallback, the user can read the var name from the row */\n }\n}\n\n/** Format a token `$value` into a short display string. */\nfunction formatValue(value: unknown): string {\n if (value == null) return '';\n if (typeof value === 'string' || typeof value === 'number') return String(value);\n if (typeof value === 'object') {\n const v = value as Record<string, unknown>;\n if (typeof v['hex'] === 'string') return v['hex'] as string;\n if ('value' in v && 'unit' in v) return `${String(v['value'])}${String(v['unit'])}`;\n return JSON.stringify(value).slice(0, 80);\n }\n return String(value);\n}\n\nconst containerStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n height: '100%',\n};\n\nconst headerStyle: CSSProperties = {\n padding: '8px 12px',\n borderBottom: '1px solid rgba(128,128,128,0.2)',\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n};\n\nconst axisIndicatorStyle: CSSProperties = {\n fontSize: 11,\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace',\n opacity: 0.7,\n};\n\nconst treeWrapperStyle: CSSProperties = {\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 12,\n padding: 8,\n};\n\nconst groupRowStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '4px 6px',\n borderRadius: 4,\n cursor: 'pointer',\n userSelect: 'none',\n outline: 'none',\n};\n\nconst leafRowStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '4px 6px',\n borderRadius: 4,\n cursor: 'pointer',\n border: 'none',\n background: 'transparent',\n color: 'inherit',\n width: '100%',\n textAlign: 'left',\n fontFamily: 'inherit',\n fontSize: 'inherit',\n outline: 'none',\n};\n\nconst caretStyle: CSSProperties = {\n display: 'inline-block',\n width: 12,\n textAlign: 'center',\n opacity: 0.6,\n};\n\nconst treeUlStyle: CSSProperties = { listStyle: 'none', margin: 0, padding: 0 };\n\nconst nestedUlStyle: CSSProperties = {\n listStyle: 'none',\n margin: 0,\n paddingLeft: 18,\n borderLeft: '1px solid rgba(128,128,128,0.2)',\n};\n\nconst typePillStyle: CSSProperties = {\n display: 'inline-block',\n padding: '1px 6px',\n borderRadius: 4,\n fontSize: 10,\n letterSpacing: 0.5,\n textTransform: 'uppercase',\n background: 'rgba(128,128,128,0.15)',\n};\n\nconst valueStyle: CSSProperties = {\n marginLeft: 'auto',\n opacity: 0.7,\n fontSize: 11,\n maxWidth: '40%',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\n\nconst countStyle: CSSProperties = {\n marginLeft: 'auto',\n fontSize: 11,\n opacity: 0.7,\n};\n\nconst swatchStyle: CSSProperties = {\n display: 'inline-block',\n width: 14,\n height: 14,\n borderRadius: 3,\n border: '1px solid rgba(128,128,128,0.3)',\n marginLeft: 8,\n};\n\nconst searchInputStyle: CSSProperties = {\n width: '100%',\n padding: '4px 8px',\n fontSize: 12,\n border: '1px solid rgba(128,128,128,0.3)',\n borderRadius: 4,\n background: 'transparent',\n color: 'inherit',\n};\n\ninterface LeafNode {\n kind: 'leaf';\n segment: string;\n path: string;\n token: VirtualToken;\n}\n\ninterface GroupNode {\n kind: 'group';\n segment: string;\n path: string;\n children: TreeNode[];\n}\n\ntype TreeNode = LeafNode | GroupNode;\n\nfunction buildTree(resolved: Record<string, VirtualToken>): TreeNode[] {\n const rootNode: GroupNode = { kind: 'group', segment: '', path: '', children: [] };\n for (const [path, token] of Object.entries(resolved)) {\n const segments = path.split('.');\n let node: GroupNode = rootNode;\n for (let i = 0; i < segments.length - 1; i += 1) {\n const seg = segments[i] as string;\n const prefix = segments.slice(0, i + 1).join('.');\n let child = node.children.find(\n (c): c is GroupNode => c.kind === 'group' && c.segment === seg,\n );\n if (!child) {\n child = { kind: 'group', segment: seg, path: prefix, children: [] };\n node.children.push(child);\n }\n node = child;\n }\n const leafSegment = segments[segments.length - 1] as string;\n node.children.push({ kind: 'leaf', segment: leafSegment, path, token });\n }\n sortTree(rootNode);\n return rootNode.children;\n}\n\nfunction sortTree(node: GroupNode): void {\n node.children.sort((a, b) => {\n if (a.kind !== b.kind) return a.kind === 'group' ? -1 : 1;\n return a.segment.localeCompare(b.segment);\n });\n for (const c of node.children) {\n if (c.kind === 'group') sortTree(c);\n }\n}\n\nfunction collectInitialExpanded(nodes: TreeNode[], remainingDepth: number, out: Set<string>): void {\n if (remainingDepth <= 0) return;\n for (const node of nodes) {\n if (node.kind !== 'group') continue;\n out.add(node.path);\n collectInitialExpanded(node.children, remainingDepth - 1, out);\n }\n}\n\nfunction countLeaves(node: TreeNode): number {\n if (node.kind === 'leaf') return 1;\n let n = 0;\n for (const c of node.children) n += countLeaves(c);\n return n;\n}\n\nfunction filterTree(nodes: TreeNode[], query: string): TreeNode[] {\n if (!query) return nodes;\n const out: TreeNode[] = [];\n for (const node of nodes) {\n if (node.kind === 'leaf') {\n if (node.path.toLowerCase().includes(query)) out.push(node);\n continue;\n }\n const filteredChildren = filterTree(node.children, query);\n if (filteredChildren.length > 0) {\n out.push({ ...node, children: filteredChildren });\n }\n }\n return out;\n}\n\nfunction collectAllGroupPaths(nodes: TreeNode[], out: Set<string>): void {\n for (const node of nodes) {\n if (node.kind === 'group') {\n out.add(node.path);\n collectAllGroupPaths(node.children, out);\n }\n }\n}\n\ninterface PanelProps {\n active: boolean;\n}\n\nexport function DesignTokensPanel({ active }: PanelProps): ReactElement | null {\n const payload = usePayload();\n const [globals] = useGlobals();\n const [query, setQuery] = useState('');\n\n const axes = useMemo(() => payload?.axes ?? [], [payload]);\n const themes = useMemo(() => payload?.themes ?? [], [payload]);\n const globalAxes = globals[AXES_GLOBAL_KEY] as Record<string, string> | undefined;\n const globalTheme = globals[GLOBAL_KEY] as string | undefined;\n\n const tuple: Record<string, string> = useMemo(() => {\n const out: Record<string, string> = {};\n for (const axis of axes) out[axis.name] = axis.default;\n if (globalAxes && typeof globalAxes === 'object') {\n for (const axis of axes) {\n const candidate = globalAxes[axis.name];\n if (candidate && axis.contexts.includes(candidate)) out[axis.name] = candidate;\n }\n return out;\n }\n if (globalTheme) {\n const match = themes.find((t) => t.name === globalTheme);\n if (match) {\n for (const axis of axes) {\n const candidate = match.input[axis.name];\n if (candidate && axis.contexts.includes(candidate)) out[axis.name] = candidate;\n }\n }\n }\n return out;\n }, [axes, themes, globalAxes, globalTheme]);\n\n const themeName = useMemo(() => {\n const match = themes.find((t) => {\n const input = t.input;\n return Object.keys(input).every((k) => input[k] === tuple[k]);\n });\n return match?.name ?? globalTheme ?? payload?.defaultTheme ?? '';\n }, [themes, tuple, globalTheme, payload]);\n\n const prefix = payload?.cssVarPrefix ?? '';\n const tokens = useMemo(() => payload?.themesResolved[themeName] ?? {}, [payload, themeName]);\n const tokenCount = Object.keys(tokens).length;\n\n const tree = useMemo(() => buildTree(tokens), [tokens]);\n const lowerQuery = query.toLowerCase();\n const filtered = useMemo(() => filterTree(tree, lowerQuery), [tree, lowerQuery]);\n\n const initialExpanded = useMemo(() => {\n const out = new Set<string>();\n collectInitialExpanded(tree, 1, out);\n return out;\n }, [tree]);\n\n const [expanded, setExpanded] = useState<Set<string>>(initialExpanded);\n useEffect(() => {\n setExpanded(initialExpanded);\n }, [initialExpanded]);\n\n // When searching, expand every matching group so hits are visible.\n const displayExpanded = useMemo(() => {\n if (!lowerQuery) return expanded;\n const all = new Set<string>();\n collectAllGroupPaths(filtered, all);\n return all;\n }, [expanded, filtered, lowerQuery]);\n\n const toggle = useCallback((path: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n }, []);\n\n const handleLeafClick = useCallback(\n (path: string) => {\n const varName = makeCssVarName(path, prefix);\n void copy(`var(${varName})`);\n },\n [prefix],\n );\n\n if (!active) return null;\n\n if (!payload) {\n return h(Placeholder, null, 'Waiting for swatchbook preview…');\n }\n\n const showAxisIndicator =\n axes.length > 1 || (axes.length === 1 && axes[0]?.source !== 'synthetic');\n const axisIndicatorText = axes\n .map((axis) => `${axis.name}: ${tuple[axis.name] ?? axis.default}`)\n .join(' · ');\n const disabledAxes = payload?.disabledAxes ?? [];\n const pinnedSample = themes[0]?.input ?? {};\n const disabledIndicatorText = disabledAxes\n .map((name) => `${name}: ${pinnedSample[name] ?? '?'} · pinned`)\n .join(' · ');\n\n return h(\n 'div',\n { style: containerStyle },\n h(\n 'div',\n { style: headerStyle },\n showAxisIndicator &&\n h(\n 'div',\n {\n style: axisIndicatorStyle,\n 'data-testid': 'design-tokens-panel-axis-indicator',\n },\n axisIndicatorText,\n ),\n disabledAxes.length > 0 &&\n h(\n 'div',\n {\n style: { ...axisIndicatorStyle, opacity: 0.5 },\n 'data-testid': 'design-tokens-panel-disabled-axes-indicator',\n },\n disabledIndicatorText,\n ),\n h('input', {\n style: searchInputStyle,\n type: 'search',\n placeholder: `Search ${tokenCount} tokens in ${themeName}…`,\n 'aria-label': 'Search tokens',\n value: query,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => setQuery(e.target.value),\n }),\n ),\n h(DiagnosticsSection, { diagnostics: payload.diagnostics }),\n h(\n ScrollArea,\n { vertical: true },\n filtered.length === 0\n ? h(Placeholder, null, query ? 'No tokens match this filter.' : 'No tokens in this theme.')\n : h(\n 'div',\n { style: treeWrapperStyle },\n h(\n 'ul',\n { style: treeUlStyle, role: 'tree' },\n filtered.map((node) =>\n h(TreeRow, {\n key: node.path || node.segment,\n node,\n expanded: displayExpanded,\n onToggle: toggle,\n onLeafClick: handleLeafClick,\n prefix,\n }),\n ),\n ),\n ),\n ),\n );\n}\n\ninterface TreeRowProps {\n node: TreeNode;\n expanded: Set<string>;\n onToggle(path: string): void;\n onLeafClick(path: string): void;\n prefix: string;\n}\n\nfunction TreeRow({ node, expanded, onToggle, onLeafClick, prefix }: TreeRowProps): ReactElement {\n if (node.kind === 'leaf') {\n return h(LeafRow, { node, onLeafClick, prefix });\n }\n const isOpen = expanded.has(node.path);\n const onKey = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onToggle(node.path);\n }\n };\n return h(\n 'li',\n { role: 'treeitem', 'aria-expanded': isOpen },\n h(\n 'div',\n {\n role: 'button',\n tabIndex: 0,\n style: groupRowStyle,\n onClick: () => onToggle(node.path),\n onKeyDown: onKey,\n 'data-path': node.path,\n 'data-testid': 'design-tokens-panel-group',\n },\n h('span', { style: caretStyle, 'aria-hidden': true }, isOpen ? '▾' : '▸'),\n h('span', null, node.segment),\n h('span', { style: countStyle }, countLeaves(node)),\n ),\n isOpen &&\n h(\n 'ul',\n { style: nestedUlStyle, role: 'group' },\n node.children.map((c) =>\n h(TreeRow, {\n key: c.path || c.segment,\n node: c,\n expanded,\n onToggle,\n onLeafClick,\n prefix,\n }),\n ),\n ),\n );\n}\n\ninterface LeafRowProps {\n node: LeafNode;\n onLeafClick(path: string): void;\n prefix: string;\n}\n\nfunction LeafRow({ node, onLeafClick, prefix }: LeafRowProps): ReactElement {\n const type = node.token.$type ?? '';\n const value = node.token.$value;\n const displayValue = formatValue(value);\n const isColor = type === 'color' && typeof value === 'object' && value !== null;\n const colorPreview =\n isColor && typeof (value as Record<string, unknown>)['hex'] === 'string'\n ? ((value as Record<string, unknown>)['hex'] as string)\n : null;\n\n const varName = makeCssVarName(node.path, prefix);\n\n return h(\n 'li',\n { role: 'treeitem' },\n h(\n 'button',\n {\n type: 'button',\n style: leafRowStyle,\n onClick: () => onLeafClick(node.path),\n title: `Click to copy var(${varName})`,\n 'data-path': node.path,\n 'data-testid': 'design-tokens-panel-leaf',\n },\n h('span', { style: caretStyle, 'aria-hidden': true }, '•'),\n h('span', null, node.segment),\n type && h('span', { style: typePillStyle }, type),\n h('span', { style: valueStyle }, displayValue),\n colorPreview &&\n h('span', {\n style: { ...swatchStyle, background: colorPreview },\n 'aria-hidden': true,\n }),\n ),\n );\n}\n\nconst severityStyle: Record<DiagnosticSeverity, CSSProperties> = {\n error: { color: '#d64545' },\n warn: { color: '#b08900' },\n info: { opacity: 0.6 },\n};\n\nconst severityLabel: Record<DiagnosticSeverity, string> = {\n error: 'ERROR',\n warn: 'WARN',\n info: 'INFO',\n};\n\nconst diagnosticsSectionStyle: CSSProperties = {\n borderBottom: '1px solid rgba(128,128,128,0.2)',\n};\n\nconst diagnosticsSummaryStyle: CSSProperties = {\n padding: '10px 12px',\n fontSize: 12,\n cursor: 'pointer',\n userSelect: 'none',\n listStyle: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n};\n\nconst diagnosticRowStyle: CSSProperties = {\n display: 'grid',\n gridTemplateColumns: '60px 1fr',\n gap: 12,\n padding: '8px 12px',\n fontSize: 12,\n borderTop: '1px solid rgba(128,128,128,0.12)',\n};\n\ninterface DiagnosticsSectionProps {\n diagnostics: readonly VirtualDiagnostic[];\n}\n\nfunction DiagnosticsSection({ diagnostics }: DiagnosticsSectionProps): ReactElement {\n const counts = diagnostics.reduce(\n (acc, d) => {\n acc[d.severity] = (acc[d.severity] ?? 0) + 1;\n return acc;\n },\n { error: 0, warn: 0, info: 0 } as Record<DiagnosticSeverity, number>,\n );\n\n const hasErrorsOrWarnings = counts.error > 0 || counts.warn > 0;\n\n const summaryText = (() => {\n if (diagnostics.length === 0) return '✔ OK · no diagnostics';\n const parts: string[] = [];\n if (counts.error > 0) parts.push(`✖ ${counts.error} error${counts.error === 1 ? '' : 's'}`);\n if (counts.warn > 0) parts.push(`⚠ ${counts.warn} warning${counts.warn === 1 ? '' : 's'}`);\n if (counts.info > 0) parts.push(`${counts.info} info`);\n return parts.join(' · ');\n })();\n\n const summaryColor = (() => {\n if (diagnostics.length === 0) return '#30a46c';\n if (counts.error > 0) return '#d64545';\n if (counts.warn > 0) return '#b08900';\n return 'inherit';\n })();\n\n return h(\n 'details',\n {\n style: diagnosticsSectionStyle,\n open: hasErrorsOrWarnings,\n 'data-testid': 'design-tokens-panel-diagnostics',\n },\n h(\n 'summary',\n { style: { ...diagnosticsSummaryStyle, color: summaryColor, fontWeight: 600 } },\n h('span', null, 'Diagnostics'),\n h('span', { style: { fontWeight: 400 } }, summaryText),\n ),\n diagnostics.length === 0\n ? null\n : h(\n 'div',\n null,\n diagnostics.map((d, i) =>\n h(\n 'div',\n { key: `${d.group}-${i}`, style: diagnosticRowStyle },\n h(\n 'span',\n { style: { ...severityStyle[d.severity], fontWeight: 600, fontSize: 10 } },\n severityLabel[d.severity],\n ),\n h(\n 'div',\n null,\n h('div', null, d.message),\n (d.filename || d.group) &&\n h(\n 'div',\n { style: { opacity: 0.5, fontSize: 10, marginTop: 4 } },\n [d.group, d.filename, d.line ? `:${d.line}` : ''].filter(Boolean).join(' · '),\n ),\n ),\n ),\n ),\n ),\n );\n}\n","import React, { useCallback, useEffect, useMemo, useRef, useState, type ReactElement } from 'react';\nimport { IconButton, WithTooltipPure } from 'storybook/internal/components';\nimport { addons, types, useGlobals, useStorybookApi } from 'storybook/manager-api';\nimport {\n ADDON_ID,\n AXES_GLOBAL_KEY,\n COLOR_FORMAT_GLOBAL_KEY,\n GLOBAL_KEY,\n INIT_EVENT,\n INIT_REQUEST_EVENT,\n PREVIEW_MOUSEDOWN_EVENT,\n PANEL_ID,\n TOOL_ID,\n} from '#/constants.ts';\nimport { DesignTokensPanel } from '#/panel.tsx';\n\n/**\n * Use explicit `React.createElement` rather than JSX so the manager bundle\n * doesn't take a hard dependency on `react/jsx-runtime`. Storybook's manager\n * page injects its own React as a runtime global; `react/jsx-runtime` isn't\n * always part of that exposure, which breaks JSX with\n * \"Cannot read properties of undefined (reading 'recentlyCreatedOwnerStacks')\".\n * Mirrors the pattern `@storybook/addon-a11y` uses in its manager.\n */\nconst h = React.createElement;\n\ninterface AxisEntry {\n name: string;\n contexts: readonly string[];\n default: string;\n description?: string;\n source: 'resolver' | 'synthetic';\n}\n\ninterface ThemeEntry {\n name: string;\n input: Record<string, string>;\n sources: string[];\n}\n\ninterface PresetEntry {\n name: string;\n axes: Partial<Record<string, string>>;\n description?: string;\n}\n\ninterface InitPayload {\n axes: readonly AxisEntry[];\n presets: readonly PresetEntry[];\n themes: ThemeEntry[];\n defaultTheme: string | null;\n}\n\nconst EMPTY_AXES: readonly AxisEntry[] = [];\nconst EMPTY_PRESETS: readonly PresetEntry[] = [];\nconst EMPTY_THEMES: ThemeEntry[] = [];\n\n/**\n * Root toolbar glyph — a split-circle (\"yinyang\") mark: a faint filled\n * disc for the full-swatch silhouette, with a darker half-and-inset-disc\n * path reading as a pair of theme variants swapped in place.\n */\nfunction SwatchbookIcon(): ReactElement {\n return h(\n 'svg',\n { width: 14, height: 14, viewBox: '0 0 14 14', 'aria-hidden': true },\n h('circle', { cx: 7, cy: 7, r: 6, fill: 'currentColor', opacity: 0.15 }),\n h('path', {\n d: 'M7 1a6 6 0 0 0 0 12 3 3 0 0 0 0-6 3 3 0 0 1 0-6Z',\n fill: 'currentColor',\n }),\n );\n}\n\nfunction tupleMatchesInput(\n tuple: Readonly<Record<string, string>>,\n input: Readonly<Record<string, string>>,\n): boolean {\n const keys = Object.keys(input);\n if (keys.length === 0) return false;\n return keys.every((k) => input[k] === tuple[k]);\n}\n\nfunction composedNameFor(\n tuple: Readonly<Record<string, string>>,\n themes: readonly ThemeEntry[],\n fallback: string,\n): string {\n const match = themes.find((t) => tupleMatchesInput(tuple, t.input));\n return match?.name ?? fallback;\n}\n\nfunction defaultTupleFor(axes: readonly AxisEntry[]): Record<string, string> {\n const out: Record<string, string> = {};\n for (const axis of axes) out[axis.name] = axis.default;\n return out;\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 uses the label \"Theme\" instead of the axis name. Authored single-axis\n * resolvers keep their real axis name (e.g. `mode`).\n */\nfunction displayLabelFor(axis: AxisEntry): string {\n if (axis.source === 'synthetic' && axis.name === 'theme') return 'Theme';\n return axis.name;\n}\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). Matches the preview decorator's own fallback\n * logic so what the toolbar sends out is what the decorator honors.\n */\nfunction presetTuple(\n preset: PresetEntry,\n axes: readonly AxisEntry[],\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\nfunction tuplesEqual(\n a: Readonly<Record<string, string>>,\n b: Readonly<Record<string, string>>,\n axes: readonly AxisEntry[],\n): boolean {\n for (const axis of axes) {\n if (a[axis.name] !== b[axis.name]) return false;\n }\n return true;\n}\n\nconst SECTION_LABEL_STYLE: React.CSSProperties = {\n padding: '8px 12px 4px',\n fontSize: 11,\n textTransform: 'uppercase',\n letterSpacing: 0.5,\n opacity: 0.6,\n};\n\nconst SECTION_BODY_STYLE: React.CSSProperties = {\n padding: '0 12px 10px',\n display: 'flex',\n flexWrap: 'wrap',\n gap: 4,\n};\n\nconst AXIS_ROW_STYLE: React.CSSProperties = {\n padding: '0 12px 10px',\n display: 'grid',\n gridTemplateColumns: 'max-content 1fr',\n columnGap: 12,\n rowGap: 4,\n alignItems: 'center',\n};\n\nconst AXIS_LABEL_STYLE: React.CSSProperties = {\n fontSize: 12,\n fontWeight: 600,\n opacity: 0.85,\n};\n\nconst AXIS_PILLS_STYLE: React.CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n gap: 4,\n};\n\nconst OPTION_PILL_BASE: React.CSSProperties = {\n padding: '3px 8px',\n borderRadius: 4,\n fontSize: 12,\n lineHeight: '18px',\n // Longhand border properties (not the `border` shorthand) so\n // active → inactive only updates `borderColor`'s value instead of\n // *removing* the key from inline style. Removing it lets Storybook's\n // theme paint a stray border-color on the previously-selected pill.\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'transparent',\n background: 'transparent',\n cursor: 'pointer',\n color: 'inherit',\n outline: 'none',\n boxShadow: 'none',\n};\n\nconst OPTION_PILL_ACTIVE: React.CSSProperties = {\n ...OPTION_PILL_BASE,\n fontWeight: 600,\n background: 'rgba(0, 122, 255, 0.12)',\n borderColor: 'rgba(0, 122, 255, 0.45)',\n};\n\nconst PRESET_PILL_MODIFIED: React.CSSProperties = {\n display: 'inline-block',\n width: 6,\n height: 6,\n marginLeft: 6,\n borderRadius: '50%',\n background: 'currentColor',\n opacity: 0.6,\n verticalAlign: 'middle',\n};\n\nconst DIVIDER_STYLE: React.CSSProperties = {\n height: 1,\n background: 'currentColor',\n opacity: 0.1,\n margin: '2px 8px',\n};\n\ninterface OptionPillProps {\n label: string;\n active: boolean;\n title?: string;\n onClick: () => void;\n trailing?: ReactElement | null;\n}\n\nfunction OptionPill({ label, active, title, onClick, trailing }: OptionPillProps): ReactElement {\n return h(\n 'button',\n {\n type: 'button',\n title,\n onClick,\n // Skip focus on mouse click so Storybook's `:focus` border-color\n // theming doesn't stick on the previously-clicked pill. Keyboard\n // tabbing still lands focus normally — preventDefault on mousedown\n // only blocks the implicit focus-on-click behavior.\n onMouseDown: (event) => event.preventDefault(),\n style: active ? OPTION_PILL_ACTIVE : OPTION_PILL_BASE,\n },\n label,\n trailing ?? null,\n );\n}\n\ninterface PresetsSectionProps {\n presets: readonly PresetEntry[];\n axes: readonly AxisEntry[];\n defaults: Readonly<Record<string, string>>;\n activeTuple: Readonly<Record<string, string>>;\n lastApplied: string | null;\n onApply: (preset: PresetEntry) => void;\n}\n\nfunction PresetsSection({\n presets,\n axes,\n defaults,\n activeTuple,\n lastApplied,\n onApply,\n}: PresetsSectionProps): ReactElement {\n return h(\n 'div',\n null,\n h('div', { style: SECTION_LABEL_STYLE }, 'Presets'),\n h(\n 'div',\n { style: SECTION_BODY_STYLE },\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 h(OptionPill, {\n key: `${TOOL_ID}/preset/${preset.name}`,\n label: preset.name,\n active: matches,\n title,\n onClick: () => onApply(preset),\n trailing: modified\n ? h('span', { 'aria-hidden': true, style: PRESET_PILL_MODIFIED })\n : null,\n });\n }),\n ),\n );\n}\n\ninterface AxisSectionProps {\n axis: AxisEntry;\n active: string;\n onSelect: (next: string) => void;\n}\n\nfunction AxisSection({ axis, active, onSelect }: AxisSectionProps): ReactElement {\n const label = displayLabelFor(axis);\n return h(\n 'div',\n { style: AXIS_ROW_STYLE },\n h('div', { style: AXIS_LABEL_STYLE, title: axis.description }, label),\n h(\n 'div',\n { style: AXIS_PILLS_STYLE },\n ...axis.contexts.map((ctx) =>\n h(OptionPill, {\n key: `${TOOL_ID}/${axis.name}/${ctx}`,\n label: ctx,\n active: ctx === active,\n onClick: () => onSelect(ctx),\n }),\n ),\n ),\n );\n}\n\nconst COLOR_FORMAT_OPTIONS: readonly { id: string; label: string }[] = [\n { id: 'hex', label: 'Hex' },\n { id: 'rgb', label: 'RGB' },\n { id: 'hsl', label: 'HSL' },\n { id: 'oklch', label: 'OKLCH' },\n { id: 'raw', label: 'Raw (JSON)' },\n];\n\ninterface ColorFormatSectionProps {\n active: string;\n onSelect: (next: string) => void;\n}\n\nfunction ColorFormatSection({ active, onSelect }: ColorFormatSectionProps): ReactElement {\n return h(\n 'div',\n null,\n h('div', { style: SECTION_LABEL_STYLE }, 'Color format'),\n h(\n 'div',\n { style: SECTION_BODY_STYLE },\n ...COLOR_FORMAT_OPTIONS.map((opt) =>\n h(OptionPill, {\n key: `${TOOL_ID}/color-format/${opt.id}`,\n label: opt.label,\n active: opt.id === active,\n onClick: () => onSelect(opt.id),\n }),\n ),\n ),\n );\n}\n\ninterface PopoverBodyProps {\n axes: readonly AxisEntry[];\n presets: readonly PresetEntry[];\n defaults: Readonly<Record<string, string>>;\n activeTuple: Readonly<Record<string, string>>;\n activeColorFormat: string;\n lastApplied: string | null;\n onApplyPreset: (preset: PresetEntry) => void;\n onSelectAxis: (axisName: string, next: string) => void;\n onSelectColorFormat: (next: string) => void;\n onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;\n}\n\nfunction PopoverBody(props: PopoverBodyProps): ReactElement {\n const {\n axes,\n presets,\n defaults,\n activeTuple,\n activeColorFormat,\n lastApplied,\n onApplyPreset,\n onSelectAxis,\n onSelectColorFormat,\n onKeyDown,\n } = props;\n const sections: ReactElement[] = [];\n if (presets.length > 0) {\n sections.push(\n h(PresetsSection, {\n key: 'presets',\n presets,\n axes,\n defaults,\n activeTuple,\n lastApplied,\n onApply: onApplyPreset,\n }),\n h('div', { key: 'presets-divider', style: DIVIDER_STYLE }),\n );\n }\n axes.forEach((axis, idx) => {\n sections.push(\n h(AxisSection, {\n key: `axis-${axis.name}`,\n axis,\n active: activeTuple[axis.name] ?? axis.default,\n onSelect: (next) => onSelectAxis(axis.name, next),\n }),\n );\n if (idx === axes.length - 1) {\n sections.push(h('div', { key: 'axes-divider', style: DIVIDER_STYLE }));\n }\n });\n sections.push(\n h(ColorFormatSection, {\n key: 'color-format',\n active: activeColorFormat,\n onSelect: onSelectColorFormat,\n }),\n );\n return h(\n 'div',\n {\n role: 'menu',\n tabIndex: -1,\n onKeyDown,\n style: { minWidth: 260, padding: '4px 0', outline: 'none' },\n 'data-testid': 'swatchbook-toolbar-popover',\n },\n ...sections,\n );\n}\n\nfunction AxesToolbar(): ReactElement {\n const [globals, updateGlobals] = useGlobals();\n const api = useStorybookApi();\n const [payload, setPayload] = useState<InitPayload | null>(null);\n const [open, setOpen] = useState(false);\n const bodyRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n const channel = addons.getChannel();\n const onInit = (next: InitPayload): void => setPayload(next);\n channel.on(INIT_EVENT, onInit);\n /**\n * Ask the preview to (re-)emit INIT_EVENT in case it already broadcast\n * before this effect subscribed. Without this request, a late-mounting\n * manager (story navigation, docs reload) can stay in \"loading…\" until\n * the user triggers a globals change.\n */\n channel.emit(INIT_REQUEST_EVENT);\n return () => {\n channel.off(INIT_EVENT, onInit);\n };\n }, []);\n\n const axes = payload?.axes ?? EMPTY_AXES;\n const presets = payload?.presets ?? EMPTY_PRESETS;\n const themes = payload?.themes ?? EMPTY_THEMES;\n const defaults = useMemo(() => defaultTupleFor(axes), [axes]);\n const [lastApplied, setLastApplied] = useState<string | null>(null);\n const globalTuple = globals[AXES_GLOBAL_KEY] as Record<string, string> | undefined;\n const activeColorFormat = (globals[COLOR_FORMAT_GLOBAL_KEY] as string | undefined) ?? 'hex';\n\n const activeTuple = useMemo<Record<string, string>>(() => {\n const out: Record<string, string> = { ...defaults };\n if (globalTuple) {\n for (const axis of axes) {\n const candidate = globalTuple[axis.name];\n if (candidate !== undefined && axis.contexts.includes(candidate)) {\n out[axis.name] = candidate;\n }\n }\n }\n return out;\n }, [axes, defaults, globalTuple]);\n\n const setAxis = useCallback(\n (axisName: string, next: string): void => {\n const tuple: Record<string, string> = { ...activeTuple, [axisName]: next };\n const fallback = payload?.defaultTheme ?? themes[0]?.name ?? '';\n const composed = composedNameFor(tuple, themes, fallback);\n updateGlobals({ [AXES_GLOBAL_KEY]: tuple, [GLOBAL_KEY]: composed });\n },\n [activeTuple, themes, payload?.defaultTheme, updateGlobals],\n );\n\n const applyPreset = useCallback(\n (preset: PresetEntry): void => {\n const tuple = presetTuple(preset, axes, defaults);\n const fallback = payload?.defaultTheme ?? themes[0]?.name ?? '';\n const composed = composedNameFor(tuple, themes, fallback);\n updateGlobals({ [AXES_GLOBAL_KEY]: tuple, [GLOBAL_KEY]: composed });\n setLastApplied(preset.name);\n },\n [axes, defaults, themes, payload?.defaultTheme, updateGlobals],\n );\n\n useEffect(() => {\n if (axes.length === 0) return;\n /**\n * alt+T cycles the primary (first) axis's contexts, keeping the rest\n * of the tuple pinned. With multi-axis projects this makes the shortcut\n * predictable — you always know which wheel you're spinning. For\n * single-axis projects the behavior is identical to the pre-N-dropdown\n * toolbar (cycle through every theme).\n */\n const primary = axes[0];\n if (!primary) return;\n api.setAddonShortcut(ADDON_ID, {\n label: `Cycle swatchbook ${displayLabelFor(primary)}`,\n defaultShortcut: ['alt', 'T'],\n actionName: 'cycleAxis',\n showInMenu: true,\n action: () => {\n const current = activeTuple[primary.name] ?? primary.default;\n const idx = primary.contexts.indexOf(current);\n const next = primary.contexts[(idx + 1) % primary.contexts.length];\n if (next !== undefined) setAxis(primary.name, next);\n },\n });\n }, [api, axes, activeTuple, setAxis]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === 'Escape') {\n event.stopPropagation();\n setOpen(false);\n }\n }, []);\n\n /**\n * Escape closes even when focus hasn't entered the popover yet (e.g. the\n * user opened it via click and the mouse is still over the canvas). We\n * attach a document-level listener when open.\n */\n useEffect(() => {\n if (!open) return;\n const onDocKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('keydown', onDocKey);\n return () => document.removeEventListener('keydown', onDocKey);\n }, [open]);\n\n /**\n * `WithTooltipPure`'s built-in `closeOnOutsideClick` misses some cases\n * (portaled popover + manager iframe boundaries). Belt-and-suspenders:\n * close when the user mouses down anywhere that isn't the trigger wrapper\n * or the popover body.\n */\n useEffect(() => {\n if (!open) return;\n const onDocMouseDown = (e: MouseEvent): void => {\n const target = e.target;\n if (!(target instanceof Element)) return;\n if (bodyRef.current?.contains(target)) return;\n if (target.closest('[data-testid=\"swatchbook-toolbar-popover\"]')) return;\n setOpen(false);\n };\n /**\n * The manager's document-level listener above can't see mousedowns\n * inside the preview iframe. Preview emits PREVIEW_MOUSEDOWN_EVENT on\n * every mousedown over its own document; listen for it here so\n * clicking the canvas / docs page also closes the popover.\n */\n const channel = addons.getChannel();\n const onPreviewMouseDown = (): void => setOpen(false);\n document.addEventListener('mousedown', onDocMouseDown);\n channel.on(PREVIEW_MOUSEDOWN_EVENT, onPreviewMouseDown);\n return () => {\n document.removeEventListener('mousedown', onDocMouseDown);\n channel.off(PREVIEW_MOUSEDOWN_EVENT, onPreviewMouseDown);\n };\n }, [open]);\n\n if (axes.length === 0) {\n return h(\n IconButton,\n { key: TOOL_ID, title: 'Swatchbook theme (loading…)', disabled: true },\n h(SwatchbookIcon),\n );\n }\n\n const summary = axes.map((a) => activeTuple[a.name] ?? a.default).join(' · ');\n const title = `Swatchbook · ${summary}`;\n\n const button = h(\n IconButton,\n {\n key: TOOL_ID,\n title,\n active: open,\n onClick: () => setOpen((prev) => !prev),\n },\n h(SwatchbookIcon),\n );\n\n const tooltipBody = h(PopoverBody, {\n axes,\n presets,\n defaults,\n activeTuple,\n activeColorFormat,\n lastApplied,\n onApplyPreset: applyPreset,\n onSelectAxis: setAxis,\n onSelectColorFormat: (next: string) => updateGlobals({ [COLOR_FORMAT_GLOBAL_KEY]: next }),\n onKeyDown: handleKeyDown,\n });\n\n return h(\n 'span',\n { ref: bodyRef, style: { display: 'inline-flex', alignItems: 'center' } },\n h(WithTooltipPure, {\n placement: 'bottom',\n trigger: 'click',\n visible: open,\n onVisibleChange: (next: boolean) => setOpen(next),\n closeOnOutsideClick: true,\n tooltip: tooltipBody,\n children: button,\n }),\n );\n}\n\naddons.register(ADDON_ID, () => {\n addons.add(TOOL_ID, {\n type: types.TOOL,\n title: 'Swatchbook theme',\n match: ({ viewMode, tabId }) => !tabId && (viewMode === 'story' || viewMode === 'docs'),\n render: () => h(AxesToolbar),\n });\n\n addons.add(PANEL_ID, {\n type: types.PANEL,\n title: 'Design Tokens',\n match: ({ viewMode }) => viewMode === 'story',\n render: ({ active }) => h(DesignTokensPanel, { active: !!active }),\n });\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"manager.mjs","names":["h"],"sources":["../src/panel.tsx","../src/manager.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactElement,\n} from 'react';\nimport { Placeholder, ScrollArea } from 'storybook/internal/components';\nimport { addons, useGlobals } from 'storybook/manager-api';\nimport { AXES_GLOBAL_KEY, GLOBAL_KEY, INIT_EVENT } from '#/constants.ts';\n\n/** `React.createElement` alias so the manager bundle avoids `react/jsx-runtime`. */\nconst h = React.createElement;\n\ninterface VirtualToken {\n $type?: string;\n $value?: unknown;\n $description?: string;\n}\n\ninterface VirtualTheme {\n name: string;\n input: Record<string, string>;\n sources: string[];\n}\n\ninterface VirtualAxis {\n name: string;\n contexts: readonly string[];\n default: string;\n description?: string;\n source: 'resolver' | 'layered' | 'synthetic';\n}\n\ntype DiagnosticSeverity = 'error' | 'warn' | 'info';\n\ninterface VirtualDiagnostic {\n severity: DiagnosticSeverity;\n group: string;\n message: string;\n filename?: string;\n line?: number;\n column?: number;\n}\n\ninterface InitPayload {\n axes: VirtualAxis[];\n disabledAxes: readonly string[];\n themes: VirtualTheme[];\n defaultTheme: string | null;\n themesResolved: Record<string, Record<string, VirtualToken>>;\n diagnostics: VirtualDiagnostic[];\n cssVarPrefix: string;\n}\n\nfunction usePayload(): InitPayload | null {\n const [payload, setPayload] = useState<InitPayload | null>(null);\n useEffect(() => {\n const channel = addons.getChannel();\n const onInit = (next: InitPayload): void => setPayload(next);\n channel.on(INIT_EVENT, onInit);\n return () => {\n channel.off(INIT_EVENT, onInit);\n };\n }, []);\n return payload;\n}\n\nfunction makeCssVarName(path: string, prefix: string): string {\n const tail = path.replaceAll('.', '-');\n return prefix ? `--${prefix}-${tail}` : `--${tail}`;\n}\n\nasync function copy(text: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n /* clipboard access denied — no fallback, the user can read the var name from the row */\n }\n}\n\n/** Format a token `$value` into a short display string. */\nfunction formatValue(value: unknown): string {\n if (value == null) return '';\n if (typeof value === 'string' || typeof value === 'number') return String(value);\n if (typeof value === 'object') {\n const v = value as Record<string, unknown>;\n if (typeof v['hex'] === 'string') return v['hex'] as string;\n if ('value' in v && 'unit' in v) return `${String(v['value'])}${String(v['unit'])}`;\n return JSON.stringify(value).slice(0, 80);\n }\n return String(value);\n}\n\nconst containerStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n height: '100%',\n};\n\nconst headerStyle: CSSProperties = {\n padding: '8px 12px',\n borderBottom: '1px solid rgba(128,128,128,0.2)',\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n};\n\nconst axisIndicatorStyle: CSSProperties = {\n fontSize: 11,\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace',\n opacity: 0.7,\n};\n\nconst treeWrapperStyle: CSSProperties = {\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 12,\n padding: 8,\n};\n\nconst groupRowStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '4px 6px',\n borderRadius: 4,\n cursor: 'pointer',\n userSelect: 'none',\n outline: 'none',\n};\n\nconst leafRowStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '4px 6px',\n borderRadius: 4,\n cursor: 'pointer',\n border: 'none',\n background: 'transparent',\n color: 'inherit',\n width: '100%',\n textAlign: 'left',\n fontFamily: 'inherit',\n fontSize: 'inherit',\n outline: 'none',\n};\n\nconst caretStyle: CSSProperties = {\n display: 'inline-block',\n width: 12,\n textAlign: 'center',\n opacity: 0.6,\n};\n\nconst treeUlStyle: CSSProperties = { listStyle: 'none', margin: 0, padding: 0 };\n\nconst nestedUlStyle: CSSProperties = {\n listStyle: 'none',\n margin: 0,\n paddingLeft: 18,\n borderLeft: '1px solid rgba(128,128,128,0.2)',\n};\n\nconst typePillStyle: CSSProperties = {\n display: 'inline-block',\n padding: '1px 6px',\n borderRadius: 4,\n fontSize: 10,\n letterSpacing: 0.5,\n textTransform: 'uppercase',\n background: 'rgba(128,128,128,0.15)',\n};\n\nconst valueStyle: CSSProperties = {\n marginLeft: 'auto',\n opacity: 0.7,\n fontSize: 11,\n maxWidth: '40%',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\n\nconst countStyle: CSSProperties = {\n marginLeft: 'auto',\n fontSize: 11,\n opacity: 0.7,\n};\n\nconst swatchStyle: CSSProperties = {\n display: 'inline-block',\n width: 14,\n height: 14,\n borderRadius: 3,\n border: '1px solid rgba(128,128,128,0.3)',\n marginLeft: 8,\n};\n\nconst searchInputStyle: CSSProperties = {\n width: '100%',\n padding: '4px 8px',\n fontSize: 12,\n border: '1px solid rgba(128,128,128,0.3)',\n borderRadius: 4,\n background: 'transparent',\n color: 'inherit',\n};\n\ninterface LeafNode {\n kind: 'leaf';\n segment: string;\n path: string;\n token: VirtualToken;\n}\n\ninterface GroupNode {\n kind: 'group';\n segment: string;\n path: string;\n children: TreeNode[];\n}\n\ntype TreeNode = LeafNode | GroupNode;\n\nfunction buildTree(resolved: Record<string, VirtualToken>): TreeNode[] {\n const rootNode: GroupNode = { kind: 'group', segment: '', path: '', children: [] };\n for (const [path, token] of Object.entries(resolved)) {\n const segments = path.split('.');\n let node: GroupNode = rootNode;\n for (let i = 0; i < segments.length - 1; i += 1) {\n const seg = segments[i] as string;\n const prefix = segments.slice(0, i + 1).join('.');\n let child = node.children.find(\n (c): c is GroupNode => c.kind === 'group' && c.segment === seg,\n );\n if (!child) {\n child = { kind: 'group', segment: seg, path: prefix, children: [] };\n node.children.push(child);\n }\n node = child;\n }\n const leafSegment = segments[segments.length - 1] as string;\n node.children.push({ kind: 'leaf', segment: leafSegment, path, token });\n }\n sortTree(rootNode);\n return rootNode.children;\n}\n\nfunction sortTree(node: GroupNode): void {\n node.children.sort((a, b) => {\n if (a.kind !== b.kind) return a.kind === 'group' ? -1 : 1;\n return a.segment.localeCompare(b.segment);\n });\n for (const c of node.children) {\n if (c.kind === 'group') sortTree(c);\n }\n}\n\nfunction collectInitialExpanded(nodes: TreeNode[], remainingDepth: number, out: Set<string>): void {\n if (remainingDepth <= 0) return;\n for (const node of nodes) {\n if (node.kind !== 'group') continue;\n out.add(node.path);\n collectInitialExpanded(node.children, remainingDepth - 1, out);\n }\n}\n\nfunction countLeaves(node: TreeNode): number {\n if (node.kind === 'leaf') return 1;\n let n = 0;\n for (const c of node.children) n += countLeaves(c);\n return n;\n}\n\nfunction filterTree(nodes: TreeNode[], query: string): TreeNode[] {\n if (!query) return nodes;\n const out: TreeNode[] = [];\n for (const node of nodes) {\n if (node.kind === 'leaf') {\n if (node.path.toLowerCase().includes(query)) out.push(node);\n continue;\n }\n const filteredChildren = filterTree(node.children, query);\n if (filteredChildren.length > 0) {\n out.push({ ...node, children: filteredChildren });\n }\n }\n return out;\n}\n\nfunction collectAllGroupPaths(nodes: TreeNode[], out: Set<string>): void {\n for (const node of nodes) {\n if (node.kind === 'group') {\n out.add(node.path);\n collectAllGroupPaths(node.children, out);\n }\n }\n}\n\ninterface PanelProps {\n active: boolean;\n}\n\nexport function DesignTokensPanel({ active }: PanelProps): ReactElement | null {\n const payload = usePayload();\n const [globals] = useGlobals();\n const [query, setQuery] = useState('');\n\n const axes = useMemo(() => payload?.axes ?? [], [payload]);\n const themes = useMemo(() => payload?.themes ?? [], [payload]);\n const globalAxes = globals[AXES_GLOBAL_KEY] as Record<string, string> | undefined;\n const globalTheme = globals[GLOBAL_KEY] as string | undefined;\n\n const tuple: Record<string, string> = useMemo(() => {\n const out: Record<string, string> = {};\n for (const axis of axes) out[axis.name] = axis.default;\n if (globalAxes && typeof globalAxes === 'object') {\n for (const axis of axes) {\n const candidate = globalAxes[axis.name];\n if (candidate && axis.contexts.includes(candidate)) out[axis.name] = candidate;\n }\n return out;\n }\n if (globalTheme) {\n const match = themes.find((t) => t.name === globalTheme);\n if (match) {\n for (const axis of axes) {\n const candidate = match.input[axis.name];\n if (candidate && axis.contexts.includes(candidate)) out[axis.name] = candidate;\n }\n }\n }\n return out;\n }, [axes, themes, globalAxes, globalTheme]);\n\n const themeName = useMemo(() => {\n const match = themes.find((t) => {\n const input = t.input;\n return Object.keys(input).every((k) => input[k] === tuple[k]);\n });\n return match?.name ?? globalTheme ?? payload?.defaultTheme ?? '';\n }, [themes, tuple, globalTheme, payload]);\n\n const prefix = payload?.cssVarPrefix ?? '';\n const tokens = useMemo(() => payload?.themesResolved[themeName] ?? {}, [payload, themeName]);\n const tokenCount = Object.keys(tokens).length;\n\n const tree = useMemo(() => buildTree(tokens), [tokens]);\n const lowerQuery = query.toLowerCase();\n const filtered = useMemo(() => filterTree(tree, lowerQuery), [tree, lowerQuery]);\n\n const initialExpanded = useMemo(() => {\n const out = new Set<string>();\n collectInitialExpanded(tree, 1, out);\n return out;\n }, [tree]);\n\n const [expanded, setExpanded] = useState<Set<string>>(initialExpanded);\n useEffect(() => {\n setExpanded(initialExpanded);\n }, [initialExpanded]);\n\n // When searching, expand every matching group so hits are visible.\n const displayExpanded = useMemo(() => {\n if (!lowerQuery) return expanded;\n const all = new Set<string>();\n collectAllGroupPaths(filtered, all);\n return all;\n }, [expanded, filtered, lowerQuery]);\n\n const toggle = useCallback((path: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n }, []);\n\n const handleLeafClick = useCallback(\n (path: string) => {\n const varName = makeCssVarName(path, prefix);\n void copy(`var(${varName})`);\n },\n [prefix],\n );\n\n if (!active) return null;\n\n if (!payload) {\n return h(Placeholder, null, 'Waiting for swatchbook preview…');\n }\n\n const showAxisIndicator =\n axes.length > 1 || (axes.length === 1 && axes[0]?.source !== 'synthetic');\n const axisIndicatorText = axes\n .map((axis) => `${axis.name}: ${tuple[axis.name] ?? axis.default}`)\n .join(' · ');\n const disabledAxes = payload?.disabledAxes ?? [];\n const pinnedSample = themes[0]?.input ?? {};\n const disabledIndicatorText = disabledAxes\n .map((name) => `${name}: ${pinnedSample[name] ?? '?'} · pinned`)\n .join(' · ');\n\n return h(\n 'div',\n { style: containerStyle },\n h(\n 'div',\n { style: headerStyle },\n showAxisIndicator &&\n h(\n 'div',\n {\n style: axisIndicatorStyle,\n 'data-testid': 'design-tokens-panel-axis-indicator',\n },\n axisIndicatorText,\n ),\n disabledAxes.length > 0 &&\n h(\n 'div',\n {\n style: { ...axisIndicatorStyle, opacity: 0.5 },\n 'data-testid': 'design-tokens-panel-disabled-axes-indicator',\n },\n disabledIndicatorText,\n ),\n h('input', {\n style: searchInputStyle,\n type: 'search',\n placeholder: `Search ${tokenCount} tokens in ${themeName}…`,\n 'aria-label': 'Search tokens',\n value: query,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => setQuery(e.target.value),\n }),\n ),\n h(DiagnosticsSection, { diagnostics: payload.diagnostics }),\n h(\n ScrollArea,\n { vertical: true },\n filtered.length === 0\n ? h(Placeholder, null, query ? 'No tokens match this filter.' : 'No tokens in this theme.')\n : h(\n 'div',\n { style: treeWrapperStyle },\n h(\n 'ul',\n { style: treeUlStyle, role: 'tree' },\n filtered.map((node) =>\n h(TreeRow, {\n key: node.path || node.segment,\n node,\n expanded: displayExpanded,\n onToggle: toggle,\n onLeafClick: handleLeafClick,\n prefix,\n }),\n ),\n ),\n ),\n ),\n );\n}\n\ninterface TreeRowProps {\n node: TreeNode;\n expanded: Set<string>;\n onToggle(path: string): void;\n onLeafClick(path: string): void;\n prefix: string;\n}\n\nfunction TreeRow({ node, expanded, onToggle, onLeafClick, prefix }: TreeRowProps): ReactElement {\n if (node.kind === 'leaf') {\n return h(LeafRow, { node, onLeafClick, prefix });\n }\n const isOpen = expanded.has(node.path);\n const onKey = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onToggle(node.path);\n }\n };\n return h(\n 'li',\n { role: 'treeitem', 'aria-expanded': isOpen },\n h(\n 'div',\n {\n role: 'button',\n tabIndex: 0,\n style: groupRowStyle,\n onClick: () => onToggle(node.path),\n onKeyDown: onKey,\n 'data-path': node.path,\n 'data-testid': 'design-tokens-panel-group',\n },\n h('span', { style: caretStyle, 'aria-hidden': true }, isOpen ? '▾' : '▸'),\n h('span', null, node.segment),\n h('span', { style: countStyle }, countLeaves(node)),\n ),\n isOpen &&\n h(\n 'ul',\n { style: nestedUlStyle, role: 'group' },\n node.children.map((c) =>\n h(TreeRow, {\n key: c.path || c.segment,\n node: c,\n expanded,\n onToggle,\n onLeafClick,\n prefix,\n }),\n ),\n ),\n );\n}\n\ninterface LeafRowProps {\n node: LeafNode;\n onLeafClick(path: string): void;\n prefix: string;\n}\n\nfunction LeafRow({ node, onLeafClick, prefix }: LeafRowProps): ReactElement {\n const type = node.token.$type ?? '';\n const value = node.token.$value;\n const displayValue = formatValue(value);\n const isColor = type === 'color' && typeof value === 'object' && value !== null;\n const colorPreview =\n isColor && typeof (value as Record<string, unknown>)['hex'] === 'string'\n ? ((value as Record<string, unknown>)['hex'] as string)\n : null;\n\n const varName = makeCssVarName(node.path, prefix);\n\n return h(\n 'li',\n { role: 'treeitem' },\n h(\n 'button',\n {\n type: 'button',\n style: leafRowStyle,\n onClick: () => onLeafClick(node.path),\n title: `Click to copy var(${varName})`,\n 'data-path': node.path,\n 'data-testid': 'design-tokens-panel-leaf',\n },\n h('span', { style: caretStyle, 'aria-hidden': true }, '•'),\n h('span', null, node.segment),\n type && h('span', { style: typePillStyle }, type),\n h('span', { style: valueStyle }, displayValue),\n colorPreview &&\n h('span', {\n style: { ...swatchStyle, background: colorPreview },\n 'aria-hidden': true,\n }),\n ),\n );\n}\n\nconst severityStyle: Record<DiagnosticSeverity, CSSProperties> = {\n error: { color: '#d64545' },\n warn: { color: '#b08900' },\n info: { opacity: 0.6 },\n};\n\nconst severityLabel: Record<DiagnosticSeverity, string> = {\n error: 'ERROR',\n warn: 'WARN',\n info: 'INFO',\n};\n\nconst diagnosticsSectionStyle: CSSProperties = {\n borderBottom: '1px solid rgba(128,128,128,0.2)',\n};\n\nconst diagnosticsSummaryStyle: CSSProperties = {\n padding: '10px 12px',\n fontSize: 12,\n cursor: 'pointer',\n userSelect: 'none',\n listStyle: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n};\n\nconst diagnosticRowStyle: CSSProperties = {\n display: 'grid',\n gridTemplateColumns: '60px 1fr',\n gap: 12,\n padding: '8px 12px',\n fontSize: 12,\n borderTop: '1px solid rgba(128,128,128,0.12)',\n};\n\ninterface DiagnosticsSectionProps {\n diagnostics: readonly VirtualDiagnostic[];\n}\n\nfunction DiagnosticsSection({ diagnostics }: DiagnosticsSectionProps): ReactElement {\n const counts = diagnostics.reduce(\n (acc, d) => {\n acc[d.severity] = (acc[d.severity] ?? 0) + 1;\n return acc;\n },\n { error: 0, warn: 0, info: 0 } as Record<DiagnosticSeverity, number>,\n );\n\n const hasErrorsOrWarnings = counts.error > 0 || counts.warn > 0;\n\n const summaryText = (() => {\n if (diagnostics.length === 0) return '✔ OK · no diagnostics';\n const parts: string[] = [];\n if (counts.error > 0) parts.push(`✖ ${counts.error} error${counts.error === 1 ? '' : 's'}`);\n if (counts.warn > 0) parts.push(`⚠ ${counts.warn} warning${counts.warn === 1 ? '' : 's'}`);\n if (counts.info > 0) parts.push(`${counts.info} info`);\n return parts.join(' · ');\n })();\n\n const summaryColor = (() => {\n if (diagnostics.length === 0) return '#30a46c';\n if (counts.error > 0) return '#d64545';\n if (counts.warn > 0) return '#b08900';\n return 'inherit';\n })();\n\n return h(\n 'details',\n {\n style: diagnosticsSectionStyle,\n open: hasErrorsOrWarnings,\n 'data-testid': 'design-tokens-panel-diagnostics',\n },\n h(\n 'summary',\n { style: { ...diagnosticsSummaryStyle, color: summaryColor, fontWeight: 600 } },\n h('span', null, 'Diagnostics'),\n h('span', { style: { fontWeight: 400 } }, summaryText),\n ),\n diagnostics.length === 0\n ? null\n : h(\n 'div',\n null,\n diagnostics.map((d, i) =>\n h(\n 'div',\n { key: `${d.group}-${i}`, style: diagnosticRowStyle },\n h(\n 'span',\n { style: { ...severityStyle[d.severity], fontWeight: 600, fontSize: 10 } },\n severityLabel[d.severity],\n ),\n h(\n 'div',\n null,\n h('div', null, d.message),\n (d.filename || d.group) &&\n h(\n 'div',\n { style: { opacity: 0.5, fontSize: 10, marginTop: 4 } },\n [d.group, d.filename, d.line ? `:${d.line}` : ''].filter(Boolean).join(' · '),\n ),\n ),\n ),\n ),\n ),\n );\n}\n","import React, { useCallback, useEffect, useMemo, useRef, useState, type ReactElement } from 'react';\nimport { IconButton, WithTooltipPure } from 'storybook/internal/components';\nimport { addons, types, useGlobals, useStorybookApi } from 'storybook/manager-api';\nimport {\n ADDON_ID,\n AXES_GLOBAL_KEY,\n COLOR_FORMAT_GLOBAL_KEY,\n GLOBAL_KEY,\n INIT_EVENT,\n INIT_REQUEST_EVENT,\n PREVIEW_MOUSEDOWN_EVENT,\n PANEL_ID,\n TOOL_ID,\n} from '#/constants.ts';\nimport { DesignTokensPanel } from '#/panel.tsx';\n\n/**\n * Use explicit `React.createElement` rather than JSX so the manager bundle\n * doesn't take a hard dependency on `react/jsx-runtime`. Storybook's manager\n * page injects its own React as a runtime global; `react/jsx-runtime` isn't\n * always part of that exposure, which breaks JSX with\n * \"Cannot read properties of undefined (reading 'recentlyCreatedOwnerStacks')\".\n * Mirrors the pattern `@storybook/addon-a11y` uses in its manager.\n */\nconst h = React.createElement;\n\ninterface AxisEntry {\n name: string;\n contexts: readonly string[];\n default: string;\n description?: string;\n source: 'resolver' | 'layered' | 'synthetic';\n}\n\ninterface ThemeEntry {\n name: string;\n input: Record<string, string>;\n sources: string[];\n}\n\ninterface PresetEntry {\n name: string;\n axes: Partial<Record<string, string>>;\n description?: string;\n}\n\ninterface InitPayload {\n axes: readonly AxisEntry[];\n presets: readonly PresetEntry[];\n themes: ThemeEntry[];\n defaultTheme: string | null;\n}\n\nconst EMPTY_AXES: readonly AxisEntry[] = [];\nconst EMPTY_PRESETS: readonly PresetEntry[] = [];\nconst EMPTY_THEMES: ThemeEntry[] = [];\n\n/**\n * Root toolbar glyph — a split-circle (\"yinyang\") mark: a faint filled\n * disc for the full-swatch silhouette, with a darker half-and-inset-disc\n * path reading as a pair of theme variants swapped in place.\n */\nfunction SwatchbookIcon(): ReactElement {\n return h(\n 'svg',\n { width: 14, height: 14, viewBox: '0 0 14 14', 'aria-hidden': true },\n h('circle', { cx: 7, cy: 7, r: 6, fill: 'currentColor', opacity: 0.15 }),\n h('path', {\n d: 'M7 1a6 6 0 0 0 0 12 3 3 0 0 0 0-6 3 3 0 0 1 0-6Z',\n fill: 'currentColor',\n }),\n );\n}\n\nfunction tupleMatchesInput(\n tuple: Readonly<Record<string, string>>,\n input: Readonly<Record<string, string>>,\n): boolean {\n const keys = Object.keys(input);\n if (keys.length === 0) return false;\n return keys.every((k) => input[k] === tuple[k]);\n}\n\nfunction composedNameFor(\n tuple: Readonly<Record<string, string>>,\n themes: readonly ThemeEntry[],\n fallback: string,\n): string {\n const match = themes.find((t) => tupleMatchesInput(tuple, t.input));\n return match?.name ?? fallback;\n}\n\nfunction defaultTupleFor(axes: readonly AxisEntry[]): Record<string, string> {\n const out: Record<string, string> = {};\n for (const axis of axes) out[axis.name] = axis.default;\n return out;\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 uses the label \"Theme\" instead of the axis name. Authored single-axis\n * resolvers keep their real axis name (e.g. `mode`).\n */\nfunction displayLabelFor(axis: AxisEntry): string {\n if (axis.source === 'synthetic' && axis.name === 'theme') return 'Theme';\n return axis.name;\n}\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). Matches the preview decorator's own fallback\n * logic so what the toolbar sends out is what the decorator honors.\n */\nfunction presetTuple(\n preset: PresetEntry,\n axes: readonly AxisEntry[],\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\nfunction tuplesEqual(\n a: Readonly<Record<string, string>>,\n b: Readonly<Record<string, string>>,\n axes: readonly AxisEntry[],\n): boolean {\n for (const axis of axes) {\n if (a[axis.name] !== b[axis.name]) return false;\n }\n return true;\n}\n\nconst SECTION_LABEL_STYLE: React.CSSProperties = {\n padding: '8px 12px 4px',\n fontSize: 11,\n textTransform: 'uppercase',\n letterSpacing: 0.5,\n opacity: 0.6,\n};\n\nconst SECTION_BODY_STYLE: React.CSSProperties = {\n padding: '0 12px 10px',\n display: 'flex',\n flexWrap: 'wrap',\n gap: 4,\n};\n\nconst AXIS_ROW_STYLE: React.CSSProperties = {\n padding: '0 12px 10px',\n display: 'grid',\n gridTemplateColumns: 'max-content 1fr',\n columnGap: 12,\n rowGap: 4,\n alignItems: 'center',\n};\n\nconst AXIS_LABEL_STYLE: React.CSSProperties = {\n fontSize: 12,\n fontWeight: 600,\n opacity: 0.85,\n};\n\nconst AXIS_PILLS_STYLE: React.CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n gap: 4,\n};\n\nconst OPTION_PILL_BASE: React.CSSProperties = {\n padding: '3px 8px',\n borderRadius: 4,\n fontSize: 12,\n lineHeight: '18px',\n // Longhand border properties (not the `border` shorthand) so\n // active → inactive only updates `borderColor`'s value instead of\n // *removing* the key from inline style. Removing it lets Storybook's\n // theme paint a stray border-color on the previously-selected pill.\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'transparent',\n background: 'transparent',\n cursor: 'pointer',\n color: 'inherit',\n outline: 'none',\n boxShadow: 'none',\n};\n\nconst OPTION_PILL_ACTIVE: React.CSSProperties = {\n ...OPTION_PILL_BASE,\n fontWeight: 600,\n background: 'rgba(0, 122, 255, 0.12)',\n borderColor: 'rgba(0, 122, 255, 0.45)',\n};\n\nconst PRESET_PILL_MODIFIED: React.CSSProperties = {\n display: 'inline-block',\n width: 6,\n height: 6,\n marginLeft: 6,\n borderRadius: '50%',\n background: 'currentColor',\n opacity: 0.6,\n verticalAlign: 'middle',\n};\n\nconst DIVIDER_STYLE: React.CSSProperties = {\n height: 1,\n background: 'currentColor',\n opacity: 0.1,\n margin: '2px 8px',\n};\n\ninterface OptionPillProps {\n label: string;\n active: boolean;\n title?: string;\n onClick: () => void;\n trailing?: ReactElement | null;\n}\n\nfunction OptionPill({ label, active, title, onClick, trailing }: OptionPillProps): ReactElement {\n return h(\n 'button',\n {\n type: 'button',\n title,\n onClick,\n // Skip focus on mouse click so Storybook's `:focus` border-color\n // theming doesn't stick on the previously-clicked pill. Keyboard\n // tabbing still lands focus normally — preventDefault on mousedown\n // only blocks the implicit focus-on-click behavior.\n onMouseDown: (event) => event.preventDefault(),\n style: active ? OPTION_PILL_ACTIVE : OPTION_PILL_BASE,\n },\n label,\n trailing ?? null,\n );\n}\n\ninterface PresetsSectionProps {\n presets: readonly PresetEntry[];\n axes: readonly AxisEntry[];\n defaults: Readonly<Record<string, string>>;\n activeTuple: Readonly<Record<string, string>>;\n lastApplied: string | null;\n onApply: (preset: PresetEntry) => void;\n}\n\nfunction PresetsSection({\n presets,\n axes,\n defaults,\n activeTuple,\n lastApplied,\n onApply,\n}: PresetsSectionProps): ReactElement {\n return h(\n 'div',\n null,\n h('div', { style: SECTION_LABEL_STYLE }, 'Presets'),\n h(\n 'div',\n { style: SECTION_BODY_STYLE },\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 h(OptionPill, {\n key: `${TOOL_ID}/preset/${preset.name}`,\n label: preset.name,\n active: matches,\n title,\n onClick: () => onApply(preset),\n trailing: modified\n ? h('span', { 'aria-hidden': true, style: PRESET_PILL_MODIFIED })\n : null,\n });\n }),\n ),\n );\n}\n\ninterface AxisSectionProps {\n axis: AxisEntry;\n active: string;\n onSelect: (next: string) => void;\n}\n\nfunction AxisSection({ axis, active, onSelect }: AxisSectionProps): ReactElement {\n const label = displayLabelFor(axis);\n return h(\n 'div',\n { style: AXIS_ROW_STYLE },\n h('div', { style: AXIS_LABEL_STYLE, title: axis.description }, label),\n h(\n 'div',\n { style: AXIS_PILLS_STYLE },\n ...axis.contexts.map((ctx) =>\n h(OptionPill, {\n key: `${TOOL_ID}/${axis.name}/${ctx}`,\n label: ctx,\n active: ctx === active,\n onClick: () => onSelect(ctx),\n }),\n ),\n ),\n );\n}\n\nconst COLOR_FORMAT_OPTIONS: readonly { id: string; label: string }[] = [\n { id: 'hex', label: 'Hex' },\n { id: 'rgb', label: 'RGB' },\n { id: 'hsl', label: 'HSL' },\n { id: 'oklch', label: 'OKLCH' },\n { id: 'raw', label: 'Raw (JSON)' },\n];\n\ninterface ColorFormatSectionProps {\n active: string;\n onSelect: (next: string) => void;\n}\n\nfunction ColorFormatSection({ active, onSelect }: ColorFormatSectionProps): ReactElement {\n return h(\n 'div',\n null,\n h('div', { style: SECTION_LABEL_STYLE }, 'Color format'),\n h(\n 'div',\n { style: SECTION_BODY_STYLE },\n ...COLOR_FORMAT_OPTIONS.map((opt) =>\n h(OptionPill, {\n key: `${TOOL_ID}/color-format/${opt.id}`,\n label: opt.label,\n active: opt.id === active,\n onClick: () => onSelect(opt.id),\n }),\n ),\n ),\n );\n}\n\ninterface PopoverBodyProps {\n axes: readonly AxisEntry[];\n presets: readonly PresetEntry[];\n defaults: Readonly<Record<string, string>>;\n activeTuple: Readonly<Record<string, string>>;\n activeColorFormat: string;\n lastApplied: string | null;\n onApplyPreset: (preset: PresetEntry) => void;\n onSelectAxis: (axisName: string, next: string) => void;\n onSelectColorFormat: (next: string) => void;\n onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;\n}\n\nfunction PopoverBody(props: PopoverBodyProps): ReactElement {\n const {\n axes,\n presets,\n defaults,\n activeTuple,\n activeColorFormat,\n lastApplied,\n onApplyPreset,\n onSelectAxis,\n onSelectColorFormat,\n onKeyDown,\n } = props;\n const sections: ReactElement[] = [];\n if (presets.length > 0) {\n sections.push(\n h(PresetsSection, {\n key: 'presets',\n presets,\n axes,\n defaults,\n activeTuple,\n lastApplied,\n onApply: onApplyPreset,\n }),\n h('div', { key: 'presets-divider', style: DIVIDER_STYLE }),\n );\n }\n axes.forEach((axis, idx) => {\n sections.push(\n h(AxisSection, {\n key: `axis-${axis.name}`,\n axis,\n active: activeTuple[axis.name] ?? axis.default,\n onSelect: (next) => onSelectAxis(axis.name, next),\n }),\n );\n if (idx === axes.length - 1) {\n sections.push(h('div', { key: 'axes-divider', style: DIVIDER_STYLE }));\n }\n });\n sections.push(\n h(ColorFormatSection, {\n key: 'color-format',\n active: activeColorFormat,\n onSelect: onSelectColorFormat,\n }),\n );\n return h(\n 'div',\n {\n role: 'menu',\n tabIndex: -1,\n onKeyDown,\n style: { minWidth: 260, padding: '4px 0', outline: 'none' },\n 'data-testid': 'swatchbook-toolbar-popover',\n },\n ...sections,\n );\n}\n\nfunction AxesToolbar(): ReactElement {\n const [globals, updateGlobals] = useGlobals();\n const api = useStorybookApi();\n const [payload, setPayload] = useState<InitPayload | null>(null);\n const [open, setOpen] = useState(false);\n const bodyRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n const channel = addons.getChannel();\n const onInit = (next: InitPayload): void => setPayload(next);\n channel.on(INIT_EVENT, onInit);\n /**\n * Ask the preview to (re-)emit INIT_EVENT in case it already broadcast\n * before this effect subscribed. Without this request, a late-mounting\n * manager (story navigation, docs reload) can stay in \"loading…\" until\n * the user triggers a globals change.\n */\n channel.emit(INIT_REQUEST_EVENT);\n return () => {\n channel.off(INIT_EVENT, onInit);\n };\n }, []);\n\n const axes = payload?.axes ?? EMPTY_AXES;\n const presets = payload?.presets ?? EMPTY_PRESETS;\n const themes = payload?.themes ?? EMPTY_THEMES;\n const defaults = useMemo(() => defaultTupleFor(axes), [axes]);\n const [lastApplied, setLastApplied] = useState<string | null>(null);\n const globalTuple = globals[AXES_GLOBAL_KEY] as Record<string, string> | undefined;\n const activeColorFormat = (globals[COLOR_FORMAT_GLOBAL_KEY] as string | undefined) ?? 'hex';\n\n const activeTuple = useMemo<Record<string, string>>(() => {\n const out: Record<string, string> = { ...defaults };\n if (globalTuple) {\n for (const axis of axes) {\n const candidate = globalTuple[axis.name];\n if (candidate !== undefined && axis.contexts.includes(candidate)) {\n out[axis.name] = candidate;\n }\n }\n }\n return out;\n }, [axes, defaults, globalTuple]);\n\n const setAxis = useCallback(\n (axisName: string, next: string): void => {\n const tuple: Record<string, string> = { ...activeTuple, [axisName]: next };\n const fallback = payload?.defaultTheme ?? themes[0]?.name ?? '';\n const composed = composedNameFor(tuple, themes, fallback);\n updateGlobals({ [AXES_GLOBAL_KEY]: tuple, [GLOBAL_KEY]: composed });\n },\n [activeTuple, themes, payload?.defaultTheme, updateGlobals],\n );\n\n const applyPreset = useCallback(\n (preset: PresetEntry): void => {\n const tuple = presetTuple(preset, axes, defaults);\n const fallback = payload?.defaultTheme ?? themes[0]?.name ?? '';\n const composed = composedNameFor(tuple, themes, fallback);\n updateGlobals({ [AXES_GLOBAL_KEY]: tuple, [GLOBAL_KEY]: composed });\n setLastApplied(preset.name);\n },\n [axes, defaults, themes, payload?.defaultTheme, updateGlobals],\n );\n\n useEffect(() => {\n if (axes.length === 0) return;\n /**\n * alt+T cycles the primary (first) axis's contexts, keeping the rest\n * of the tuple pinned. With multi-axis projects this makes the shortcut\n * predictable — you always know which wheel you're spinning. For\n * single-axis projects the behavior is identical to the pre-N-dropdown\n * toolbar (cycle through every theme).\n */\n const primary = axes[0];\n if (!primary) return;\n api.setAddonShortcut(ADDON_ID, {\n label: `Cycle swatchbook ${displayLabelFor(primary)}`,\n defaultShortcut: ['alt', 'T'],\n actionName: 'cycleAxis',\n showInMenu: true,\n action: () => {\n const current = activeTuple[primary.name] ?? primary.default;\n const idx = primary.contexts.indexOf(current);\n const next = primary.contexts[(idx + 1) % primary.contexts.length];\n if (next !== undefined) setAxis(primary.name, next);\n },\n });\n }, [api, axes, activeTuple, setAxis]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === 'Escape') {\n event.stopPropagation();\n setOpen(false);\n }\n }, []);\n\n /**\n * Escape closes even when focus hasn't entered the popover yet (e.g. the\n * user opened it via click and the mouse is still over the canvas). We\n * attach a document-level listener when open.\n */\n useEffect(() => {\n if (!open) return;\n const onDocKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('keydown', onDocKey);\n return () => document.removeEventListener('keydown', onDocKey);\n }, [open]);\n\n /**\n * `WithTooltipPure`'s built-in `closeOnOutsideClick` misses some cases\n * (portaled popover + manager iframe boundaries). Belt-and-suspenders:\n * close when the user mouses down anywhere that isn't the trigger wrapper\n * or the popover body.\n */\n useEffect(() => {\n if (!open) return;\n const onDocMouseDown = (e: MouseEvent): void => {\n const target = e.target;\n if (!(target instanceof Element)) return;\n if (bodyRef.current?.contains(target)) return;\n if (target.closest('[data-testid=\"swatchbook-toolbar-popover\"]')) return;\n setOpen(false);\n };\n /**\n * The manager's document-level listener above can't see mousedowns\n * inside the preview iframe. Preview emits PREVIEW_MOUSEDOWN_EVENT on\n * every mousedown over its own document; listen for it here so\n * clicking the canvas / docs page also closes the popover.\n */\n const channel = addons.getChannel();\n const onPreviewMouseDown = (): void => setOpen(false);\n document.addEventListener('mousedown', onDocMouseDown);\n channel.on(PREVIEW_MOUSEDOWN_EVENT, onPreviewMouseDown);\n return () => {\n document.removeEventListener('mousedown', onDocMouseDown);\n channel.off(PREVIEW_MOUSEDOWN_EVENT, onPreviewMouseDown);\n };\n }, [open]);\n\n if (axes.length === 0) {\n return h(\n IconButton,\n { key: TOOL_ID, title: 'Swatchbook theme (loading…)', disabled: true },\n h(SwatchbookIcon),\n );\n }\n\n const summary = axes.map((a) => activeTuple[a.name] ?? a.default).join(' · ');\n const title = `Swatchbook · ${summary}`;\n\n const button = h(\n IconButton,\n {\n key: TOOL_ID,\n title,\n active: open,\n onClick: () => setOpen((prev) => !prev),\n },\n h(SwatchbookIcon),\n );\n\n const tooltipBody = h(PopoverBody, {\n axes,\n presets,\n defaults,\n activeTuple,\n activeColorFormat,\n lastApplied,\n onApplyPreset: applyPreset,\n onSelectAxis: setAxis,\n onSelectColorFormat: (next: string) => updateGlobals({ [COLOR_FORMAT_GLOBAL_KEY]: next }),\n onKeyDown: handleKeyDown,\n });\n\n return h(\n 'span',\n { ref: bodyRef, style: { display: 'inline-flex', alignItems: 'center' } },\n h(WithTooltipPure, {\n placement: 'bottom',\n trigger: 'click',\n visible: open,\n onVisibleChange: (next: boolean) => setOpen(next),\n closeOnOutsideClick: true,\n tooltip: tooltipBody,\n children: button,\n }),\n );\n}\n\naddons.register(ADDON_ID, () => {\n addons.add(TOOL_ID, {\n type: types.TOOL,\n title: 'Swatchbook theme',\n match: ({ viewMode, tabId }) => !tabId && (viewMode === 'story' || viewMode === 'docs'),\n render: () => h(AxesToolbar),\n });\n\n addons.add(PANEL_ID, {\n type: types.PANEL,\n title: 'Design Tokens',\n match: ({ viewMode }) => viewMode === 'story',\n render: ({ active }) => h(DesignTokensPanel, { active: !!active }),\n });\n});\n"],"mappings":"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"}
|
|
@@ -61,6 +61,32 @@ html, body {
|
|
|
61
61
|
if (style.textContent !== text) style.textContent = text;
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
+
* Apply `cb(axisName, value)` for every pinned (disabled) axis whose value
|
|
65
|
+
* is present on any surviving theme's `input`. Disabled axes don't appear
|
|
66
|
+
* in `virtualAxes`, but CSS may still reference their pinned value on
|
|
67
|
+
* compound selectors — every theme that survived filtering carries the
|
|
68
|
+
* same pinned context per disabled axis, so sampling any theme works.
|
|
69
|
+
*/
|
|
70
|
+
function forEachPinnedAxis(cb) {
|
|
71
|
+
const pinnedSample = themes[0]?.input;
|
|
72
|
+
if (!pinnedSample) return;
|
|
73
|
+
for (const name of disabledAxes) {
|
|
74
|
+
const value = pinnedSample[name];
|
|
75
|
+
if (value !== void 0) cb(name, value);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Pick the theme name for a tuple, falling back to `defaultTheme` and then
|
|
80
|
+
* the first theme. Returns empty string when the project has no themes so
|
|
81
|
+
* callers can omit the attr instead of writing a made-up context name.
|
|
82
|
+
*/
|
|
83
|
+
function matchThemeName(tuple) {
|
|
84
|
+
return themes.find((t) => {
|
|
85
|
+
const input = t.input;
|
|
86
|
+
return Object.keys(input).every((k) => input[k] === tuple[k]);
|
|
87
|
+
})?.name ?? defaultTheme ?? themes[0]?.name ?? "";
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
64
90
|
* Write the composed permutation ID to `data-<prefix>-theme` plus one
|
|
65
91
|
* `data-<prefix>-<axis>=<context>` per axis. Prefix follows `cssVarPrefix`
|
|
66
92
|
* so the attr namespace and the emitted-CSS selectors stay in lockstep;
|
|
@@ -69,24 +95,18 @@ html, body {
|
|
|
69
95
|
function setRootAxes(themeName, tuple) {
|
|
70
96
|
if (typeof document === "undefined") return;
|
|
71
97
|
const root = document.documentElement;
|
|
72
|
-
|
|
98
|
+
const themeAttr = dataAttr(cssVarPrefix, "theme");
|
|
99
|
+
if (themeName) root.setAttribute(themeAttr, themeName);
|
|
100
|
+
else root.removeAttribute(themeAttr);
|
|
73
101
|
for (const axis of axes) {
|
|
74
102
|
const attr = dataAttr(cssVarPrefix, axis.name);
|
|
75
103
|
const value = tuple[axis.name];
|
|
76
104
|
if (value === void 0) root.removeAttribute(attr);
|
|
77
105
|
else root.setAttribute(attr, value);
|
|
78
106
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
* from any surviving theme's `input` — every theme that survived filtering
|
|
83
|
-
* carries the same pinned context for each disabled axis.
|
|
84
|
-
*/
|
|
85
|
-
const pinnedSample = themes[0]?.input;
|
|
86
|
-
if (pinnedSample) for (const name of disabledAxes) {
|
|
87
|
-
const value = pinnedSample[name];
|
|
88
|
-
if (value !== void 0) root.setAttribute(dataAttr(cssVarPrefix, name), value);
|
|
89
|
-
}
|
|
107
|
+
forEachPinnedAxis((name, value) => {
|
|
108
|
+
root.setAttribute(dataAttr(cssVarPrefix, name), value);
|
|
109
|
+
});
|
|
90
110
|
}
|
|
91
111
|
/**
|
|
92
112
|
* Emit the full virtual-module payload to the manager over Storybook's
|
|
@@ -162,12 +182,7 @@ function resolveColorFormat(globals) {
|
|
|
162
182
|
const themedDecorator = (Story, context) => {
|
|
163
183
|
const tuple = useMemo(() => resolveTuple(context.globals, context.parameters), [context.globals, context.parameters]);
|
|
164
184
|
const colorFormat = useMemo(() => resolveColorFormat(context.globals), [context.globals]);
|
|
165
|
-
const themeName = useMemo(() =>
|
|
166
|
-
return themes.find((t) => {
|
|
167
|
-
const input = t.input;
|
|
168
|
-
return Object.keys(input).every((k) => input[k] === tuple[k]);
|
|
169
|
-
})?.name ?? defaultTheme ?? themes[0]?.name ?? "Light";
|
|
170
|
-
}, [tuple]);
|
|
185
|
+
const themeName = useMemo(() => matchThemeName(tuple), [tuple]);
|
|
171
186
|
useEffect(() => {
|
|
172
187
|
ensureStylesheet();
|
|
173
188
|
broadcastInit();
|
|
@@ -175,16 +190,15 @@ const themedDecorator = (Story, context) => {
|
|
|
175
190
|
useEffect(() => {
|
|
176
191
|
setRootAxes(themeName, tuple);
|
|
177
192
|
}, [themeName, tuple]);
|
|
178
|
-
const wrapperAttrs = {
|
|
193
|
+
const wrapperAttrs = {};
|
|
194
|
+
if (themeName) wrapperAttrs[dataAttr(cssVarPrefix, "theme")] = themeName;
|
|
179
195
|
for (const axis of axes) {
|
|
180
196
|
const value = tuple[axis.name];
|
|
181
197
|
if (value !== void 0) wrapperAttrs[dataAttr(cssVarPrefix, axis.name)] = value;
|
|
182
198
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (value !== void 0) wrapperAttrs[dataAttr(cssVarPrefix, name)] = value;
|
|
187
|
-
}
|
|
199
|
+
forEachPinnedAxis((name, value) => {
|
|
200
|
+
wrapperAttrs[dataAttr(cssVarPrefix, name)] = value;
|
|
201
|
+
});
|
|
188
202
|
const snapshot = useMemo(() => ({
|
|
189
203
|
axes,
|
|
190
204
|
disabledAxes,
|
|
@@ -243,7 +257,7 @@ function buildInitialAxes() {
|
|
|
243
257
|
return out;
|
|
244
258
|
}
|
|
245
259
|
const initialGlobals = {
|
|
246
|
-
[GLOBAL_KEY]: defaultTheme ?? themes[0]?.name ?? "
|
|
260
|
+
[GLOBAL_KEY]: defaultTheme ?? themes[0]?.name ?? "",
|
|
247
261
|
[AXES_GLOBAL_KEY]: buildInitialAxes(),
|
|
248
262
|
[COLOR_FORMAT_GLOBAL_KEY]: "hex"
|
|
249
263
|
};
|
|
@@ -280,10 +294,7 @@ function installGlobalAxisApplier() {
|
|
|
280
294
|
const apply = (globals) => {
|
|
281
295
|
ensureStylesheet();
|
|
282
296
|
const tuple = resolveTuple(globals, {});
|
|
283
|
-
setRootAxes(
|
|
284
|
-
const input = t.input;
|
|
285
|
-
return Object.keys(input).every((k) => input[k] === tuple[k]);
|
|
286
|
-
})?.name ?? defaultTheme ?? themes[0]?.name ?? "Light", tuple);
|
|
297
|
+
setRootAxes(matchThemeName(tuple), tuple);
|
|
287
298
|
};
|
|
288
299
|
const onGlobals = (payload) => {
|
|
289
300
|
if (payload.globals) apply(payload.globals);
|
|
@@ -311,4 +322,4 @@ installPreviewMouseDownBridge();
|
|
|
311
322
|
//#endregion
|
|
312
323
|
export { preview_exports as i, globalTypes as n, initialGlobals as r, decorators as t };
|
|
313
324
|
|
|
314
|
-
//# sourceMappingURL=preview-
|
|
325
|
+
//# sourceMappingURL=preview-AtzpsqFq.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-AtzpsqFq.mjs","names":["virtualDisabledAxes","virtualAxes","virtualPresets"],"sources":["../src/data-attr.ts","../src/preview.tsx"],"sourcesContent":["/**\n * Produce a prefixed `data-*` attribute name when `prefix` is set, bare\n * `data-<key>` otherwise. Duplicated from `@unpunnyfuns/swatchbook-core`'s\n * `dataAttr` so the preview bundle doesn't have to import from core —\n * core's top-level entry pulls in Node-only modules (`node:fs/promises`\n * via the loader) that throw when evaluated in the browser.\n */\nexport function dataAttr(prefix: string, key: string): string {\n return prefix ? `data-${prefix}-${key}` : `data-${key}`;\n}\n","import type { Decorator, Preview } from '@storybook/react-vite';\nimport { useEffect, useMemo } from 'react';\nimport { addons } from 'storybook/preview-api';\nimport { dataAttr } from '#/data-attr.ts';\nimport {\n axes as virtualAxes,\n css,\n cssVarPrefix,\n defaultTheme,\n diagnostics,\n disabledAxes as virtualDisabledAxes,\n presets as virtualPresets,\n themes,\n themesResolved,\n} from 'virtual:swatchbook/tokens';\nimport {\n AxesContext,\n COLOR_FORMATS,\n type ColorFormat,\n ColorFormatContext,\n type ProjectSnapshot,\n SwatchbookContext,\n ThemeContext,\n} from '@unpunnyfuns/swatchbook-blocks';\nimport {\n AXES_GLOBAL_KEY,\n COLOR_FORMAT_GLOBAL_KEY,\n GLOBAL_KEY,\n INIT_EVENT,\n INIT_REQUEST_EVENT,\n PREVIEW_MOUSEDOWN_EVENT,\n PARAM_KEY,\n STYLE_ELEMENT_ID,\n} from '#/constants.ts';\n\n/** CSS var name with the active prefix applied. */\nfunction v(name: string): string {\n return cssVarPrefix ? `--${cssVarPrefix}-${name}` : `--${name}`;\n}\n\n/**\n * Inject the per-theme stylesheet plus a tiny `html, body { ... }` block so\n * the iframe's own chrome (outside any decorator wrapper — Docs mode,\n * autodocs, empty gutters) also picks up the active theme.\n */\nfunction ensureStylesheet(): void {\n if (typeof document === 'undefined') return;\n const bodyRules = `\nhtml, body {\n background: var(${v('color-sys-surface-default')}, Canvas);\n color: var(${v('color-sys-text-default')}, CanvasText);\n margin: 0;\n}\n`;\n const text = `${css}\\n${bodyRules}`;\n let style = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!style) {\n style = document.createElement('style');\n style.id = STYLE_ELEMENT_ID;\n document.head.appendChild(style);\n }\n if (style.textContent !== text) style.textContent = text;\n}\n\n/**\n * Apply `cb(axisName, value)` for every pinned (disabled) axis whose value\n * is present on any surviving theme's `input`. Disabled axes don't appear\n * in `virtualAxes`, but CSS may still reference their pinned value on\n * compound selectors — every theme that survived filtering carries the\n * same pinned context per disabled axis, so sampling any theme works.\n */\nfunction forEachPinnedAxis(cb: (name: string, value: string) => void): void {\n const pinnedSample = themes[0]?.input;\n if (!pinnedSample) return;\n for (const name of virtualDisabledAxes) {\n const value = pinnedSample[name];\n if (value !== undefined) cb(name, value);\n }\n}\n\n/**\n * Pick the theme name for a tuple, falling back to `defaultTheme` and then\n * the first theme. Returns empty string when the project has no themes so\n * callers can omit the attr instead of writing a made-up context name.\n */\nfunction matchThemeName(tuple: Readonly<Record<string, string>>): string {\n const match = themes.find((t) => {\n const input = t.input as Record<string, string>;\n return Object.keys(input).every((k) => input[k] === tuple[k]);\n });\n return match?.name ?? defaultTheme ?? themes[0]?.name ?? '';\n}\n\n/**\n * Write the composed permutation ID to `data-<prefix>-theme` plus one\n * `data-<prefix>-<axis>=<context>` per axis. Prefix follows `cssVarPrefix`\n * so the attr namespace and the emitted-CSS selectors stay in lockstep;\n * empty prefix keeps the bare `data-theme` form.\n */\nfunction setRootAxes(themeName: string, tuple: Readonly<Record<string, string>>): void {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n const themeAttr = dataAttr(cssVarPrefix, 'theme');\n if (themeName) root.setAttribute(themeAttr, themeName);\n else root.removeAttribute(themeAttr);\n for (const axis of virtualAxes) {\n const attr = dataAttr(cssVarPrefix, axis.name);\n const value = tuple[axis.name];\n if (value === undefined) {\n root.removeAttribute(attr);\n } else {\n root.setAttribute(attr, value);\n }\n }\n forEachPinnedAxis((name, value) => {\n root.setAttribute(dataAttr(cssVarPrefix, name), value);\n });\n}\n\n/**\n * Emit the full virtual-module payload to the manager over Storybook's\n * channel so the toolbar + panel (which run in the manager bundle and\n * can't import our virtual module) can render from it.\n */\nfunction broadcastInit(): void {\n const channel = addons.getChannel();\n channel.emit(INIT_EVENT, {\n axes: virtualAxes,\n disabledAxes: virtualDisabledAxes,\n presets: virtualPresets,\n themes,\n defaultTheme,\n themesResolved,\n diagnostics,\n cssVarPrefix,\n });\n}\n\n/** Axis-default tuple, used as the baseline before overrides. */\nfunction defaultTuple(): Record<string, string> {\n const out: Record<string, string> = {};\n for (const axis of virtualAxes) out[axis.name] = axis.default;\n return out;\n}\n\n/** Look up a `Theme.input` by composed name. Returns `undefined` if no theme matches. */\nfunction tupleForName(name: string): Record<string, string> | undefined {\n const match = themes.find((t) => t.name === name);\n return match?.input;\n}\n\n/**\n * Merge a partial tuple onto the axis defaults, dropping keys for axes that\n * don't exist and silently falling back to the default for contexts that\n * aren't listed on the axis.\n */\nfunction normalizeTuple(partial: Readonly<Record<string, string>>): Record<string, string> {\n const out = defaultTuple();\n for (const axis of virtualAxes) {\n const candidate = partial[axis.name];\n if (candidate !== undefined && axis.contexts.includes(candidate)) {\n out[axis.name] = candidate;\n }\n }\n return out;\n}\n\n/**\n * Resolve the active tuple from all four input channels, in priority order:\n * 1. `parameters.swatchbook.axes` — per-story tuple.\n * 2. `parameters.swatchbook.theme` — per-story composed name (legacy).\n * 3. `globals.swatchbookAxes` — toolbar-set tuple.\n * 4. `globals.swatchbookTheme` — toolbar-set composed name.\n * 5. virtual module default.\n */\nfunction resolveTuple(\n globals: Record<string, unknown>,\n parameters: Record<string, Record<string, unknown>>,\n): Record<string, string> {\n const param = parameters[PARAM_KEY];\n const paramAxes = param?.['axes'];\n if (paramAxes && typeof paramAxes === 'object') {\n return normalizeTuple(paramAxes as Record<string, string>);\n }\n const paramTheme = param?.['theme'];\n if (typeof paramTheme === 'string') {\n const hit = tupleForName(paramTheme);\n if (hit) return normalizeTuple(hit);\n }\n const globalAxes = globals[AXES_GLOBAL_KEY];\n if (globalAxes && typeof globalAxes === 'object') {\n return normalizeTuple(globalAxes as Record<string, string>);\n }\n const globalTheme = globals[GLOBAL_KEY];\n if (typeof globalTheme === 'string') {\n const hit = tupleForName(globalTheme);\n if (hit) return normalizeTuple(hit);\n }\n return defaultTuple();\n}\n\nfunction resolveColorFormat(globals: Record<string, unknown>): ColorFormat {\n const raw = globals[COLOR_FORMAT_GLOBAL_KEY];\n if (typeof raw === 'string' && (COLOR_FORMATS as readonly string[]).includes(raw)) {\n return raw as ColorFormat;\n }\n return 'hex';\n}\n\nconst themedDecorator: Decorator = (Story, context) => {\n const tuple = useMemo(\n () =>\n resolveTuple(\n context.globals as Record<string, unknown>,\n context.parameters as Record<string, Record<string, unknown>>,\n ),\n [context.globals, context.parameters],\n );\n const colorFormat = useMemo(\n () => resolveColorFormat(context.globals as Record<string, unknown>),\n [context.globals],\n );\n const themeName = useMemo(() => matchThemeName(tuple), [tuple]);\n\n useEffect(() => {\n ensureStylesheet();\n broadcastInit();\n }, []);\n\n useEffect(() => {\n setRootAxes(themeName, tuple);\n }, [themeName, tuple]);\n\n const wrapperAttrs: Record<string, string> = {};\n if (themeName) wrapperAttrs[dataAttr(cssVarPrefix, 'theme')] = themeName;\n for (const axis of virtualAxes) {\n const value = tuple[axis.name];\n if (value !== undefined) wrapperAttrs[dataAttr(cssVarPrefix, axis.name)] = value;\n }\n forEachPinnedAxis((name, value) => {\n wrapperAttrs[dataAttr(cssVarPrefix, name)] = value;\n });\n\n const snapshot = useMemo<ProjectSnapshot>(\n () => ({\n axes: virtualAxes,\n disabledAxes: virtualDisabledAxes,\n presets: virtualPresets,\n themes,\n themesResolved,\n activeTheme: themeName,\n activeAxes: tuple,\n cssVarPrefix,\n diagnostics,\n css,\n }),\n [themeName, tuple],\n );\n\n return (\n <SwatchbookContext.Provider value={snapshot}>\n <ThemeContext.Provider value={themeName}>\n <AxesContext.Provider value={tuple}>\n <ColorFormatContext.Provider value={colorFormat}>\n <div\n {...wrapperAttrs}\n style={{\n padding: '1rem',\n minHeight: '100%',\n }}\n >\n <Story />\n </div>\n </ColorFormatContext.Provider>\n </AxesContext.Provider>\n </ThemeContext.Provider>\n </SwatchbookContext.Provider>\n );\n};\n\n/**\n * Named exports consumed by `definePreviewAddon(previewExports)` in the\n * addon's CSF Next factory (`src/index.ts`).\n */\nexport const decorators: NonNullable<Preview['decorators']> = [themedDecorator];\n\nexport const globalTypes: NonNullable<Preview['globalTypes']> = {\n [GLOBAL_KEY]: {\n name: 'Theme',\n description: 'Active swatchbook theme (composed permutation ID).',\n },\n [AXES_GLOBAL_KEY]: {\n name: 'Axes',\n description: 'Per-axis context selection. Takes precedence over the composed theme name.',\n },\n [COLOR_FORMAT_GLOBAL_KEY]: {\n name: 'Color format',\n description: 'Display format for color tokens in blocks. Emitted CSS is unaffected.',\n },\n};\n\nfunction buildInitialAxes(): Record<string, string> {\n const out: Record<string, string> = {};\n for (const axis of virtualAxes) out[axis.name] = axis.default;\n return out;\n}\n\nexport const initialGlobals: NonNullable<Preview['initialGlobals']> = {\n [GLOBAL_KEY]: defaultTheme ?? themes[0]?.name ?? '',\n [AXES_GLOBAL_KEY]: buildInitialAxes(),\n [COLOR_FORMAT_GLOBAL_KEY]: 'hex',\n};\n\n/**\n * Module-level channel subscription: writes the active tuple's attributes\n * onto `<html>` regardless of whether a story decorator is rendering.\n *\n * The {@link themedDecorator} already sets these inside story renders, but\n * it never runs on MDX docs pages that embed blocks without `<Story />`.\n * Without attrs on an ancestor, the per-tuple CSS selectors\n * (`[data-mode=\"Dark\"][data-brand=\"…\"]`) don't match and everything falls\n * back to the `:root` default tuple — so colors stay defaults even after\n * the toolbar switches axes. Subscribing globally fixes MDX docs at the\n * cost of one idempotent redundant write per story render.\n */\nfunction installGlobalAxisApplier(): void {\n if (typeof document === 'undefined') return;\n const channel = addons.getChannel();\n /**\n * Inject the stylesheet and emit the init payload once on module load so\n * the manager's toolbar populates and CSS vars are available even when no\n * story/decorator ever runs (bare MDX docs pages). Without these, the\n * toolbar sits in its disabled \"loading…\" state and nothing is styled.\n */\n ensureStylesheet();\n broadcastInit();\n /**\n * If the manager subscribes to INIT_EVENT after our initial broadcast,\n * it misses the payload and the toolbar stays in its \"loading…\" state\n * until something else re-fires it. Honor an explicit request event so\n * a late-mounting manager can ask for the payload.\n */\n channel.on(INIT_REQUEST_EVENT, broadcastInit);\n const apply = (globals: Record<string, unknown>): void => {\n ensureStylesheet();\n const tuple = resolveTuple(globals, {});\n setRootAxes(matchThemeName(tuple), tuple);\n };\n const onGlobals = (payload: { globals?: Record<string, unknown> }): void => {\n if (payload.globals) apply(payload.globals);\n };\n channel.on('globalsUpdated', onGlobals);\n channel.on('setGlobals', onGlobals);\n channel.on('updateGlobals', onGlobals);\n}\n\ninstallGlobalAxisApplier();\n\n/**\n * Bridge `mousedown` inside the preview iframe to the manager via a\n * dedicated channel event. The toolbar popover's outside-click listener\n * runs on the manager's document, which can't observe mousedowns inside\n * the preview; without this bridge, clicking the canvas leaves the\n * popover open. Idempotent: fires at most once per real mousedown.\n */\nfunction installPreviewMouseDownBridge(): void {\n if (typeof document === 'undefined') return;\n const channel = addons.getChannel();\n document.addEventListener('mousedown', () => {\n channel.emit(PREVIEW_MOUSEDOWN_EVENT);\n });\n}\n\ninstallPreviewMouseDownBridge();\n"],"mappings":"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"}
|
package/dist/preview.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as globalTypes, r as initialGlobals, t as decorators } from "./preview-
|
|
1
|
+
import { n as globalTypes, r as initialGlobals, t as decorators } from "./preview-AtzpsqFq.mjs";
|
|
2
2
|
export { decorators, globalTypes, initialGlobals };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unpunnyfuns/swatchbook-addon",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Storybook addon for DTCG design tokens — toolbar, panel, and useToken hook.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "unpunnyfuns <unpunnyfuns@gmail.com>",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"jiti": "^2.4.0",
|
|
72
|
-
"@unpunnyfuns/swatchbook-blocks": "0.2.
|
|
73
|
-
"@unpunnyfuns/swatchbook-core": "0.2.
|
|
72
|
+
"@unpunnyfuns/swatchbook-blocks": "0.2.1",
|
|
73
|
+
"@unpunnyfuns/swatchbook-core": "0.2.1"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"react": ">=18",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"preview-CBs7RXif.mjs","names":["virtualAxes","virtualDisabledAxes","virtualPresets"],"sources":["../src/data-attr.ts","../src/preview.tsx"],"sourcesContent":["/**\n * Produce a prefixed `data-*` attribute name when `prefix` is set, bare\n * `data-<key>` otherwise. Duplicated from `@unpunnyfuns/swatchbook-core`'s\n * `dataAttr` so the preview bundle doesn't have to import from core —\n * core's top-level entry pulls in Node-only modules (`node:fs/promises`\n * via the loader) that throw when evaluated in the browser.\n */\nexport function dataAttr(prefix: string, key: string): string {\n return prefix ? `data-${prefix}-${key}` : `data-${key}`;\n}\n","import type { Decorator, Preview } from '@storybook/react-vite';\nimport { useEffect, useMemo } from 'react';\nimport { addons } from 'storybook/preview-api';\nimport { dataAttr } from '#/data-attr.ts';\nimport {\n axes as virtualAxes,\n css,\n cssVarPrefix,\n defaultTheme,\n diagnostics,\n disabledAxes as virtualDisabledAxes,\n presets as virtualPresets,\n themes,\n themesResolved,\n} from 'virtual:swatchbook/tokens';\nimport {\n AxesContext,\n COLOR_FORMATS,\n type ColorFormat,\n ColorFormatContext,\n type ProjectSnapshot,\n SwatchbookContext,\n ThemeContext,\n} from '@unpunnyfuns/swatchbook-blocks';\nimport {\n AXES_GLOBAL_KEY,\n COLOR_FORMAT_GLOBAL_KEY,\n GLOBAL_KEY,\n INIT_EVENT,\n INIT_REQUEST_EVENT,\n PREVIEW_MOUSEDOWN_EVENT,\n PARAM_KEY,\n STYLE_ELEMENT_ID,\n} from '#/constants.ts';\n\n/** CSS var name with the active prefix applied. */\nfunction v(name: string): string {\n return cssVarPrefix ? `--${cssVarPrefix}-${name}` : `--${name}`;\n}\n\n/**\n * Inject the per-theme stylesheet plus a tiny `html, body { ... }` block so\n * the iframe's own chrome (outside any decorator wrapper — Docs mode,\n * autodocs, empty gutters) also picks up the active theme.\n */\nfunction ensureStylesheet(): void {\n if (typeof document === 'undefined') return;\n const bodyRules = `\nhtml, body {\n background: var(${v('color-sys-surface-default')}, Canvas);\n color: var(${v('color-sys-text-default')}, CanvasText);\n margin: 0;\n}\n`;\n const text = `${css}\\n${bodyRules}`;\n let style = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!style) {\n style = document.createElement('style');\n style.id = STYLE_ELEMENT_ID;\n document.head.appendChild(style);\n }\n if (style.textContent !== text) style.textContent = text;\n}\n\n/**\n * Write the composed permutation ID to `data-<prefix>-theme` plus one\n * `data-<prefix>-<axis>=<context>` per axis. Prefix follows `cssVarPrefix`\n * so the attr namespace and the emitted-CSS selectors stay in lockstep;\n * empty prefix keeps the bare `data-theme` form.\n */\nfunction setRootAxes(themeName: string, tuple: Readonly<Record<string, string>>): void {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n root.setAttribute(dataAttr(cssVarPrefix, 'theme'), themeName);\n for (const axis of virtualAxes) {\n const attr = dataAttr(cssVarPrefix, axis.name);\n const value = tuple[axis.name];\n if (value === undefined) {\n root.removeAttribute(attr);\n } else {\n root.setAttribute(attr, value);\n }\n }\n /**\n * Disabled axes aren't in `virtualAxes`, but CSS may still reference their\n * pinned value on compound selectors in extension code. Read the value\n * from any surviving theme's `input` — every theme that survived filtering\n * carries the same pinned context for each disabled axis.\n */\n const pinnedSample = themes[0]?.input;\n if (pinnedSample) {\n for (const name of virtualDisabledAxes) {\n const value = pinnedSample[name];\n if (value !== undefined) root.setAttribute(dataAttr(cssVarPrefix, name), value);\n }\n }\n}\n\n/**\n * Emit the full virtual-module payload to the manager over Storybook's\n * channel so the toolbar + panel (which run in the manager bundle and\n * can't import our virtual module) can render from it.\n */\nfunction broadcastInit(): void {\n const channel = addons.getChannel();\n channel.emit(INIT_EVENT, {\n axes: virtualAxes,\n disabledAxes: virtualDisabledAxes,\n presets: virtualPresets,\n themes,\n defaultTheme,\n themesResolved,\n diagnostics,\n cssVarPrefix,\n });\n}\n\n/** Axis-default tuple, used as the baseline before overrides. */\nfunction defaultTuple(): Record<string, string> {\n const out: Record<string, string> = {};\n for (const axis of virtualAxes) out[axis.name] = axis.default;\n return out;\n}\n\n/** Look up a `Theme.input` by composed name. Returns `undefined` if no theme matches. */\nfunction tupleForName(name: string): Record<string, string> | undefined {\n const match = themes.find((t) => t.name === name);\n return match?.input;\n}\n\n/**\n * Merge a partial tuple onto the axis defaults, dropping keys for axes that\n * don't exist and silently falling back to the default for contexts that\n * aren't listed on the axis.\n */\nfunction normalizeTuple(partial: Readonly<Record<string, string>>): Record<string, string> {\n const out = defaultTuple();\n for (const axis of virtualAxes) {\n const candidate = partial[axis.name];\n if (candidate !== undefined && axis.contexts.includes(candidate)) {\n out[axis.name] = candidate;\n }\n }\n return out;\n}\n\n/**\n * Resolve the active tuple from all four input channels, in priority order:\n * 1. `parameters.swatchbook.axes` — per-story tuple.\n * 2. `parameters.swatchbook.theme` — per-story composed name (legacy).\n * 3. `globals.swatchbookAxes` — toolbar-set tuple.\n * 4. `globals.swatchbookTheme` — toolbar-set composed name.\n * 5. virtual module default.\n */\nfunction resolveTuple(\n globals: Record<string, unknown>,\n parameters: Record<string, Record<string, unknown>>,\n): Record<string, string> {\n const param = parameters[PARAM_KEY];\n const paramAxes = param?.['axes'];\n if (paramAxes && typeof paramAxes === 'object') {\n return normalizeTuple(paramAxes as Record<string, string>);\n }\n const paramTheme = param?.['theme'];\n if (typeof paramTheme === 'string') {\n const hit = tupleForName(paramTheme);\n if (hit) return normalizeTuple(hit);\n }\n const globalAxes = globals[AXES_GLOBAL_KEY];\n if (globalAxes && typeof globalAxes === 'object') {\n return normalizeTuple(globalAxes as Record<string, string>);\n }\n const globalTheme = globals[GLOBAL_KEY];\n if (typeof globalTheme === 'string') {\n const hit = tupleForName(globalTheme);\n if (hit) return normalizeTuple(hit);\n }\n return defaultTuple();\n}\n\nfunction resolveColorFormat(globals: Record<string, unknown>): ColorFormat {\n const raw = globals[COLOR_FORMAT_GLOBAL_KEY];\n if (typeof raw === 'string' && (COLOR_FORMATS as readonly string[]).includes(raw)) {\n return raw as ColorFormat;\n }\n return 'hex';\n}\n\nconst themedDecorator: Decorator = (Story, context) => {\n const tuple = useMemo(\n () =>\n resolveTuple(\n context.globals as Record<string, unknown>,\n context.parameters as Record<string, Record<string, unknown>>,\n ),\n [context.globals, context.parameters],\n );\n const colorFormat = useMemo(\n () => resolveColorFormat(context.globals as Record<string, unknown>),\n [context.globals],\n );\n const themeName = useMemo(() => {\n const match = themes.find((t) => {\n const input = t.input as Record<string, string>;\n return Object.keys(input).every((k) => input[k] === tuple[k]);\n });\n return match?.name ?? defaultTheme ?? themes[0]?.name ?? 'Light';\n }, [tuple]);\n\n useEffect(() => {\n ensureStylesheet();\n broadcastInit();\n }, []);\n\n useEffect(() => {\n setRootAxes(themeName, tuple);\n }, [themeName, tuple]);\n\n const wrapperAttrs: Record<string, string> = {\n [dataAttr(cssVarPrefix, 'theme')]: themeName,\n };\n for (const axis of virtualAxes) {\n const value = tuple[axis.name];\n if (value !== undefined) wrapperAttrs[dataAttr(cssVarPrefix, axis.name)] = value;\n }\n const pinnedSample = themes[0]?.input;\n if (pinnedSample) {\n for (const name of virtualDisabledAxes) {\n const value = pinnedSample[name];\n if (value !== undefined) wrapperAttrs[dataAttr(cssVarPrefix, name)] = value;\n }\n }\n\n const snapshot = useMemo<ProjectSnapshot>(\n () => ({\n axes: virtualAxes,\n disabledAxes: virtualDisabledAxes,\n presets: virtualPresets,\n themes,\n themesResolved,\n activeTheme: themeName,\n activeAxes: tuple,\n cssVarPrefix,\n diagnostics,\n css,\n }),\n [themeName, tuple],\n );\n\n return (\n <SwatchbookContext.Provider value={snapshot}>\n <ThemeContext.Provider value={themeName}>\n <AxesContext.Provider value={tuple}>\n <ColorFormatContext.Provider value={colorFormat}>\n <div\n {...wrapperAttrs}\n style={{\n padding: '1rem',\n minHeight: '100%',\n }}\n >\n <Story />\n </div>\n </ColorFormatContext.Provider>\n </AxesContext.Provider>\n </ThemeContext.Provider>\n </SwatchbookContext.Provider>\n );\n};\n\n/**\n * Named exports consumed by `definePreviewAddon(previewExports)` in the\n * addon's CSF Next factory (`src/index.ts`).\n */\nexport const decorators: NonNullable<Preview['decorators']> = [themedDecorator];\n\nexport const globalTypes: NonNullable<Preview['globalTypes']> = {\n [GLOBAL_KEY]: {\n name: 'Theme',\n description: 'Active swatchbook theme (composed permutation ID).',\n },\n [AXES_GLOBAL_KEY]: {\n name: 'Axes',\n description: 'Per-axis context selection. Takes precedence over the composed theme name.',\n },\n [COLOR_FORMAT_GLOBAL_KEY]: {\n name: 'Color format',\n description: 'Display format for color tokens in blocks. Emitted CSS is unaffected.',\n },\n};\n\nfunction buildInitialAxes(): Record<string, string> {\n const out: Record<string, string> = {};\n for (const axis of virtualAxes) out[axis.name] = axis.default;\n return out;\n}\n\nexport const initialGlobals: NonNullable<Preview['initialGlobals']> = {\n [GLOBAL_KEY]: defaultTheme ?? themes[0]?.name ?? 'Light',\n [AXES_GLOBAL_KEY]: buildInitialAxes(),\n [COLOR_FORMAT_GLOBAL_KEY]: 'hex',\n};\n\n/**\n * Module-level channel subscription: writes the active tuple's attributes\n * onto `<html>` regardless of whether a story decorator is rendering.\n *\n * The {@link themedDecorator} already sets these inside story renders, but\n * it never runs on MDX docs pages that embed blocks without `<Story />`.\n * Without attrs on an ancestor, the per-tuple CSS selectors\n * (`[data-mode=\"Dark\"][data-brand=\"…\"]`) don't match and everything falls\n * back to the `:root` default tuple — so colors stay defaults even after\n * the toolbar switches axes. Subscribing globally fixes MDX docs at the\n * cost of one idempotent redundant write per story render.\n */\nfunction installGlobalAxisApplier(): void {\n if (typeof document === 'undefined') return;\n const channel = addons.getChannel();\n /**\n * Inject the stylesheet and emit the init payload once on module load so\n * the manager's toolbar populates and CSS vars are available even when no\n * story/decorator ever runs (bare MDX docs pages). Without these, the\n * toolbar sits in its disabled \"loading…\" state and nothing is styled.\n */\n ensureStylesheet();\n broadcastInit();\n /**\n * If the manager subscribes to INIT_EVENT after our initial broadcast,\n * it misses the payload and the toolbar stays in its \"loading…\" state\n * until something else re-fires it. Honor an explicit request event so\n * a late-mounting manager can ask for the payload.\n */\n channel.on(INIT_REQUEST_EVENT, broadcastInit);\n const apply = (globals: Record<string, unknown>): void => {\n ensureStylesheet();\n const tuple = resolveTuple(globals, {});\n const match = themes.find((t) => {\n const input = t.input as Record<string, string>;\n return Object.keys(input).every((k) => input[k] === tuple[k]);\n });\n const themeName = match?.name ?? defaultTheme ?? themes[0]?.name ?? 'Light';\n setRootAxes(themeName, tuple);\n };\n const onGlobals = (payload: { globals?: Record<string, unknown> }): void => {\n if (payload.globals) apply(payload.globals);\n };\n channel.on('globalsUpdated', onGlobals);\n channel.on('setGlobals', onGlobals);\n channel.on('updateGlobals', onGlobals);\n}\n\ninstallGlobalAxisApplier();\n\n/**\n * Bridge `mousedown` inside the preview iframe to the manager via a\n * dedicated channel event. The toolbar popover's outside-click listener\n * runs on the manager's document, which can't observe mousedowns inside\n * the preview; without this bridge, clicking the canvas leaves the\n * popover open. Idempotent: fires at most once per real mousedown.\n */\nfunction installPreviewMouseDownBridge(): void {\n if (typeof document === 'undefined') return;\n const channel = addons.getChannel();\n document.addEventListener('mousedown', () => {\n channel.emit(PREVIEW_MOUSEDOWN_EVENT);\n });\n}\n\ninstallPreviewMouseDownBridge();\n"],"mappings":"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"}
|