Abstract SettingsContext out

This commit is contained in:
1ilit 2024-03-09 22:39:46 +02:00
parent f3eb6d7c04
commit 6c0ebcf7bc
15 changed files with 210 additions and 210 deletions

View File

@ -4,39 +4,85 @@ import Survey from "./pages/Survey";
import BugReport from "./pages/BugReport"; import BugReport from "./pages/BugReport";
import Shortcuts from "./pages/Shortcuts"; import Shortcuts from "./pages/Shortcuts";
import Templates from "./pages/Templates"; import Templates from "./pages/Templates";
import { useEffect } from "react"; import { useEffect, useLayoutEffect } from "react";
import LandingPage from "./pages/LandingPage"; import LandingPage from "./pages/LandingPage";
import LayoutContextProvider from "./context/LayoutContext"; import SettingsContextProvider from "./context/SettingsContext";
import useSettings from "./hooks/useSettings";
const Wrapper = ({ children }) => { function ThemedPage({ children }) {
const { setSettings } = useSettings();
useLayoutEffect(() => {
const theme = localStorage.getItem("theme");
if (theme === "dark") {
setSettings((prev) => ({ ...prev, mode: "dark" }));
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "dark");
}
} else {
setSettings((prev) => ({ ...prev, mode: "light" }));
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "light");
}
}
}, [setSettings]);
return children;
}
function RestoreScroll() {
const location = useLocation(); const location = useLocation();
useEffect(() => { useEffect(() => {
window.scroll(0, 0); window.scroll(0, 0);
}, [location.pathname]); }, [location.pathname]);
return children; return null;
}; }
function App() { function App() {
return ( return (
<BrowserRouter> <SettingsContextProvider>
<Wrapper> <BrowserRouter>
<RestoreScroll />
<Routes> <Routes>
<Route path="/" element={<LandingPage />} /> <Route path="/" element={<LandingPage />} />
<Route <Route
path="/editor" path="/editor"
element={ element={
<LayoutContextProvider> <ThemedPage>
<Editor /> <Editor />
</LayoutContextProvider> </ThemedPage>
}
/>
<Route
path="/survey"
element={
<ThemedPage>
<Survey />
</ThemedPage>
}
/>
<Route
path="/shortcuts"
element={
<ThemedPage>
<Shortcuts />
</ThemedPage>
}
/>
<Route
path="/bug_report"
element={
<ThemedPage>
<BugReport />
</ThemedPage>
} }
/> />
<Route path="/survey" element={<Survey />} />
<Route path="/shortcuts" element={<Shortcuts />} />
<Route path="/bug_report" element={<BugReport />} />
<Route path="/templates" element={<Templates />} /> <Route path="/templates" element={<Templates />} />
</Routes> </Routes>
</Wrapper> </BrowserRouter>
</BrowserRouter> </SettingsContextProvider>
); );
} }

View File

@ -16,19 +16,19 @@ import {
import { import {
AreaContext, AreaContext,
SelectContext, SelectContext,
SettingsContext,
StateContext, StateContext,
TabContext, TabContext,
UndoRedoContext, UndoRedoContext,
} from "../pages/Editor"; } from "../pages/Editor";
import useLayout from "../hooks/useLayout"; import useLayout from "../hooks/useLayout";
import useSettings from "../hooks/useSettings";
export default function Area(props) { export default function Area(props) {
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
const [editField, setEditField] = useState({}); const [editField, setEditField] = useState({});
const { setState } = useContext(StateContext); const { setState } = useContext(StateContext);
const {layout} = useLayout(); const { layout } = useLayout();
const { settings } = useContext(SettingsContext); const { settings } = useSettings();
const { tab, setTab } = useContext(TabContext); const { tab, setTab } = useContext(TabContext);
const { updateArea, deleteArea } = useContext(AreaContext); const { updateArea, deleteArea } = useContext(AreaContext);
const { setUndoStack, setRedoStack } = useContext(UndoRedoContext); const { setUndoStack, setRedoStack } = useContext(UndoRedoContext);

View File

@ -6,7 +6,6 @@ import Relationship from "./Relationship";
import { import {
AreaContext, AreaContext,
NoteContext, NoteContext,
SettingsContext,
TableContext, TableContext,
UndoRedoContext, UndoRedoContext,
SelectContext, SelectContext,
@ -14,13 +13,14 @@ import {
} from "../pages/Editor"; } from "../pages/Editor";
import Note from "./Note"; import Note from "./Note";
import { Toast } from "@douyinfe/semi-ui"; import { Toast } from "@douyinfe/semi-ui";
import useSettings from "../hooks/useSettings";
export default function Canvas() { export default function Canvas() {
const { tables, updateTable, relationships, addRelationship } = const { tables, updateTable, relationships, addRelationship } =
useContext(TableContext); useContext(TableContext);
const { areas, updateArea } = useContext(AreaContext); const { areas, updateArea } = useContext(AreaContext);
const { notes, updateNote } = useContext(NoteContext); const { notes, updateNote } = useContext(NoteContext);
const { settings } = useContext(SettingsContext); const { settings } = useSettings();
const { setUndoStack, setRedoStack } = useContext(UndoRedoContext); const { setUndoStack, setRedoStack } = useContext(UndoRedoContext);
const { transform, setTransform } = useContext(TransformContext); const { transform, setTransform } = useContext(TransformContext);
const { selectedElement, setSelectedElement } = useContext(SelectContext); const { selectedElement, setSelectedElement } = useContext(SelectContext);

View File

@ -53,7 +53,6 @@ import {
NoteContext, NoteContext,
TransformContext, TransformContext,
SelectContext, SelectContext,
SettingsContext,
StateContext, StateContext,
TabContext, TabContext,
TableContext, TableContext,
@ -73,6 +72,7 @@ import { Parser } from "node-sql-parser";
import Todo from "./Todo"; import Todo from "./Todo";
import { Thumbnail } from "./Thumbnail"; import { Thumbnail } from "./Thumbnail";
import useLayout from "../hooks/useLayout"; import useLayout from "../hooks/useLayout";
import useSettings from "../hooks/useSettings";
export default function ControlPanel({ export default function ControlPanel({
diagramId, diagramId,
@ -124,7 +124,7 @@ export default function ControlPanel({
const [data, setData] = useState(null); const [data, setData] = useState(null);
const { state, setState } = useContext(StateContext); const { state, setState } = useContext(StateContext);
const { layout, setLayout } = useLayout(); const { layout, setLayout } = useLayout();
const { settings, setSettings } = useContext(SettingsContext); const { settings, setSettings } = useSettings();
const { const {
relationships, relationships,
tables, tables,

View File

@ -1,10 +1,11 @@
import { useContext, useState, useEffect } from "react"; import { useContext, useState, useEffect } from "react";
import { Collapse, Badge } from "@douyinfe/semi-ui"; import { Collapse, Badge } from "@douyinfe/semi-ui";
import { SettingsContext, TableContext, TypeContext } from "../pages/Editor"; import { TableContext, TypeContext } from "../pages/Editor";
import { validateDiagram, arrayIsEqual } from "../utils"; import { validateDiagram, arrayIsEqual } from "../utils";
import useSettings from "../hooks/useSettings";
export default function Issues() { export default function Issues() {
const { settings } = useContext(SettingsContext); const { settings } = useSettings();
const { types } = useContext(TypeContext); const { types } = useContext(TypeContext);
const { tables, relationships } = useContext(TableContext); const { tables, relationships } = useContext(TableContext);
const [issues, setIssues] = useState([]); const [issues, setIssues] = useState([]);

View File

@ -1,11 +1,11 @@
import { useContext, useRef, useState } from "react"; import { useRef, useState } from "react";
import { calcPath } from "../utils"; import { calcPath } from "../utils";
import { Cardinality } from "../data/data"; import { Cardinality } from "../data/data";
import { SettingsContext } from "../pages/Editor"; import useSettings from "../hooks/useSettings";
export default function Relationship(props) { export default function Relationship(props) {
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
const { settings } = useContext(SettingsContext); const { settings } = useSettings();
const pathRef = useRef(); const pathRef = useRef();
let cardinalityStart = "1"; let cardinalityStart = "1";

View File

@ -9,12 +9,12 @@ import { Tooltip, SideSheet, List, Badge } from "@douyinfe/semi-ui";
import { import {
BotMessageContext, BotMessageContext,
MessageContext, MessageContext,
SettingsContext,
UndoRedoContext, UndoRedoContext,
} from "../pages/Editor"; } from "../pages/Editor";
import Todo from "./Todo"; import Todo from "./Todo";
import Chat from "./Chat"; import Chat from "./Chat";
import DrawBot from "./DrawBot"; import DrawBot from "./DrawBot";
import useSettings from "../hooks/useSettings";
export default function Sidebar() { export default function Sidebar() {
const SidesheetType = { const SidesheetType = {
@ -27,7 +27,7 @@ export default function Sidebar() {
}; };
const { undoStack } = useContext(UndoRedoContext); const { undoStack } = useContext(UndoRedoContext);
const { messages } = useContext(MessageContext); const { messages } = useContext(MessageContext);
const { settings } = useContext(SettingsContext); const { settings } = useSettings();
const { botMessages } = useContext(BotMessageContext); const { botMessages } = useContext(BotMessageContext);
const [sidesheet, setSidesheet] = useState(SidesheetType.NONE); const [sidesheet, setSidesheet] = useState(SidesheetType.NONE);
const [seen, setSeen] = useState(0); const [seen, setSeen] = useState(0);

View File

@ -33,7 +33,6 @@ import {
} from "@douyinfe/semi-ui"; } from "@douyinfe/semi-ui";
import { import {
SelectContext, SelectContext,
SettingsContext,
TabContext, TabContext,
TableContext, TableContext,
TypeContext, TypeContext,
@ -41,6 +40,7 @@ import {
} from "../pages/Editor"; } from "../pages/Editor";
import { getSize, hasCheck, hasPrecision, isSized } from "../utils"; import { getSize, hasCheck, hasPrecision, isSized } from "../utils";
import useLayout from "../hooks/useLayout"; import useLayout from "../hooks/useLayout";
import useSettings from "../hooks/useSettings";
export default function Table(props) { export default function Table(props) {
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
@ -50,7 +50,7 @@ export default function Table(props) {
const { deleteTable, updateTable, updateField, setRelationships } = const { deleteTable, updateTable, updateField, setRelationships } =
useContext(TableContext); useContext(TableContext);
const { tab, setTab } = useContext(TabContext); const { tab, setTab } = useContext(TabContext);
const { settings } = useContext(SettingsContext); const { settings } = useSettings();
const { types } = useContext(TypeContext); const { types } = useContext(TypeContext);
const { setUndoStack, setRedoStack } = useContext(UndoRedoContext); const { setUndoStack, setRedoStack } = useContext(UndoRedoContext);
const { selectedElement, setSelectedElement } = useContext(SelectContext); const { selectedElement, setSelectedElement } = useContext(SelectContext);

View File

@ -0,0 +1,21 @@
import { createContext, useState } from "react";
export const SettingsContext = createContext(null);
export default function SettingsContextProvider({ children }) {
const [settings, setSettings] = useState({
strictMode: false,
showFieldSummary: true,
showGrid: true,
mode: "light",
autosave: true,
panning: true,
showCardinality: true,
});
return (
<SettingsContext.Provider value={{ settings, setSettings }}>
{children}
</SettingsContext.Provider>
);
}

View File

@ -1,13 +1,6 @@
import { useContext } from "react"; import { useContext } from "react";
import { LayoutContext } from "../context/LayoutContext"; import { LayoutContext } from "../context/LayoutContext";
/**
* Access layout state
*
* Layout includes: header, sidebar, toolbar, issues, fullscreen
*
* @returns `{ layout, setLayout }`
*/
export default function useLayout() { export default function useLayout() {
return useContext(LayoutContext); return useContext(LayoutContext);
} }

6
src/hooks/useSettings.js Normal file
View File

@ -0,0 +1,6 @@
import { useContext } from "react";
import { SettingsContext } from "../context/SettingsContext";
export default function useSettings() {
return useContext(SettingsContext);
}

View File

@ -136,19 +136,7 @@ export default function BugReport() {
const [theme, setTheme] = useState(""); const [theme, setTheme] = useState("");
useEffect(() => { useEffect(() => {
const t = localStorage.getItem("theme"); setTheme(localStorage.getItem("theme"));
setTheme(t);
if (t === "dark") {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "dark");
}
} else {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "light");
}
}
document.title = "Report a bug | drawDB"; document.title = "Report a bug | drawDB";
document.body.setAttribute("class", "theme"); document.body.setAttribute("class", "theme");
}, [setTheme]); }, [setTheme]);

View File

@ -15,13 +15,13 @@ import { Divider, Tooltip } from "@douyinfe/semi-ui";
import { exitFullscreen } from "../utils"; import { exitFullscreen } from "../utils";
import useLayout from "../hooks/useLayout"; import useLayout from "../hooks/useLayout";
import LayoutContextProvider from "../context/LayoutContext"; import LayoutContextProvider from "../context/LayoutContext";
import useSettings from "../hooks/useSettings";
export const StateContext = createContext(); export const StateContext = createContext();
export const TableContext = createContext(); export const TableContext = createContext();
export const AreaContext = createContext(); export const AreaContext = createContext();
export const TabContext = createContext(); export const TabContext = createContext();
export const NoteContext = createContext(); export const NoteContext = createContext();
export const SettingsContext = createContext();
export const UndoRedoContext = createContext(); export const UndoRedoContext = createContext();
export const SelectContext = createContext(); export const SelectContext = createContext();
export const TaskContext = createContext(); export const TaskContext = createContext();
@ -52,15 +52,7 @@ function WorkSpace() {
const [width, setWidth] = useState(340); const [width, setWidth] = useState(340);
const [tab, setTab] = useState(Tab.tables); const [tab, setTab] = useState(Tab.tables);
const { layout, setLayout } = useLayout(); const { layout, setLayout } = useLayout();
const [settings, setSettings] = useState({ const { settings, setSettings } = useSettings();
strictMode: false,
showFieldSummary: true,
showGrid: true,
mode: "light",
autosave: true,
panning: true,
showCardinality: true,
});
const [transform, setTransform] = useState({ const [transform, setTransform] = useState({
zoom: 1, zoom: 1,
pan: { x: 0, y: 0 }, pan: { x: 0, y: 0 },
@ -94,8 +86,8 @@ function WorkSpace() {
{ {
id: prev.length, id: prev.length,
name: `table_${prev.length}`, name: `table_${prev.length}`,
x: -settings.pan.x, x: -transform.pan.x,
y: -settings.pan.y, y: -transform.pan.y,
fields: [ fields: [
{ {
name: "id", name: "id",
@ -211,8 +203,8 @@ function WorkSpace() {
{ {
id: prev.length, id: prev.length,
name: `area_${prev.length}`, name: `area_${prev.length}`,
x: -settings.pan.x, x: -transform.pan.x,
y: -settings.pan.y, y: -transform.pan.y,
width: 200, width: 200,
height: 200, height: 200,
color: defaultTableTheme, color: defaultTableTheme,
@ -244,8 +236,8 @@ function WorkSpace() {
...prev, ...prev,
{ {
id: prev.length, id: prev.length,
x: -settings.pan.x, x: -transform.pan.x,
y: -settings.pan.y, y: -transform.pan.y,
title: `note_${prev.length}`, title: `note_${prev.length}`,
content: "", content: "",
color: defaultNoteTheme, color: defaultNoteTheme,
@ -690,22 +682,7 @@ function WorkSpace() {
break; break;
} }
} }
}, [setSettings]);
const theme = localStorage.getItem("theme");
if (theme === "dark") {
setSettings((prev) => ({ ...prev, mode: "dark" }));
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "dark");
}
} else {
setSettings((prev) => ({ ...prev, mode: "light" }));
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "light");
}
}
}, []);
return ( return (
<StateContext.Provider value={{ state, setState }}> <StateContext.Provider value={{ state, setState }}>
@ -731,115 +708,107 @@ function WorkSpace() {
value={{ notes, setNotes, updateNote, addNote, deleteNote }} value={{ notes, setNotes, updateNote, addNote, deleteNote }}
> >
<TabContext.Provider value={{ tab, setTab }}> <TabContext.Provider value={{ tab, setTab }}>
<SettingsContext.Provider value={{ settings, setSettings }}> <UndoRedoContext.Provider
<UndoRedoContext.Provider value={{ undoStack, redoStack, setUndoStack, setRedoStack }}
value={{ undoStack, redoStack, setUndoStack, setRedoStack }} >
<SelectContext.Provider
value={{ selectedElement, setSelectedElement }}
> >
<SelectContext.Provider <TaskContext.Provider
value={{ selectedElement, setSelectedElement }} value={{ tasks, setTasks, updateTask }}
> >
<TaskContext.Provider <TypeContext.Provider
value={{ tasks, setTasks, updateTask }} value={{
types,
setTypes,
addType,
updateType,
deleteType,
}}
> >
<TypeContext.Provider <div className="h-[100vh] flex flex-col overflow-hidden theme">
value={{ <ControlPanel
types, diagramId={id}
setTypes, setDiagramId={setId}
addType, title={title}
updateType, setTitle={setTitle}
deleteType, lastSaved={lastSaved}
}} setLastSaved={setLastSaved}
> />
<div className="h-[100vh] flex flex-col overflow-hidden theme"> <div
<ControlPanel className="flex h-full overflow-y-auto"
diagramId={id} onMouseUp={() => setResize(false)}
setDiagramId={setId} onMouseMove={dragHandler}
title={title} >
setTitle={setTitle} {layout.sidebar && (
lastSaved={lastSaved} <SidePanel
setLastSaved={setLastSaved} resize={resize}
/> setResize={setResize}
<div width={width}
className="flex h-full overflow-y-auto" />
onMouseUp={() => setResize(false)} )}
onMouseMove={dragHandler} <div className="relative w-full h-full overflow-hidden">
> <Canvas state={state} setState={setState} />
{layout.sidebar && ( {!(
<SidePanel layout.sidebar ||
resize={resize} layout.toolbar ||
setResize={setResize} layout.header
width={width} ) && (
/> <div className="fixed right-5 bottom-4 flex gap-2">
)} <div className="popover-theme flex rounded-lg items-center">
<div className="relative w-full h-full overflow-hidden"> <button
<Canvas state={state} setState={setState} /> className="px-3 py-2"
{!( onClick={() =>
layout.sidebar || setTransform((prev) => ({
layout.toolbar || ...prev,
layout.header zoom: prev.zoom / 1.2,
) && ( }))
<div className="fixed right-5 bottom-4 flex gap-2"> }
<div className="popover-theme flex rounded-lg items-center"> >
<button <i className="bi bi-dash-lg"></i>
className="px-3 py-2" </button>
onClick={() => <Divider align="center" layout="vertical" />
setTransform((prev) => ({ <div className="px-3 py-2">
...prev, {parseInt(transform.zoom * 100)}%
zoom: prev.zoom / 1.2,
}))
}
>
<i className="bi bi-dash-lg"></i>
</button>
<Divider
align="center"
layout="vertical"
/>
<div className="px-3 py-2">
{parseInt(transform.zoom * 100)}%
</div>
<Divider
align="center"
layout="vertical"
/>
<button
className="px-3 py-2"
onClick={() =>
setTransform((prev) => ({
...prev,
zoom: prev.zoom * 1.2,
}))
}
>
<i className="bi bi-plus-lg"></i>
</button>
</div> </div>
<Tooltip content="Exit"> <Divider align="center" layout="vertical" />
<button <button
className="px-3 py-2 rounded-lg popover-theme" className="px-3 py-2"
onClick={() => { onClick={() =>
setLayout((prev) => ({ setTransform((prev) => ({
...prev, ...prev,
sidebar: true, zoom: prev.zoom * 1.2,
toolbar: true, }))
header: true, }
})); >
exitFullscreen(); <i className="bi bi-plus-lg"></i>
}} </button>
>
<i className="bi bi-fullscreen-exit"></i>
</button>
</Tooltip>
</div> </div>
)} <Tooltip content="Exit">
</div> <button
className="px-3 py-2 rounded-lg popover-theme"
onClick={() => {
setLayout((prev) => ({
...prev,
sidebar: true,
toolbar: true,
header: true,
}));
exitFullscreen();
}}
>
<i className="bi bi-fullscreen-exit"></i>
</button>
</Tooltip>
</div>
)}
</div> </div>
</div> </div>
</TypeContext.Provider> </div>
</TaskContext.Provider> </TypeContext.Provider>
</SelectContext.Provider> </TaskContext.Provider>
</UndoRedoContext.Provider> </SelectContext.Provider>
</SettingsContext.Provider> </UndoRedoContext.Provider>
</TabContext.Provider> </TabContext.Provider>
</NoteContext.Provider> </NoteContext.Provider>
</AreaContext.Provider> </AreaContext.Provider>

View File

@ -76,19 +76,7 @@ export default function Shortcuts() {
}; };
useEffect(() => { useEffect(() => {
const t = localStorage.getItem("theme"); setTheme(localStorage.getItem("theme"));
setTheme(t);
if (t === "dark") {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "dark");
}
} else {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "light");
}
}
document.title = "Shortcuts | drawDB"; document.title = "Shortcuts | drawDB";
document.body.setAttribute("class", "theme"); document.body.setAttribute("class", "theme");
}, [setTheme]); }, [setTheme]);

View File

@ -239,19 +239,7 @@ export default function Survey() {
useEffect(() => window.scroll(0, 0)); useEffect(() => window.scroll(0, 0));
useEffect(() => { useEffect(() => {
const t = localStorage.getItem("theme"); setTheme(localStorage.getItem("theme"));
setTheme(t);
if (t === "dark") {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "dark");
}
} else {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "light");
}
}
document.title = "Share you feedback | drawDB"; document.title = "Share you feedback | drawDB";
document.body.setAttribute("class", "theme"); document.body.setAttribute("class", "theme");
}, [setTheme]); }, [setTheme]);