diff --git a/.storybook/preview.js b/.storybook/preview.js index 86df5cb..da6af88 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,4 +1,4 @@ -import { themes } from '@storybook/theming'; +import { themes } from '@storybook/theming' import 'theme/css/preflight.css' import './assets/global-style.css' diff --git a/package.json b/package.json index 73f89f7..5552603 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "react", "typescript" ], - "version": "2.1.0", + "version": "2.1.1", "main": "./dist/index.cjs.js", "module": "./dist/index.esm.js", "types": "./dist/index.d.ts", @@ -66,6 +66,7 @@ "cross-env": "^7.0.3", "css-minify": "^2.0.0", "cz-conventional-changelog": "^3.3.0", + "darkreader": "^4.9.58", "dotenv": "^16.0.3", "esbuild": "^0.14.25", "eslint": "^8.25.0", diff --git a/src/core/DarkMode.tsx b/src/core/DarkMode.tsx new file mode 100644 index 0000000..83fd1a4 --- /dev/null +++ b/src/core/DarkMode.tsx @@ -0,0 +1,93 @@ +import React, { useCallback, useEffect, useState } from 'react' + +import { enable, disable } from 'darkreader' + +const themeStorageName = 'design-system-theme-mode' + +const DarkMode = () => { + const [isDarkModeActive, setIsDarkModeActive] = useState(() => { + try { + if (typeof localStorage !== 'undefined') { + if (localStorage.getItem(themeStorageName) === 'dark') { + return true + } + } + } catch { + // do nothing + } + + return false + }) + + const handleEnable = useCallback(() => { + try { + if (typeof localStorage !== 'undefined') { + localStorage.setItem(themeStorageName, 'dark') + } + } catch { + // do nothing + } + + setIsDarkModeActive(true) + }, []) + + const handleDisable = useCallback(() => { + try { + if (typeof localStorage !== 'undefined') { + localStorage.setItem(themeStorageName, 'default') + } + } catch { + // do nothing + } + + setIsDarkModeActive(false) + }, []) + + useEffect(() => { + if (isDarkModeActive) { + enable({ + brightness: 100, + contrast: 100, + sepia: 0, + }) + } else { + disable() + } + }, [isDarkModeActive]) + + useEffect(() => { + const interval = setInterval(() => { + try { + if (typeof localStorage !== 'undefined') { + if (localStorage.getItem(themeStorageName) === 'dark') { + setIsDarkModeActive(true) + } else { + setIsDarkModeActive(false) + } + } + } catch { + // do nothing + } + }, 200) + + return () => { + clearInterval(interval) + } + }, [isDarkModeActive]) + + if (isDarkModeActive) { + return ( + + ) + } + + return ( + + ) +} + +export default DarkMode diff --git a/src/core/storybookWrappers.tsx b/src/core/storybookWrappers.tsx index f556baa..d86bad0 100644 --- a/src/core/storybookWrappers.tsx +++ b/src/core/storybookWrappers.tsx @@ -6,13 +6,18 @@ import { PreviewProps as PurePreviewProps } from '@storybook/components' import { ThemeProvider } from 'theme' import { WithChildren } from 'types/react-definitions' +import DarkMode from './DarkMode' + type BaseCanvasProps = PurePreviewProps & { withSource?: SourceState mdxSource?: string } export const Canvas = ({ children, ...props }: WithChildren) => ( - - {children} - + <> + + + {children} + + ) diff --git a/yarn.lock b/yarn.lock index ae5dbbc..3f3c173 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5728,6 +5728,11 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== +darkreader@^4.9.58: + version "4.9.58" + resolved "https://registry.yarnpkg.com/darkreader/-/darkreader-4.9.58.tgz#6544d6cb51fa643dc7995d128d32b357e1650e49" + integrity sha512-D/JGoJqW3m2AWBLhO+Pev+eThfs+CwRT4bcLb/1zKjql2yVwG0lx8C2XRDdSVGHw4y11n26W7syWoBpUfuhMqQ== + date-fns@^2.29.1: version "2.29.3" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"