From b9ee1b9d083acfcd5e163cc77714fc9203f7bc91 Mon Sep 17 00:00:00 2001
From: Ivan <8611739+IRBorisov@users.noreply.github.com>
Date: Wed, 15 Jan 2025 22:16:27 +0300
Subject: [PATCH] R: Migrating to zustand for local state management pt3
---
.../frontend/src/app/ApplicationLayout.tsx | 4 ++
.../frontend/src/app/GlobalProviders.tsx | 4 +-
rsconcept/frontend/src/app/GlobalTooltips.tsx | 32 +++++++++++
.../src/components/info/BadgeConstituenta.tsx | 6 +-
.../src/context/ConceptOptionsContext.tsx | 56 -------------------
.../frontend/src/hooks/useLocalStorage.ts | 31 ----------
.../pages/OssPage/EditorOssGraph/OssFlow.tsx | 16 ++----
.../EditorOssGraph/ToolbarOssGraph.tsx | 23 ++++----
.../RSFormPage/EditorTermGraph/ViewHidden.tsx | 6 +-
.../ViewConstituents/ConstituentsSearch.tsx | 42 +++++++-------
rsconcept/frontend/src/stores/cstSearch.ts | 42 ++++++++++++++
rsconcept/frontend/src/stores/ossGraph.ts | 32 +++++++++++
rsconcept/frontend/src/stores/tooltips.ts | 13 +++++
rsconcept/frontend/src/utils/constants.ts | 15 -----
14 files changed, 167 insertions(+), 155 deletions(-)
create mode 100644 rsconcept/frontend/src/app/GlobalTooltips.tsx
delete mode 100644 rsconcept/frontend/src/context/ConceptOptionsContext.tsx
delete mode 100644 rsconcept/frontend/src/hooks/useLocalStorage.ts
create mode 100644 rsconcept/frontend/src/stores/cstSearch.ts
create mode 100644 rsconcept/frontend/src/stores/ossGraph.ts
create mode 100644 rsconcept/frontend/src/stores/tooltips.ts
diff --git a/rsconcept/frontend/src/app/ApplicationLayout.tsx b/rsconcept/frontend/src/app/ApplicationLayout.tsx
index e9fe77ac..4b92bba5 100644
--- a/rsconcept/frontend/src/app/ApplicationLayout.tsx
+++ b/rsconcept/frontend/src/app/ApplicationLayout.tsx
@@ -9,6 +9,8 @@ import { NavigationState } from '@/context/NavigationContext';
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
import { globals } from '@/utils/constants';
+import { GlobalTooltips } from './GlobalTooltips';
+
function ApplicationLayout() {
const mainHeight = useMainHeight();
const viewportHeight = useViewportHeight();
@@ -27,6 +29,8 @@ function ApplicationLayout() {
pauseOnFocusLoss={false}
/>
+
+
-
+
{children}
@@ -43,7 +42,6 @@ function GlobalProviders({ children }: React.PropsWithChildren) {
-
);
}
diff --git a/rsconcept/frontend/src/app/GlobalTooltips.tsx b/rsconcept/frontend/src/app/GlobalTooltips.tsx
new file mode 100644
index 00000000..fdbbc6a2
--- /dev/null
+++ b/rsconcept/frontend/src/app/GlobalTooltips.tsx
@@ -0,0 +1,32 @@
+'use client';
+
+import InfoConstituenta from '@/components/info/InfoConstituenta';
+import Loader from '@/components/ui/Loader';
+import Tooltip from '@/components/ui/Tooltip';
+import { useTooltipsStore } from '@/stores/tooltips';
+import { globals } from '@/utils/constants';
+
+export const GlobalTooltips = () => {
+ const hoverCst = useTooltipsStore(state => state.activeCst);
+
+ return (
+ <>
+
+
+
+ {hoverCst ? event.stopPropagation()} /> : }
+
+ >
+ );
+};
diff --git a/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx b/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx
index 7acaa4e6..aa457aa9 100644
--- a/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx
+++ b/rsconcept/frontend/src/components/info/BadgeConstituenta.tsx
@@ -1,7 +1,7 @@
import clsx from 'clsx';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { CstClass, IConstituenta } from '@/models/rsform';
+import { useTooltipsStore } from '@/stores/tooltips';
import { APP_COLORS, colorFgCstStatus } from '@/styling/color';
import { globals } from '@/utils/constants';
@@ -19,7 +19,7 @@ interface BadgeConstituentaProps extends CProps.Styling {
* Displays a badge with a constituenta alias and information tooltip.
*/
function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstituentaProps) {
- const { setHoverCst } = useConceptOptions();
+ const setActiveCst = useTooltipsStore(state => state.setActiveCst);
return (
setHoverCst(value)}
+ onMouseEnter={() => setActiveCst(value)}
>
{value.alias}
diff --git a/rsconcept/frontend/src/context/ConceptOptionsContext.tsx b/rsconcept/frontend/src/context/ConceptOptionsContext.tsx
deleted file mode 100644
index 8a6ebb4d..00000000
--- a/rsconcept/frontend/src/context/ConceptOptionsContext.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-'use client';
-
-import { createContext, useContext, useState } from 'react';
-
-import InfoConstituenta from '@/components/info/InfoConstituenta';
-import Loader from '@/components/ui/Loader';
-import Tooltip from '@/components/ui/Tooltip';
-import { IConstituenta } from '@/models/rsform';
-import { globals } from '@/utils/constants';
-import { contextOutsideScope } from '@/utils/labels';
-
-interface IOptionsContext {
- setHoverCst: (newValue: IConstituenta | undefined) => void;
-}
-
-const OptionsContext = createContext
(null);
-export const useConceptOptions = () => {
- const context = useContext(OptionsContext);
- if (!context) {
- throw new Error(contextOutsideScope('useConceptTheme', 'ThemeState'));
- }
- return context;
-};
-
-export const OptionsState = ({ children }: React.PropsWithChildren) => {
- const [hoverCst, setHoverCst] = useState(undefined);
-
- return (
-
- <>
-
-
-
- {hoverCst ? event.stopPropagation()} /> : }
-
-
- {children}
- >
-
- );
-};
diff --git a/rsconcept/frontend/src/hooks/useLocalStorage.ts b/rsconcept/frontend/src/hooks/useLocalStorage.ts
deleted file mode 100644
index 8c78bdcd..00000000
--- a/rsconcept/frontend/src/hooks/useLocalStorage.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-
-import { storage } from '@/utils/constants';
-
-function useLocalStorage(key: string, defaultValue: ValueType | (() => ValueType)) {
- const prefixedKey = `${storage.PREFIX}${key}`;
- const [value, setValue] = useState(() => {
- const loadedJson = localStorage.getItem(prefixedKey);
- if (loadedJson != null) {
- return JSON.parse(loadedJson) as ValueType;
- } else if (typeof defaultValue === 'function') {
- return (defaultValue as () => ValueType)();
- } else {
- return defaultValue;
- }
- });
-
- useEffect(() => {
- if (value === undefined) {
- localStorage.removeItem(prefixedKey);
- } else {
- localStorage.setItem(prefixedKey, JSON.stringify(value));
- }
- }, [prefixedKey, value]);
-
- return [value, setValue] as [ValueType, typeof setValue];
-}
-
-export default useLocalStorage;
diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx
index 2ffbb855..a03dd412 100644
--- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx
+++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx
@@ -19,12 +19,12 @@ import {
import { CProps } from '@/components/props';
import Overlay from '@/components/ui/Overlay';
import { useOSS } from '@/context/OssContext';
-import useLocalStorage from '@/hooks/useLocalStorage';
import { OssNode } from '@/models/miscellaneous';
import { OperationID } from '@/models/oss';
import { useMainHeight } from '@/stores/appLayout';
+import { useOSSGraphStore } from '@/stores/ossGraph';
import { APP_COLORS } from '@/styling/color';
-import { PARAMETER, storage } from '@/utils/constants';
+import { PARAMETER } from '@/utils/constants';
import { errors } from '@/utils/labels';
import { useOssEdit } from '../OssEditContext';
@@ -46,9 +46,9 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
const controller = useOssEdit();
const flow = useReactFlow();
- const [showGrid, setShowGrid] = useLocalStorage(storage.ossShowGrid, false);
- const [edgeAnimate, setEdgeAnimate] = useLocalStorage(storage.ossEdgeAnimate, false);
- const [edgeStraight, setEdgeStraight] = useLocalStorage(storage.ossEdgeStraight, false);
+ const showGrid = useOSSGraphStore(state => state.showGrid);
+ const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate);
+ const edgeStraight = useOSSGraphStore(state => state.edgeStraight);
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
@@ -277,9 +277,6 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
flow.fitView({ duration: PARAMETER.zoomDuration })}
onCreate={() => handleCreateOperation(controller.selected)}
onDelete={handleDeleteSelected}
@@ -288,9 +285,6 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
onResetPositions={() => setToggleReset(prev => !prev)}
onSavePositions={handleSavePositions}
onSaveImage={handleSaveImage}
- toggleShowGrid={() => setShowGrid(prev => !prev)}
- toggleEdgeAnimate={() => setEdgeAnimate(prev => !prev)}
- toggleEdgeStraight={() => setEdgeStraight(prev => !prev)}
/>
{menuProps ? (
diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx
index ea622b9e..f9c112ac 100644
--- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx
+++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/ToolbarOssGraph.tsx
@@ -21,6 +21,7 @@ import BadgeHelp from '@/components/info/BadgeHelp';
import MiniButton from '@/components/ui/MiniButton';
import { HelpTopic } from '@/models/miscellaneous';
import { OperationType } from '@/models/oss';
+import { useOSSGraphStore } from '@/stores/ossGraph';
import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/labels';
@@ -28,9 +29,6 @@ import { useOssEdit } from '../OssEditContext';
interface ToolbarOssGraphProps {
isModified: boolean;
- showGrid: boolean;
- edgeAnimate: boolean;
- edgeStraight: boolean;
onCreate: () => void;
onDelete: () => void;
onEdit: () => void;
@@ -39,16 +37,10 @@ interface ToolbarOssGraphProps {
onSaveImage: () => void;
onSavePositions: () => void;
onResetPositions: () => void;
- toggleShowGrid: () => void;
- toggleEdgeAnimate: () => void;
- toggleEdgeStraight: () => void;
}
function ToolbarOssGraph({
isModified,
- showGrid,
- edgeAnimate,
- edgeStraight,
onCreate,
onDelete,
onEdit,
@@ -56,13 +48,18 @@ function ToolbarOssGraph({
onFitView,
onSaveImage,
onSavePositions,
- onResetPositions,
- toggleShowGrid,
- toggleEdgeAnimate,
- toggleEdgeStraight
+ onResetPositions
}: ToolbarOssGraphProps) {
const controller = useOssEdit();
const selectedOperation = controller.schema?.operationByID.get(controller.selected[0]);
+
+ const showGrid = useOSSGraphStore(state => state.showGrid);
+ const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate);
+ const edgeStraight = useOSSGraphStore(state => state.edgeStraight);
+ const toggleShowGrid = useOSSGraphStore(state => state.toggleShowGrid);
+ const toggleEdgeAnimate = useOSSGraphStore(state => state.toggleEdgeAnimate);
+ const toggleEdgeStraight = useOSSGraphStore(state => state.toggleEdgeStraight);
+
const readyForSynthesis = (() => {
if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) {
return false;
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
index 9c596cfa..df4424ac 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
@@ -6,12 +6,12 @@ import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
import { CProps } from '@/components/props';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useWindowSize from '@/hooks/useWindowSize';
import { GraphColoring } from '@/models/miscellaneous';
import { ConstituentaID, IRSForm } from '@/models/rsform';
import { useFitHeight } from '@/stores/appLayout';
import { useTermGraphStore } from '@/stores/termGraph';
+import { useTooltipsStore } from '@/stores/tooltips';
import { APP_COLORS, colorBgGraphNode } from '@/styling/color';
import { globals, PARAMETER, prefixes } from '@/utils/constants';
@@ -32,7 +32,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
const isFolded = useTermGraphStore(state => state.foldHidden);
const toggleFolded = useTermGraphStore(state => state.toggleFoldHidden);
- const { setHoverCst } = useConceptOptions();
+ const setActiveCst = useTooltipsStore(state => state.setActiveCst);
const hiddenHeight = useFitHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px');
function handleClick(cstID: ConstituentaID, event: CProps.EventMouse) {
@@ -110,7 +110,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
onClick={event => handleClick(cstID, event)}
onDoubleClick={() => onEdit(cstID)}
data-tooltip-id={globals.constituenta_tooltip}
- onMouseEnter={() => setHoverCst(cst)}
+ onMouseEnter={() => setActiveCst(cst)}
>
{cst.alias}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx
index 0c138ba6..49901dbf 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx
@@ -1,18 +1,16 @@
'use client';
-import { useEffect, useState } from 'react';
+import { useEffect } from 'react';
import { IconChild } from '@/components/Icons';
import SelectGraphFilter from '@/components/select/SelectGraphFilter';
import SelectMatchMode from '@/components/select/SelectMatchMode';
import MiniButton from '@/components/ui/MiniButton';
import SearchBar from '@/components/ui/SearchBar';
-import useLocalStorage from '@/hooks/useLocalStorage';
-import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
import { applyGraphFilter } from '@/models/miscellaneousAPI';
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
import { matchConstituenta } from '@/models/rsformAPI';
-import { storage } from '@/utils/constants';
+import { useCstSearchStore } from '@/stores/cstSearch';
interface ConstituentsSearchProps {
schema?: IRSForm;
@@ -23,10 +21,14 @@ interface ConstituentsSearchProps {
}
function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFiltered }: ConstituentsSearchProps) {
- const [filterMatch, setFilterMatch] = useLocalStorage(storage.cstFilterMatch, CstMatchMode.ALL);
- const [filterSource, setFilterSource] = useLocalStorage(storage.cstFilterGraph, DependencyMode.ALL);
- const [filterText, setFilterText] = useState('');
- const [showInherited, setShowInherited] = useLocalStorage(storage.cstFilterShowInherited, true);
+ const query = useCstSearchStore(state => state.query);
+ const filterMatch = useCstSearchStore(state => state.match);
+ const filterSource = useCstSearchStore(state => state.source);
+ const includeInherited = useCstSearchStore(state => state.includeInherited);
+ const setQuery = useCstSearchStore(state => state.setQuery);
+ const setMatch = useCstSearchStore(state => state.setMatch);
+ const setSource = useCstSearchStore(state => state.setSource);
+ const toggleInherited = useCstSearchStore(state => state.toggleInherited);
useEffect(() => {
if (!schema || schema.items.length === 0) {
@@ -39,15 +41,15 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
} else {
result = applyGraphFilter(schema, activeID, filterSource);
}
- if (filterText) {
- result = result.filter(cst => matchConstituenta(cst, filterText, filterMatch));
+ if (query) {
+ result = result.filter(cst => matchConstituenta(cst, query, filterMatch));
}
- if (!showInherited) {
+ if (!includeInherited) {
result = result.filter(cst => !cst.is_inherited);
}
setFiltered(result);
}, [
- filterText,
+ query,
setFiltered,
filterSource,
activeExpression,
@@ -55,7 +57,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
schema,
filterMatch,
activeID,
- showInherited
+ includeInherited
]);
return (
@@ -64,18 +66,18 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
id='constituents_search'
noBorder
className='min-w-[6rem] w-[6rem] mr-2 flex-grow'
- query={filterText}
- onChangeQuery={setFilterText}
+ query={query}
+ onChangeQuery={setQuery}
/>
- setFilterMatch(newValue)} dense={dense} />
- setFilterSource(newValue)} dense={dense} />
+ setMatch(newValue)} dense={dense} />
+ setSource(newValue)} dense={dense} />
{schema && schema?.stats.count_inherited > 0 ? (
${showInherited ? 'отображать' : 'скрывать'}`}
- icon={}
+ titleHtml={`Наследованные: ${includeInherited ? 'отображать' : 'скрывать'}`}
+ icon={}
className='h-fit self-center'
- onClick={() => setShowInherited(prev => !prev)}
+ onClick={toggleInherited}
/>
) : null}
diff --git a/rsconcept/frontend/src/stores/cstSearch.ts b/rsconcept/frontend/src/stores/cstSearch.ts
new file mode 100644
index 00000000..68b519a3
--- /dev/null
+++ b/rsconcept/frontend/src/stores/cstSearch.ts
@@ -0,0 +1,42 @@
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+
+import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
+
+interface CstSearchStore {
+ query: string;
+ setQuery: (value: string) => void;
+
+ match: CstMatchMode;
+ setMatch: (value: CstMatchMode) => void;
+
+ source: DependencyMode;
+ setSource: (value: DependencyMode) => void;
+
+ includeInherited: boolean;
+ toggleInherited: () => void;
+}
+
+export const useCstSearchStore = create