From 8c3e5dfd4d9b579bba216b309144c1f911c45d24 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:06:42 +0300 Subject: [PATCH] R: Migrating to zustand for local state management pt2 --- .vscode/settings.json | 1 + rsconcept/frontend/index.html | 16 -- rsconcept/frontend/src/app/ConceptToaster.tsx | 4 +- .../frontend/src/app/Navigation/Logo.tsx | 4 +- .../src/app/Navigation/ToggleNavigation.tsx | 5 +- .../src/app/Navigation/UserDropdown.tsx | 4 +- .../src/components/RSInput/RSInput.tsx | 4 +- .../src/components/RefsInput/RefsInput.tsx | 4 +- .../frontend/src/components/ui/Tooltip.tsx | 4 +- .../src/context/ConceptOptionsContext.tsx | 63 +------- .../pages/CreateItemPage/FormCreateItem.tsx | 14 +- .../src/pages/LibraryPage/LibraryPage.tsx | 130 ++------------- .../pages/LibraryPage/TableLibraryItems.tsx | 19 ++- .../src/pages/LibraryPage/ToolbarSearch.tsx | 79 +++------ .../pages/LibraryPage/ViewSideLocation.tsx | 35 ++-- .../EditorConstituenta/EditorConstituenta.tsx | 9 +- .../ToolbarConstituenta.tsx | 12 +- .../EditorRSExpression/EditorRSExpression.tsx | 13 +- .../ToolbarRSExpression.tsx | 14 +- .../EditorRSFormCard/EditorLibraryItem.tsx | 7 +- .../RSFormPage/EditorTermGraph/TGFlow.tsx | 99 ++++-------- .../RSFormPage/EditorTermGraph/ViewHidden.tsx | 13 +- .../frontend/src/stores/librarySearch.ts | 152 ++++++++++++++++++ rsconcept/frontend/src/stores/preferences.ts | 84 +++++++++- rsconcept/frontend/src/stores/termGraph.ts | 52 ++++++ rsconcept/frontend/src/utils/constants.ts | 19 --- 26 files changed, 438 insertions(+), 422 deletions(-) create mode 100644 rsconcept/frontend/src/stores/librarySearch.ts create mode 100644 rsconcept/frontend/src/stores/termGraph.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 96da4662..6de2fcff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -119,6 +119,7 @@ "NUMR", "Opencorpora", "overscroll", + "partialize", "passwordreset", "perfectivity", "PNCT", diff --git a/rsconcept/frontend/index.html b/rsconcept/frontend/index.html index d5d2ffe0..8870e50c 100644 --- a/rsconcept/frontend/index.html +++ b/rsconcept/frontend/index.html @@ -20,22 +20,6 @@ /> Концепт Портал - - -
diff --git a/rsconcept/frontend/src/app/ConceptToaster.tsx b/rsconcept/frontend/src/app/ConceptToaster.tsx index 262759e8..8ee8d170 100644 --- a/rsconcept/frontend/src/app/ConceptToaster.tsx +++ b/rsconcept/frontend/src/app/ConceptToaster.tsx @@ -1,11 +1,11 @@ import { ToastContainer, type ToastContainerProps } from 'react-toastify'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; +import { usePreferencesStore } from '@/stores/preferences'; interface ToasterThemedProps extends Omit {} function ToasterThemed(props: ToasterThemedProps) { - const { darkMode } = useConceptOptions(); + const darkMode = usePreferencesStore(state => state.darkMode); return ; } diff --git a/rsconcept/frontend/src/app/Navigation/Logo.tsx b/rsconcept/frontend/src/app/Navigation/Logo.tsx index b04f934b..7c3695df 100644 --- a/rsconcept/frontend/src/app/Navigation/Logo.tsx +++ b/rsconcept/frontend/src/app/Navigation/Logo.tsx @@ -1,8 +1,8 @@ -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import useWindowSize from '@/hooks/useWindowSize'; +import { usePreferencesStore } from '@/stores/preferences'; function Logo() { - const { darkMode } = useConceptOptions(); + const darkMode = usePreferencesStore(state => state.darkMode); const size = useWindowSize(); return ( diff --git a/rsconcept/frontend/src/app/Navigation/ToggleNavigation.tsx b/rsconcept/frontend/src/app/Navigation/ToggleNavigation.tsx index 74eb26e8..7aa5d0ff 100644 --- a/rsconcept/frontend/src/app/Navigation/ToggleNavigation.tsx +++ b/rsconcept/frontend/src/app/Navigation/ToggleNavigation.tsx @@ -1,12 +1,13 @@ import clsx from 'clsx'; import { IconDarkTheme, IconLightTheme, IconPin, IconUnpin } from '@/components/Icons'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useAppLayoutStore } from '@/stores/appLayout'; +import { usePreferencesStore } from '@/stores/preferences'; import { globals, PARAMETER } from '@/utils/constants'; function ToggleNavigation() { - const { toggleDarkMode, darkMode } = useConceptOptions(); + const darkMode = usePreferencesStore(state => state.darkMode); + const toggleDarkMode = usePreferencesStore(state => state.toggleDarkMode); const noNavigation = useAppLayoutStore(state => state.noNavigation); const noNavigationAnimation = useAppLayoutStore(state => state.noNavigationAnimation); const toggleNoNavigation = useAppLayoutStore(state => state.toggleNoNavigation); diff --git a/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx b/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx index 12bc88db..4284f515 100644 --- a/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx +++ b/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx @@ -16,7 +16,6 @@ import { CProps } from '@/components/props'; import Dropdown from '@/components/ui/Dropdown'; import DropdownButton from '@/components/ui/DropdownButton'; import { useAuth } from '@/context/AuthContext'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useConceptNavigation } from '@/context/NavigationContext'; import { usePreferencesStore } from '@/stores/preferences'; @@ -28,10 +27,11 @@ interface UserDropdownProps { } function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) { - const { darkMode, toggleDarkMode } = useConceptOptions(); const router = useConceptNavigation(); const { user, logout } = useAuth(); + const darkMode = usePreferencesStore(state => state.darkMode); + const toggleDarkMode = usePreferencesStore(state => state.toggleDarkMode); const showHelp = usePreferencesStore(state => state.showHelp); const toggleShowHelp = usePreferencesStore(state => state.toggleShowHelp); const adminMode = usePreferencesStore(state => state.adminMode); diff --git a/rsconcept/frontend/src/components/RSInput/RSInput.tsx b/rsconcept/frontend/src/components/RSInput/RSInput.tsx index 9ee15edd..a1791edf 100644 --- a/rsconcept/frontend/src/components/RSInput/RSInput.tsx +++ b/rsconcept/frontend/src/components/RSInput/RSInput.tsx @@ -9,10 +9,10 @@ import { EditorView } from 'codemirror'; import { forwardRef, useRef } from 'react'; import Label from '@/components/ui/Label'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { ConstituentaID, IRSForm } from '@/models/rsform'; import { generateAlias, getCstTypePrefix, guessCstType } from '@/models/rsformAPI'; import { extractGlobals } from '@/models/rslangAPI'; +import { usePreferencesStore } from '@/stores/preferences'; import { APP_COLORS } from '@/styling/color'; import { ccBracketMatching } from './bracketMatching'; @@ -64,7 +64,7 @@ const RSInput = forwardRef( }, ref ) => { - const { darkMode } = useConceptOptions(); + const darkMode = usePreferencesStore(state => state.darkMode); const internalRef = useRef(null); const thisRef = !ref || typeof ref === 'function' ? internalRef : ref; diff --git a/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx b/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx index b8858161..a1687fab 100644 --- a/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx +++ b/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx @@ -9,10 +9,10 @@ import { EditorView } from 'codemirror'; import { forwardRef, useRef, useState } from 'react'; import Label from '@/components/ui/Label'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import DlgEditReference from '@/dialogs/DlgEditReference'; import { ReferenceType } from '@/models/language'; import { ConstituentaID, IRSForm } from '@/models/rsform'; +import { usePreferencesStore } from '@/stores/preferences'; import { APP_COLORS } from '@/styling/color'; import { CodeMirrorWrapper } from '@/utils/codemirror'; import { PARAMETER } from '@/utils/constants'; @@ -92,7 +92,7 @@ const RefsInput = forwardRef( }, ref ) => { - const { darkMode } = useConceptOptions(); + const darkMode = usePreferencesStore(state => state.darkMode); const [isFocused, setIsFocused] = useState(false); diff --git a/rsconcept/frontend/src/components/ui/Tooltip.tsx b/rsconcept/frontend/src/components/ui/Tooltip.tsx index cc8e6cd5..93033833 100644 --- a/rsconcept/frontend/src/components/ui/Tooltip.tsx +++ b/rsconcept/frontend/src/components/ui/Tooltip.tsx @@ -5,7 +5,7 @@ import { ReactNode } from 'react'; import { createPortal } from 'react-dom'; import { ITooltip, Tooltip as TooltipImpl } from 'react-tooltip'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; +import { usePreferencesStore } from '@/stores/preferences'; export type { PlacesType } from 'react-tooltip'; @@ -29,7 +29,7 @@ function Tooltip({ style, ...restProps }: TooltipProps) { - const { darkMode } = useConceptOptions(); + const darkMode = usePreferencesStore(state => state.darkMode); if (typeof window === 'undefined') { return null; } diff --git a/rsconcept/frontend/src/context/ConceptOptionsContext.tsx b/rsconcept/frontend/src/context/ConceptOptionsContext.tsx index c1dbb926..8a6ebb4d 100644 --- a/rsconcept/frontend/src/context/ConceptOptionsContext.tsx +++ b/rsconcept/frontend/src/context/ConceptOptionsContext.tsx @@ -1,26 +1,15 @@ 'use client'; -import { createContext, useCallback, useContext, useEffect, useState } from 'react'; -import { flushSync } from 'react-dom'; +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 useLocalStorage from '@/hooks/useLocalStorage'; import { IConstituenta } from '@/models/rsform'; -import { globals, PARAMETER, storage } from '@/utils/constants'; +import { globals } from '@/utils/constants'; import { contextOutsideScope } from '@/utils/labels'; interface IOptionsContext { - darkMode: boolean; - toggleDarkMode: () => void; - - folderMode: boolean; - setFolderMode: React.Dispatch>; - - location: string; - setLocation: React.Dispatch>; - setHoverCst: (newValue: IConstituenta | undefined) => void; } @@ -34,59 +23,11 @@ export const useConceptOptions = () => { }; export const OptionsState = ({ children }: React.PropsWithChildren) => { - const [darkMode, setDarkMode] = useLocalStorage(storage.themeDark, false); - - const [folderMode, setFolderMode] = useLocalStorage(storage.librarySearchFolderMode, true); - const [location, setLocation] = useLocalStorage(storage.librarySearchLocation, ''); - const [hoverCst, setHoverCst] = useState(undefined); - function setDarkClass(isDark: boolean) { - const root = window.document.documentElement; - if (isDark) { - root.classList.add('dark'); - } else { - root.classList.remove('dark'); - } - root.setAttribute('data-color-scheme', !isDark ? 'light' : 'dark'); - } - - useEffect(() => { - setDarkClass(darkMode); - }, [darkMode]); - - const toggleDarkMode = useCallback(() => { - if (!document.startViewTransition) { - setDarkMode(prev => !prev); - } else { - const style = document.createElement('style'); - style.innerHTML = ` - * { - animation: none !important; - transition: none !important; - } - `; - document.head.appendChild(style); - - document.startViewTransition(() => { - flushSync(() => { - setDarkMode(prev => !prev); - }); - }); - - setTimeout(() => document.head.removeChild(style), PARAMETER.moveDuration); - } - }, [setDarkMode]); - return ( diff --git a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx index 59747ea3..9b9d3f77 100644 --- a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx +++ b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx @@ -20,21 +20,23 @@ import SubmitButton from '@/components/ui/SubmitButton'; import TextArea from '@/components/ui/TextArea'; import TextInput from '@/components/ui/TextInput'; import { useAuth } from '@/context/AuthContext'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useLibrary } from '@/context/LibraryContext'; import { useConceptNavigation } from '@/context/NavigationContext'; import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library'; import { ILibraryCreateData } from '@/models/library'; import { combineLocation, validateLocation } from '@/models/libraryAPI'; +import { useLibrarySearchStore } from '@/stores/librarySearch'; import { EXTEOR_TRS_FILE } from '@/utils/constants'; import { information } from '@/utils/labels'; function FormCreateItem() { const router = useConceptNavigation(); - const options = useConceptOptions(); const { user } = useAuth(); const { createItem, processingError, setProcessingError, processing, folders } = useLibrary(); + const searchLocation = useLibrarySearchStore(state => state.location); + const setSearchLocation = useLibrarySearchStore(state => state.setLocation); + const [itemType, setItemType] = useState(LibraryItemType.RSFORM); const [title, setTitle] = useState(''); const [alias, setAlias] = useState(''); @@ -81,7 +83,7 @@ function FormCreateItem() { file: file, fileName: file?.name }; - options.setLocation(location); + setSearchLocation(location); createItem(data, newItem => { toast.success(information.newLibraryItem); if (itemType == LibraryItemType.RSFORM) { @@ -108,11 +110,11 @@ function FormCreateItem() { }, []); useEffect(() => { - if (!options.location) { + if (!searchLocation) { return; } - handleSelectLocation(options.location); - }, [options.location, handleSelectLocation]); + handleSelectLocation(searchLocation); + }, [searchLocation, handleSelectLocation]); useEffect(() => { if (itemType !== LibraryItemType.RSFORM) { diff --git a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx index aae45feb..7aac876d 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx @@ -1,25 +1,20 @@ 'use client'; import fileDownload from 'js-file-download'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useState } from 'react'; import { toast } from 'react-toastify'; import { IconCSV } from '@/components/Icons'; import MiniButton from '@/components/ui/MiniButton'; import Overlay from '@/components/ui/Overlay'; import DataLoader from '@/components/wrap/DataLoader'; -import { useAuth } from '@/context/AuthContext'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useLibrary } from '@/context/LibraryContext'; import DlgChangeLocation from '@/dialogs/DlgChangeLocation'; -import useLocalStorage from '@/hooks/useLocalStorage'; -import { ILibraryItem, IRenameLocationData, LocationHead } from '@/models/library'; -import { ILibraryFilter } from '@/models/miscellaneous'; -import { UserID } from '@/models/user'; +import { IRenameLocationData } from '@/models/library'; import { useAppLayoutStore } from '@/stores/appLayout'; -import { storage } from '@/utils/constants'; +import { useLibraryFilter, useLibrarySearchStore } from '@/stores/librarySearch'; import { information } from '@/utils/labels'; -import { convertToCSV, toggleTristateFlag } from '@/utils/utils'; +import { convertToCSV } from '@/utils/utils'; import TableLibraryItems from './TableLibraryItems'; import ToolbarSearch from './ToolbarSearch'; @@ -27,89 +22,29 @@ import ViewSideLocation from './ViewSideLocation'; function LibraryPage() { const library = useLibrary(); - const { user } = useAuth(); - const [items, setItems] = useState([]); - const options = useConceptOptions(); const noNavigation = useAppLayoutStore(state => state.noNavigation); - const [query, setQuery] = useState(''); - const [path, setPath] = useState(''); + const folderMode = useLibrarySearchStore(state => state.folderMode); + const location = useLibrarySearchStore(state => state.location); + const setLocation = useLibrarySearchStore(state => state.setLocation); + + const filter = useLibraryFilter(); + const items = library.applyFilter(filter); - const [head, setHead] = useLocalStorage(storage.librarySearchHead, undefined); - const [subfolders, setSubfolders] = useLocalStorage(storage.librarySearchSubfolders, false); - const [isVisible, setIsVisible] = useLocalStorage(storage.librarySearchVisible, true); - const [isOwned, setIsOwned] = useLocalStorage(storage.librarySearchOwned, undefined); - const [isEditor, setIsEditor] = useLocalStorage(storage.librarySearchEditor, undefined); - const [filterUser, setFilterUser] = useLocalStorage(storage.librarySearchUser, undefined); const [showRenameLocation, setShowRenameLocation] = useState(false); - const filter: ILibraryFilter = useMemo( - () => ({ - head: head, - path: path, - query: query, - isEditor: user ? isEditor : undefined, - isOwned: user ? isOwned : undefined, - isVisible: user ? isVisible : true, - folderMode: options.folderMode, - subfolders: subfolders, - location: options.location, - filterUser: filterUser - }), - [ - head, - path, - query, - isEditor, - isOwned, - isVisible, - user, - options.folderMode, - options.location, - subfolders, - filterUser - ] - ); - - const hasCustomFilter = - !!filter.path || - !!filter.query || - filter.head !== undefined || - filter.isEditor !== undefined || - filter.isOwned !== undefined || - filter.isVisible !== true || - filter.filterUser !== undefined || - !!filter.location; - - useEffect(() => { - setItems(library.applyFilter(filter)); - }, [library, library.items.length, filter]); - - const toggleFolderMode = () => options.setFolderMode(prev => !prev); - - const resetFilter = useCallback(() => { - setQuery(''); - setPath(''); - setHead(undefined); - setIsVisible(true); - setIsOwned(undefined); - setIsEditor(undefined); - setFilterUser(undefined); - options.setLocation(''); - }, [setHead, setIsVisible, setIsOwned, setIsEditor, setFilterUser, options]); - const handleRenameLocation = useCallback( (newLocation: string) => { const data: IRenameLocationData = { - target: options.location, + target: location, new_location: newLocation }; library.renameLocation(data, () => { - options.setLocation(newLocation); + setLocation(newLocation); toast.success(information.locationRenamed); }); }, - [options, library] + [location, setLocation, library] ); const handleDownloadCSV = useCallback(() => { @@ -129,7 +64,7 @@ function LibraryPage() { {showRenameLocation ? ( setShowRenameLocation(false)} /> @@ -145,47 +80,16 @@ function LibraryPage() { onClick={handleDownloadCSV} /> - setIsOwned(prev => toggleTristateFlag(prev))} - toggleVisible={() => setIsVisible(prev => toggleTristateFlag(prev))} - isEditor={isEditor} - toggleEditor={() => setIsEditor(prev => toggleTristateFlag(prev))} - filterUser={filterUser} - onChangeFilterUser={setFilterUser} - resetFilter={resetFilter} - folderMode={options.folderMode} - toggleFolderMode={toggleFolderMode} - /> +
setSubfolders(prev => !prev)} onRenameLocation={() => setShowRenameLocation(true)} /> - +
); diff --git a/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx b/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx index 249a3f0d..2dadb173 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx @@ -14,27 +14,30 @@ import MiniButton from '@/components/ui/MiniButton'; import TextURL from '@/components/ui/TextURL'; import { useConceptNavigation } from '@/context/NavigationContext'; import { useUsers } from '@/context/UsersContext'; -import useLocalStorage from '@/hooks/useLocalStorage'; import useWindowSize from '@/hooks/useWindowSize'; import { ILibraryItem, LibraryItemType } from '@/models/library'; import { useFitHeight } from '@/stores/appLayout'; +import { useLibrarySearchStore } from '@/stores/librarySearch'; +import { usePreferencesStore } from '@/stores/preferences'; import { APP_COLORS } from '@/styling/color'; -import { storage } from '@/utils/constants'; interface TableLibraryItemsProps { items: ILibraryItem[]; - resetQuery: () => void; - folderMode: boolean; - toggleFolderMode: () => void; } const columnHelper = createColumnHelper(); -function TableLibraryItems({ items, resetQuery, folderMode, toggleFolderMode }: TableLibraryItemsProps) { +function TableLibraryItems({ items }: TableLibraryItemsProps) { const router = useConceptNavigation(); const intl = useIntl(); const { getUserLabel } = useUsers(); - const [itemsPerPage, setItemsPerPage] = useLocalStorage(storage.libraryPagination, 50); + + const folderMode = useLibrarySearchStore(state => state.folderMode); + const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode); + const resetFilter = useLibrarySearchStore(state => state.resetFilter); + + const itemsPerPage = usePreferencesStore(state => state.libraryPagination); + const setItemsPerPage = usePreferencesStore(state => state.setLibraryPagination); function handleOpenItem(item: ILibraryItem, event: CProps.EventMouse) { const selection = window.getSelection(); @@ -163,7 +166,7 @@ function TableLibraryItems({ items, resetQuery, folderMode, toggleFolderMode }:

Список схем пуст

- +

} diff --git a/rsconcept/frontend/src/pages/LibraryPage/ToolbarSearch.tsx b/rsconcept/frontend/src/pages/LibraryPage/ToolbarSearch.tsx index e3d18a4c..a77ec4d5 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/ToolbarSearch.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/ToolbarSearch.tsx @@ -22,7 +22,7 @@ import SelectorButton from '@/components/ui/SelectorButton'; import { useUsers } from '@/context/UsersContext'; import useDropdown from '@/hooks/useDropdown'; import { LocationHead } from '@/models/library'; -import { UserID } from '@/models/user'; +import { useHasCustomFilter, useLibrarySearchStore } from '@/stores/librarySearch'; import { prefixes } from '@/utils/constants'; import { describeLocationHead, labelLocationHead } from '@/utils/labels'; import { tripleToggleColor } from '@/utils/utils'; @@ -30,65 +30,38 @@ import { tripleToggleColor } from '@/utils/utils'; interface ToolbarSearchProps { total: number; filtered: number; - hasCustomFilter: boolean; - - query: string; - onChangeQuery: (newValue: string) => void; - path: string; - onChangePath: (newValue: string) => void; - head: LocationHead | undefined; - onChangeHead: (newValue: LocationHead | undefined) => void; - - folderMode: boolean; - toggleFolderMode: () => void; - - isVisible: boolean | undefined; - toggleVisible: () => void; - isOwned: boolean | undefined; - toggleOwned: () => void; - isEditor: boolean | undefined; - toggleEditor: () => void; - filterUser: UserID | undefined; - onChangeFilterUser: (newValue: UserID | undefined) => void; - - resetFilter: () => void; } -function ToolbarSearch({ - total, - filtered, - hasCustomFilter, - - query, - onChangeQuery, - path, - onChangePath, - head, - onChangeHead, - - folderMode, - toggleFolderMode, - - isVisible, - toggleVisible, - isOwned, - toggleOwned, - isEditor, - toggleEditor, - filterUser, - onChangeFilterUser, - - resetFilter -}: ToolbarSearchProps) { +function ToolbarSearch({ total, filtered }: ToolbarSearchProps) { const headMenu = useDropdown(); const userMenu = useDropdown(); const { users } = useUsers(); + const query = useLibrarySearchStore(state => state.query); + const setQuery = useLibrarySearchStore(state => state.setQuery); + const path = useLibrarySearchStore(state => state.path); + const setPath = useLibrarySearchStore(state => state.setPath); + const head = useLibrarySearchStore(state => state.head); + const setHead = useLibrarySearchStore(state => state.setHead); + const folderMode = useLibrarySearchStore(state => state.folderMode); + const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode); + const isOwned = useLibrarySearchStore(state => state.isOwned); + const toggleOwned = useLibrarySearchStore(state => state.toggleOwned); + const isEditor = useLibrarySearchStore(state => state.isEditor); + const toggleEditor = useLibrarySearchStore(state => state.toggleEditor); + const isVisible = useLibrarySearchStore(state => state.isVisible); + const toggleVisible = useLibrarySearchStore(state => state.toggleVisible); + const filterUser = useLibrarySearchStore(state => state.filterUser); + const setFilterUser = useLibrarySearchStore(state => state.setFilterUser); + + const resetFilter = useLibrarySearchStore(state => state.resetFilter); + const hasCustomFilter = useHasCustomFilter(); + const userActive = isOwned !== undefined || isEditor !== undefined || filterUser !== undefined; function handleChange(newValue: LocationHead | undefined) { headMenu.hide(); - onChangeHead(newValue); + setHead(newValue); } function handleToggleFolder() { @@ -157,7 +130,7 @@ function ToolbarSearch({ className='min-w-[15rem] text-sm mx-1 mb-1' items={users} value={filterUser} - onSelectValue={onChangeFilterUser} + onSelectValue={setFilterUser} /> @@ -177,7 +150,7 @@ function ToolbarSearch({ noBorder className={clsx('min-w-[7rem] sm:min-w-[10rem] max-w-[20rem]', folderMode && 'flex-grow')} query={query} - onChangeQuery={onChangeQuery} + onChangeQuery={setQuery} /> {!folderMode ? (
@@ -236,7 +209,7 @@ function ToolbarSearch({ noBorder className='w-[4.5rem] sm:w-[5rem] flex-grow' query={path} - onChangeQuery={onChangePath} + onChangeQuery={setPath} /> ) : null}
diff --git a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx index 260766b1..24d7ca21 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx @@ -13,45 +13,36 @@ import useWindowSize from '@/hooks/useWindowSize'; import { FolderNode, FolderTree } from '@/models/FolderTree'; import { HelpTopic } from '@/models/miscellaneous'; import { useFitHeight } from '@/stores/appLayout'; +import { useLibrarySearchStore } from '@/stores/librarySearch'; import { PARAMETER, prefixes } from '@/utils/constants'; import { information } from '@/utils/labels'; interface ViewSideLocationProps { folderTree: FolderTree; isVisible: boolean; - subfolders: boolean; - activeLocation: string; - onChangeActiveLocation: (newValue: string) => void; - toggleFolderMode: () => void; - toggleSubfolders: () => void; onRenameLocation: () => void; } -function ViewSideLocation({ - folderTree, - activeLocation, - subfolders, - isVisible, - onChangeActiveLocation, - toggleFolderMode, - toggleSubfolders, - onRenameLocation -}: ViewSideLocationProps) { +function ViewSideLocation({ folderTree, isVisible, onRenameLocation }: ViewSideLocationProps) { const { user } = useAuth(); const { items } = useLibrary(); const windowSize = useWindowSize(); + const location = useLibrarySearchStore(state => state.location); + const setLocation = useLibrarySearchStore(state => state.setLocation); + const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode); + const subfolders = useLibrarySearchStore(state => state.subfolders); + const toggleSubfolders = useLibrarySearchStore(state => state.toggleSubfolders); + const canRename = (() => { - if (activeLocation.length <= 3 || !user) { + if (location.length <= 3 || !user) { return false; } if (user.is_staff) { return true; } const owned = items.filter(item => item.owner == user.id); - const located = owned.filter( - item => item.location == activeLocation || item.location.startsWith(`${activeLocation}/`) - ); + const located = owned.filter(item => item.location == location || item.location.startsWith(`${location}/`)); return located.length !== 0; })(); @@ -66,7 +57,7 @@ function ViewSideLocation({ .then(() => toast.success(information.pathReady)) .catch(console.error); } else { - onChangeActiveLocation(target.getPath()); + setLocation(target.getPath()); } } @@ -97,7 +88,7 @@ function ViewSideLocation({ onClick={onRenameLocation} /> ) : null} - {!!activeLocation ? ( + {!!location ? ( } @@ -112,7 +103,7 @@ function ViewSideLocation({ state.showCstSideList); + const [toggleReset, setToggleReset] = useState(false); const disabled = !activeCst || !controller.isContentEditable || controller.isProcessing; @@ -78,10 +79,8 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit } activeCst={activeCst} disabled={disabled} modified={isModified} - showList={showList} onSubmit={initiateSubmit} onReset={() => setToggleReset(prev => !prev)} - onToggleList={() => setShowList(prev => !prev)} />
void; onReset: () => void; - onToggleList: () => void; } function ToolbarConstituenta({ activeCst, disabled, modified, - showList, onSubmit, - onReset, - onToggleList + onReset }: ToolbarConstituentaProps) { const controller = useRSEdit(); + const showList = usePreferencesStore(state => state.showCstSideList); + const toggleList = usePreferencesStore(state => state.toggleShowCstSideList); + return ( : } - onClick={onToggleList} + onClick={toggleList} /> {controller.isContentEditable ? ( diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx index 7f1f8a21..3f5de518 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx @@ -13,14 +13,13 @@ import Overlay from '@/components/ui/Overlay'; import { useRSForm } from '@/context/RSFormContext'; import DlgShowAST from '@/dialogs/DlgShowAST'; import useCheckConstituenta from '@/hooks/useCheckConstituenta'; -import useLocalStorage from '@/hooks/useLocalStorage'; import { HelpTopic } from '@/models/miscellaneous'; import { ConstituentaID, IConstituenta } from '@/models/rsform'; import { getDefinitionPrefix } from '@/models/rsformAPI'; import { IExpressionParse, IRSErrorDescription, SyntaxTree } from '@/models/rslang'; import { TokenID } from '@/models/rslang'; +import { usePreferencesStore } from '@/stores/preferences'; import { transformAST } from '@/utils/codemirror'; -import { storage } from '@/utils/constants'; import { errors, labelTypification } from '@/utils/labels'; import ParsingResult from './ParsingResult'; @@ -64,10 +63,10 @@ function EditorRSExpression({ const { resetParse } = parser; const rsInput = useRef(null); + const showControls = usePreferencesStore(state => state.showExpressionControls); const [syntaxTree, setSyntaxTree] = useState([]); const [expression, setExpression] = useState(''); const [showAST, setShowAST] = useState(false); - const [showControls, setShowControls] = useLocalStorage(storage.rseditShowControls, true); useEffect(() => { setIsModified(false); @@ -154,13 +153,7 @@ function EditorRSExpression({ setShowAST(false)} /> ) : null} - setShowControls(prev => !prev)} - showTypeGraph={onShowTypeGraph} - /> + void; showAST: (event: CProps.EventMouse) => void; showTypeGraph: (event: CProps.EventMouse) => void; } -function ToolbarRSExpression({ - disabled, - showControls, - showTypeGraph, - toggleControls, - showAST -}: ToolbarRSExpressionProps) { +function ToolbarRSExpression({ disabled, showTypeGraph, showAST }: ToolbarRSExpressionProps) { const model = useRSForm(); + const showControls = usePreferencesStore(state => state.showExpressionControls); + const toggleControls = usePreferencesStore(state => state.toggleShowExpressionControls); return ( diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx index ee371e55..43916f0c 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem.tsx @@ -18,12 +18,12 @@ import Overlay from '@/components/ui/Overlay'; import Tooltip from '@/components/ui/Tooltip'; import ValueIcon from '@/components/ui/ValueIcon'; import { useAccessMode } from '@/context/AccessModeContext'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useConceptNavigation } from '@/context/NavigationContext'; import { useUsers } from '@/context/UsersContext'; import useDropdown from '@/hooks/useDropdown'; import { ILibraryItemData, ILibraryItemEditor } from '@/models/library'; import { UserID, UserLevel } from '@/models/user'; +import { useLibrarySearchStore } from '@/stores/librarySearch'; import { prefixes } from '@/utils/constants'; import { prompts } from '@/utils/labels'; @@ -38,7 +38,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr const { accessLevel } = useAccessMode(); const intl = useIntl(); const router = useConceptNavigation(); - const { setLocation, setFolderMode } = useConceptOptions(); + const setLocation = useLibrarySearchStore(state => state.setLocation); const ownerSelector = useDropdown(); const onSelectUser = useCallback( @@ -61,10 +61,9 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr return; } setLocation(item.location); - setFolderMode(true); router.push(urls.library, event.ctrlKey || event.metaKey); }, - [setLocation, setFolderMode, item, router] + [setLocation, item, router] ); if (!item) { diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx index 9fd40af8..9fd2a7af 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx @@ -25,13 +25,12 @@ import { CProps } from '@/components/props'; import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection'; import Overlay from '@/components/ui/Overlay'; import DlgGraphParams from '@/dialogs/DlgGraphParams'; -import useLocalStorage from '@/hooks/useLocalStorage'; -import { GraphColoring, GraphFilterParams } from '@/models/miscellaneous'; import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform'; import { isBasicConcept } from '@/models/rsformAPI'; import { useMainHeight } from '@/stores/appLayout'; +import { useTermGraphStore } from '@/stores/termGraph'; import { APP_COLORS, colorBgGraphNode } from '@/styling/color'; -import { PARAMETER, storage } from '@/utils/constants'; +import { PARAMETER } from '@/utils/constants'; import { errors } from '@/utils/labels'; import { useRSEdit } from '../RSEditContext'; @@ -62,29 +61,14 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { const { addSelectedNodes } = store.getState(); const [showParamsDialog, setShowParamsDialog] = useState(false); - const [filterParams, setFilterParams] = useLocalStorage(storage.rsgraphFilter, { - noHermits: true, - noTemplates: false, - noTransitive: true, - noText: false, - foldDerived: false, - focusShowInputs: true, - focusShowOutputs: true, - - allowBase: true, - allowStruct: true, - allowTerm: true, - allowAxiom: true, - allowFunction: true, - allowPredicate: true, - allowConstant: true, - allowTheorem: true - }); - const [coloring, setColoring] = useLocalStorage(storage.rsgraphColoring, 'type'); + const filter = useTermGraphStore(state => state.filter); + const setFilter = useTermGraphStore(state => state.setFilter); + const coloring = useTermGraphStore(state => state.coloring); + const setColoring = useTermGraphStore(state => state.setColoring); const [focusCst, setFocusCst] = useState(undefined); - const filteredGraph = useGraphFilter(controller.schema, filterParams, focusCst); + const filteredGraph = useGraphFilter(controller.schema, filter, focusCst); const [hidden, setHidden] = useState([]); const [isDragging, setIsDragging] = useState(false); @@ -139,7 +123,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { data: { fill: focusCst === cst ? APP_COLORS.bgPurple : colorBgGraphNode(cst, coloring), label: cst.alias, - description: !filterParams.noText ? cst.term_resolved : '' + description: !filter.noText ? cst.term_resolved : '' } }); } @@ -167,24 +151,15 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { }); }); - applyLayout(newNodes, newEdges, !filterParams.noText); + applyLayout(newNodes, newEdges, !filter.noText); setNodes(newNodes); setEdges(newEdges); - }, [ - controller.schema, - filteredGraph, - setNodes, - setEdges, - filterParams.noText, - controller.selected, - focusCst, - coloring - ]); + }, [controller.schema, filteredGraph, setNodes, setEdges, filter.noText, controller.selected, focusCst, coloring]); useEffect(() => { setNeedReset(true); - }, [controller.schema, filterParams.noText, focusCst, coloring, flow.viewportInitialized]); + }, [controller.schema, filter.noText, focusCst, coloring, flow.viewportInitialized]); useEffect(() => { if (!controller.schema || !needReset || !flow.viewportInitialized) { @@ -198,7 +173,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { setTimeout(() => { flow.fitView({ duration: PARAMETER.zoomDuration }); }, PARAMETER.minimalTimeout); - }, [toggleResetView, flow, focusCst, filterParams]); + }, [toggleResetView, flow, focusCst, filter]); function handleSetSelected(newSelection: number[]) { controller.setSelected(newSelection); @@ -220,10 +195,6 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { controller.promptDeleteCst(); } - function handleChangeParams(params: GraphFilterParams) { - setFilterParams(params); - } - function handleSaveImage() { if (!controller.schema) { return; @@ -283,10 +254,10 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { } function handleFoldDerived() { - setFilterParams(prev => ({ - ...prev, - foldDerived: !prev.foldDerived - })); + setFilter({ + ...filter, + foldDerived: !filter.foldDerived + }); setTimeout(() => { setToggleResetView(prev => !prev); }, PARAMETER.graphRefreshDelay); @@ -325,17 +296,13 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { return ( <> {showParamsDialog ? ( - setShowParamsDialog(false)} - initial={filterParams} - onConfirm={handleChangeParams} - /> + setShowParamsDialog(false)} initial={filter} onConfirm={setFilter} /> ) : null} setShowParamsDialog(true)} onCreate={handleCreateCst} onDelete={handleDeleteCst} @@ -343,10 +310,10 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { onSaveImage={handleSaveImage} toggleFoldDerived={handleFoldDerived} toggleNoText={() => - setFilterParams(prev => ({ - ...prev, - noText: !prev.noText - })) + setFilter({ + ...filter, + noText: !filter.noText + }) } /> {!focusCst ? ( @@ -367,19 +334,19 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { handleSetFocus(undefined)} - showInputs={filterParams.focusShowInputs} - showOutputs={filterParams.focusShowOutputs} + showInputs={filter.focusShowInputs} + showOutputs={filter.focusShowOutputs} toggleShowInputs={() => - setFilterParams(prev => ({ - ...prev, - focusShowInputs: !prev.focusShowInputs - })) + setFilter({ + ...filter, + focusShowInputs: !filter.focusShowInputs + }) } toggleShowOutputs={() => - setFilterParams(prev => ({ - ...prev, - focusShowOutputs: !prev.focusShowOutputs - })) + setFilter({ + ...filter, + focusShowOutputs: !filter.focusShowOutputs + }) } /> ) : null} diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx index d64f1fb6..78d9fe19 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx @@ -7,13 +7,13 @@ import { CProps } from '@/components/props'; import MiniButton from '@/components/ui/MiniButton'; import Overlay from '@/components/ui/Overlay'; import { useConceptOptions } from '@/context/ConceptOptionsContext'; -import useLocalStorage from '@/hooks/useLocalStorage'; 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 { APP_COLORS, colorBgGraphNode } from '@/styling/color'; -import { globals, PARAMETER, prefixes, storage } from '@/utils/constants'; +import { globals, PARAMETER, prefixes } from '@/utils/constants'; interface ViewHiddenProps { items: ConstituentaID[]; @@ -29,7 +29,9 @@ interface ViewHiddenProps { function ViewHidden({ items, selected, toggleSelection, setFocus, schema, coloringScheme, onEdit }: ViewHiddenProps) { const windowSize = useWindowSize(); const localSelected = items.filter(id => selected.includes(id)); - const [isFolded, setIsFolded] = useLocalStorage(storage.rsgraphFoldHidden, false); + + const isFolded = useTermGraphStore(state => state.foldHidden); + const toggleFolded = useTermGraphStore(state => state.toggleFoldHidden); const { setHoverCst } = useConceptOptions(); const hiddenHeight = useFitHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px'); @@ -52,7 +54,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori noHover title={!isFolded ? 'Свернуть' : 'Развернуть'} icon={!isFolded ? : } - onClick={() => setIsFolded(prev => !prev)} + onClick={toggleFolded} />
@@ -87,7 +89,6 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori > {items.map(cstID => { const cst = schema.cstByID.get(cstID)!; - const adjustedColoring = coloringScheme === 'none' ? 'status' : coloringScheme; const id = `${prefixes.cst_hidden_list}${cst.alias}`; return (