@vueless/storybook-dark-mode 9.0.10 → 10.0.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Storybook Dark Mode
2
2
 
3
- A Storybook v9-optimized addon that enables users to toggle between dark and light modes. For support with earlier Storybook versions, see the [original addon](https://github.com/hipstersmoothie/storybook-dark-mode).
3
+ A Storybook v10-optimized addon that enables users to toggle between dark and light modes. For support with earlier Storybook versions, see the [original addon](https://github.com/hipstersmoothie/storybook-dark-mode).
4
4
 
5
5
  The project is supported and maintained by the [Vueless UI](https://github.com/vuelessjs/vueless) core team. | [See the demo](https://ui.vueless.com) 🌗
6
6
 
@@ -0,0 +1,9 @@
1
+ declare const DARK_MODE_EVENT_NAME = "DARK_MODE";
2
+ declare const UPDATE_DARK_MODE_EVENT_NAME = "UPDATE_DARK_MODE";
3
+
4
+ /**
5
+ * Returns the current state of storybook's dark-mode
6
+ */
7
+ declare function useDarkMode(): boolean;
8
+
9
+ export { DARK_MODE_EVENT_NAME, UPDATE_DARK_MODE_EVENT_NAME, useDarkMode };
package/dist/index.js ADDED
@@ -0,0 +1,86 @@
1
+ import { useState, useEffect, addons } from 'storybook/preview-api';
2
+ import 'react';
3
+ import { global } from '@storybook/global';
4
+ import { themes } from 'storybook/theming';
5
+ import 'storybook/internal/components';
6
+ import '@storybook/icons';
7
+ import 'storybook/internal/core-events';
8
+ import 'storybook/manager-api';
9
+ import equal from 'fast-deep-equal';
10
+
11
+ // src/index.tsx
12
+
13
+ // src/constants.ts
14
+ var DARK_MODE_EVENT_NAME = "DARK_MODE";
15
+ var UPDATE_DARK_MODE_EVENT_NAME = "UPDATE_DARK_MODE";
16
+ var { document, window } = global;
17
+ var STORAGE_KEY = "sb-addon-themes-3";
18
+ window.matchMedia?.("(prefers-color-scheme: dark)");
19
+ var defaultParams = {
20
+ classTarget: "body",
21
+ dark: themes.dark,
22
+ darkClass: ["dark"],
23
+ light: themes.light,
24
+ lightClass: ["light"],
25
+ stylePreview: false,
26
+ userHasExplicitlySetTheTheme: false
27
+ };
28
+ var updateStore = (newStore) => {
29
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newStore));
30
+ };
31
+ var toggleDarkClass = (el, {
32
+ current,
33
+ darkClass = defaultParams.darkClass,
34
+ lightClass = defaultParams.lightClass
35
+ }) => {
36
+ if (current === "dark") {
37
+ el.classList.remove(...arrayify(lightClass));
38
+ el.classList.add(...arrayify(darkClass));
39
+ } else {
40
+ el.classList.remove(...arrayify(darkClass));
41
+ el.classList.add(...arrayify(lightClass));
42
+ }
43
+ };
44
+ var arrayify = (classes) => {
45
+ const arr = [];
46
+ return arr.concat(classes).map((item) => item);
47
+ };
48
+ var updateManager = (store2) => {
49
+ const manager = document.querySelector(store2.classTarget);
50
+ if (!manager) {
51
+ return;
52
+ }
53
+ toggleDarkClass(manager, store2);
54
+ };
55
+ var store = (userTheme = {}) => {
56
+ const storedItem = window.localStorage.getItem(STORAGE_KEY);
57
+ if (typeof storedItem === "string") {
58
+ const stored = JSON.parse(storedItem);
59
+ if (userTheme) {
60
+ if (userTheme.dark && !equal(stored.dark, userTheme.dark)) {
61
+ stored.dark = userTheme.dark;
62
+ updateStore(stored);
63
+ }
64
+ if (userTheme.light && !equal(stored.light, userTheme.light)) {
65
+ stored.light = userTheme.light;
66
+ updateStore(stored);
67
+ }
68
+ }
69
+ return stored;
70
+ }
71
+ return { ...defaultParams, ...userTheme };
72
+ };
73
+ updateManager(store());
74
+
75
+ // src/index.tsx
76
+ function useDarkMode() {
77
+ const [isDark, setIsDark] = useState(() => store().current === "dark");
78
+ useEffect(() => {
79
+ const chan = addons.getChannel();
80
+ chan.on(DARK_MODE_EVENT_NAME, setIsDark);
81
+ return () => chan.off(DARK_MODE_EVENT_NAME, setIsDark);
82
+ }, []);
83
+ return isDark;
84
+ }
85
+
86
+ export { DARK_MODE_EVENT_NAME, UPDATE_DARK_MODE_EVENT_NAME, useDarkMode };
@@ -0,0 +1,199 @@
1
+ import { addons, useParameter } from 'storybook/manager-api';
2
+ import { Addon_TypesEnum } from 'storybook/internal/types';
3
+ import { themes } from 'storybook/theming';
4
+ import * as React from 'react';
5
+ import { global } from '@storybook/global';
6
+ import { IconButton } from 'storybook/internal/components';
7
+ import { SunIcon, MoonIcon } from '@storybook/icons';
8
+ import { STORY_CHANGED, SET_STORIES, DOCS_RENDERED } from 'storybook/internal/core-events';
9
+ import equal from 'fast-deep-equal';
10
+
11
+ // src/preset/manager.tsx
12
+
13
+ // src/constants.ts
14
+ var DARK_MODE_EVENT_NAME = "DARK_MODE";
15
+ var UPDATE_DARK_MODE_EVENT_NAME = "UPDATE_DARK_MODE";
16
+
17
+ // src/Tool.tsx
18
+ var { document, window } = global;
19
+ var STORAGE_KEY = "sb-addon-themes-3";
20
+ var prefersDark = window.matchMedia?.("(prefers-color-scheme: dark)");
21
+ var defaultParams = {
22
+ classTarget: "body",
23
+ dark: themes.dark,
24
+ darkClass: ["dark"],
25
+ light: themes.light,
26
+ lightClass: ["light"],
27
+ stylePreview: false,
28
+ userHasExplicitlySetTheTheme: false
29
+ };
30
+ var updateStore = (newStore) => {
31
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newStore));
32
+ };
33
+ var toggleDarkClass = (el, {
34
+ current,
35
+ darkClass = defaultParams.darkClass,
36
+ lightClass = defaultParams.lightClass
37
+ }) => {
38
+ if (current === "dark") {
39
+ el.classList.remove(...arrayify(lightClass));
40
+ el.classList.add(...arrayify(darkClass));
41
+ } else {
42
+ el.classList.remove(...arrayify(darkClass));
43
+ el.classList.add(...arrayify(lightClass));
44
+ }
45
+ };
46
+ var arrayify = (classes) => {
47
+ const arr = [];
48
+ return arr.concat(classes).map((item) => item);
49
+ };
50
+ var updatePreview = (store2) => {
51
+ const iframe = document.getElementById("storybook-preview-iframe");
52
+ if (!iframe) {
53
+ return;
54
+ }
55
+ const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
56
+ const target = iframeDocument?.querySelector(store2.classTarget);
57
+ if (!target) {
58
+ return;
59
+ }
60
+ toggleDarkClass(target, store2);
61
+ };
62
+ var updateManager = (store2) => {
63
+ const manager = document.querySelector(store2.classTarget);
64
+ if (!manager) {
65
+ return;
66
+ }
67
+ toggleDarkClass(manager, store2);
68
+ };
69
+ var store = (userTheme = {}) => {
70
+ const storedItem = window.localStorage.getItem(STORAGE_KEY);
71
+ if (typeof storedItem === "string") {
72
+ const stored = JSON.parse(storedItem);
73
+ if (userTheme) {
74
+ if (userTheme.dark && !equal(stored.dark, userTheme.dark)) {
75
+ stored.dark = userTheme.dark;
76
+ updateStore(stored);
77
+ }
78
+ if (userTheme.light && !equal(stored.light, userTheme.light)) {
79
+ stored.light = userTheme.light;
80
+ updateStore(stored);
81
+ }
82
+ }
83
+ return stored;
84
+ }
85
+ return { ...defaultParams, ...userTheme };
86
+ };
87
+ updateManager(store());
88
+ function DarkMode({ api }) {
89
+ const [isDark, setDark] = React.useState(prefersDark.matches);
90
+ const darkModeParams = useParameter("darkMode", {});
91
+ const { current: defaultMode, stylePreview, ...params } = darkModeParams;
92
+ const channel = api.getChannel();
93
+ const userHasExplicitlySetTheTheme = React.useMemo(
94
+ () => store(params).userHasExplicitlySetTheTheme,
95
+ [params]
96
+ );
97
+ const setMode = React.useCallback(
98
+ (mode) => {
99
+ const currentStore2 = store();
100
+ api.setOptions({ theme: currentStore2[mode] });
101
+ setDark(mode === "dark");
102
+ api.getChannel().emit(DARK_MODE_EVENT_NAME, mode === "dark");
103
+ updateManager(currentStore2);
104
+ if (stylePreview) {
105
+ updatePreview(currentStore2);
106
+ }
107
+ },
108
+ [api, stylePreview]
109
+ );
110
+ const updateMode = React.useCallback(
111
+ (mode) => {
112
+ const currentStore2 = store();
113
+ const current = mode || (currentStore2.current === "dark" ? "light" : "dark");
114
+ updateStore({ ...currentStore2, current });
115
+ setMode(current);
116
+ },
117
+ [setMode]
118
+ );
119
+ function prefersDarkUpdate(event) {
120
+ if (userHasExplicitlySetTheTheme || defaultMode) {
121
+ return;
122
+ }
123
+ updateMode(event.matches ? "dark" : "light");
124
+ }
125
+ const renderTheme = React.useCallback(() => {
126
+ const { current = "light" } = store();
127
+ setMode(current);
128
+ }, [setMode]);
129
+ const handleIconClick = () => {
130
+ updateMode();
131
+ const currentStore2 = store();
132
+ updateStore({ ...currentStore2, userHasExplicitlySetTheTheme: true });
133
+ };
134
+ React.useEffect(() => {
135
+ const currentStore2 = store();
136
+ updateStore({
137
+ ...currentStore2,
138
+ ...darkModeParams,
139
+ current: currentStore2.current || darkModeParams.current
140
+ });
141
+ renderTheme();
142
+ }, [darkModeParams, renderTheme]);
143
+ React.useEffect(() => {
144
+ channel.on(STORY_CHANGED, renderTheme);
145
+ channel.on(SET_STORIES, renderTheme);
146
+ channel.on(DOCS_RENDERED, renderTheme);
147
+ prefersDark.addListener(prefersDarkUpdate);
148
+ return () => {
149
+ channel.removeListener(STORY_CHANGED, renderTheme);
150
+ channel.removeListener(SET_STORIES, renderTheme);
151
+ channel.removeListener(DOCS_RENDERED, renderTheme);
152
+ prefersDark.removeListener(prefersDarkUpdate);
153
+ };
154
+ });
155
+ React.useEffect(() => {
156
+ channel.on(UPDATE_DARK_MODE_EVENT_NAME, updateMode);
157
+ return () => {
158
+ channel.removeListener(UPDATE_DARK_MODE_EVENT_NAME, updateMode);
159
+ };
160
+ });
161
+ React.useEffect(() => {
162
+ if (userHasExplicitlySetTheTheme) {
163
+ return;
164
+ }
165
+ if (defaultMode) {
166
+ updateMode(defaultMode);
167
+ } else if (prefersDark.matches) {
168
+ updateMode("dark");
169
+ }
170
+ }, [defaultMode, updateMode, userHasExplicitlySetTheTheme]);
171
+ return /* @__PURE__ */ React.createElement(
172
+ IconButton,
173
+ {
174
+ key: "dark-mode",
175
+ title: isDark ? "Change theme to light mode" : "Change theme to dark mode",
176
+ onClick: handleIconClick
177
+ },
178
+ isDark ? /* @__PURE__ */ React.createElement(SunIcon, { "aria-hidden": "true" }) : /* @__PURE__ */ React.createElement(MoonIcon, { "aria-hidden": "true" })
179
+ );
180
+ }
181
+ var Tool_default = DarkMode;
182
+
183
+ // src/preset/manager.tsx
184
+ var currentStore = store();
185
+ var currentTheme = currentStore.current || prefersDark.matches && "dark" || "light";
186
+ addons.setConfig({
187
+ theme: {
188
+ ...themes[currentTheme],
189
+ ...currentStore[currentTheme]
190
+ }
191
+ });
192
+ addons.register("storybook/dark-mode", (api) => {
193
+ addons.add("storybook/dark-mode", {
194
+ title: "dark mode",
195
+ type: Addon_TypesEnum.TOOL,
196
+ match: ({ viewMode }) => viewMode === "story" || viewMode === "docs",
197
+ render: () => /* @__PURE__ */ React.createElement(Tool_default, { api })
198
+ });
199
+ });
package/dist/preset.js ADDED
@@ -0,0 +1,6 @@
1
+ // src/preset.ts
2
+ function managerEntries(entry = []) {
3
+ return [...entry, import.meta.resolve("./manager")];
4
+ }
5
+
6
+ export { managerEntries };
package/package.json CHANGED
@@ -1,14 +1,27 @@
1
1
  {
2
2
  "name": "@vueless/storybook-dark-mode",
3
- "version": "9.0.10",
3
+ "version": "10.0.1-beta.0",
4
4
  "description": "Toggle between light and dark mode in Storybook",
5
- "main": "dist/cjs/index.js",
6
- "module": "dist/esm/index.js",
7
- "types": "dist/ts/index.d.ts",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ },
11
+ "./preview": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./preset": "./dist/preset.js",
16
+ "./manager": "./dist/manager.js",
17
+ "./package.json": "./package.json"
18
+ },
19
+ "main": "dist/index.js",
20
+ "types": "dist/index.d.ts",
8
21
  "files": [
9
22
  "src",
10
23
  "dist",
11
- "preset.js"
24
+ "preset.ts"
12
25
  ],
13
26
  "author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
14
27
  "repository": {
@@ -20,57 +33,64 @@
20
33
  },
21
34
  "scripts": {
22
35
  "clean": "rimraf ./dist",
23
- "buildBabel": "concurrently \"npm run buildBabel:cjs\" \"npm run buildBabel:esm\"",
24
- "buildBabel:cjs": "babel ./src -d ./dist/cjs --extensions \".js,.jsx,.ts,.tsx\"",
25
- "buildBabel:esm": "babel ./src -d ./dist/esm --env-name esm --extensions \".js,.jsx,.ts,.tsx\"",
26
- "buildTsc": "tsc --declaration --emitDeclarationOnly --outDir ./dist/ts -p tsconfig.build.json",
27
36
  "prebuild": "npm run clean",
28
- "build": "concurrently \"npm run buildBabel\" \"npm run buildTsc\"",
29
- "build:watch": "concurrently \"npm run buildBabel:esm -- --watch\" \"npm run buildTsc -- --watch\"",
30
- "lint": "eslint --ext .ts --ext .tsx src/**",
31
- "release": "auto shipit"
37
+ "build": "tsup",
38
+ "build:watch": "tsup --watch",
39
+ "lint": "eslint --no-fix src/",
40
+ "lint:fix": "eslint --fix src/",
41
+ "lint:ci": "eslint --no-fix src/ --max-warnings=0",
42
+ "release:beta": "release-it --increment=prerelease --preRelease=beta --ci --no-git.tag --no-github.release",
43
+ "release:patch": "release-it patch --ci",
44
+ "release:minor": "release-it minor --ci",
45
+ "release:major": "release-it major --ci"
32
46
  },
33
47
  "dependencies": {
34
48
  "@storybook/global": "^5.0.0",
35
- "@storybook/icons": "^1.4.0",
36
49
  "fast-deep-equal": "^3.1.3",
37
50
  "memoizerific": "^1.11.3"
38
51
  },
39
52
  "devDependencies": {
40
- "@babel/cli": "^7.28.3",
41
- "@babel/core": "^7.28.3",
42
- "@babel/preset-env": "^7.28.3",
43
- "@babel/preset-react": "^7.27.1",
44
- "@babel/preset-typescript": "^7.27.1",
45
- "@storybook/builder-vite": "^9.1.3",
46
- "@storybook/react": "^9.1.3",
47
- "@storybook/react-vite": "^9.1.3",
53
+ "@eslint/js": "^9.33.0",
54
+ "@release-it/bumper": "^7.0.5",
55
+ "@release-it/conventional-changelog": "^10.0.1",
56
+ "@storybook/builder-vite": "^10.0.0",
57
+ "@storybook/icons": "^1.4.0",
58
+ "@storybook/react": "^10.0.0",
59
+ "@storybook/react-vite": "^10.0.0",
60
+ "@stylistic/eslint-plugin": "^5.2.3",
48
61
  "@types/node": "^24.3.0",
49
62
  "@types/react": "^18.0.26",
50
- "@typescript-eslint/eslint-plugin": "5.45.1",
51
- "@typescript-eslint/parser": "5.45.1",
52
- "all-contributors-cli": "^6.26.1",
53
- "auto": "^11.3.0",
54
- "auto-config-hipstersmoothie": "^4.0.0",
55
- "babel-loader": "^10.0.0",
56
- "concurrently": "^7.6.0",
57
- "eslint": "8.29.0",
58
- "eslint-config-prettier": "8.5.0",
59
- "eslint-config-xo": "0.43.1",
60
- "eslint-config-xo-react": "0.27.0",
61
- "eslint-plugin-react": "7.31.11",
62
- "eslint-plugin-react-hooks": "4.6.0",
63
- "prettier": "^2.8.0",
63
+ "eslint": "^9.33.0",
64
+ "eslint-config-prettier": "^10.1.8",
65
+ "eslint-plugin-prettier": "^5.5.4",
66
+ "eslint-plugin-react": "^7.37.5",
67
+ "eslint-plugin-react-hooks": "^5.1.0",
68
+ "globals": "^15.14.0",
69
+ "prettier": "^3.6.2",
64
70
  "react": "^18.2.0",
65
71
  "react-dom": "^18.2.0",
72
+ "release-it": "^19.0.4",
66
73
  "rimraf": "^3.0.2",
67
- "storybook": "^9.1.3",
74
+ "storybook": "^10.0.0",
68
75
  "ts-node": "^10.9.2",
76
+ "tsup": "^8.0.0",
69
77
  "typescript": "^5.9.2",
78
+ "typescript-eslint": "^8.40.0",
70
79
  "vite": "^7.1.11"
71
80
  },
72
- "auto": {
73
- "extends": "hipstersmoothie"
81
+ "peerDependencies": {
82
+ "storybook": "^10.0.0"
83
+ },
84
+ "bundler": {
85
+ "managerEntries": [
86
+ "src/preset/manager.tsx"
87
+ ],
88
+ "previewEntries": [
89
+ "src/index.tsx"
90
+ ],
91
+ "nodeEntries": [
92
+ "src/preset.ts"
93
+ ]
74
94
  },
75
95
  "prettier": {
76
96
  "singleQuote": true
@@ -79,7 +99,7 @@
79
99
  "node": ">=20"
80
100
  },
81
101
  "keywords": [
82
- "storybook-addons",
102
+ "storybook-addon",
83
103
  "appearance"
84
104
  ]
85
105
  }
package/preset.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './dist/preset.js';
2
+
package/src/Tool.tsx CHANGED
@@ -3,18 +3,14 @@ import { global } from '@storybook/global';
3
3
  import { themes, ThemeVars } from 'storybook/theming';
4
4
  import { IconButton } from 'storybook/internal/components';
5
5
  import { MoonIcon, SunIcon } from '@storybook/icons';
6
- import {
7
- STORY_CHANGED,
8
- SET_STORIES,
9
- DOCS_RENDERED,
10
- } from 'storybook/internal/core-events';
6
+ import { STORY_CHANGED, SET_STORIES, DOCS_RENDERED } from 'storybook/internal/core-events';
11
7
  import { API, useParameter } from 'storybook/manager-api';
12
8
  import equal from 'fast-deep-equal';
13
9
  import { DARK_MODE_EVENT_NAME, UPDATE_DARK_MODE_EVENT_NAME } from './constants';
14
10
 
15
11
  const { document, window } = global as { document: Document; window: Window };
16
- const modes = ['light', 'dark'] as const;
17
- type Mode = typeof modes[number];
12
+
13
+ type Mode = 'light' | 'dark';
18
14
 
19
15
  interface DarkModeStore {
20
16
  /** The class target in the preview iframe */
@@ -36,6 +32,7 @@ interface DarkModeStore {
36
32
  }
37
33
 
38
34
  const STORAGE_KEY = 'sb-addon-themes-3';
35
+
39
36
  export const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)');
40
37
 
41
38
  const defaultParams: Required<Omit<DarkModeStore, 'current'>> = {
@@ -60,7 +57,7 @@ const toggleDarkClass = (
60
57
  current,
61
58
  darkClass = defaultParams.darkClass,
62
59
  lightClass = defaultParams.lightClass,
63
- }: DarkModeStore
60
+ }: DarkModeStore,
64
61
  ) => {
65
62
  if (current === 'dark') {
66
63
  el.classList.remove(...arrayify(lightClass));
@@ -74,21 +71,19 @@ const toggleDarkClass = (
74
71
  /** Coerce a string to a single item array, or return an array as-is */
75
72
  const arrayify = (classes: string | string[]): string[] => {
76
73
  const arr: string[] = [];
74
+
77
75
  return arr.concat(classes).map((item) => item);
78
76
  };
79
77
 
80
78
  /** Update the preview iframe class */
81
79
  const updatePreview = (store: DarkModeStore) => {
82
- const iframe = document.getElementById(
83
- 'storybook-preview-iframe'
84
- ) as HTMLIFrameElement;
80
+ const iframe = document.getElementById('storybook-preview-iframe') as HTMLIFrameElement;
85
81
 
86
82
  if (!iframe) {
87
83
  return;
88
84
  }
89
85
 
90
- const iframeDocument =
91
- iframe.contentDocument || iframe.contentWindow?.document;
86
+ const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
92
87
  const target = iframeDocument?.querySelector<HTMLElement>(store.classTarget);
93
88
 
94
89
  if (!target) {
@@ -110,9 +105,7 @@ const updateManager = (store: DarkModeStore) => {
110
105
  };
111
106
 
112
107
  /** Update changed dark mode settings and persist to localStorage */
113
- export const store = (
114
- userTheme: Partial<DarkModeStore> = {}
115
- ): DarkModeStore => {
108
+ export const store = (userTheme: Partial<DarkModeStore> = {}): DarkModeStore => {
116
109
  const storedItem = window.localStorage.getItem(STORAGE_KEY);
117
110
 
118
111
  if (typeof storedItem === 'string') {
@@ -155,33 +148,35 @@ export function DarkMode({ api }: DarkModeProps) {
155
148
  // Save custom themes on init
156
149
  const userHasExplicitlySetTheTheme = React.useMemo(
157
150
  () => store(params).userHasExplicitlySetTheTheme,
158
- [params]
151
+ [params],
159
152
  );
160
153
  /** Set the theme in storybook, update the local state, and emit an event */
161
154
  const setMode = React.useCallback(
162
155
  (mode: Mode) => {
163
156
  const currentStore = store();
157
+
164
158
  api.setOptions({ theme: currentStore[mode] });
165
159
  setDark(mode === 'dark');
166
160
  api.getChannel().emit(DARK_MODE_EVENT_NAME, mode === 'dark');
167
161
  updateManager(currentStore);
162
+
168
163
  if (stylePreview) {
169
164
  updatePreview(currentStore);
170
165
  }
171
166
  },
172
- [api, stylePreview]
167
+ [api, stylePreview],
173
168
  );
174
169
 
175
170
  /** Update the theme settings in localStorage, react, and storybook */
176
171
  const updateMode = React.useCallback(
177
172
  (mode?: Mode) => {
178
173
  const currentStore = store();
179
- const current =
180
- mode || (currentStore.current === 'dark' ? 'light' : 'dark');
174
+ const current = mode || (currentStore.current === 'dark' ? 'light' : 'dark');
175
+
181
176
  updateStore({ ...currentStore, current });
182
177
  setMode(current);
183
178
  },
184
- [setMode]
179
+ [setMode],
185
180
  );
186
181
 
187
182
  /** Update the theme based on the color preference */
@@ -196,6 +191,7 @@ export function DarkMode({ api }: DarkModeProps) {
196
191
  /** Render the current theme */
197
192
  const renderTheme = React.useCallback(() => {
198
193
  const { current = 'light' } = store();
194
+
199
195
  setMode(current);
200
196
  }, [setMode]);
201
197
 
@@ -203,12 +199,14 @@ export function DarkMode({ api }: DarkModeProps) {
203
199
  const handleIconClick = () => {
204
200
  updateMode();
205
201
  const currentStore = store();
202
+
206
203
  updateStore({ ...currentStore, userHasExplicitlySetTheTheme: true });
207
204
  };
208
205
 
209
206
  /** When storybook params change update the stored themes */
210
207
  React.useEffect(() => {
211
208
  const currentStore = store();
209
+
212
210
  // Ensure we use the stores `current` value first to persist
213
211
  // themeing between page loads and story changes.
214
212
  updateStore({
@@ -223,6 +221,7 @@ export function DarkMode({ api }: DarkModeProps) {
223
221
  channel.on(SET_STORIES, renderTheme);
224
222
  channel.on(DOCS_RENDERED, renderTheme);
225
223
  prefersDark.addListener(prefersDarkUpdate);
224
+
226
225
  return () => {
227
226
  channel.removeListener(STORY_CHANGED, renderTheme);
228
227
  channel.removeListener(SET_STORIES, renderTheme);
@@ -232,6 +231,7 @@ export function DarkMode({ api }: DarkModeProps) {
232
231
  });
233
232
  React.useEffect(() => {
234
233
  channel.on(UPDATE_DARK_MODE_EVENT_NAME, updateMode);
234
+
235
235
  return () => {
236
236
  channel.removeListener(UPDATE_DARK_MODE_EVENT_NAME, updateMode);
237
237
  };
@@ -250,12 +250,11 @@ export function DarkMode({ api }: DarkModeProps) {
250
250
  updateMode('dark');
251
251
  }
252
252
  }, [defaultMode, updateMode, userHasExplicitlySetTheTheme]);
253
+
253
254
  return (
254
255
  <IconButton
255
256
  key="dark-mode"
256
- title={
257
- isDark ? 'Change theme to light mode' : 'Change theme to dark mode'
258
- }
257
+ title={isDark ? 'Change theme to light mode' : 'Change theme to dark mode'}
259
258
  onClick={handleIconClick}
260
259
  >
261
260
  {isDark ? <SunIcon aria-hidden="true" /> : <MoonIcon aria-hidden="true" />}
package/src/index.tsx CHANGED
@@ -10,7 +10,9 @@ export function useDarkMode(): boolean {
10
10
 
11
11
  useEffect(() => {
12
12
  const chan = addons.getChannel();
13
+
13
14
  chan.on(DARK_MODE_EVENT_NAME, setIsDark);
15
+
14
16
  return () => chan.off(DARK_MODE_EVENT_NAME, setIsDark);
15
17
  }, []);
16
18
 
@@ -6,8 +6,7 @@ import * as React from 'react';
6
6
  import Tool, { prefersDark, store } from '../Tool';
7
7
 
8
8
  const currentStore = store();
9
- const currentTheme =
10
- currentStore.current || (prefersDark.matches && 'dark') || 'light';
9
+ const currentTheme = currentStore.current || (prefersDark.matches && 'dark') || 'light';
11
10
 
12
11
  addons.setConfig({
13
12
  theme: {
package/src/preset.ts ADDED
@@ -0,0 +1,3 @@
1
+ export function managerEntries(entry: string[] = []) {
2
+ return [...entry, import.meta.resolve('./manager')];
3
+ }