Replace own theme solution with next-themes
This commit is contained in:
parent
d92b9ecadb
commit
03326adbaf
15 changed files with 95 additions and 82 deletions
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/node'
|
||||
import React, { useRef } from 'react'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
|
||||
import { ErrorBoundary } from 'react-error-boundary'
|
||||
import state, { useSelector } from 'state'
|
||||
|
@ -33,23 +33,28 @@ export default function Canvas(): JSX.Element {
|
|||
|
||||
const events = useCanvasEvents(rCanvas)
|
||||
|
||||
const isSettingCamera = useSelector((s) => s.isIn('settingCamera'))
|
||||
const isReady = useSelector((s) => s.isIn('ready'))
|
||||
|
||||
useEffect(() => {
|
||||
if (isSettingCamera) {
|
||||
state.send('MOUNTED_SHAPES')
|
||||
}
|
||||
}, [isSettingCamera])
|
||||
|
||||
return (
|
||||
<ContextMenu>
|
||||
<MainSVG ref={rCanvas} {...events}>
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={resetError}>
|
||||
<Defs />
|
||||
{isReady && (
|
||||
<g ref={rGroup} id="shapes">
|
||||
<BoundsBg />
|
||||
<Page />
|
||||
<Coop />
|
||||
<Bounds />
|
||||
<Handles />
|
||||
<Brush />
|
||||
</g>
|
||||
)}
|
||||
<g ref={rGroup} id="shapes" opacity={isReady ? 1 : 0}>
|
||||
<BoundsBg />
|
||||
<Page />
|
||||
<Coop />
|
||||
<Bounds />
|
||||
<Handles />
|
||||
<Brush />
|
||||
</g>
|
||||
</ErrorBoundary>
|
||||
</MainSVG>
|
||||
</ContextMenu>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Editor, { Monaco } from '@monaco-editor/react'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { useTheme } from 'next-themes'
|
||||
import libImport from './es5-lib'
|
||||
import typesImport from './types-import'
|
||||
import React, { useCallback, useEffect, useRef } from 'react'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import useKeyboardEvents from 'hooks/useKeyboardEvents'
|
||||
import useLoadOnMount from 'hooks/useLoadOnMount'
|
||||
import useStateTheme from 'hooks/useStateTheme'
|
||||
import Menu from './menu/menu'
|
||||
import Canvas from './canvas/canvas'
|
||||
import ToolsPanel from './tools-panel/tools-panel'
|
||||
|
@ -13,6 +14,7 @@ import ControlsPanel from './controls-panel/controls-panel'
|
|||
export default function Editor({ roomId }: { roomId?: string }): JSX.Element {
|
||||
useKeyboardEvents()
|
||||
useLoadOnMount(roomId)
|
||||
useStateTheme()
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
|
|
|
@ -18,11 +18,11 @@ import {
|
|||
import state, { useSelector } from 'state'
|
||||
import { commandKey } from 'utils'
|
||||
import { signOut } from 'next-auth/client'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
const handleNew = () => state.send('CREATED_NEW_PROJECT')
|
||||
const handleSave = () => state.send('SAVED')
|
||||
const handleLoad = () => state.send('LOADED_FROM_FILE_STSTEM')
|
||||
const toggleDarkMode = () => state.send('TOGGLED_DARK_MODE')
|
||||
const toggleDebugMode = () => state.send('TOGGLED_DEBUG_MODE')
|
||||
|
||||
function Menu() {
|
||||
|
@ -99,14 +99,16 @@ function RecentFiles() {
|
|||
}
|
||||
|
||||
function Preferences() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
const isDebugMode = useSelector((s) => s.data.settings.isDebugMode)
|
||||
const isDarkMode = useSelector((s) => s.data.settings.isDarkMode)
|
||||
const isDarkMode = theme === 'dark'
|
||||
|
||||
return (
|
||||
<DropdownMenuSubMenu label="Preferences">
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={isDarkMode}
|
||||
onCheckedChange={toggleDarkMode}
|
||||
onCheckedChange={() => setTheme(isDarkMode ? 'light' : 'dark')}
|
||||
>
|
||||
<span>Dark Mode</span>
|
||||
</DropdownMenuCheckboxItem>
|
||||
|
|
|
@ -4,7 +4,7 @@ import Tooltip from 'components/tooltip'
|
|||
import { strokes } from 'state/shape-styles'
|
||||
import state, { useSelector } from 'state'
|
||||
import { BoxIcon, Item, DropdownContent } from '../shared'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { ColorStyle } from 'types'
|
||||
|
||||
function handleColorChange(color: ColorStyle): void {
|
||||
|
|
|
@ -115,19 +115,21 @@ export default function useCanvasEvents(
|
|||
rCanvas.current.addEventListener('touchstart', preventNavigation)
|
||||
|
||||
return () => {
|
||||
rCanvas.current.removeEventListener(
|
||||
'gestureend',
|
||||
preventGestureNavigation
|
||||
)
|
||||
rCanvas.current.removeEventListener(
|
||||
'gesturechange',
|
||||
preventGestureNavigation
|
||||
)
|
||||
rCanvas.current.removeEventListener(
|
||||
'gesturestart',
|
||||
preventGestureNavigation
|
||||
)
|
||||
rCanvas.current.removeEventListener('touchstart', preventNavigation)
|
||||
if (rCanvas.current) {
|
||||
rCanvas.current.removeEventListener(
|
||||
'gestureend',
|
||||
preventGestureNavigation
|
||||
)
|
||||
rCanvas.current.removeEventListener(
|
||||
'gesturechange',
|
||||
preventGestureNavigation
|
||||
)
|
||||
rCanvas.current.removeEventListener(
|
||||
'gesturestart',
|
||||
preventGestureNavigation
|
||||
)
|
||||
rCanvas.current.removeEventListener('touchstart', preventNavigation)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
|
11
hooks/useStateTheme.ts
Normal file
11
hooks/useStateTheme.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { useTheme } from 'next-themes'
|
||||
import { useEffect } from 'react'
|
||||
import state from 'state'
|
||||
|
||||
export default function useStateTheme(): void {
|
||||
const { theme } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
state.send('CHANGED_DARK_MODE', { isDarkMode: theme === 'dark' })
|
||||
}, [theme])
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { useCallback } from 'react'
|
||||
import state, { useSelector } from 'state'
|
||||
import { Theme } from 'types'
|
||||
|
||||
export default function useTheme() {
|
||||
const theme: Theme = useSelector((state) =>
|
||||
state.data.settings.isDarkMode ? 'dark' : 'light'
|
||||
)
|
||||
|
||||
const toggleTheme = useCallback(() => state.send('TOGGLED_THEME'), [])
|
||||
|
||||
return { theme, toggleTheme }
|
||||
}
|
|
@ -57,6 +57,7 @@
|
|||
"next": "^11.0.1",
|
||||
"next-auth": "^3.27.3",
|
||||
"next-pwa": "^5.2.23",
|
||||
"next-themes": "^0.0.15",
|
||||
"perfect-freehand": "^0.4.91",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import useGtag from 'hooks/useGtag'
|
||||
import Head from 'next/head'
|
||||
import { AppProps } from 'next/app'
|
||||
import { globalStyles } from 'styles'
|
||||
import { globalStyles, dark, light } from 'styles'
|
||||
import { Provider } from 'next-auth/client'
|
||||
import { init } from 'utils/sentry'
|
||||
import 'styles/globals.css'
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
|
||||
init()
|
||||
|
||||
|
@ -22,9 +23,16 @@ function MyApp({ Component, pageProps }: AppProps): JSX.Element {
|
|||
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, user-scalable=no, viewport-fit=cover"
|
||||
/>
|
||||
</Head>
|
||||
<Provider session={pageProps.session}>
|
||||
<Component {...pageProps} />
|
||||
</Provider>
|
||||
<ThemeProvider
|
||||
disableTransitionOnChange
|
||||
attribute="class"
|
||||
value={{ light: light.toString(), dark: dark.toString() }}
|
||||
defaultTheme="light"
|
||||
>
|
||||
<Provider session={pageProps.session}>
|
||||
<Component {...pageProps} />
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import NextDocument, {
|
|||
NextScript,
|
||||
DocumentContext,
|
||||
} from 'next/document'
|
||||
import { dark, getCssString } from 'styles'
|
||||
import { getCssString } from 'styles'
|
||||
import { GA_TRACKING_ID } from 'utils/gtag'
|
||||
|
||||
class MyDocument extends NextDocument {
|
||||
|
@ -90,7 +90,7 @@ class MyDocument extends NextDocument {
|
|||
}}
|
||||
/>
|
||||
</Head>
|
||||
<body className={dark}>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
|
|
|
@ -8,7 +8,6 @@ import storage from './storage'
|
|||
import session from './session'
|
||||
import clipboard from './clipboard'
|
||||
import commands from './commands'
|
||||
import { dark, light } from 'styles'
|
||||
import {
|
||||
vec,
|
||||
getCommonBounds,
|
||||
|
@ -47,8 +46,8 @@ const initialData: Data = {
|
|||
settings: {
|
||||
fontSize: 13,
|
||||
isTestMode: false,
|
||||
isDarkMode: true,
|
||||
isCodeOpen: false,
|
||||
isDarkMode: false,
|
||||
isDebugMode: false,
|
||||
isDebugOpen: false,
|
||||
isStyleOpen: false,
|
||||
|
@ -148,8 +147,8 @@ for (let i = 0; i < count; i++) {
|
|||
|
||||
const state = createState({
|
||||
data: initialData,
|
||||
onEnter: 'applyTheme',
|
||||
on: {
|
||||
CHANGED_DARK_MODE: 'setDarkMode',
|
||||
TOGGLED_DEBUG_PANEL: 'toggleDebugPanel',
|
||||
TOGGLED_DEBUG_MODE: 'toggleDebugMode',
|
||||
TOGGLED_TEST_MODE: 'toggleTestMode',
|
||||
|
@ -168,20 +167,23 @@ const state = createState({
|
|||
'resetHistory',
|
||||
'resetStorage',
|
||||
'restoredPreviousDocument',
|
||||
{ to: 'settingCamera' },
|
||||
],
|
||||
},
|
||||
},
|
||||
settingCamera: {
|
||||
on: {
|
||||
MOUNTED_SHAPES: [
|
||||
{
|
||||
if: 'hasSelection',
|
||||
do: 'zoomCameraToSelectionActual',
|
||||
else: 'zoomCameraToFit',
|
||||
},
|
||||
{ to: 'ready' },
|
||||
],
|
||||
},
|
||||
},
|
||||
ready: {
|
||||
onEnter: [
|
||||
'applyTheme',
|
||||
{
|
||||
wait: 0.01,
|
||||
if: 'hasSelection',
|
||||
do: 'zoomCameraToSelectionActual',
|
||||
else: ['zoomCameraToActual'],
|
||||
},
|
||||
],
|
||||
on: {
|
||||
UNMOUNTED: {
|
||||
do: ['saveDocumentState', 'resetDocumentState'],
|
||||
|
@ -214,9 +216,6 @@ const state = createState({
|
|||
unlessAny: ['isReadOnly', 'isInSession'],
|
||||
do: 'pasteShapesFromClipboard',
|
||||
},
|
||||
TOGGLED_DARK_MODE: {
|
||||
do: ['toggleDarkMode', 'applyTheme'],
|
||||
},
|
||||
TOGGLED_SHAPE_LOCK: {
|
||||
unlessAny: ['isReadOnly', 'isInSession'],
|
||||
if: 'hasSelection',
|
||||
|
@ -1290,6 +1289,10 @@ const state = createState({
|
|||
},
|
||||
},
|
||||
actions: {
|
||||
setDarkMode(data, payload: { isDarkMode: boolean }) {
|
||||
data.settings.isDarkMode = payload.isDarkMode
|
||||
},
|
||||
|
||||
/* ---------------------- Debug --------------------- */
|
||||
|
||||
closeDebugPanel(data) {
|
||||
|
@ -2041,21 +2044,6 @@ const state = createState({
|
|||
history.reset()
|
||||
},
|
||||
|
||||
/* ------------------- Preferences ------------------ */
|
||||
|
||||
toggleDarkMode(data) {
|
||||
data.settings.isDarkMode = !data.settings.isDarkMode
|
||||
},
|
||||
applyTheme(data) {
|
||||
if (data.settings.isDarkMode && typeof document !== 'undefined') {
|
||||
document.body.classList.remove(light)
|
||||
document.body.classList.add(dark)
|
||||
} else {
|
||||
document.body.classList.remove(dark)
|
||||
document.body.classList.add(light)
|
||||
}
|
||||
},
|
||||
|
||||
/* --------------------- Styles --------------------- */
|
||||
|
||||
toggleStylePanel(data) {
|
||||
|
|
|
@ -30,10 +30,13 @@ class Storage {
|
|||
data.document.id = roomId ? roomId : uniqueId()
|
||||
} else {
|
||||
// If we did find a state and document, load it into state.
|
||||
const restoredDocument: Data = JSON.parse(decompress(savedState))
|
||||
const restoredState: Data = JSON.parse(decompress(savedState))
|
||||
|
||||
// Lose the settings, these are meant to be stable
|
||||
delete restoredState.settings
|
||||
|
||||
// Merge restored data into state.
|
||||
Object.assign(data, restoredDocument)
|
||||
Object.assign(data, restoredState)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
2
types.ts
2
types.ts
|
@ -6,12 +6,12 @@ export interface Data {
|
|||
isReadOnly: boolean
|
||||
settings: {
|
||||
fontSize: number
|
||||
isDarkMode: boolean
|
||||
isCodeOpen: boolean
|
||||
isTestMode: boolean
|
||||
isDebugOpen: boolean
|
||||
isDebugMode: boolean
|
||||
isStyleOpen: boolean
|
||||
isDarkMode: boolean
|
||||
nudgeDistanceSmall: number
|
||||
nudgeDistanceLarge: number
|
||||
isToolLocked: boolean
|
||||
|
|
|
@ -5984,6 +5984,11 @@ next-pwa@^5.2.23:
|
|||
workbox-webpack-plugin "^6.1.5"
|
||||
workbox-window "^6.1.5"
|
||||
|
||||
next-themes@^0.0.15:
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.15.tgz#ab0cee69cd763b77d41211f631e108beab39bf7d"
|
||||
integrity sha512-LTmtqYi03c4gMTJmWwVK9XkHL7h0/+XrtR970Ujvtu3s0kZNeJN24aJsi4rkZOI8i19+qq6f8j+8Duwy5jqcrQ==
|
||||
|
||||
next@^11.0.1:
|
||||
version "11.0.1"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-11.0.1.tgz#b8e3914d153aaf7143cb98c09bcd3c8230eeb17a"
|
||||
|
|
Loading…
Reference in a new issue