@datalayer/primer-addons 1.0.14 → 1.0.15

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.
@@ -1,3 +1,4 @@
1
+ import { type ThemeVariant } from '../themeRegistry';
1
2
  export interface SvgPalette {
2
3
  bg: string;
3
4
  bgPanel: string;
@@ -16,6 +17,7 @@ export interface SvgPalette {
16
17
  textMuted: string;
17
18
  isLight: boolean;
18
19
  }
20
+ export declare function getSvgPalette(theme?: ThemeVariant, colorMode?: 'light' | 'dark' | 'auto'): SvgPalette;
19
21
  export declare function useSvgPalette(): SvgPalette;
20
22
  /** @deprecated Use `useSvgPalette` instead. */
21
23
  export declare const useBlogSvgPalette: typeof useSvgPalette;
@@ -213,8 +213,7 @@ const palettes = {
213
213
  },
214
214
  },
215
215
  };
216
- export function useSvgPalette() {
217
- const { colorMode, theme } = useThemeStore();
216
+ export function getSvgPalette(theme = 'datalayer', colorMode = 'auto') {
218
217
  const mode = colorMode === 'auto'
219
218
  ? typeof window !== 'undefined' &&
220
219
  window.matchMedia('(prefers-color-scheme: dark)').matches
@@ -223,6 +222,10 @@ export function useSvgPalette() {
223
222
  : colorMode;
224
223
  return palettes[theme]?.[mode] ?? palettes.datalayer.dark;
225
224
  }
225
+ export function useSvgPalette() {
226
+ const { colorMode, theme } = useThemeStore();
227
+ return getSvgPalette(theme, colorMode);
228
+ }
226
229
  /** @deprecated Use `useSvgPalette` instead. */
227
230
  export const useBlogSvgPalette = useSvgPalette;
228
231
  export const LightBoostFilter = () => (_jsx("filter", { id: "svgLightBoost", colorInterpolationFilters: "sRGB", children: _jsx("feComponentTransfer", { children: _jsx("feFuncA", { type: "gamma", exponent: "0.45", amplitude: "1", offset: "0" }) }) }));
@@ -1,14 +1,32 @@
1
1
  /**
2
2
  * Zustand store for theme preferences – reusable across Datalayer apps.
3
3
  *
4
- * Persists `colorMode` and `theme` to localStorage. The store key is
4
+ * Persists `colorMode` and `theme` to localStorage. The store key is
5
5
  * configurable so that different apps can maintain independent prefs.
6
6
  *
7
+ * ## Variants
8
+ *
9
+ * A *variant* is a named set of theme/colorMode defaults (e.g. `anonymous`,
10
+ * `authenticated`). When a variant is active, `setTheme`, `setColorMode`,
11
+ * and `toggleColorMode` automatically update the active variant's stored
12
+ * preferences. Calling `setVariant(name)` switches theme/colorMode to
13
+ * that variant's prefs in one step.
14
+ *
15
+ * Variants can be registered at build time (via defaults) or at runtime
16
+ * (via `registerVariants`). `registerVariants` only sets defaults for
17
+ * variants that don't already exist, preserving user-customised prefs
18
+ * across page loads.
19
+ *
7
20
  * @module theme/useThemeStore
8
21
  */
9
22
  import { type StoreApi, type UseBoundStore } from 'zustand';
10
23
  import type { ThemeVariant } from './themeRegistry';
11
24
  import type { ColorMode } from './DatalayerBrandThemeProvider';
25
+ /** Per-variant theme + colorMode preferences. */
26
+ export type VariantDefaults = Record<string, {
27
+ theme: ThemeVariant;
28
+ colorMode: ColorMode;
29
+ }>;
12
30
  export interface ThemeState {
13
31
  /** Current color mode (light, dark, or auto = follow OS). */
14
32
  colorMode: ColorMode;
@@ -24,6 +42,21 @@ export interface ThemeState {
24
42
  * color mode to the theme's configured default.
25
43
  */
26
44
  setTheme: (theme: ThemeVariant, applyDefaultColorMode?: boolean) => void;
45
+ /** The currently active variant (`null` when no variant is active). */
46
+ activeVariant: string | null;
47
+ /** Per-variant stored preferences. */
48
+ variants: VariantDefaults;
49
+ /**
50
+ * Register variant definitions. Only sets defaults for variants that
51
+ * don't already exist — existing (possibly user-customised) variants
52
+ * are preserved.
53
+ */
54
+ registerVariants: (defs: VariantDefaults) => void;
55
+ /**
56
+ * Activate a named variant. Sets `theme` / `colorMode` to the
57
+ * variant's stored prefs in one step.
58
+ */
59
+ setVariant: (variant: string) => void;
27
60
  }
28
61
  /**
29
62
  * Create a theme store bound to a specific localStorage key.
@@ -34,11 +67,12 @@ export interface ThemeState {
34
67
  * ```
35
68
  *
36
69
  * @param storageKey localStorage key for persistence (e.g. `'otel-example-theme'`)
37
- * @param defaults Optional overrides for the initial colorMode / theme
70
+ * @param defaults Optional overrides for the initial colorMode / theme / variants
38
71
  */
39
72
  export declare function createThemeStore(storageKey: string, defaults?: {
40
73
  colorMode?: ColorMode;
41
74
  theme?: ThemeVariant;
75
+ variants?: VariantDefaults;
42
76
  }): UseBoundStore<StoreApi<ThemeState>>;
43
77
  /**
44
78
  * Default theme store for Datalayer applications.
@@ -5,14 +5,33 @@
5
5
  /**
6
6
  * Zustand store for theme preferences – reusable across Datalayer apps.
7
7
  *
8
- * Persists `colorMode` and `theme` to localStorage. The store key is
8
+ * Persists `colorMode` and `theme` to localStorage. The store key is
9
9
  * configurable so that different apps can maintain independent prefs.
10
10
  *
11
+ * ## Variants
12
+ *
13
+ * A *variant* is a named set of theme/colorMode defaults (e.g. `anonymous`,
14
+ * `authenticated`). When a variant is active, `setTheme`, `setColorMode`,
15
+ * and `toggleColorMode` automatically update the active variant's stored
16
+ * preferences. Calling `setVariant(name)` switches theme/colorMode to
17
+ * that variant's prefs in one step.
18
+ *
19
+ * Variants can be registered at build time (via defaults) or at runtime
20
+ * (via `registerVariants`). `registerVariants` only sets defaults for
21
+ * variants that don't already exist, preserving user-customised prefs
22
+ * across page loads.
23
+ *
11
24
  * @module theme/useThemeStore
12
25
  */
13
26
  import { create } from 'zustand';
14
27
  import { persist, createJSONStorage } from 'zustand/middleware';
15
28
  import { themeConfigs } from './themeRegistry';
29
+ /* ── Helpers ─────────────────────────────────────────────────────── */
30
+ const colorModeCycle = {
31
+ light: 'dark',
32
+ dark: 'auto',
33
+ auto: 'light',
34
+ };
16
35
  /**
17
36
  * Create a theme store bound to a specific localStorage key.
18
37
  *
@@ -22,35 +41,118 @@ import { themeConfigs } from './themeRegistry';
22
41
  * ```
23
42
  *
24
43
  * @param storageKey localStorage key for persistence (e.g. `'otel-example-theme'`)
25
- * @param defaults Optional overrides for the initial colorMode / theme
44
+ * @param defaults Optional overrides for the initial colorMode / theme / variants
26
45
  */
27
46
  export function createThemeStore(storageKey, defaults) {
28
47
  return create()(persist(set => ({
29
48
  colorMode: defaults?.colorMode ?? 'dark',
30
49
  theme: defaults?.theme ?? 'matrix',
50
+ /* ── variant state ──────────────────────────────────── */
51
+ activeVariant: null,
52
+ variants: defaults?.variants ?? {},
53
+ /* ── actions ────────────────────────────────────────── */
31
54
  toggleColorMode: () => set(state => {
32
- const cycle = {
33
- light: 'dark',
34
- dark: 'auto',
35
- auto: 'light',
36
- };
37
- return { colorMode: cycle[state.colorMode] };
55
+ const next = colorModeCycle[state.colorMode];
56
+ if (state.activeVariant && state.variants[state.activeVariant]) {
57
+ return {
58
+ colorMode: next,
59
+ variants: {
60
+ ...state.variants,
61
+ [state.activeVariant]: { ...state.variants[state.activeVariant], colorMode: next },
62
+ },
63
+ };
64
+ }
65
+ return { colorMode: next };
66
+ }),
67
+ setColorMode: (mode) => set(state => {
68
+ if (state.activeVariant && state.variants[state.activeVariant]) {
69
+ return {
70
+ colorMode: mode,
71
+ variants: {
72
+ ...state.variants,
73
+ [state.activeVariant]: { ...state.variants[state.activeVariant], colorMode: mode },
74
+ },
75
+ };
76
+ }
77
+ return { colorMode: mode };
38
78
  }),
39
- setColorMode: (mode) => set({ colorMode: mode }),
40
- setTheme: (theme, applyDefaultColorMode = true) => set(() => {
41
- const next = { theme };
42
- if (applyDefaultColorMode) {
43
- next.colorMode = themeConfigs[theme].defaultColorMode;
79
+ setTheme: (theme, applyDefaultColorMode = true) => set(state => {
80
+ const nextColorMode = applyDefaultColorMode
81
+ ? themeConfigs[theme].defaultColorMode
82
+ : state.colorMode;
83
+ if (state.activeVariant && state.variants[state.activeVariant]) {
84
+ return {
85
+ theme,
86
+ colorMode: nextColorMode,
87
+ variants: {
88
+ ...state.variants,
89
+ [state.activeVariant]: { theme, colorMode: nextColorMode },
90
+ },
91
+ };
44
92
  }
45
- return next;
93
+ return { theme, colorMode: nextColorMode };
94
+ }),
95
+ registerVariants: (defs) => set(state => {
96
+ const merged = { ...state.variants };
97
+ for (const [key, val] of Object.entries(defs)) {
98
+ if (!merged[key]) {
99
+ merged[key] = val;
100
+ }
101
+ }
102
+ return { variants: merged };
103
+ }),
104
+ setVariant: (variant) => set(state => {
105
+ const v = state.variants[variant];
106
+ if (!v) {
107
+ return { activeVariant: variant };
108
+ }
109
+ return {
110
+ activeVariant: variant,
111
+ theme: v.theme,
112
+ colorMode: v.colorMode,
113
+ };
46
114
  }),
47
115
  }), {
48
116
  name: storageKey,
117
+ version: 2,
49
118
  storage: createJSONStorage(() => localStorage),
50
119
  partialize: state => ({
51
120
  colorMode: state.colorMode,
52
121
  theme: state.theme,
122
+ activeVariant: state.activeVariant,
123
+ variants: state.variants,
53
124
  }),
125
+ /**
126
+ * Migration: when `version` is missing or differs from the current
127
+ * value the persisted state is discarded and the store starts fresh
128
+ * from code-defined defaults.
129
+ */
130
+ migrate: () => ({}),
131
+ /**
132
+ * Deep-merge variants during hydration so that:
133
+ * - Code-defined variants (from defaults or registerVariants) are
134
+ * always present even when localStorage has an older snapshot.
135
+ * - Per-variant prefs that the user customised (persisted) win over
136
+ * code defaults for each variant key.
137
+ */
138
+ merge: (persisted, current) => {
139
+ const p = (persisted ?? {});
140
+ // Build merged variants: start from code-defined, then overlay
141
+ // any persisted variant whose prefs differ (user customisations).
142
+ const mergedVariants = { ...current.variants };
143
+ if (p.variants) {
144
+ for (const [key, val] of Object.entries(p.variants)) {
145
+ if (val && typeof val === 'object' && 'theme' in val) {
146
+ mergedVariants[key] = val;
147
+ }
148
+ }
149
+ }
150
+ return {
151
+ ...current,
152
+ ...p,
153
+ variants: mergedVariants,
154
+ };
155
+ },
54
156
  }));
55
157
  }
56
158
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/primer-addons",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "license": "BSD-3-Clause",
5
5
  "scripts": {
6
6
  "build": "tsc && vite build",