diff --git a/package-lock.json b/package-lock.json index 7c57316..ee8f054 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,36 +22,36 @@ "react": "18.2.0", "react-dom": "18.2.0", "typescript": "5.3.3", - "zustand": "4.5.0" + "zustand": "4.5.1" }, "devDependencies": { - "@slicemachine/adapter-next": "0.3.34", + "@slicemachine/adapter-next": "0.3.35", "@tailwindcss/aspect-ratio": "0.4.2", - "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.2.1", - "@types/jest": "^29.5.12", - "@types/node": "20.11.16", - "@types/react": "18.2.55", - "@types/react-dom": "18.2.18", + "@testing-library/jest-dom": "6.4.2", + "@testing-library/react": "14.2.1", + "@types/jest": "29.5.12", + "@types/node": "20.11.19", + "@types/react": "18.2.57", + "@types/react-dom": "18.2.19", "autoprefixer": "10.4.17", "concurrently": "8.2.2", "eslint": "8.56.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "9.1.0", "eslint-config-typescript": "3.0.0", - "eslint-plugin-jest": "^27.6.3", - "eslint-plugin-jest-formatting": "^3.1.0", + "eslint-plugin-jest": "27.9.0", + "eslint-plugin-jest-formatting": "3.1.0", "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-testing-library": "^6.2.0", + "eslint-plugin-testing-library": "6.2.0", "eslint-plugin-unicorn": "51.0.1", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "postcss": "8.4.34", + "jest": "29.7.0", + "jest-environment-jsdom": "29.7.0", + "postcss": "8.4.35", "prettier": "3.2.5", "prettier-plugin-tailwindcss": "0.5.11", - "slice-machine-ui": "1.23.1", + "slice-machine-ui": "1.24.0", "tailwindcss": "3.4.1", - "ts-node": "^10.9.2" + "ts-node": "10.9.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2253,14 +2253,14 @@ } }, "node_modules/@slicemachine/adapter-next": { - "version": "0.3.34", - "resolved": "https://registry.npmjs.org/@slicemachine/adapter-next/-/adapter-next-0.3.34.tgz", - "integrity": "sha512-tK6GcI8YOvPk0wfFImMlOTzG0cQ3Nc1HyT92Z/bZG5I7quKrzRgLm5tlrYQ1KkolNwN55cGnFToFNExcHe8t7g==", + "version": "0.3.35", + "resolved": "https://registry.npmjs.org/@slicemachine/adapter-next/-/adapter-next-0.3.35.tgz", + "integrity": "sha512-0rAk1PqHx91XnVIqUbYqA2Cb7i6plRrL+PTolsad3ojGs2M/L2KKb91nRDBa9kcSyVwgMNpkEM7i2tlVOPFg1w==", "dev": true, "dependencies": { "@prismicio/simulator": "^0.1.4", "@prismicio/types-internal": "^2.2.0", - "@slicemachine/plugin-kit": "0.4.34", + "@slicemachine/plugin-kit": "0.4.35", "common-tags": "^1.8.2", "fp-ts": "^2.13.1", "io-ts": "^2.2.20", @@ -2280,9 +2280,9 @@ } }, "node_modules/@slicemachine/plugin-kit": { - "version": "0.4.34", - "resolved": "https://registry.npmjs.org/@slicemachine/plugin-kit/-/plugin-kit-0.4.34.tgz", - "integrity": "sha512-B4Rng2NBZknpVLoKg3ll2gj7/46OtH0yRHZgCPafzID4P0xJJfCCa3y2crEBnFFEVmFmwm1A6k+ErgeROVpPGA==", + "version": "0.4.35", + "resolved": "https://registry.npmjs.org/@slicemachine/plugin-kit/-/plugin-kit-0.4.35.tgz", + "integrity": "sha512-RBhjRdMImarZtf5Ll4VJHVm4MNhuHE1Im6J72O6pftvEl9IviwymuJ6sKuxWzRREsjMNsDPU5s99ftWbuRSEzQ==", "dev": true, "dependencies": { "common-tags": "^1.8.2", @@ -2571,9 +2571,9 @@ } }, "node_modules/@types/hast": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.9.tgz", - "integrity": "sha512-pTHyNlaMD/oKJmS+ZZUyFUcsZeBZpC0lmGquw98CqRVNgAdJZJeD7GoeLiT6Xbx5rU9VCjSt0RwEvDgzh4obFw==", + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "dev": true, "dependencies": { "@types/unist": "^2" @@ -2705,9 +2705,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", - "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2732,9 +2732,9 @@ "devOptional": true }, "node_modules/@types/react": { - "version": "18.2.55", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", - "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", + "version": "18.2.57", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.57.tgz", + "integrity": "sha512-ZvQsktJgSYrQiMirAN60y4O/LRevIV8hUzSOSNB6gfR3/o3wCBFQx3sPwIYtuDMeiVgsSS3UzCV26tEzgnfvQw==", "devOptional": true, "dependencies": { "@types/prop-types": "*", @@ -2743,9 +2743,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.18", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", - "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", + "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", "dev": true, "dependencies": { "@types/react": "*" @@ -5410,9 +5410,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.6.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.3.tgz", - "integrity": "sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA==", + "version": "27.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", + "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -5421,7 +5421,7 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", + "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", "eslint": "^7.0.0 || ^8.0.0", "jest": "*" }, @@ -10457,9 +10457,9 @@ } }, "node_modules/node-fetch-native": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.1.tgz", - "integrity": "sha512-bW9T/uJDPAJB2YNYEpWzE54U5O3MQidXsOyTfnbKYtTtFexRvGzb1waphBN4ZwP6EcIvYYEOwW0b72BpAqydTw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.2.tgz", + "integrity": "sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w==", "dev": true }, "node_modules/node-int64": { @@ -11059,9 +11059,9 @@ } }, "node_modules/postcss": { - "version": "8.4.34", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.34.tgz", - "integrity": "sha512-4eLTO36woPSocqZ1zIrFD2K1v6wH7pY1uBh0JIM2KKfrVtGvPFiAku6aNOP0W1Wr9qwnaCsF0Z+CrVnryB2A8Q==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -12389,14 +12389,14 @@ } }, "node_modules/slice-machine-ui": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/slice-machine-ui/-/slice-machine-ui-1.23.1.tgz", - "integrity": "sha512-7SXsJYbFtnajdeMosDGN2GM50SufeeuushIpUP7QFTCqSaupHLhhoceJ33vswz/Cnra04Ipz8jBSNPBOGR31VA==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/slice-machine-ui/-/slice-machine-ui-1.24.0.tgz", + "integrity": "sha512-TvpG1SHEJirwTdBon+eDRbCAyM2m2NbSo1jYBAz89ybiNJjcPGArVfzlmWmv6GOQ5wSw/VAKBJo8SmXpRA7N3w==", "dev": true, "dependencies": { "@radix-ui/react-visually-hidden": "1.0.3", - "@slicemachine/manager": "0.17.8", - "start-slicemachine": "0.12.14" + "@slicemachine/manager": "0.18.0", + "start-slicemachine": "0.12.15" }, "bin": { "start-slicemachine": "bin/start-slicemachine.cjs" @@ -12446,9 +12446,9 @@ } }, "node_modules/slice-machine-ui/node_modules/@slicemachine/manager": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/@slicemachine/manager/-/manager-0.17.8.tgz", - "integrity": "sha512-/TqxPciCFIRFmzrl/GeZTrrfeW/KfKzooYq26NdIuxeIM7Szpw+Je/Cf4t1YMGnM9Xp1zPLKisGoZQZoJhDRtA==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@slicemachine/manager/-/manager-0.18.0.tgz", + "integrity": "sha512-v5YJWr3lyTQSdVbxQVAeOjmZ9clMzOZziZjnmmhbUiUt4ieWFzsr+4sWJSCAZQMfxoBv2J1/UK3/ZLXwAootag==", "dev": true, "dependencies": { "@antfu/ni": "^0.20.0", @@ -12456,7 +12456,7 @@ "@prismicio/mocks": "2.0.0", "@prismicio/types-internal": "^2.2.0", "@segment/analytics-node": "1.1.3", - "@slicemachine/plugin-kit": "0.4.34", + "@slicemachine/plugin-kit": "0.4.35", "@wooorm/starry-night": "^1.6.0", "cookie": "^0.5.0", "cors": "^2.8.5", @@ -12634,14 +12634,14 @@ } }, "node_modules/start-slicemachine": { - "version": "0.12.14", - "resolved": "https://registry.npmjs.org/start-slicemachine/-/start-slicemachine-0.12.14.tgz", - "integrity": "sha512-cDmvYxmZudRNSxrMCDhMTUBEluX7Colt8WJ9oKoJD+2+F+Bjj9VR+/eZVvHCw34DBkayiMN199s8rSE7Qg6/iA==", + "version": "0.12.15", + "resolved": "https://registry.npmjs.org/start-slicemachine/-/start-slicemachine-0.12.15.tgz", + "integrity": "sha512-axKGznkN2AR5qC7jK5jeQBZOkWBIJ69mUSd1FsEvaX6H19qUYz1/tuTqBPb/Hp/3uyEpVRfauuYeFnMBT5wJ1Q==", "dev": true, "dependencies": { "@prismicio/mocks": "^2.0.0-alpha.2", "@prismicio/types-internal": "^2.2.0", - "@slicemachine/manager": "0.17.8", + "@slicemachine/manager": "0.18.0", "body-parser": "^1.20.2", "chalk": "^4.1.2", "cors": "^2.8.5", @@ -12705,9 +12705,9 @@ } }, "node_modules/start-slicemachine/node_modules/@slicemachine/manager": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/@slicemachine/manager/-/manager-0.17.8.tgz", - "integrity": "sha512-/TqxPciCFIRFmzrl/GeZTrrfeW/KfKzooYq26NdIuxeIM7Szpw+Je/Cf4t1YMGnM9Xp1zPLKisGoZQZoJhDRtA==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@slicemachine/manager/-/manager-0.18.0.tgz", + "integrity": "sha512-v5YJWr3lyTQSdVbxQVAeOjmZ9clMzOZziZjnmmhbUiUt4ieWFzsr+4sWJSCAZQMfxoBv2J1/UK3/ZLXwAootag==", "dev": true, "dependencies": { "@antfu/ni": "^0.20.0", @@ -12715,7 +12715,7 @@ "@prismicio/mocks": "2.0.0", "@prismicio/types-internal": "^2.2.0", "@segment/analytics-node": "1.1.3", - "@slicemachine/plugin-kit": "0.4.34", + "@slicemachine/plugin-kit": "0.4.35", "@wooorm/starry-night": "^1.6.0", "cookie": "^0.5.0", "cors": "^2.8.5", @@ -13372,9 +13372,9 @@ } }, "node_modules/trough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", - "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", "dev": true, "funding": { "type": "github", @@ -13619,9 +13619,9 @@ } }, "node_modules/ufo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.2.tgz", - "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", + "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", "dev": true }, "node_modules/unbox-primitive": { @@ -14354,9 +14354,9 @@ } }, "node_modules/zustand": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz", - "integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.1.tgz", + "integrity": "sha512-XlauQmH64xXSC1qGYNv00ODaQ3B+tNPoy22jv2diYiP4eoDKr9LA+Bh5Bc3gplTrFdb6JVI+N4kc1DZ/tbtfPg==", "dependencies": { "use-sync-external-store": "1.2.0" }, diff --git a/package.json b/package.json index c6f3e44..cbba7a7 100644 --- a/package.json +++ b/package.json @@ -32,35 +32,35 @@ "react": "18.2.0", "react-dom": "18.2.0", "typescript": "5.3.3", - "zustand": "4.5.0" + "zustand": "4.5.1" }, "devDependencies": { - "@slicemachine/adapter-next": "0.3.34", + "@slicemachine/adapter-next": "0.3.35", "@tailwindcss/aspect-ratio": "0.4.2", - "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.2.1", - "@types/jest": "^29.5.12", - "@types/node": "20.11.16", - "@types/react": "18.2.55", - "@types/react-dom": "18.2.18", + "@testing-library/jest-dom": "6.4.2", + "@testing-library/react": "14.2.1", + "@types/jest": "29.5.12", + "@types/node": "20.11.19", + "@types/react": "18.2.57", + "@types/react-dom": "18.2.19", "autoprefixer": "10.4.17", "concurrently": "8.2.2", "eslint": "8.56.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "9.1.0", "eslint-config-typescript": "3.0.0", - "eslint-plugin-jest": "^27.6.3", - "eslint-plugin-jest-formatting": "^3.1.0", + "eslint-plugin-jest": "27.9.0", + "eslint-plugin-jest-formatting": "3.1.0", "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-testing-library": "^6.2.0", + "eslint-plugin-testing-library": "6.2.0", "eslint-plugin-unicorn": "51.0.1", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "postcss": "8.4.34", + "jest": "29.7.0", + "jest-environment-jsdom": "29.7.0", + "postcss": "8.4.35", "prettier": "3.2.5", "prettier-plugin-tailwindcss": "0.5.11", - "slice-machine-ui": "1.23.1", + "slice-machine-ui": "1.24.0", "tailwindcss": "3.4.1", - "ts-node": "^10.9.2" + "ts-node": "10.9.2" } -} +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 95ba6fa..eab1e25 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -44,7 +44,7 @@ @apply inline-block font-headings uppercase text-3xl; transition: all 0.15s ease-in-out; - &.button--cta { + &--cta { box-shadow: -3px 3px 0 0 theme('colors.red.500'); &:hover { diff --git a/src/components/forms/ContactForm.tsx b/src/components/forms/ContactForm.tsx index 3509adb..c449344 100644 --- a/src/components/forms/ContactForm.tsx +++ b/src/components/forms/ContactForm.tsx @@ -18,7 +18,7 @@ const ButtonText = { DEFAULT: 'Send Message', PENDING: 'Sending...', SUCCESS: 'Message Sent!', -}; +} as const; function SubmitButton({ isSuccess }: { isSuccess: boolean }) { const formStatus = useFormStatus(); diff --git a/src/components/settings/DarkModeSetting.tsx b/src/components/settings/DarkModeSetting.tsx index 56eca20..d3f363c 100644 --- a/src/components/settings/DarkModeSetting.tsx +++ b/src/components/settings/DarkModeSetting.tsx @@ -3,6 +3,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { + IconDefinition, faComputer, faMoon, faSunBright, @@ -28,16 +29,13 @@ function setThemeOnDocument(newTheme: 'dark' | 'light') { export function DarkModeSetting() { const [isOpen, setIsOpen] = useState(false); - const isOpenRef = useRef(() => isOpen); const { theme, setTheme, isDarkMode, systemDarkMode } = useBoundStore(); - const wrapperRef = useRef(null); + const popoverRef = useRef(null); useOutsideClick( - wrapperRef, + popoverRef, useCallback(() => { - if (isOpenRef.current()) { - setIsOpen(false); - } + setIsOpen(false); }, []), ); @@ -57,9 +55,7 @@ export function DarkModeSetting() { }, [isDarkMode]); return ( -
+
- - + {THEME_OPTIONS.map((option) => ( + + ))}
) : null}
); } + +const THEME_OPTIONS: { + label: string; + value: Theme; + icon: IconDefinition; + ariaTitle: string; +}[] = [ + { + label: 'Dark Theme', + value: 'dark', + icon: faMoon, + ariaTitle: 'Change colour theme to dark mode', + }, + { + label: 'Light Theme', + value: 'light', + icon: faSunBright, + ariaTitle: 'Change colour theme to light mode', + }, + { + label: 'Use System Setting', + value: 'system', + icon: faComputer, + ariaTitle: 'Use system preference for dark mode', + }, +]; diff --git a/src/store/bound.ts b/src/store/bound.ts index 695f628..3c475f7 100644 --- a/src/store/bound.ts +++ b/src/store/bound.ts @@ -3,13 +3,9 @@ import { devtools, persist } from 'zustand/middleware'; import { IS_DEV } from '@/utils/constants'; -import { - SettingsActions, - SettingsState, - createSettingsStore, -} from './settings'; +import { SettingsStore, createSettingsStore } from './settings'; -export type StoreState = SettingsState & SettingsActions; +export type StoreState = SettingsStore; export type PersistedStoreState = { state: Pick; }; @@ -23,8 +19,8 @@ export const useBoundStore = create()( { name: 'persisted-store', onRehydrateStorage: IS_DEV - ? (state) => { - console.log({ stateBeforeHydrate: state }); + ? (stateBeforeHydrate) => { + console.log({ stateBeforeHydrate }); return (stateAfterHydrate, error) => { console.log({ stateAfterHydrate }); @@ -38,7 +34,7 @@ export const useBoundStore = create()( }; } : undefined, - partialize: (state) => ({ + partialize: (state): PersistedStoreState['state'] => ({ theme: state.theme, isDarkMode: state.isDarkMode, }), diff --git a/src/store/settings.ts b/src/store/settings.ts index 29aeaeb..907f85e 100644 --- a/src/store/settings.ts +++ b/src/store/settings.ts @@ -1,5 +1,6 @@ import { StateCreator } from 'zustand'; +import { detectPrefersDarkMode } from '@/theme/detect-prefers-dark-mode'; import { Theme } from '@/theme/types'; export type SettingsState = { @@ -21,22 +22,18 @@ export type SettingsActions = { setTheme: (theme: Theme) => void; }; -export const createSettingsStore: StateCreator< - SettingsState & SettingsActions -> = (set) => { +export type SettingsStore = SettingsState & SettingsActions; + +export const createSettingsStore: StateCreator = (set) => { return { theme: 'system', - isDarkMode: - typeof window !== 'undefined' && - window.matchMedia('(prefers-color-scheme: dark)').matches, - systemDarkMode: - typeof window !== 'undefined' && - window.matchMedia('(prefers-color-scheme: dark)').matches, + isDarkMode: detectPrefersDarkMode(), + systemDarkMode: detectPrefersDarkMode(), setTheme: (theme: Theme) => { let isDarkMode = theme === 'dark'; if (theme === 'system') { - isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; + isDarkMode = detectPrefersDarkMode(); } set({ theme, isDarkMode }); @@ -45,7 +42,9 @@ export const createSettingsStore: StateCreator< }; export const SettingsSelectors = { - getThemeSetting: (state: SettingsState) => state.theme, - getThemeSettingWithSetter: (state: SettingsState & SettingsActions) => + getTheme: (state: SettingsStore) => state.theme, + getThemeWithSetter: (state: SettingsStore) => [state.theme, state.setTheme] as const, + getIsDarkMode: (state: SettingsStore) => state.isDarkMode, + getSystemDarkMode: (state: SettingsStore) => state.systemDarkMode, }; diff --git a/src/theme/detect-prefers-dark-mode.ts b/src/theme/detect-prefers-dark-mode.ts new file mode 100644 index 0000000..ff10e46 --- /dev/null +++ b/src/theme/detect-prefers-dark-mode.ts @@ -0,0 +1,6 @@ +export function detectPrefersDarkMode() { + return ( + typeof window !== 'undefined' && + window.matchMedia('(prefers-color-scheme: dark)').matches + ); +}