@vueless/storybook-dark-mode 10.0.1-beta.1 → 10.0.2

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 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).
3
+ A Storybook v9 and 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
 
@@ -11,7 +11,11 @@ The project is supported and maintained by the [Vueless UI](https://github.com/v
11
11
  Install the following npm module:
12
12
 
13
13
  ```sh
14
+ # for Storybook v10
14
15
  npm i -D @vueless/storybook-dark-mode
16
+
17
+ # for Storybook v9
18
+ npm i -D @vueless/storybook-dark-mode@^9
15
19
  ```
16
20
 
17
21
  Then, add following content to `.storybook/main.js`
package/dist/index.js CHANGED
@@ -2,11 +2,7 @@ import { useState, useEffect } from 'react';
2
2
  import { addons } from 'storybook/preview-api';
3
3
  import { global } from '@storybook/global';
4
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';
5
+ import { isEqual } from 'lodash-es';
10
6
 
11
7
  // src/index.tsx
12
8
 
@@ -28,6 +24,10 @@ var defaultParams = {
28
24
  var updateStore = (newStore) => {
29
25
  window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newStore));
30
26
  };
27
+ var arrayify = (classes) => {
28
+ const arr = [];
29
+ return arr.concat(classes).map((item) => item);
30
+ };
31
31
  var toggleDarkClass = (el, {
32
32
  current,
33
33
  darkClass = defaultParams.darkClass,
@@ -41,10 +41,6 @@ var toggleDarkClass = (el, {
41
41
  el.classList.add(...arrayify(lightClass));
42
42
  }
43
43
  };
44
- var arrayify = (classes) => {
45
- const arr = [];
46
- return arr.concat(classes).map((item) => item);
47
- };
48
44
  var updateManager = (store2) => {
49
45
  const manager = document.querySelector(store2.classTarget);
50
46
  if (!manager) {
@@ -57,11 +53,11 @@ var store = (userTheme = {}) => {
57
53
  if (typeof storedItem === "string") {
58
54
  const stored = JSON.parse(storedItem);
59
55
  if (userTheme) {
60
- if (userTheme.dark && !equal(stored.dark, userTheme.dark)) {
56
+ if (userTheme.dark && !isEqual(stored.dark, userTheme.dark)) {
61
57
  stored.dark = userTheme.dark;
62
58
  updateStore(stored);
63
59
  }
64
- if (userTheme.light && !equal(stored.light, userTheme.light)) {
60
+ if (userTheme.light && !isEqual(stored.light, userTheme.light)) {
65
61
  stored.light = userTheme.light;
66
62
  updateStore(stored);
67
63
  }
package/dist/manager.js CHANGED
@@ -2,19 +2,17 @@ import { addons, useParameter } from 'storybook/manager-api';
2
2
  import { Addon_TypesEnum } from 'storybook/internal/types';
3
3
  import { themes } from 'storybook/theming';
4
4
  import * as React from 'react';
5
- import { global } from '@storybook/global';
6
5
  import { IconButton } from 'storybook/internal/components';
7
6
  import { SunIcon, MoonIcon } from '@storybook/icons';
8
7
  import { STORY_CHANGED, SET_STORIES, DOCS_RENDERED } from 'storybook/internal/core-events';
9
- import equal from 'fast-deep-equal';
8
+ import { global } from '@storybook/global';
9
+ import { isEqual } from 'lodash-es';
10
10
 
11
11
  // src/preset/manager.tsx
12
12
 
13
13
  // src/constants.ts
14
14
  var DARK_MODE_EVENT_NAME = "DARK_MODE";
15
15
  var UPDATE_DARK_MODE_EVENT_NAME = "UPDATE_DARK_MODE";
16
-
17
- // src/Tool.tsx
18
16
  var { document, window } = global;
19
17
  var STORAGE_KEY = "sb-addon-themes-3";
20
18
  var prefersDark = window.matchMedia?.("(prefers-color-scheme: dark)");
@@ -30,6 +28,10 @@ var defaultParams = {
30
28
  var updateStore = (newStore) => {
31
29
  window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newStore));
32
30
  };
31
+ var arrayify = (classes) => {
32
+ const arr = [];
33
+ return arr.concat(classes).map((item) => item);
34
+ };
33
35
  var toggleDarkClass = (el, {
34
36
  current,
35
37
  darkClass = defaultParams.darkClass,
@@ -43,10 +45,6 @@ var toggleDarkClass = (el, {
43
45
  el.classList.add(...arrayify(lightClass));
44
46
  }
45
47
  };
46
- var arrayify = (classes) => {
47
- const arr = [];
48
- return arr.concat(classes).map((item) => item);
49
- };
50
48
  var updatePreview = (store2) => {
51
49
  const iframe = document.getElementById("storybook-preview-iframe");
52
50
  if (!iframe) {
@@ -71,11 +69,11 @@ var store = (userTheme = {}) => {
71
69
  if (typeof storedItem === "string") {
72
70
  const stored = JSON.parse(storedItem);
73
71
  if (userTheme) {
74
- if (userTheme.dark && !equal(stored.dark, userTheme.dark)) {
72
+ if (userTheme.dark && !isEqual(stored.dark, userTheme.dark)) {
75
73
  stored.dark = userTheme.dark;
76
74
  updateStore(stored);
77
75
  }
78
- if (userTheme.light && !equal(stored.light, userTheme.light)) {
76
+ if (userTheme.light && !isEqual(stored.light, userTheme.light)) {
79
77
  stored.light = userTheme.light;
80
78
  updateStore(stored);
81
79
  }
@@ -85,6 +83,8 @@ var store = (userTheme = {}) => {
85
83
  return { ...defaultParams, ...userTheme };
86
84
  };
87
85
  updateManager(store());
86
+
87
+ // src/Tool.tsx
88
88
  function DarkMode({ api }) {
89
89
  const [isDark, setDark] = React.useState(prefersDark.matches);
90
90
  const darkModeParams = useParameter("darkMode", {});
@@ -189,8 +189,8 @@ addons.setConfig({
189
189
  ...currentStore[currentTheme]
190
190
  }
191
191
  });
192
- addons.register("storybook/dark-mode", (api) => {
193
- addons.add("storybook/dark-mode", {
192
+ addons.register("@vueless/storybook-dark-mode", (api) => {
193
+ addons.add("@vueless/storybook-dark-mode", {
194
194
  title: "dark mode",
195
195
  type: Addon_TypesEnum.TOOL,
196
196
  match: ({ viewMode }) => viewMode === "story" || viewMode === "docs",
package/dist/preset.js CHANGED
@@ -1,8 +1 @@
1
- import { fileURLToPath } from 'url';
2
1
 
3
- // src/preset.ts
4
- function managerEntries(entry = []) {
5
- return [...entry, fileURLToPath(import.meta.resolve("./manager"))];
6
- }
7
-
8
- export { managerEntries };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vueless/storybook-dark-mode",
3
- "version": "10.0.1-beta.1",
3
+ "version": "10.0.2",
4
4
  "description": "Toggle between light and dark mode in Storybook",
5
5
  "author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
6
6
  "repository": {
@@ -25,18 +25,18 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@storybook/global": "^5.0.0",
28
- "fast-deep-equal": "^3.1.3",
29
- "memoizerific": "^1.11.3"
28
+ "lodash-es": "^4.17.21"
30
29
  },
31
30
  "devDependencies": {
32
31
  "@eslint/js": "^9.33.0",
33
32
  "@release-it/bumper": "^7.0.5",
34
33
  "@release-it/conventional-changelog": "^10.0.1",
35
- "@storybook/builder-vite": "^10.0.0",
34
+ "@storybook/builder-vite": "^10.0.4",
36
35
  "@storybook/icons": "^1.4.0",
37
- "@storybook/react": "^10.0.0",
38
- "@storybook/react-vite": "^10.0.0",
36
+ "@storybook/react": "^10.0.4",
37
+ "@storybook/react-vite": "^10.0.4",
39
38
  "@stylistic/eslint-plugin": "^5.2.3",
39
+ "@types/lodash-es": "^4.17.12",
40
40
  "@types/node": "^24.3.0",
41
41
  "@types/react": "^18.0.26",
42
42
  "eslint": "^9.33.0",
@@ -50,7 +50,7 @@
50
50
  "react-dom": "^18.2.0",
51
51
  "release-it": "^19.0.4",
52
52
  "rimraf": "^3.0.2",
53
- "storybook": "^10.0.0",
53
+ "storybook": "^10.0.4",
54
54
  "ts-node": "^10.9.2",
55
55
  "tsup": "^8.0.0",
56
56
  "typescript": "^5.9.2",
@@ -58,7 +58,7 @@
58
58
  "vite": "^7.1.11"
59
59
  },
60
60
  "peerDependencies": {
61
- "storybook": "^9.0.0 || ^10.0.0"
61
+ "storybook": "^10.0.0"
62
62
  },
63
63
  "type": "module",
64
64
  "main": "dist/index.js",
@@ -83,13 +83,13 @@
83
83
  ],
84
84
  "bundler": {
85
85
  "managerEntries": [
86
- "src/preset/manager.tsx"
86
+ "./src/preset/manager.tsx"
87
87
  ],
88
88
  "previewEntries": [
89
- "src/index.tsx"
89
+ "./src/index.tsx"
90
90
  ],
91
91
  "nodeEntries": [
92
- "src/preset.ts"
92
+ "./src/preset.ts"
93
93
  ]
94
94
  },
95
95
  "prettier": {
package/preset.js CHANGED
@@ -1 +1,7 @@
1
- export * from "./dist/preset.js";
1
+ import { fileURLToPath } from "url";
2
+
3
+ function managerEntries(entry = []) {
4
+ return [...entry, fileURLToPath(import.meta.resolve("./dist/manager"))];
5
+ }
6
+
7
+ export { managerEntries };
package/src/Tool.tsx CHANGED
@@ -1,138 +1,18 @@
1
1
  import * as React from "react";
2
- import { global } from "@storybook/global";
3
- import { themes, ThemeVars } from "storybook/theming";
4
2
  import { IconButton } from "storybook/internal/components";
5
3
  import { MoonIcon, SunIcon } from "@storybook/icons";
6
- import { STORY_CHANGED, SET_STORIES, DOCS_RENDERED } from "storybook/internal/core-events";
7
4
  import { API, useParameter } from "storybook/manager-api";
8
- import equal from "fast-deep-equal";
5
+ import { SET_STORIES, DOCS_RENDERED, STORY_CHANGED } from "storybook/internal/core-events";
9
6
  import { DARK_MODE_EVENT_NAME, UPDATE_DARK_MODE_EVENT_NAME } from "./constants";
10
-
11
- const { document, window } = global as { document: Document; window: Window };
12
-
13
- type Mode = "light" | "dark";
14
-
15
- interface DarkModeStore {
16
- /** The class target in the preview iframe */
17
- classTarget: string;
18
- /** The current mode the storybook is set to */
19
- current: Mode;
20
- /** The dark theme for storybook */
21
- dark: ThemeVars;
22
- /** The dark class name for the preview iframe */
23
- darkClass: string | string[];
24
- /** The light theme for storybook */
25
- light: ThemeVars;
26
- /** The light class name for the preview iframe */
27
- lightClass: string | string[];
28
- /** Apply mode to iframe */
29
- stylePreview: boolean;
30
- /** Persist if the user has set the theme */
31
- userHasExplicitlySetTheTheme: boolean;
32
- }
33
-
34
- const STORAGE_KEY = "sb-addon-themes-3";
35
-
36
- export const prefersDark = window.matchMedia?.("(prefers-color-scheme: dark)");
37
-
38
- const defaultParams: Required<Omit<DarkModeStore, "current">> = {
39
- classTarget: "body",
40
- dark: themes.dark,
41
- darkClass: ["dark"],
42
- light: themes.light,
43
- lightClass: ["light"],
44
- stylePreview: false,
45
- userHasExplicitlySetTheTheme: false,
46
- };
47
-
48
- /** Persist the dark mode settings in localStorage */
49
- export const updateStore = (newStore: DarkModeStore) => {
50
- window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newStore));
51
- };
52
-
53
- /** Add the light/dark class to an element */
54
- const toggleDarkClass = (
55
- el: Element,
56
- {
57
- current,
58
- darkClass = defaultParams.darkClass,
59
- lightClass = defaultParams.lightClass,
60
- }: DarkModeStore,
61
- ) => {
62
- if (current === "dark") {
63
- el.classList.remove(...arrayify(lightClass));
64
- el.classList.add(...arrayify(darkClass));
65
- } else {
66
- el.classList.remove(...arrayify(darkClass));
67
- el.classList.add(...arrayify(lightClass));
68
- }
69
- };
70
-
71
- /** Coerce a string to a single item array, or return an array as-is */
72
- const arrayify = (classes: string | string[]): string[] => {
73
- const arr: string[] = [];
74
-
75
- return arr.concat(classes).map((item) => item);
76
- };
77
-
78
- /** Update the preview iframe class */
79
- const updatePreview = (store: DarkModeStore) => {
80
- const iframe = document.getElementById("storybook-preview-iframe") as HTMLIFrameElement;
81
-
82
- if (!iframe) {
83
- return;
84
- }
85
-
86
- const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
87
- const target = iframeDocument?.querySelector<HTMLElement>(store.classTarget);
88
-
89
- if (!target) {
90
- return;
91
- }
92
-
93
- toggleDarkClass(target, store);
94
- };
95
-
96
- /** Update the manager iframe class */
97
- const updateManager = (store: DarkModeStore) => {
98
- const manager = document.querySelector(store.classTarget);
99
-
100
- if (!manager) {
101
- return;
102
- }
103
-
104
- toggleDarkClass(manager, store);
105
- };
106
-
107
- /** Update changed dark mode settings and persist to localStorage */
108
- export const store = (userTheme: Partial<DarkModeStore> = {}): DarkModeStore => {
109
- const storedItem = window.localStorage.getItem(STORAGE_KEY);
110
-
111
- if (typeof storedItem === "string") {
112
- const stored = JSON.parse(storedItem) as DarkModeStore;
113
-
114
- if (userTheme) {
115
- if (userTheme.dark && !equal(stored.dark, userTheme.dark)) {
116
- stored.dark = userTheme.dark;
117
- updateStore(stored);
118
- }
119
-
120
- if (userTheme.light && !equal(stored.light, userTheme.light)) {
121
- stored.light = userTheme.light;
122
- updateStore(stored);
123
- }
124
- }
125
-
126
- return stored;
127
- }
128
-
129
- return { ...defaultParams, ...userTheme } as DarkModeStore;
130
- };
131
-
132
- // On initial load, set the dark mode class on the manager
133
- // This is needed if you're using mostly CSS overrides to styles the storybook
134
- // Otherwise the default theme is set in src/preset/manager.tsx
135
- updateManager(store());
7
+ import {
8
+ store,
9
+ prefersDark,
10
+ updateStore,
11
+ updateManager,
12
+ updatePreview,
13
+ type DarkModeStore,
14
+ type Mode,
15
+ } from "./store";
136
16
 
137
17
  interface DarkModeProps {
138
18
  /** The storybook API */
package/src/index.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useState, useEffect } from "react";
2
2
  import { addons } from "storybook/preview-api";
3
3
  import { DARK_MODE_EVENT_NAME } from "./constants";
4
- import { store } from "./Tool";
4
+ import { store } from "./store";
5
5
 
6
6
  /**
7
7
  * Returns the current state of storybook's dark-mode
@@ -3,7 +3,8 @@ import { Addon_TypesEnum } from "storybook/internal/types";
3
3
  import { themes } from "storybook/theming";
4
4
  import * as React from "react";
5
5
 
6
- import Tool, { prefersDark, store } from "../Tool";
6
+ import Tool from "../Tool";
7
+ import { prefersDark, store } from "../store";
7
8
 
8
9
  const currentStore = store();
9
10
  const currentTheme = currentStore.current || (prefersDark.matches && "dark") || "light";
@@ -15,8 +16,8 @@ addons.setConfig({
15
16
  },
16
17
  });
17
18
 
18
- addons.register("storybook/dark-mode", (api) => {
19
- addons.add("storybook/dark-mode", {
19
+ addons.register("@vueless/storybook-dark-mode", (api) => {
20
+ addons.add("@vueless/storybook-dark-mode", {
20
21
  title: "dark mode",
21
22
  type: Addon_TypesEnum.TOOL,
22
23
  match: ({ viewMode }) => viewMode === "story" || viewMode === "docs",
package/src/preset.ts CHANGED
@@ -1,5 +1 @@
1
- import { fileURLToPath } from "node:url";
2
-
3
- export function managerEntries(entry: string[] = []) {
4
- return [...entry, fileURLToPath(import.meta.resolve("./manager"))];
5
- }
1
+ /* define addon preset here */
package/src/store.ts ADDED
@@ -0,0 +1,115 @@
1
+ import { global } from "@storybook/global";
2
+ import { themes, ThemeVars } from "storybook/theming";
3
+ import { isEqual } from "lodash-es";
4
+
5
+ const { document, window } = global as { document: Document; window: Window };
6
+
7
+ type Mode = "light" | "dark";
8
+
9
+ interface DarkModeStore {
10
+ classTarget: string;
11
+ current: Mode;
12
+ dark: ThemeVars;
13
+ darkClass: string | string[];
14
+ light: ThemeVars;
15
+ lightClass: string | string[];
16
+ stylePreview: boolean;
17
+ userHasExplicitlySetTheTheme: boolean;
18
+ }
19
+
20
+ const STORAGE_KEY = "sb-addon-themes-3";
21
+
22
+ export const prefersDark = window.matchMedia?.("(prefers-color-scheme: dark)");
23
+
24
+ const defaultParams: Required<Omit<DarkModeStore, "current">> = {
25
+ classTarget: "body",
26
+ dark: themes.dark,
27
+ darkClass: ["dark"],
28
+ light: themes.light,
29
+ lightClass: ["light"],
30
+ stylePreview: false,
31
+ userHasExplicitlySetTheTheme: false,
32
+ };
33
+
34
+ export const updateStore = (newStore: DarkModeStore) => {
35
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newStore));
36
+ };
37
+
38
+ const arrayify = (classes: string | string[]): string[] => {
39
+ const arr: string[] = [];
40
+
41
+ return arr.concat(classes).map((item) => item);
42
+ };
43
+
44
+ const toggleDarkClass = (
45
+ el: Element,
46
+ {
47
+ current,
48
+ darkClass = defaultParams.darkClass,
49
+ lightClass = defaultParams.lightClass,
50
+ }: DarkModeStore,
51
+ ) => {
52
+ if (current === "dark") {
53
+ el.classList.remove(...arrayify(lightClass));
54
+ el.classList.add(...arrayify(darkClass));
55
+ } else {
56
+ el.classList.remove(...arrayify(darkClass));
57
+ el.classList.add(...arrayify(lightClass));
58
+ }
59
+ };
60
+
61
+ const updatePreview = (store: DarkModeStore) => {
62
+ const iframe = document.getElementById("storybook-preview-iframe") as HTMLIFrameElement;
63
+
64
+ if (!iframe) {
65
+ return;
66
+ }
67
+
68
+ const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
69
+ const target = iframeDocument?.querySelector<HTMLElement>(store.classTarget);
70
+
71
+ if (!target) {
72
+ return;
73
+ }
74
+
75
+ toggleDarkClass(target, store);
76
+ };
77
+
78
+ export const updateManager = (store: DarkModeStore) => {
79
+ const manager = document.querySelector(store.classTarget);
80
+
81
+ if (!manager) {
82
+ return;
83
+ }
84
+
85
+ toggleDarkClass(manager, store);
86
+ };
87
+
88
+ export const store = (userTheme: Partial<DarkModeStore> = {}): DarkModeStore => {
89
+ const storedItem = window.localStorage.getItem(STORAGE_KEY);
90
+
91
+ if (typeof storedItem === "string") {
92
+ const stored = JSON.parse(storedItem) as DarkModeStore;
93
+
94
+ if (userTheme) {
95
+ if (userTheme.dark && !isEqual(stored.dark, userTheme.dark)) {
96
+ stored.dark = userTheme.dark;
97
+ updateStore(stored);
98
+ }
99
+
100
+ if (userTheme.light && !isEqual(stored.light, userTheme.light)) {
101
+ stored.light = userTheme.light;
102
+ updateStore(stored);
103
+ }
104
+ }
105
+
106
+ return stored;
107
+ }
108
+
109
+ return { ...defaultParams, ...userTheme } as DarkModeStore;
110
+ };
111
+
112
+ updateManager(store());
113
+
114
+ export { updatePreview };
115
+ export type { DarkModeStore, Mode };