state.showHelp);
+ const toggleShowHelp = usePreferencesStore(state => state.toggleShowHelp);
+ const adminMode = usePreferencesStore(state => state.adminMode);
+ const toggleAdminMode = usePreferencesStore(state => state.toggleAdminMode);
+
function navigateProfile(event: CProps.EventMouse) {
hideDropdown();
router.push(urls.profile, event.ctrlKey || event.metaKey);
diff --git a/rsconcept/frontend/src/app/Navigation/UserMenu.tsx b/rsconcept/frontend/src/app/Navigation/UserMenu.tsx
index 7075c2fe..236336da 100644
--- a/rsconcept/frontend/src/app/Navigation/UserMenu.tsx
+++ b/rsconcept/frontend/src/app/Navigation/UserMenu.tsx
@@ -1,9 +1,9 @@
import { IconLogin, IconUser2 } from '@/components/Icons';
import Loader from '@/components/ui/Loader';
import { useAuth } from '@/context/AuthContext';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import useDropdown from '@/hooks/useDropdown';
+import { usePreferencesStore } from '@/stores/preferences';
import { urls } from '../urls';
import NavigationButton from './NavigationButton';
@@ -12,7 +12,7 @@ import UserDropdown from './UserDropdown';
function UserMenu() {
const router = useConceptNavigation();
const { user, loading } = useAuth();
- const { adminMode } = useConceptOptions();
+ const adminMode = usePreferencesStore(state => state.adminMode);
const menu = useDropdown();
const navigateLogin = () => router.push(urls.login);
diff --git a/rsconcept/frontend/src/components/info/BadgeHelp.tsx b/rsconcept/frontend/src/components/info/BadgeHelp.tsx
index e5aa2ac9..568e424a 100644
--- a/rsconcept/frontend/src/components/info/BadgeHelp.tsx
+++ b/rsconcept/frontend/src/components/info/BadgeHelp.tsx
@@ -2,8 +2,8 @@ import React, { Suspense } from 'react';
import TextURL from '@/components/ui/TextURL';
import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { HelpTopic } from '@/models/miscellaneous';
+import { usePreferencesStore } from '@/stores/preferences';
import { IconHelp } from '../Icons';
import { CProps } from '../props';
@@ -29,7 +29,7 @@ interface BadgeHelpProps extends CProps.Styling {
* Display help icon with a manual page tooltip.
*/
function BadgeHelp({ topic, padding = 'p-1', ...restProps }: BadgeHelpProps) {
- const { showHelp } = useConceptOptions();
+ const showHelp = usePreferencesStore(state => state.showHelp);
if (!showHelp) {
return null;
diff --git a/rsconcept/frontend/src/components/ui/PDFViewer/PDFViewer.tsx b/rsconcept/frontend/src/components/ui/PDFViewer/PDFViewer.tsx
index 52c3cde1..59c1a7bc 100644
--- a/rsconcept/frontend/src/components/ui/PDFViewer/PDFViewer.tsx
+++ b/rsconcept/frontend/src/components/ui/PDFViewer/PDFViewer.tsx
@@ -1,7 +1,7 @@
'use client';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useWindowSize from '@/hooks/useWindowSize';
+import { useFitHeight } from '@/stores/appLayout';
/** Maximum width of the viewer. */
const MAXIMUM_WIDTH = 1600;
@@ -25,10 +25,9 @@ interface PDFViewerProps {
*/
function PDFViewer({ file, offsetXpx, minWidth = MINIMUM_WIDTH }: PDFViewerProps) {
const windowSize = useWindowSize();
- const { calculateHeight } = useConceptOptions();
const pageWidth = Math.max(minWidth, Math.min((windowSize?.width ?? 0) - (offsetXpx ?? 0) - 10, MAXIMUM_WIDTH));
- const pageHeight = calculateHeight('1rem');
+ const pageHeight = useFitHeight('1rem');
return
;
}
diff --git a/rsconcept/frontend/src/context/ConceptOptionsContext.tsx b/rsconcept/frontend/src/context/ConceptOptionsContext.tsx
index 9ea0ad46..c1dbb926 100644
--- a/rsconcept/frontend/src/context/ConceptOptionsContext.tsx
+++ b/rsconcept/frontend/src/context/ConceptOptionsContext.tsx
@@ -1,6 +1,6 @@
'use client';
-import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
+import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { flushSync } from 'react-dom';
import InfoConstituenta from '@/components/info/InfoConstituenta';
@@ -12,28 +12,9 @@ import { globals, PARAMETER, storage } from '@/utils/constants';
import { contextOutsideScope } from '@/utils/labels';
interface IOptionsContext {
- viewportHeight: string;
- mainHeight: string;
-
darkMode: boolean;
toggleDarkMode: () => void;
- adminMode: boolean;
- toggleAdminMode: () => void;
-
- noNavigationAnimation: boolean;
- noNavigation: boolean;
- toggleNoNavigation: () => void;
-
- noFooter: boolean;
- setNoFooter: React.Dispatch
>;
-
- showScroll: boolean;
- setShowScroll: React.Dispatch>;
-
- showHelp: boolean;
- toggleShowHelp: () => void;
-
folderMode: boolean;
setFolderMode: React.Dispatch>;
@@ -41,8 +22,6 @@ interface IOptionsContext {
setLocation: React.Dispatch>;
setHoverCst: (newValue: IConstituenta | undefined) => void;
-
- calculateHeight: (offset: string, minimum?: string) => string;
}
const OptionsContext = createContext(null);
@@ -56,17 +35,10 @@ export const useConceptOptions = () => {
export const OptionsState = ({ children }: React.PropsWithChildren) => {
const [darkMode, setDarkMode] = useLocalStorage(storage.themeDark, false);
- const [adminMode, setAdminMode] = useLocalStorage(storage.optionsAdmin, false);
- const [showHelp, setShowHelp] = useLocalStorage(storage.optionsHelp, true);
- const [noNavigation, setNoNavigation] = useState(false);
const [folderMode, setFolderMode] = useLocalStorage(storage.librarySearchFolderMode, true);
const [location, setLocation] = useLocalStorage(storage.librarySearchLocation, '');
- const [noNavigationAnimation, setNoNavigationAnimation] = useState(false);
- const [noFooter, setNoFooter] = useState(false);
- const [showScroll, setShowScroll] = useState(false);
-
const [hoverCst, setHoverCst] = useState(undefined);
function setDarkClass(isDark: boolean) {
@@ -83,29 +55,6 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
setDarkClass(darkMode);
}, [darkMode]);
- const toggleNoNavigation = useCallback(() => {
- if (noNavigation) {
- setNoNavigationAnimation(false);
- setNoNavigation(false);
- } else {
- setNoNavigationAnimation(true);
- setTimeout(() => setNoNavigation(true), PARAMETER.moveDuration);
- }
- }, [noNavigation]);
-
- const calculateHeight = useCallback(
- (offset: string, minimum: string = '0px') => {
- if (noNavigation) {
- return `max(calc(100dvh - (${offset})), ${minimum})`;
- } else if (noFooter) {
- return `max(calc(100dvh - 3rem - (${offset})), ${minimum})`;
- } else {
- return `max(calc(100dvh - 6.75rem - (${offset})), ${minimum})`;
- }
- },
- [noNavigation, noFooter]
- );
-
const toggleDarkMode = useCallback(() => {
if (!document.startViewTransition) {
setDarkMode(prev => !prev);
@@ -129,43 +78,15 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
}
}, [setDarkMode]);
- const mainHeight = useMemo(() => {
- if (noNavigation) {
- return '100dvh';
- } else if (noFooter) {
- return 'calc(100dvh - 3rem)';
- } else {
- return 'calc(100dvh - 6.75rem)';
- }
- }, [noNavigation, noFooter]);
-
- const viewportHeight = useMemo(() => {
- return !noNavigation ? 'calc(100dvh - 3rem)' : '100dvh';
- }, [noNavigation]);
-
return (
setAdminMode(prev => !prev),
- toggleNoNavigation: toggleNoNavigation,
- setNoFooter,
- setShowScroll,
- toggleShowHelp: () => setShowHelp(prev => !prev),
- viewportHeight,
- mainHeight,
- calculateHeight,
setHoverCst
}}
>
diff --git a/rsconcept/frontend/src/context/LibraryContext.tsx b/rsconcept/frontend/src/context/LibraryContext.tsx
index 9a9295eb..e087c0ab 100644
--- a/rsconcept/frontend/src/context/LibraryContext.tsx
+++ b/rsconcept/frontend/src/context/LibraryContext.tsx
@@ -21,10 +21,10 @@ import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI'
import { ILibraryFilter } from '@/models/miscellaneous';
import { IRSForm, IRSFormCloneData, IRSFormData } from '@/models/rsform';
import { RSFormLoader } from '@/models/RSFormLoader';
+import { usePreferencesStore } from '@/stores/preferences';
import { contextOutsideScope } from '@/utils/labels';
import { useAuth } from './AuthContext';
-import { useConceptOptions } from './ConceptOptionsContext';
interface ILibraryContext {
items: ILibraryItem[];
@@ -63,7 +63,7 @@ export const useLibrary = (): ILibraryContext => {
export const LibraryState = ({ children }: React.PropsWithChildren) => {
const { user, loading: userLoading } = useAuth();
- const { adminMode } = useConceptOptions();
+ const adminMode = usePreferencesStore(state => state.adminMode);
const [items, setItems] = useState([]);
const [templates, setTemplates] = useState([]);
diff --git a/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx b/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx
index 1e3067d2..dec3f68d 100644
--- a/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx
+++ b/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx
@@ -3,18 +3,18 @@
import { useEffect } from 'react';
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
+import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
import { resources } from '@/utils/constants';
function DatabaseSchemaPage() {
- const { calculateHeight, setNoFooter } = useConceptOptions();
+ const hideFooter = useAppLayoutStore(state => state.hideFooter);
- const panelHeight = calculateHeight('0px');
+ const panelHeight = useFitHeight('0px');
useEffect(() => {
- setNoFooter(true);
- return () => setNoFooter(false);
- }, [setNoFooter]);
+ hideFooter(true);
+ return () => hideFooter(false);
+ }, [hideFooter]);
return (
diff --git a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx
index 3b5adf31..aae45feb 100644
--- a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx
+++ b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx
@@ -16,6 +16,7 @@ import useLocalStorage from '@/hooks/useLocalStorage';
import { ILibraryItem, IRenameLocationData, LocationHead } from '@/models/library';
import { ILibraryFilter } from '@/models/miscellaneous';
import { UserID } from '@/models/user';
+import { useAppLayoutStore } from '@/stores/appLayout';
import { storage } from '@/utils/constants';
import { information } from '@/utils/labels';
import { convertToCSV, toggleTristateFlag } from '@/utils/utils';
@@ -29,6 +30,7 @@ function LibraryPage() {
const { user } = useAuth();
const [items, setItems] = useState
([]);
const options = useConceptOptions();
+ const noNavigation = useAppLayoutStore(state => state.noNavigation);
const [query, setQuery] = useState('');
const [path, setPath] = useState('');
@@ -133,7 +135,7 @@ function LibraryPage() {
/>
) : null}
diff --git a/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx b/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx
index 2227b3b7..249a3f0d 100644
--- a/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx
+++ b/rsconcept/frontend/src/pages/LibraryPage/TableLibraryItems.tsx
@@ -12,12 +12,12 @@ import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } fro
import FlexColumn from '@/components/ui/FlexColumn';
import MiniButton from '@/components/ui/MiniButton';
import TextURL from '@/components/ui/TextURL';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
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 { APP_COLORS } from '@/styling/color';
import { storage } from '@/utils/constants';
@@ -34,7 +34,6 @@ function TableLibraryItems({ items, resetQuery, folderMode, toggleFolderMode }:
const router = useConceptNavigation();
const intl = useIntl();
const { getUserLabel } = useUsers();
- const { calculateHeight } = useConceptOptions();
const [itemsPerPage, setItemsPerPage] = useLocalStorage(storage.libraryPagination, 50);
function handleOpenItem(item: ILibraryItem, event: CProps.EventMouse) {
@@ -140,7 +139,7 @@ function TableLibraryItems({ items, resetQuery, folderMode, toggleFolderMode }:
})
];
- const tableHeight = calculateHeight('2.2rem');
+ const tableHeight = useFitHeight('2.2rem');
const conditionalRowStyles: IConditionalStyle[] = [
{
diff --git a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx
index 59f681a1..260766b1 100644
--- a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx
+++ b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx
@@ -8,11 +8,11 @@ import { CProps } from '@/components/props';
import SelectLocation from '@/components/select/SelectLocation';
import MiniButton from '@/components/ui/MiniButton';
import { useAuth } from '@/context/AuthContext';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { useLibrary } from '@/context/LibraryContext';
import useWindowSize from '@/hooks/useWindowSize';
import { FolderNode, FolderTree } from '@/models/FolderTree';
import { HelpTopic } from '@/models/miscellaneous';
+import { useFitHeight } from '@/stores/appLayout';
import { PARAMETER, prefixes } from '@/utils/constants';
import { information } from '@/utils/labels';
@@ -39,7 +39,6 @@ function ViewSideLocation({
}: ViewSideLocationProps) {
const { user } = useAuth();
const { items } = useLibrary();
- const { calculateHeight } = useConceptOptions();
const windowSize = useWindowSize();
const canRename = (() => {
@@ -56,7 +55,7 @@ function ViewSideLocation({
return located.length !== 0;
})();
- const maxHeight = calculateHeight('4.5rem');
+ const maxHeight = useFitHeight('4.5rem');
function handleClickFolder(event: CProps.EventMouse, target: FolderNode) {
event.preventDefault();
diff --git a/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx b/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx
index 2443cdf5..e0119635 100644
--- a/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx
+++ b/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx
@@ -3,10 +3,10 @@
import { useCallback } from 'react';
import { urls } from '@/app/urls';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import useQueryStrings from '@/hooks/useQueryStrings';
import { HelpTopic } from '@/models/miscellaneous';
+import { useMainHeight } from '@/stores/appLayout';
import { PARAMETER } from '@/utils/constants';
import TopicsList from './TopicsList';
@@ -17,7 +17,7 @@ function ManualsPage() {
const query = useQueryStrings();
const activeTopic = (query.get('topic') || HelpTopic.MAIN) as HelpTopic;
- const { mainHeight } = useConceptOptions();
+ const mainHeight = useMainHeight();
const onSelectTopic = useCallback(
(newTopic: HelpTopic) => {
diff --git a/rsconcept/frontend/src/pages/ManualsPage/TopicsDropdown.tsx b/rsconcept/frontend/src/pages/ManualsPage/TopicsDropdown.tsx
index 91c613f3..e03e8ef9 100644
--- a/rsconcept/frontend/src/pages/ManualsPage/TopicsDropdown.tsx
+++ b/rsconcept/frontend/src/pages/ManualsPage/TopicsDropdown.tsx
@@ -6,9 +6,9 @@ import { useCallback } from 'react';
import { IconMenuFold, IconMenuUnfold } from '@/components/Icons';
import Button from '@/components/ui/Button';
import SelectTree from '@/components/ui/SelectTree';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useDropdown from '@/hooks/useDropdown';
import { HelpTopic, topicParent } from '@/models/miscellaneous';
+import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
import { PARAMETER, prefixes } from '@/utils/constants';
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
@@ -19,7 +19,8 @@ interface TopicsDropdownProps {
function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
const menu = useDropdown();
- const { noNavigation, calculateHeight } = useConceptOptions();
+ const noNavigation = useAppLayoutStore(state => state.noNavigation);
+ const treeHeight = useFitHeight('4rem + 2px');
const handleSelectTopic = useCallback(
(topic: HelpTopic) => {
@@ -67,7 +68,7 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
'bg-prim-200'
)}
style={{
- maxHeight: calculateHeight('4rem + 2px'),
+ maxHeight: treeHeight,
transitionProperty: 'clip-path',
transitionDuration: `${PARAMETER.moveDuration}ms`,
clipPath: menu.isOpen ? 'inset(0% 0% 0% 0%)' : 'inset(0% 100% 0% 0%)'
diff --git a/rsconcept/frontend/src/pages/ManualsPage/TopicsStatic.tsx b/rsconcept/frontend/src/pages/ManualsPage/TopicsStatic.tsx
index 55f7b93d..fee2404c 100644
--- a/rsconcept/frontend/src/pages/ManualsPage/TopicsStatic.tsx
+++ b/rsconcept/frontend/src/pages/ManualsPage/TopicsStatic.tsx
@@ -1,8 +1,8 @@
import clsx from 'clsx';
import SelectTree from '@/components/ui/SelectTree';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { HelpTopic, topicParent } from '@/models/miscellaneous';
+import { useFitHeight } from '@/stores/appLayout';
import { prefixes } from '@/utils/constants';
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
@@ -12,7 +12,7 @@ interface TopicsStaticProps {
}
function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
- const { calculateHeight } = useConceptOptions();
+ const topicsHeight = useFitHeight('1rem + 2px');
return (
item as HelpTopic)}
@@ -31,7 +31,7 @@ function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
'text-xs sm:text-sm bg-prim-200',
'select-none'
)}
- style={{ maxHeight: calculateHeight('1rem + 2px') }}
+ style={{ maxHeight: topicsHeight }}
/>
);
}
diff --git a/rsconcept/frontend/src/pages/ManualsPage/ViewTopic.tsx b/rsconcept/frontend/src/pages/ManualsPage/ViewTopic.tsx
index c61daa9c..068967a7 100644
--- a/rsconcept/frontend/src/pages/ManualsPage/ViewTopic.tsx
+++ b/rsconcept/frontend/src/pages/ManualsPage/ViewTopic.tsx
@@ -1,15 +1,15 @@
'use client';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { HelpTopic } from '@/models/miscellaneous';
import TopicPage from '@/pages/ManualsPage/TopicPage';
+import { useMainHeight } from '@/stores/appLayout';
interface ViewTopicProps {
topic: HelpTopic;
}
function ViewTopic({ topic }: ViewTopicProps) {
- const { mainHeight } = useConceptOptions();
+ const mainHeight = useMainHeight();
return (
) => {
const router = useConceptNavigation();
const { user } = useAuth();
- const { adminMode } = useConceptOptions();
+ const adminMode = usePreferencesStore(state => state.adminMode);
const { accessLevel, setAccessLevel } = useAccessMode();
const model = useOSS();
const library = useLibrary();
diff --git a/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx b/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx
index 5c85cf3a..ef1c0e67 100644
--- a/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx
+++ b/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx
@@ -13,12 +13,12 @@ import Overlay from '@/components/ui/Overlay';
import TabLabel from '@/components/ui/TabLabel';
import TextURL from '@/components/ui/TextURL';
import { useAuth } from '@/context/AuthContext';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { useLibrary } from '@/context/LibraryContext';
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
import { useOSS } from '@/context/OssContext';
import useQueryStrings from '@/hooks/useQueryStrings';
import { OperationID } from '@/models/oss';
+import { useAppLayoutStore } from '@/stores/appLayout';
import { information, prompts } from '@/utils/labels';
import EditorRSForm from './EditorOssCard';
@@ -37,7 +37,7 @@ function OssTabs() {
const activeTab = query.get('tab') ? (Number(query.get('tab')) as OssTabID) : OssTabID.GRAPH;
const { user } = useAuth();
- const { setNoFooter } = useConceptOptions();
+ const hideFooter = useAppLayoutStore(state => state.hideFooter);
const { schema, loading, loadingError: errorLoading } = useOSS();
const { destroyItem } = useLibrary();
@@ -61,8 +61,8 @@ function OssTabs() {
}, [schema, schema?.title]);
useEffect(() => {
- setNoFooter(activeTab === OssTabID.GRAPH);
- }, [activeTab, setNoFooter]);
+ hideFooter(activeTab === OssTabID.GRAPH);
+ }, [activeTab, hideFooter]);
function navigateTab(tab: OssTabID) {
if (!schema) {
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx
index b481b480..53ef8d89 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx
@@ -3,10 +3,10 @@
import clsx from 'clsx';
import { useState } from 'react';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useLocalStorage from '@/hooks/useLocalStorage';
import useWindowSize from '@/hooks/useWindowSize';
import { ConstituentaID, IConstituenta } from '@/models/rsform';
+import { useMainHeight } from '@/stores/appLayout';
import { globals, storage } from '@/utils/constants';
import { useRSEdit } from '../RSEditContext';
@@ -27,7 +27,7 @@ interface EditorConstituentaProps {
function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }: EditorConstituentaProps) {
const controller = useRSEdit();
const windowSize = useWindowSize();
- const { mainHeight } = useConceptOptions();
+ const mainHeight = useMainHeight();
const [showList, setShowList] = useLocalStorage(storage.rseditShowList, true);
const [toggleReset, setToggleReset] = useState(false);
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx
index b05d79ac..0535a528 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx
@@ -9,10 +9,10 @@ import { type RowSelectionState } from '@/components/ui/DataTable';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import SearchBar from '@/components/ui/SearchBar';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { CstMatchMode } from '@/models/miscellaneous';
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
import { matchConstituenta } from '@/models/rsformAPI';
+import { useFitHeight } from '@/stores/appLayout';
import { information } from '@/utils/labels';
import { convertToCSV } from '@/utils/utils';
@@ -25,7 +25,6 @@ interface EditorRSListProps {
}
function EditorRSList({ onOpenEdit }: EditorRSListProps) {
- const { calculateHeight } = useConceptOptions();
const [rowSelection, setRowSelection] = useState
({});
const controller = useRSEdit();
@@ -136,7 +135,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
return false;
}
- const tableHeight = calculateHeight('4.05rem + 5px');
+ const tableHeight = useFitHeight('4.05rem + 5px');
return (
<>
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx
index 1d05ca6a..9fd40af8 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx
@@ -24,12 +24,12 @@ import SelectedCounter from '@/components/info/SelectedCounter';
import { CProps } from '@/components/props';
import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection';
import Overlay from '@/components/ui/Overlay';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
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 { APP_COLORS, colorBgGraphNode } from '@/styling/color';
import { PARAMETER, storage } from '@/utils/constants';
import { errors } from '@/utils/labels';
@@ -53,7 +53,7 @@ interface TGFlowProps {
}
function TGFlow({ onOpenEdit }: TGFlowProps) {
- const { mainHeight } = useConceptOptions();
+ const mainHeight = useMainHeight();
const controller = useRSEdit();
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges] = useEdgesState([]);
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
index fc69756a..d64f1fb6 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
@@ -11,6 +11,7 @@ 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 { APP_COLORS, colorBgGraphNode } from '@/styling/color';
import { globals, PARAMETER, prefixes, storage } from '@/utils/constants';
@@ -26,11 +27,11 @@ interface ViewHiddenProps {
}
function ViewHidden({ items, selected, toggleSelection, setFocus, schema, coloringScheme, onEdit }: ViewHiddenProps) {
- const { calculateHeight } = useConceptOptions();
const windowSize = useWindowSize();
const localSelected = items.filter(id => selected.includes(id));
const [isFolded, setIsFolded] = useLocalStorage(storage.rsgraphFoldHidden, false);
const { setHoverCst } = useConceptOptions();
+ const hiddenHeight = useFitHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px');
function handleClick(cstID: ConstituentaID, event: CProps.EventMouse) {
if (event.ctrlKey || event.metaKey) {
@@ -77,7 +78,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
'cc-scroll-y'
)}
style={{
- maxHeight: calculateHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px'),
+ maxHeight: hiddenHeight,
transitionProperty: 'clip-path',
transitionDuration: `${PARAMETER.fastAnimation}ms`,
transitionTimingFunction: 'ease-out',
diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx
index 4c3ad8d6..90d939ef 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx
@@ -7,7 +7,6 @@ import { toast } from 'react-toastify';
import { urls } from '@/app/urls';
import { useAccessMode } from '@/context/AccessModeContext';
import { useAuth } from '@/context/AuthContext';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import { useRSForm } from '@/context/RSFormContext';
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
@@ -51,6 +50,7 @@ import {
} from '@/models/rsform';
import { generateAlias } from '@/models/rsformAPI';
import { UserID, UserLevel } from '@/models/user';
+import { usePreferencesStore } from '@/stores/preferences';
import { EXTEOR_TRS_FILE } from '@/utils/constants';
import { information, prompts } from '@/utils/labels';
import { promptUnsaved } from '@/utils/utils';
@@ -142,7 +142,7 @@ export const RSEditState = ({
}: React.PropsWithChildren) => {
const router = useConceptNavigation();
const { user } = useAuth();
- const { adminMode } = useConceptOptions();
+ const adminMode = usePreferencesStore(state => state.adminMode);
const { accessLevel, setAccessLevel } = useAccessMode();
const model = useRSForm();
diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx
index 8e5ef18d..83d8e587 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx
@@ -13,13 +13,13 @@ import Loader from '@/components/ui/Loader';
import Overlay from '@/components/ui/Overlay';
import TabLabel from '@/components/ui/TabLabel';
import TextURL from '@/components/ui/TextURL';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import { useGlobalOss } from '@/context/GlobalOssContext';
import { useLibrary } from '@/context/LibraryContext';
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
import { useRSForm } from '@/context/RSFormContext';
import useQueryStrings from '@/hooks/useQueryStrings';
import { ConstituentaID, IConstituenta, IConstituentaMeta } from '@/models/rsform';
+import { useAppLayoutStore } from '@/stores/appLayout';
import { PARAMETER, prefixes } from '@/utils/constants';
import { information, labelVersion, prompts } from '@/utils/labels';
@@ -45,7 +45,7 @@ function RSTabs() {
const version = query.get('v') ? Number(query.get('v')) : undefined;
const cstQuery = query.get('active');
- const { setNoFooter } = useConceptOptions();
+ const hideFooter = useAppLayoutStore(state => state.hideFooter);
const { schema, loading, errorLoading, isArchive, itemID } = useRSForm();
const library = useLibrary();
const oss = useGlobalOss();
@@ -73,7 +73,7 @@ function RSTabs() {
}, [schema, schema?.title]);
useEffect(() => {
- setNoFooter(activeTab !== RSTabID.CARD);
+ hideFooter(activeTab !== RSTabID.CARD);
setIsModified(false);
if (activeTab === RSTabID.CST_EDIT) {
const cstID = Number(cstQuery);
@@ -83,8 +83,8 @@ function RSTabs() {
setSelected([]);
}
}
- return () => setNoFooter(false);
- }, [activeTab, cstQuery, setSelected, schema, setNoFooter, setIsModified]);
+ return () => hideFooter(false);
+ }, [activeTab, cstQuery, setSelected, schema, hideFooter, setIsModified]);
function navigateTab(tab: RSTabID, activeID?: ConstituentaID) {
if (!schema) {
diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx
index e16eda0c..99a8781c 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx
@@ -4,10 +4,10 @@ import clsx from 'clsx';
import { useState } from 'react';
import { useAccessMode } from '@/context/AccessModeContext';
-import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useWindowSize from '@/hooks/useWindowSize';
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
import { UserLevel } from '@/models/user';
+import { useFitHeight } from '@/stores/appLayout';
import { PARAMETER } from '@/utils/constants';
import ConstituentsSearch from './ConstituentsSearch';
@@ -26,9 +26,9 @@ interface ViewConstituentsProps {
}
function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit, isMounted }: ViewConstituentsProps) {
- const { calculateHeight } = useConceptOptions();
const windowSize = useWindowSize();
const { accessLevel } = useAccessMode();
+ const listHeight = useFitHeight(!isBottom ? '8.2rem' : accessLevel !== UserLevel.READER ? '42rem' : '35rem', '10rem');
const [filteredData, setFilteredData] = useState(schema?.items ?? []);
@@ -57,11 +57,7 @@ function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit,
setFiltered={setFilteredData}
/>
void;
+
+ noFooter: boolean;
+ hideFooter: (value?: boolean) => void;
+
+ noScroll: boolean;
+ hideScroll: (value?: boolean) => void;
+}
+
+export const useAppLayoutStore = create()(set => ({
+ noNavigation: false,
+ noNavigationAnimation: false,
+ toggleNoNavigation: () =>
+ set(state => {
+ if (state.noNavigation) {
+ return { noNavigation: false, noNavigationAnimation: false };
+ } else {
+ setTimeout(() => set({ noNavigation: true, noNavigationAnimation: true }), PARAMETER.moveDuration);
+ return { noNavigation: false, noNavigationAnimation: true };
+ }
+ }),
+
+ noFooter: false,
+ hideFooter: value => set({ noFooter: value ?? true }),
+
+ noScroll: true,
+ hideScroll: value => set({ noScroll: value ?? true })
+}));
+
+/** Utility function that returns the height of the main area. */
+export function useMainHeight(): string {
+ const noNavigation = useAppLayoutStore(state => state.noNavigation);
+ const noFooter = useAppLayoutStore(state => state.noFooter);
+ if (noNavigation) {
+ return '100dvh';
+ } else if (noFooter) {
+ return 'calc(100dvh - 3rem)';
+ } else {
+ return 'calc(100dvh - 6.75rem)';
+ }
+}
+
+/** Utility function that returns the height of the viewport. */
+export function useViewportHeight(): string {
+ const noNavigation = useAppLayoutStore(state => state.noNavigation);
+ return !noNavigation ? 'calc(100dvh - 3rem)' : '100dvh';
+}
+
+/** Utility function that returns the height of the viewport with a given offset. */
+export function useFitHeight(offset: string, minimum: string = '0px'): string {
+ const noNavigation = useAppLayoutStore(state => state.noNavigation);
+ const noFooter = useAppLayoutStore(state => state.noFooter);
+ if (noNavigation) {
+ return `max(calc(100dvh - (${offset})), ${minimum})`;
+ } else if (noFooter) {
+ return `max(calc(100dvh - 3rem - (${offset})), ${minimum})`;
+ } else {
+ return `max(calc(100dvh - 6.75rem - (${offset})), ${minimum})`;
+ }
+}
diff --git a/rsconcept/frontend/src/stores/preferences.ts b/rsconcept/frontend/src/stores/preferences.ts
new file mode 100644
index 00000000..875b4aed
--- /dev/null
+++ b/rsconcept/frontend/src/stores/preferences.ts
@@ -0,0 +1,24 @@
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+
+interface PreferencesStore {
+ showHelp: boolean;
+ adminMode: boolean;
+ toggleShowHelp: () => void;
+ toggleAdminMode: () => void;
+}
+
+export const usePreferencesStore = create()(
+ persist(
+ set => ({
+ showHelp: true,
+ adminMode: false,
+ toggleShowHelp: () => set(state => ({ showHelp: !state.showHelp })),
+ toggleAdminMode: () => set(state => ({ adminMode: !state.adminMode }))
+ }),
+ {
+ version: 1,
+ name: 'portal.preferences'
+ }
+ )
+);
diff --git a/rsconcept/frontend/src/utils/constants.ts b/rsconcept/frontend/src/utils/constants.ts
index 283ce67f..46f7b408 100644
--- a/rsconcept/frontend/src/utils/constants.ts
+++ b/rsconcept/frontend/src/utils/constants.ts
@@ -109,8 +109,6 @@ export const storage = {
PREFIX: 'portal.',
themeDark: 'theme.dark',
- optionsAdmin: 'options.admin',
- optionsHelp: 'options.help',
rseditShowList: 'rsedit.show_list',
rseditShowControls: 'rsedit.show_controls',