mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
R: Migrating to zustand for local state management pt3
This commit is contained in:
parent
912fd22bfa
commit
b9ee1b9d08
|
@ -9,6 +9,8 @@ import { NavigationState } from '@/context/NavigationContext';
|
||||||
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
|
import { GlobalTooltips } from './GlobalTooltips';
|
||||||
|
|
||||||
function ApplicationLayout() {
|
function ApplicationLayout() {
|
||||||
const mainHeight = useMainHeight();
|
const mainHeight = useMainHeight();
|
||||||
const viewportHeight = useViewportHeight();
|
const viewportHeight = useViewportHeight();
|
||||||
|
@ -27,6 +29,8 @@ function ApplicationLayout() {
|
||||||
pauseOnFocusLoss={false}
|
pauseOnFocusLoss={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<GlobalTooltips />
|
||||||
|
|
||||||
<Navigation />
|
<Navigation />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
|
|
||||||
import { AuthState } from '@/context/AuthContext';
|
import { AuthState } from '@/context/AuthContext';
|
||||||
import { OptionsState } from '@/context/ConceptOptionsContext';
|
|
||||||
import { GlobalOssState } from '@/context/GlobalOssContext';
|
import { GlobalOssState } from '@/context/GlobalOssContext';
|
||||||
import { LibraryState } from '@/context/LibraryContext';
|
import { LibraryState } from '@/context/LibraryContext';
|
||||||
import { UsersState } from '@/context/UsersContext';
|
import { UsersState } from '@/context/UsersContext';
|
||||||
|
@ -31,11 +30,11 @@ function GlobalProviders({ children }: React.PropsWithChildren) {
|
||||||
onError={logError}
|
onError={logError}
|
||||||
>
|
>
|
||||||
<IntlProvider locale='ru' defaultLocale='ru'>
|
<IntlProvider locale='ru' defaultLocale='ru'>
|
||||||
<OptionsState>
|
|
||||||
<UsersState>
|
<UsersState>
|
||||||
<AuthState>
|
<AuthState>
|
||||||
<LibraryState>
|
<LibraryState>
|
||||||
<GlobalOssState>
|
<GlobalOssState>
|
||||||
|
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
|
@ -43,7 +42,6 @@ function GlobalProviders({ children }: React.PropsWithChildren) {
|
||||||
</LibraryState>
|
</LibraryState>
|
||||||
</AuthState>
|
</AuthState>
|
||||||
</UsersState>
|
</UsersState>
|
||||||
</OptionsState>
|
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
</ErrorBoundary>);
|
</ErrorBoundary>);
|
||||||
}
|
}
|
||||||
|
|
32
rsconcept/frontend/src/app/GlobalTooltips.tsx
Normal file
32
rsconcept/frontend/src/app/GlobalTooltips.tsx
Normal file
|
@ -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 (
|
||||||
|
<>
|
||||||
|
<Tooltip
|
||||||
|
float
|
||||||
|
id={globals.tooltip}
|
||||||
|
layer='z-topmost'
|
||||||
|
place='right-start'
|
||||||
|
className='mt-8 max-w-[20rem] break-words'
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
float
|
||||||
|
id={globals.value_tooltip}
|
||||||
|
layer='z-topmost'
|
||||||
|
className='max-w-[calc(min(40rem,100dvw-2rem))] text-justify'
|
||||||
|
/>
|
||||||
|
<Tooltip clickable id={globals.constituenta_tooltip} layer='z-modalTooltip' className='max-w-[30rem]'>
|
||||||
|
{hoverCst ? <InfoConstituenta data={hoverCst} onClick={event => event.stopPropagation()} /> : <Loader />}
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { CstClass, IConstituenta } from '@/models/rsform';
|
import { CstClass, IConstituenta } from '@/models/rsform';
|
||||||
|
import { useTooltipsStore } from '@/stores/tooltips';
|
||||||
import { APP_COLORS, colorFgCstStatus } from '@/styling/color';
|
import { APP_COLORS, colorFgCstStatus } from '@/styling/color';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ interface BadgeConstituentaProps extends CProps.Styling {
|
||||||
* Displays a badge with a constituenta alias and information tooltip.
|
* Displays a badge with a constituenta alias and information tooltip.
|
||||||
*/
|
*/
|
||||||
function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstituentaProps) {
|
function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstituentaProps) {
|
||||||
const { setHoverCst } = useConceptOptions();
|
const setActiveCst = useTooltipsStore(state => state.setActiveCst);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -39,7 +39,7 @@ function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstitue
|
||||||
...style
|
...style
|
||||||
}}
|
}}
|
||||||
data-tooltip-id={globals.constituenta_tooltip}
|
data-tooltip-id={globals.constituenta_tooltip}
|
||||||
onMouseEnter={() => setHoverCst(value)}
|
onMouseEnter={() => setActiveCst(value)}
|
||||||
>
|
>
|
||||||
{value.alias}
|
{value.alias}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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<IOptionsContext | null>(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<IConstituenta | undefined>(undefined);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<OptionsContext
|
|
||||||
value={{
|
|
||||||
setHoverCst
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
<Tooltip
|
|
||||||
float
|
|
||||||
id={globals.tooltip}
|
|
||||||
layer='z-topmost'
|
|
||||||
place='right-start'
|
|
||||||
className='mt-8 max-w-[20rem] break-words'
|
|
||||||
/>
|
|
||||||
<Tooltip
|
|
||||||
float
|
|
||||||
id={globals.value_tooltip}
|
|
||||||
layer='z-topmost'
|
|
||||||
className='max-w-[calc(min(40rem,100dvw-2rem))] text-justify'
|
|
||||||
/>
|
|
||||||
<Tooltip clickable id={globals.constituenta_tooltip} layer='z-modalTooltip' className='max-w-[30rem]'>
|
|
||||||
{hoverCst ? <InfoConstituenta data={hoverCst} onClick={event => event.stopPropagation()} /> : <Loader />}
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
</OptionsContext>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,31 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { storage } from '@/utils/constants';
|
|
||||||
|
|
||||||
function useLocalStorage<ValueType>(key: string, defaultValue: ValueType | (() => ValueType)) {
|
|
||||||
const prefixedKey = `${storage.PREFIX}${key}`;
|
|
||||||
const [value, setValue] = useState<ValueType>(() => {
|
|
||||||
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;
|
|
|
@ -19,12 +19,12 @@ import {
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import { useOSS } from '@/context/OssContext';
|
import { useOSS } from '@/context/OssContext';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
|
||||||
import { OssNode } from '@/models/miscellaneous';
|
import { OssNode } from '@/models/miscellaneous';
|
||||||
import { OperationID } from '@/models/oss';
|
import { OperationID } from '@/models/oss';
|
||||||
import { useMainHeight } from '@/stores/appLayout';
|
import { useMainHeight } from '@/stores/appLayout';
|
||||||
|
import { useOSSGraphStore } from '@/stores/ossGraph';
|
||||||
import { APP_COLORS } from '@/styling/color';
|
import { APP_COLORS } from '@/styling/color';
|
||||||
import { PARAMETER, storage } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { errors } from '@/utils/labels';
|
import { errors } from '@/utils/labels';
|
||||||
|
|
||||||
import { useOssEdit } from '../OssEditContext';
|
import { useOssEdit } from '../OssEditContext';
|
||||||
|
@ -46,9 +46,9 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
const controller = useOssEdit();
|
const controller = useOssEdit();
|
||||||
const flow = useReactFlow();
|
const flow = useReactFlow();
|
||||||
|
|
||||||
const [showGrid, setShowGrid] = useLocalStorage<boolean>(storage.ossShowGrid, false);
|
const showGrid = useOSSGraphStore(state => state.showGrid);
|
||||||
const [edgeAnimate, setEdgeAnimate] = useLocalStorage<boolean>(storage.ossEdgeAnimate, false);
|
const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate);
|
||||||
const [edgeStraight, setEdgeStraight] = useLocalStorage<boolean>(storage.ossEdgeStraight, false);
|
const edgeStraight = useOSSGraphStore(state => state.edgeStraight);
|
||||||
|
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
|
@ -277,9 +277,6 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
<Overlay position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
<Overlay position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
||||||
<ToolbarOssGraph
|
<ToolbarOssGraph
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
showGrid={showGrid}
|
|
||||||
edgeAnimate={edgeAnimate}
|
|
||||||
edgeStraight={edgeStraight}
|
|
||||||
onFitView={() => flow.fitView({ duration: PARAMETER.zoomDuration })}
|
onFitView={() => flow.fitView({ duration: PARAMETER.zoomDuration })}
|
||||||
onCreate={() => handleCreateOperation(controller.selected)}
|
onCreate={() => handleCreateOperation(controller.selected)}
|
||||||
onDelete={handleDeleteSelected}
|
onDelete={handleDeleteSelected}
|
||||||
|
@ -288,9 +285,6 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
onResetPositions={() => setToggleReset(prev => !prev)}
|
onResetPositions={() => setToggleReset(prev => !prev)}
|
||||||
onSavePositions={handleSavePositions}
|
onSavePositions={handleSavePositions}
|
||||||
onSaveImage={handleSaveImage}
|
onSaveImage={handleSaveImage}
|
||||||
toggleShowGrid={() => setShowGrid(prev => !prev)}
|
|
||||||
toggleEdgeAnimate={() => setEdgeAnimate(prev => !prev)}
|
|
||||||
toggleEdgeStraight={() => setEdgeStraight(prev => !prev)}
|
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
{menuProps ? (
|
{menuProps ? (
|
||||||
|
|
|
@ -21,6 +21,7 @@ import BadgeHelp from '@/components/info/BadgeHelp';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { OperationType } from '@/models/oss';
|
import { OperationType } from '@/models/oss';
|
||||||
|
import { useOSSGraphStore } from '@/stores/ossGraph';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { prepareTooltip } from '@/utils/labels';
|
import { prepareTooltip } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -28,9 +29,6 @@ import { useOssEdit } from '../OssEditContext';
|
||||||
|
|
||||||
interface ToolbarOssGraphProps {
|
interface ToolbarOssGraphProps {
|
||||||
isModified: boolean;
|
isModified: boolean;
|
||||||
showGrid: boolean;
|
|
||||||
edgeAnimate: boolean;
|
|
||||||
edgeStraight: boolean;
|
|
||||||
onCreate: () => void;
|
onCreate: () => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
onEdit: () => void;
|
onEdit: () => void;
|
||||||
|
@ -39,16 +37,10 @@ interface ToolbarOssGraphProps {
|
||||||
onSaveImage: () => void;
|
onSaveImage: () => void;
|
||||||
onSavePositions: () => void;
|
onSavePositions: () => void;
|
||||||
onResetPositions: () => void;
|
onResetPositions: () => void;
|
||||||
toggleShowGrid: () => void;
|
|
||||||
toggleEdgeAnimate: () => void;
|
|
||||||
toggleEdgeStraight: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolbarOssGraph({
|
function ToolbarOssGraph({
|
||||||
isModified,
|
isModified,
|
||||||
showGrid,
|
|
||||||
edgeAnimate,
|
|
||||||
edgeStraight,
|
|
||||||
onCreate,
|
onCreate,
|
||||||
onDelete,
|
onDelete,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
@ -56,13 +48,18 @@ function ToolbarOssGraph({
|
||||||
onFitView,
|
onFitView,
|
||||||
onSaveImage,
|
onSaveImage,
|
||||||
onSavePositions,
|
onSavePositions,
|
||||||
onResetPositions,
|
onResetPositions
|
||||||
toggleShowGrid,
|
|
||||||
toggleEdgeAnimate,
|
|
||||||
toggleEdgeStraight
|
|
||||||
}: ToolbarOssGraphProps) {
|
}: ToolbarOssGraphProps) {
|
||||||
const controller = useOssEdit();
|
const controller = useOssEdit();
|
||||||
const selectedOperation = controller.schema?.operationByID.get(controller.selected[0]);
|
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 = (() => {
|
const readyForSynthesis = (() => {
|
||||||
if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) {
|
if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -6,12 +6,12 @@ import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { GraphColoring } from '@/models/miscellaneous';
|
import { GraphColoring } from '@/models/miscellaneous';
|
||||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||||
import { useFitHeight } from '@/stores/appLayout';
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
import { useTermGraphStore } from '@/stores/termGraph';
|
import { useTermGraphStore } from '@/stores/termGraph';
|
||||||
|
import { useTooltipsStore } from '@/stores/tooltips';
|
||||||
import { APP_COLORS, colorBgGraphNode } from '@/styling/color';
|
import { APP_COLORS, colorBgGraphNode } from '@/styling/color';
|
||||||
import { globals, PARAMETER, prefixes } from '@/utils/constants';
|
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 isFolded = useTermGraphStore(state => state.foldHidden);
|
||||||
const toggleFolded = useTermGraphStore(state => state.toggleFoldHidden);
|
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');
|
const hiddenHeight = useFitHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px');
|
||||||
|
|
||||||
function handleClick(cstID: ConstituentaID, event: CProps.EventMouse) {
|
function handleClick(cstID: ConstituentaID, event: CProps.EventMouse) {
|
||||||
|
@ -110,7 +110,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
|
||||||
onClick={event => handleClick(cstID, event)}
|
onClick={event => handleClick(cstID, event)}
|
||||||
onDoubleClick={() => onEdit(cstID)}
|
onDoubleClick={() => onEdit(cstID)}
|
||||||
data-tooltip-id={globals.constituenta_tooltip}
|
data-tooltip-id={globals.constituenta_tooltip}
|
||||||
onMouseEnter={() => setHoverCst(cst)}
|
onMouseEnter={() => setActiveCst(cst)}
|
||||||
>
|
>
|
||||||
{cst.alias}
|
{cst.alias}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { IconChild } from '@/components/Icons';
|
import { IconChild } from '@/components/Icons';
|
||||||
import SelectGraphFilter from '@/components/select/SelectGraphFilter';
|
import SelectGraphFilter from '@/components/select/SelectGraphFilter';
|
||||||
import SelectMatchMode from '@/components/select/SelectMatchMode';
|
import SelectMatchMode from '@/components/select/SelectMatchMode';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import SearchBar from '@/components/ui/SearchBar';
|
import SearchBar from '@/components/ui/SearchBar';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
|
||||||
import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
|
|
||||||
import { applyGraphFilter } from '@/models/miscellaneousAPI';
|
import { applyGraphFilter } from '@/models/miscellaneousAPI';
|
||||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { matchConstituenta } from '@/models/rsformAPI';
|
import { matchConstituenta } from '@/models/rsformAPI';
|
||||||
import { storage } from '@/utils/constants';
|
import { useCstSearchStore } from '@/stores/cstSearch';
|
||||||
|
|
||||||
interface ConstituentsSearchProps {
|
interface ConstituentsSearchProps {
|
||||||
schema?: IRSForm;
|
schema?: IRSForm;
|
||||||
|
@ -23,10 +21,14 @@ interface ConstituentsSearchProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFiltered }: ConstituentsSearchProps) {
|
function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFiltered }: ConstituentsSearchProps) {
|
||||||
const [filterMatch, setFilterMatch] = useLocalStorage(storage.cstFilterMatch, CstMatchMode.ALL);
|
const query = useCstSearchStore(state => state.query);
|
||||||
const [filterSource, setFilterSource] = useLocalStorage(storage.cstFilterGraph, DependencyMode.ALL);
|
const filterMatch = useCstSearchStore(state => state.match);
|
||||||
const [filterText, setFilterText] = useState('');
|
const filterSource = useCstSearchStore(state => state.source);
|
||||||
const [showInherited, setShowInherited] = useLocalStorage(storage.cstFilterShowInherited, true);
|
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(() => {
|
useEffect(() => {
|
||||||
if (!schema || schema.items.length === 0) {
|
if (!schema || schema.items.length === 0) {
|
||||||
|
@ -39,15 +41,15 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
|
||||||
} else {
|
} else {
|
||||||
result = applyGraphFilter(schema, activeID, filterSource);
|
result = applyGraphFilter(schema, activeID, filterSource);
|
||||||
}
|
}
|
||||||
if (filterText) {
|
if (query) {
|
||||||
result = result.filter(cst => matchConstituenta(cst, filterText, filterMatch));
|
result = result.filter(cst => matchConstituenta(cst, query, filterMatch));
|
||||||
}
|
}
|
||||||
if (!showInherited) {
|
if (!includeInherited) {
|
||||||
result = result.filter(cst => !cst.is_inherited);
|
result = result.filter(cst => !cst.is_inherited);
|
||||||
}
|
}
|
||||||
setFiltered(result);
|
setFiltered(result);
|
||||||
}, [
|
}, [
|
||||||
filterText,
|
query,
|
||||||
setFiltered,
|
setFiltered,
|
||||||
filterSource,
|
filterSource,
|
||||||
activeExpression,
|
activeExpression,
|
||||||
|
@ -55,7 +57,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
|
||||||
schema,
|
schema,
|
||||||
filterMatch,
|
filterMatch,
|
||||||
activeID,
|
activeID,
|
||||||
showInherited
|
includeInherited
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -64,18 +66,18 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt
|
||||||
id='constituents_search'
|
id='constituents_search'
|
||||||
noBorder
|
noBorder
|
||||||
className='min-w-[6rem] w-[6rem] mr-2 flex-grow'
|
className='min-w-[6rem] w-[6rem] mr-2 flex-grow'
|
||||||
query={filterText}
|
query={query}
|
||||||
onChangeQuery={setFilterText}
|
onChangeQuery={setQuery}
|
||||||
/>
|
/>
|
||||||
<SelectMatchMode value={filterMatch} onChange={newValue => setFilterMatch(newValue)} dense={dense} />
|
<SelectMatchMode value={filterMatch} onChange={newValue => setMatch(newValue)} dense={dense} />
|
||||||
<SelectGraphFilter value={filterSource} onChange={newValue => setFilterSource(newValue)} dense={dense} />
|
<SelectGraphFilter value={filterSource} onChange={newValue => setSource(newValue)} dense={dense} />
|
||||||
{schema && schema?.stats.count_inherited > 0 ? (
|
{schema && schema?.stats.count_inherited > 0 ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
titleHtml={`Наследованные: <b>${showInherited ? 'отображать' : 'скрывать'}</b>`}
|
titleHtml={`Наследованные: <b>${includeInherited ? 'отображать' : 'скрывать'}</b>`}
|
||||||
icon={<IconChild size='1rem' className={showInherited ? 'icon-primary' : 'clr-text-controls'} />}
|
icon={<IconChild size='1rem' className={includeInherited ? 'icon-primary' : 'clr-text-controls'} />}
|
||||||
className='h-fit self-center'
|
className='h-fit self-center'
|
||||||
onClick={() => setShowInherited(prev => !prev)}
|
onClick={toggleInherited}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
42
rsconcept/frontend/src/stores/cstSearch.ts
Normal file
42
rsconcept/frontend/src/stores/cstSearch.ts
Normal file
|
@ -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<CstSearchStore>()(
|
||||||
|
persist(
|
||||||
|
set => ({
|
||||||
|
query: '',
|
||||||
|
setQuery: value => set({ query: value }),
|
||||||
|
match: CstMatchMode.ALL,
|
||||||
|
setMatch: value => set({ match: value }),
|
||||||
|
source: DependencyMode.ALL,
|
||||||
|
setSource: value => set({ source: value }),
|
||||||
|
includeInherited: true,
|
||||||
|
toggleInherited: () => set(state => ({ includeInherited: !state.includeInherited }))
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
version: 1,
|
||||||
|
partialize: state => ({
|
||||||
|
match: state.match,
|
||||||
|
source: state.source,
|
||||||
|
includeInherited: state.includeInherited
|
||||||
|
}),
|
||||||
|
name: 'portal.constituenta.search'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
32
rsconcept/frontend/src/stores/ossGraph.ts
Normal file
32
rsconcept/frontend/src/stores/ossGraph.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
|
interface OSSGraphStore {
|
||||||
|
showGrid: boolean;
|
||||||
|
toggleShowGrid: () => void;
|
||||||
|
|
||||||
|
edgeAnimate: boolean;
|
||||||
|
toggleEdgeAnimate: () => void;
|
||||||
|
|
||||||
|
edgeStraight: boolean;
|
||||||
|
toggleEdgeStraight: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useOSSGraphStore = create<OSSGraphStore>()(
|
||||||
|
persist(
|
||||||
|
set => ({
|
||||||
|
showGrid: false,
|
||||||
|
toggleShowGrid: () => set(state => ({ showGrid: !state.showGrid })),
|
||||||
|
|
||||||
|
edgeAnimate: false,
|
||||||
|
toggleEdgeAnimate: () => set(state => ({ edgeAnimate: !state.edgeAnimate })),
|
||||||
|
|
||||||
|
edgeStraight: false,
|
||||||
|
toggleEdgeStraight: () => set(state => ({ edgeStraight: !state.edgeStraight }))
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
version: 1,
|
||||||
|
name: 'portal.ossGraph'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
13
rsconcept/frontend/src/stores/tooltips.ts
Normal file
13
rsconcept/frontend/src/stores/tooltips.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
import { IConstituenta } from '@/models/rsform';
|
||||||
|
|
||||||
|
interface TooltipsStore {
|
||||||
|
activeCst: IConstituenta | undefined;
|
||||||
|
setActiveCst: (value: IConstituenta | undefined) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTooltipsStore = create<TooltipsStore>()(set => ({
|
||||||
|
activeCst: undefined,
|
||||||
|
setActiveCst: value => set({ activeCst: value })
|
||||||
|
}));
|
|
@ -102,21 +102,6 @@ export const external_urls = {
|
||||||
restAPI: 'https://api.portal.acconcept.ru'
|
restAPI: 'https://api.portal.acconcept.ru'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Local storage ID.
|
|
||||||
*/
|
|
||||||
export const storage = {
|
|
||||||
PREFIX: 'portal.',
|
|
||||||
|
|
||||||
ossShowGrid: 'oss.show_grid',
|
|
||||||
ossEdgeStraight: 'oss.edge_straight',
|
|
||||||
ossEdgeAnimate: 'oss.edge_animate',
|
|
||||||
|
|
||||||
cstFilterMatch: 'cst.filter.match',
|
|
||||||
cstFilterGraph: 'cst.filter.graph',
|
|
||||||
cstFilterShowInherited: 'cst.filter.show_inherited'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global element ID.
|
* Global element ID.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue
Block a user