From d5582463b2d67c66f55bbbe43a399961366eef8e Mon Sep 17 00:00:00 2001 From: jerensl <54782057+jerensl@users.noreply.github.com> Date: Fri, 19 Jul 2024 19:31:42 +0800 Subject: [PATCH] feat(state-local-storage): add editor size when resizable on local storage --- .../contexts/PlaygroundLayoutContext.tsx | 22 +- modelina-website/src/store/useLayoutStore.tsx | 196 +++++++++--------- 2 files changed, 119 insertions(+), 99 deletions(-) diff --git a/modelina-website/src/components/contexts/PlaygroundLayoutContext.tsx b/modelina-website/src/components/contexts/PlaygroundLayoutContext.tsx index 58398df04b..fb50cf4dca 100644 --- a/modelina-website/src/components/contexts/PlaygroundLayoutContext.tsx +++ b/modelina-website/src/components/contexts/PlaygroundLayoutContext.tsx @@ -6,12 +6,28 @@ import { createContext, useContext, useEffect, useReducer } from 'react'; import type { Dispatch, State } from '@/store/useLayoutStore'; import { initialState, playgroundLayoutReducer } from '@/store/useLayoutStore'; +const LocalStorageKey = 'PlaygroundLayout'; + type PlaygroundtProviderProps = { children: React.ReactNode }; const PlaygroundtLayoutContext = createContext< { state: State; dispatch: Dispatch } | undefined >(undefined); +const localStorageInitializer = (initialValue = initialState) => { + if (typeof window !== 'undefined') { + const resizablePercentageOpen = JSON.parse(localStorage.getItem(LocalStorageKey)!) ?? 0.5; + const newValue = { + ...initialValue, + editorSize: resizablePercentageOpen + }; + + return newValue; + } + + return initialValue; +}; + /** * This component to consume Plaground Layout State. * @@ -34,11 +50,15 @@ function usePlaygroundLayout() { * @returns {ReactNode} A React element that share context state. */ function PlaygroundLayoutProvider({ children }: PlaygroundtProviderProps) { - const [state, dispatch] = useReducer(playgroundLayoutReducer, initialState); + const [state, dispatch] = useReducer(playgroundLayoutReducer, initialState, localStorageInitializer); const value = { state, dispatch }; const [ref, { width }] = useMeasure(); + useEffect(() => { + localStorage.setItem(LocalStorageKey, JSON.stringify({ editorSize: state.editorSize })); + }, [state]); + useEffect(() => { if (width !== null) { if (width < 768) { diff --git a/modelina-website/src/store/useLayoutStore.tsx b/modelina-website/src/store/useLayoutStore.tsx index 1a5420003e..e7c5ccb141 100644 --- a/modelina-website/src/store/useLayoutStore.tsx +++ b/modelina-website/src/store/useLayoutStore.tsx @@ -5,54 +5,54 @@ import { LuFileInput, LuFileOutput } from 'react-icons/lu'; import { VscListSelection } from 'react-icons/vsc'; interface ISidebarItem { - name: string; - title: string; - isOpen: boolean; - devices: 'mobile' | 'tablet' | 'desktop' | 'all' ; - icon: React.ReactNode; - tooltip: string; - } + name: string; + title: string; + isOpen: boolean; + devices: 'mobile' | 'tablet' | 'desktop' | 'all' ; + icon: React.ReactNode; + tooltip: string; +} const sidebarItems: Map = new Map([ [ - 'input-editor', { - name: 'input-editor', - title: 'Input Editor', - isOpen: false, - devices: 'mobile', - icon: , - tooltip: 'Show Input Editor' - } + 'input-editor', { + name: 'input-editor', + title: 'Input Editor', + isOpen: false, + devices: 'mobile', + icon: , + tooltip: 'Show Input Editor' + } ], [ - 'output-editor', { - name: 'output-editor', - title: 'Output Editor', - isOpen: false, - devices: 'mobile', - icon: , - tooltip: 'Show Output Editor' - } + 'output-editor', { + name: 'output-editor', + title: 'Output Editor', + isOpen: false, + devices: 'mobile', + icon: , + tooltip: 'Show Output Editor' + } ], [ - 'general-options', { - name: 'general-options', - title: 'Options', - isOpen: true, - devices: 'all', - icon: , - tooltip: 'Show or hide all the options' - } + 'general-options', { + name: 'general-options', + title: 'Options', + isOpen: true, + devices: 'all', + icon: , + tooltip: 'Show or hide all the options' + } ], [ - 'output-options', { - name: 'output-options', - title: 'Output', - isOpen: false, - devices: 'all', - icon: , - tooltip: 'Show or hide the list of output models' - } + 'output-options', { + name: 'output-options', + title: 'Output', + isOpen: false, + devices: 'all', + icon: , + tooltip: 'Show or hide the list of output models' + } ] ]); @@ -68,86 +68,86 @@ export const playgroundLayoutReducer = produce((draft: Draf enableMapSet(); switch (action.type) { - case 'update-device': { - // eslint-disable-next-line no-param-reassign - draft.device = action.payload; + case 'update-device': { + // eslint-disable-next-line no-param-reassign + draft.device = action.payload; + break; + } + case 'resizable-size': { + // eslint-disable-next-line no-param-reassign + draft.editorSize = action.total; + break; + } + case 'open-option': { + const findOpts = draft.sidebarItems.get(action.name); + + const alwaysOpen = ['input-editor', 'output-editor', 'general-options']; + const isMobile = draft.device === 'mobile'; + + if (!findOpts) { break; - } - case 'resizable-size': { + } + + if (isMobile && alwaysOpen.includes(action.name)) { + if (draft.open === action.name) { // eslint-disable-next-line no-param-reassign - draft.editorSize = action.total; + draft.open = findOpts.name ?? draft.open; break; } - case 'open-option': { - const findOpts = draft.sidebarItems.get(action.name); + } - const alwaysOpen = ['input-editor', 'output-editor', 'general-options']; - const isMobile = draft.device === 'mobile'; + draft.sidebarItems.set(action.name, { + ...findOpts, + isOpen: !findOpts.isOpen + }); - if (!findOpts) { - break; - } + if (isMobile && action.name === 'output-options' && !findOpts.isOpen) { + // eslint-disable-next-line no-param-reassign + draft.open = findOpts.name ?? draft.open; + break; + } - if (isMobile && alwaysOpen.includes(action.name)) { - if (draft.open === action.name) { + if (isMobile && action.name === 'output-options' && findOpts.isOpen) { + draft.sidebarItems.forEach((value, key) => { + if (value.isOpen) { // eslint-disable-next-line no-param-reassign - draft.open = findOpts.name ?? draft.open; - break; - } + draft.open = key; } - - draft.sidebarItems.set(action.name, { - ...findOpts, - isOpen: !findOpts.isOpen }); + break; + } - if (isMobile && action.name === 'output-options' && !findOpts.isOpen) { - // eslint-disable-next-line no-param-reassign - draft.open = findOpts.name ?? draft.open; - break; - } + if (draft.open !== findOpts.name) { + const findOne = draft.sidebarItems.get(draft.open); - if (isMobile && action.name === 'output-options' && findOpts.isOpen) { - draft.sidebarItems.forEach((value, key) => { - if (value.isOpen) { - // eslint-disable-next-line no-param-reassign - draft.open = key; + if (findOne) { + draft.sidebarItems.set(draft.open, { + ...findOne, + isOpen: false } - }); - break; + ); } - if (draft.open !== findOpts.name) { - const findOne = draft.sidebarItems.get(draft.open); - - if (findOne) { - draft.sidebarItems.set(draft.open, { - ...findOne, + if (isMobile && draft.open === 'output-options') { + draft.sidebarItems.forEach((value, key) => { + if (value.isOpen && key !== action.name) { + draft.sidebarItems.set(key, { + ...value, isOpen: false - } - ); } - - if (isMobile && draft.open === 'output-options') { - draft.sidebarItems.forEach((value, key) => { - if (value.isOpen && key !== action.name) { - draft.sidebarItems.set(key, { - ...value, - isOpen: false - } - ); - } - }); + ); } + }); } + } - // eslint-disable-next-line no-param-reassign - draft.open = findOpts.name ?? draft.open; + // eslint-disable-next-line no-param-reassign + draft.open = findOpts.name ?? draft.open; - break; - } -default: { + break; + } + default: { throw new Error(`Unhandled action type: ${action.type}`); - } + } } });