mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
R: Migrating to zustand for local state management pt1
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
This commit is contained in:
parent
5679c86dac
commit
4c9b0b28b8
|
@ -44,6 +44,7 @@ This readme file is used mostly to document project dependencies and conventions
|
||||||
- use-debounce
|
- use-debounce
|
||||||
- qrcode.react
|
- qrcode.react
|
||||||
- html-to-image
|
- html-to-image
|
||||||
|
- zustand
|
||||||
- @tanstack/react-table
|
- @tanstack/react-table
|
||||||
- @uiw/react-codemirror
|
- @uiw/react-codemirror
|
||||||
- @uiw/codemirror-themes
|
- @uiw/codemirror-themes
|
||||||
|
|
190
rsconcept/frontend/package-lock.json
generated
190
rsconcept/frontend/package-lock.json
generated
|
@ -30,7 +30,8 @@
|
||||||
"react-tooltip": "^5.28.0",
|
"react-tooltip": "^5.28.0",
|
||||||
"react-zoom-pan-pinch": "^3.6.1",
|
"react-zoom-pan-pinch": "^3.6.1",
|
||||||
"reactflow": "^11.11.4",
|
"reactflow": "^11.11.4",
|
||||||
"use-debounce": "^10.0.4"
|
"use-debounce": "^10.0.4",
|
||||||
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lezer/generator": "^1.7.2",
|
"@lezer/generator": "^1.7.2",
|
||||||
|
@ -2445,6 +2446,34 @@
|
||||||
"react-dom": ">=17"
|
"react-dom": ">=17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/background/node_modules/zustand": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reactflow/controls": {
|
"node_modules/@reactflow/controls": {
|
||||||
"version": "11.2.14",
|
"version": "11.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz",
|
||||||
|
@ -2460,6 +2489,34 @@
|
||||||
"react-dom": ">=17"
|
"react-dom": ">=17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/controls/node_modules/zustand": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reactflow/core": {
|
"node_modules/@reactflow/core": {
|
||||||
"version": "11.11.4",
|
"version": "11.11.4",
|
||||||
"resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz",
|
"resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz",
|
||||||
|
@ -2481,6 +2538,34 @@
|
||||||
"react-dom": ">=17"
|
"react-dom": ">=17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/core/node_modules/zustand": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reactflow/minimap": {
|
"node_modules/@reactflow/minimap": {
|
||||||
"version": "11.7.14",
|
"version": "11.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz",
|
||||||
|
@ -2500,6 +2585,34 @@
|
||||||
"react-dom": ">=17"
|
"react-dom": ">=17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/minimap/node_modules/zustand": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reactflow/node-resizer": {
|
"node_modules/@reactflow/node-resizer": {
|
||||||
"version": "2.2.14",
|
"version": "2.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz",
|
||||||
|
@ -2517,6 +2630,34 @@
|
||||||
"react-dom": ">=17"
|
"react-dom": ">=17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/node-resizer/node_modules/zustand": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@reactflow/node-toolbar": {
|
"node_modules/@reactflow/node-toolbar": {
|
||||||
"version": "1.3.14",
|
"version": "1.3.14",
|
||||||
"resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz",
|
"resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz",
|
||||||
|
@ -2532,6 +2673,34 @@
|
||||||
"react-dom": ">=17"
|
"react-dom": ">=17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reactflow/node-toolbar/node_modules/zustand": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.30.1",
|
"version": "4.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
|
||||||
|
@ -10711,20 +10880,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/zustand": {
|
"node_modules/zustand": {
|
||||||
"version": "4.5.6",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz",
|
||||||
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
"integrity": "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
|
||||||
"use-sync-external-store": "^1.2.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.7.0"
|
"node": ">=12.20.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": ">=16.8",
|
"@types/react": ">=18.0.0",
|
||||||
"immer": ">=9.0.6",
|
"immer": ">=9.0.6",
|
||||||
"react": ">=16.8"
|
"react": ">=18.0.0",
|
||||||
|
"use-sync-external-store": ">=1.2.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
|
@ -10735,6 +10902,9 @@
|
||||||
},
|
},
|
||||||
"react": {
|
"react": {
|
||||||
"optional": true
|
"optional": true
|
||||||
|
},
|
||||||
|
"use-sync-external-store": {
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
"react-tooltip": "^5.28.0",
|
"react-tooltip": "^5.28.0",
|
||||||
"react-zoom-pan-pinch": "^3.6.1",
|
"react-zoom-pan-pinch": "^3.6.1",
|
||||||
"reactflow": "^11.11.4",
|
"reactflow": "^11.11.4",
|
||||||
"use-debounce": "^10.0.4"
|
"use-debounce": "^10.0.4",
|
||||||
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lezer/generator": "^1.7.2",
|
"@lezer/generator": "^1.7.2",
|
||||||
|
|
|
@ -5,12 +5,17 @@ import ConceptToaster from '@/app/ConceptToaster';
|
||||||
import Footer from '@/app/Footer';
|
import Footer from '@/app/Footer';
|
||||||
import Navigation from '@/app/Navigation';
|
import Navigation from '@/app/Navigation';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { NavigationState } from '@/context/NavigationContext';
|
import { NavigationState } from '@/context/NavigationContext';
|
||||||
|
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
function ApplicationLayout() {
|
function ApplicationLayout() {
|
||||||
const { viewportHeight, mainHeight, showScroll, noNavigationAnimation } = useConceptOptions();
|
const mainHeight = useMainHeight();
|
||||||
|
const viewportHeight = useViewportHeight();
|
||||||
|
const showScroll = useAppLayoutStore(state => !state.noScroll);
|
||||||
|
const noNavigationAnimation = useAppLayoutStore(state => state.noNavigationAnimation);
|
||||||
|
const noNavigation = useAppLayoutStore(state => state.noNavigation);
|
||||||
|
const noFooter = useAppLayoutStore(state => state.noFooter);
|
||||||
return (
|
return (
|
||||||
<NavigationState>
|
<NavigationState>
|
||||||
<div className='min-w-[20rem] antialiased h-full max-w-[120rem] mx-auto'>
|
<div className='min-w-[20rem] antialiased h-full max-w-[120rem] mx-auto'>
|
||||||
|
@ -36,7 +41,7 @@ function ApplicationLayout() {
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
{!noNavigation && !noFooter ? <Footer /> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NavigationState>
|
</NavigationState>
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { external_urls } from '@/utils/constants';
|
import { external_urls } from '@/utils/constants';
|
||||||
|
|
||||||
import TextURL from '../components/ui/TextURL';
|
import TextURL from '../components/ui/TextURL';
|
||||||
|
|
||||||
function Footer() {
|
function Footer() {
|
||||||
const { noNavigation, noFooter } = useConceptOptions();
|
|
||||||
if (noNavigation || noFooter) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<footer
|
<footer
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|
|
@ -2,9 +2,9 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
|
@ -16,7 +16,7 @@ import UserMenu from './UserMenu';
|
||||||
function Navigation() {
|
function Navigation() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const size = useWindowSize();
|
const size = useWindowSize();
|
||||||
const { noNavigationAnimation } = useConceptOptions();
|
const noNavigationAnimation = useAppLayoutStore(state => state.noNavigationAnimation);
|
||||||
|
|
||||||
const navigateHome = (event: CProps.EventMouse) => router.push(urls.home, event.ctrlKey || event.metaKey);
|
const navigateHome = (event: CProps.EventMouse) => router.push(urls.home, event.ctrlKey || event.metaKey);
|
||||||
const navigateLibrary = (event: CProps.EventMouse) => router.push(urls.library, event.ctrlKey || event.metaKey);
|
const navigateLibrary = (event: CProps.EventMouse) => router.push(urls.library, event.ctrlKey || event.metaKey);
|
||||||
|
|
|
@ -2,10 +2,14 @@ import clsx from 'clsx';
|
||||||
|
|
||||||
import { IconDarkTheme, IconLightTheme, IconPin, IconUnpin } from '@/components/Icons';
|
import { IconDarkTheme, IconLightTheme, IconPin, IconUnpin } from '@/components/Icons';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
|
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||||
import { globals, PARAMETER } from '@/utils/constants';
|
import { globals, PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
function ToggleNavigation() {
|
function ToggleNavigation() {
|
||||||
const { noNavigationAnimation, noNavigation, toggleNoNavigation, toggleDarkMode, darkMode } = useConceptOptions();
|
const { toggleDarkMode, darkMode } = useConceptOptions();
|
||||||
|
const noNavigation = useAppLayoutStore(state => state.noNavigation);
|
||||||
|
const noNavigationAnimation = useAppLayoutStore(state => state.noNavigationAnimation);
|
||||||
|
const toggleNoNavigation = useAppLayoutStore(state => state.toggleNoNavigation);
|
||||||
const iconSize = !noNavigationAnimation ? '0.75rem' : '1rem';
|
const iconSize = !noNavigationAnimation ? '0.75rem' : '1rem';
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -18,6 +18,7 @@ import DropdownButton from '@/components/ui/DropdownButton';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
|
|
||||||
|
@ -27,10 +28,15 @@ interface UserDropdownProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
const { darkMode, adminMode, toggleAdminMode, toggleDarkMode, showHelp, toggleShowHelp } = useConceptOptions();
|
const { darkMode, toggleDarkMode } = useConceptOptions();
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user, logout } = useAuth();
|
const { user, logout } = useAuth();
|
||||||
|
|
||||||
|
const showHelp = usePreferencesStore(state => 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) {
|
function navigateProfile(event: CProps.EventMouse) {
|
||||||
hideDropdown();
|
hideDropdown();
|
||||||
router.push(urls.profile, event.ctrlKey || event.metaKey);
|
router.push(urls.profile, event.ctrlKey || event.metaKey);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { IconLogin, IconUser2 } from '@/components/Icons';
|
import { IconLogin, IconUser2 } from '@/components/Icons';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
import NavigationButton from './NavigationButton';
|
import NavigationButton from './NavigationButton';
|
||||||
|
@ -12,7 +12,7 @@ import UserDropdown from './UserDropdown';
|
||||||
function UserMenu() {
|
function UserMenu() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user, loading } = useAuth();
|
const { user, loading } = useAuth();
|
||||||
const { adminMode } = useConceptOptions();
|
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||||
const menu = useDropdown();
|
const menu = useDropdown();
|
||||||
|
|
||||||
const navigateLogin = () => router.push(urls.login);
|
const navigateLogin = () => router.push(urls.login);
|
||||||
|
|
|
@ -2,8 +2,8 @@ import React, { Suspense } from 'react';
|
||||||
|
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
|
import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
|
|
||||||
import { IconHelp } from '../Icons';
|
import { IconHelp } from '../Icons';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
@ -29,7 +29,7 @@ interface BadgeHelpProps extends CProps.Styling {
|
||||||
* Display help icon with a manual page tooltip.
|
* Display help icon with a manual page tooltip.
|
||||||
*/
|
*/
|
||||||
function BadgeHelp({ topic, padding = 'p-1', ...restProps }: BadgeHelpProps) {
|
function BadgeHelp({ topic, padding = 'p-1', ...restProps }: BadgeHelpProps) {
|
||||||
const { showHelp } = useConceptOptions();
|
const showHelp = usePreferencesStore(state => state.showHelp);
|
||||||
|
|
||||||
if (!showHelp) {
|
if (!showHelp) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
|
|
||||||
/** Maximum width of the viewer. */
|
/** Maximum width of the viewer. */
|
||||||
const MAXIMUM_WIDTH = 1600;
|
const MAXIMUM_WIDTH = 1600;
|
||||||
|
@ -25,10 +25,9 @@ interface PDFViewerProps {
|
||||||
*/
|
*/
|
||||||
function PDFViewer({ file, offsetXpx, minWidth = MINIMUM_WIDTH }: PDFViewerProps) {
|
function PDFViewer({ file, offsetXpx, minWidth = MINIMUM_WIDTH }: PDFViewerProps) {
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const { calculateHeight } = useConceptOptions();
|
|
||||||
|
|
||||||
const pageWidth = Math.max(minWidth, Math.min((windowSize?.width ?? 0) - (offsetXpx ?? 0) - 10, MAXIMUM_WIDTH));
|
const pageWidth = Math.max(minWidth, Math.min((windowSize?.width ?? 0) - (offsetXpx ?? 0) - 10, MAXIMUM_WIDTH));
|
||||||
const pageHeight = calculateHeight('1rem');
|
const pageHeight = useFitHeight('1rem');
|
||||||
|
|
||||||
return <embed src={`${file}#toolbar=0`} className='p-3' style={{ width: pageWidth, height: pageHeight }} />;
|
return <embed src={`${file}#toolbar=0`} className='p-3' style={{ width: pageWidth, height: pageHeight }} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'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 { flushSync } from 'react-dom';
|
||||||
|
|
||||||
import InfoConstituenta from '@/components/info/InfoConstituenta';
|
import InfoConstituenta from '@/components/info/InfoConstituenta';
|
||||||
|
@ -12,28 +12,9 @@ import { globals, PARAMETER, storage } from '@/utils/constants';
|
||||||
import { contextOutsideScope } from '@/utils/labels';
|
import { contextOutsideScope } from '@/utils/labels';
|
||||||
|
|
||||||
interface IOptionsContext {
|
interface IOptionsContext {
|
||||||
viewportHeight: string;
|
|
||||||
mainHeight: string;
|
|
||||||
|
|
||||||
darkMode: boolean;
|
darkMode: boolean;
|
||||||
toggleDarkMode: () => void;
|
toggleDarkMode: () => void;
|
||||||
|
|
||||||
adminMode: boolean;
|
|
||||||
toggleAdminMode: () => void;
|
|
||||||
|
|
||||||
noNavigationAnimation: boolean;
|
|
||||||
noNavigation: boolean;
|
|
||||||
toggleNoNavigation: () => void;
|
|
||||||
|
|
||||||
noFooter: boolean;
|
|
||||||
setNoFooter: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
|
|
||||||
showScroll: boolean;
|
|
||||||
setShowScroll: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
|
|
||||||
showHelp: boolean;
|
|
||||||
toggleShowHelp: () => void;
|
|
||||||
|
|
||||||
folderMode: boolean;
|
folderMode: boolean;
|
||||||
setFolderMode: React.Dispatch<React.SetStateAction<boolean>>;
|
setFolderMode: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
|
||||||
|
@ -41,8 +22,6 @@ interface IOptionsContext {
|
||||||
setLocation: React.Dispatch<React.SetStateAction<string>>;
|
setLocation: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
|
||||||
setHoverCst: (newValue: IConstituenta | undefined) => void;
|
setHoverCst: (newValue: IConstituenta | undefined) => void;
|
||||||
|
|
||||||
calculateHeight: (offset: string, minimum?: string) => string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const OptionsContext = createContext<IOptionsContext | null>(null);
|
const OptionsContext = createContext<IOptionsContext | null>(null);
|
||||||
|
@ -56,17 +35,10 @@ export const useConceptOptions = () => {
|
||||||
|
|
||||||
export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
||||||
const [darkMode, setDarkMode] = useLocalStorage(storage.themeDark, false);
|
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<boolean>(storage.librarySearchFolderMode, true);
|
const [folderMode, setFolderMode] = useLocalStorage<boolean>(storage.librarySearchFolderMode, true);
|
||||||
const [location, setLocation] = useLocalStorage<string>(storage.librarySearchLocation, '');
|
const [location, setLocation] = useLocalStorage<string>(storage.librarySearchLocation, '');
|
||||||
|
|
||||||
const [noNavigationAnimation, setNoNavigationAnimation] = useState(false);
|
|
||||||
const [noFooter, setNoFooter] = useState(false);
|
|
||||||
const [showScroll, setShowScroll] = useState(false);
|
|
||||||
|
|
||||||
const [hoverCst, setHoverCst] = useState<IConstituenta | undefined>(undefined);
|
const [hoverCst, setHoverCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
|
|
||||||
function setDarkClass(isDark: boolean) {
|
function setDarkClass(isDark: boolean) {
|
||||||
|
@ -83,29 +55,6 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
||||||
setDarkClass(darkMode);
|
setDarkClass(darkMode);
|
||||||
}, [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(() => {
|
const toggleDarkMode = useCallback(() => {
|
||||||
if (!document.startViewTransition) {
|
if (!document.startViewTransition) {
|
||||||
setDarkMode(prev => !prev);
|
setDarkMode(prev => !prev);
|
||||||
|
@ -129,43 +78,15 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
||||||
}
|
}
|
||||||
}, [setDarkMode]);
|
}, [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 (
|
return (
|
||||||
<OptionsContext
|
<OptionsContext
|
||||||
value={{
|
value={{
|
||||||
darkMode,
|
darkMode,
|
||||||
adminMode,
|
|
||||||
noNavigationAnimation,
|
|
||||||
noNavigation,
|
|
||||||
noFooter,
|
|
||||||
folderMode,
|
folderMode,
|
||||||
setFolderMode,
|
setFolderMode,
|
||||||
location,
|
location,
|
||||||
setLocation,
|
setLocation,
|
||||||
showScroll,
|
|
||||||
showHelp,
|
|
||||||
toggleDarkMode: toggleDarkMode,
|
toggleDarkMode: toggleDarkMode,
|
||||||
toggleAdminMode: () => setAdminMode(prev => !prev),
|
|
||||||
toggleNoNavigation: toggleNoNavigation,
|
|
||||||
setNoFooter,
|
|
||||||
setShowScroll,
|
|
||||||
toggleShowHelp: () => setShowHelp(prev => !prev),
|
|
||||||
viewportHeight,
|
|
||||||
mainHeight,
|
|
||||||
calculateHeight,
|
|
||||||
setHoverCst
|
setHoverCst
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -21,10 +21,10 @@ import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI'
|
||||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||||
import { IRSForm, IRSFormCloneData, IRSFormData } from '@/models/rsform';
|
import { IRSForm, IRSFormCloneData, IRSFormData } from '@/models/rsform';
|
||||||
import { RSFormLoader } from '@/models/RSFormLoader';
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||||
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { contextOutsideScope } from '@/utils/labels';
|
import { contextOutsideScope } from '@/utils/labels';
|
||||||
|
|
||||||
import { useAuth } from './AuthContext';
|
import { useAuth } from './AuthContext';
|
||||||
import { useConceptOptions } from './ConceptOptionsContext';
|
|
||||||
|
|
||||||
interface ILibraryContext {
|
interface ILibraryContext {
|
||||||
items: ILibraryItem[];
|
items: ILibraryItem[];
|
||||||
|
@ -63,7 +63,7 @@ export const useLibrary = (): ILibraryContext => {
|
||||||
|
|
||||||
export const LibraryState = ({ children }: React.PropsWithChildren) => {
|
export const LibraryState = ({ children }: React.PropsWithChildren) => {
|
||||||
const { user, loading: userLoading } = useAuth();
|
const { user, loading: userLoading } = useAuth();
|
||||||
const { adminMode } = useConceptOptions();
|
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||||
|
|
||||||
const [items, setItems] = useState<ILibraryItem[]>([]);
|
const [items, setItems] = useState<ILibraryItem[]>([]);
|
||||||
const [templates, setTemplates] = useState<ILibraryItem[]>([]);
|
const [templates, setTemplates] = useState<ILibraryItem[]>([]);
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
|
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
|
||||||
import { resources } from '@/utils/constants';
|
import { resources } from '@/utils/constants';
|
||||||
|
|
||||||
function DatabaseSchemaPage() {
|
function DatabaseSchemaPage() {
|
||||||
const { calculateHeight, setNoFooter } = useConceptOptions();
|
const hideFooter = useAppLayoutStore(state => state.hideFooter);
|
||||||
|
|
||||||
const panelHeight = calculateHeight('0px');
|
const panelHeight = useFitHeight('0px');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNoFooter(true);
|
hideFooter(true);
|
||||||
return () => setNoFooter(false);
|
return () => hideFooter(false);
|
||||||
}, [setNoFooter]);
|
}, [hideFooter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-fade-in flex justify-center overflow-hidden' style={{ maxHeight: panelHeight }}>
|
<div className='cc-fade-in flex justify-center overflow-hidden' style={{ maxHeight: panelHeight }}>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { ILibraryItem, IRenameLocationData, LocationHead } from '@/models/library';
|
import { ILibraryItem, IRenameLocationData, LocationHead } from '@/models/library';
|
||||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||||
import { UserID } from '@/models/user';
|
import { UserID } from '@/models/user';
|
||||||
|
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||||
import { storage } from '@/utils/constants';
|
import { storage } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
import { information } from '@/utils/labels';
|
||||||
import { convertToCSV, toggleTristateFlag } from '@/utils/utils';
|
import { convertToCSV, toggleTristateFlag } from '@/utils/utils';
|
||||||
|
@ -29,6 +30,7 @@ function LibraryPage() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [items, setItems] = useState<ILibraryItem[]>([]);
|
const [items, setItems] = useState<ILibraryItem[]>([]);
|
||||||
const options = useConceptOptions();
|
const options = useConceptOptions();
|
||||||
|
const noNavigation = useAppLayoutStore(state => state.noNavigation);
|
||||||
|
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [path, setPath] = useState('');
|
const [path, setPath] = useState('');
|
||||||
|
@ -133,7 +135,7 @@ function LibraryPage() {
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Overlay
|
<Overlay
|
||||||
position={options.noNavigation ? 'top-[0.25rem] right-[3rem]' : 'top-[0.25rem] right-0'}
|
position={noNavigation ? 'top-[0.25rem] right-[3rem]' : 'top-[0.25rem] right-0'}
|
||||||
layer='z-tooltip'
|
layer='z-tooltip'
|
||||||
className='cc-animate-position'
|
className='cc-animate-position'
|
||||||
>
|
>
|
||||||
|
|
|
@ -12,12 +12,12 @@ import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } fro
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { useUsers } from '@/context/UsersContext';
|
import { useUsers } from '@/context/UsersContext';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { ILibraryItem, LibraryItemType } from '@/models/library';
|
import { ILibraryItem, LibraryItemType } from '@/models/library';
|
||||||
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
import { APP_COLORS } from '@/styling/color';
|
import { APP_COLORS } from '@/styling/color';
|
||||||
import { storage } from '@/utils/constants';
|
import { storage } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ function TableLibraryItems({ items, resetQuery, folderMode, toggleFolderMode }:
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { getUserLabel } = useUsers();
|
const { getUserLabel } = useUsers();
|
||||||
const { calculateHeight } = useConceptOptions();
|
|
||||||
const [itemsPerPage, setItemsPerPage] = useLocalStorage<number>(storage.libraryPagination, 50);
|
const [itemsPerPage, setItemsPerPage] = useLocalStorage<number>(storage.libraryPagination, 50);
|
||||||
|
|
||||||
function handleOpenItem(item: ILibraryItem, event: CProps.EventMouse) {
|
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<ILibraryItem>[] = [
|
const conditionalRowStyles: IConditionalStyle<ILibraryItem>[] = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,11 +8,11 @@ import { CProps } from '@/components/props';
|
||||||
import SelectLocation from '@/components/select/SelectLocation';
|
import SelectLocation from '@/components/select/SelectLocation';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { FolderNode, FolderTree } from '@/models/FolderTree';
|
import { FolderNode, FolderTree } from '@/models/FolderTree';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ function ViewSideLocation({
|
||||||
}: ViewSideLocationProps) {
|
}: ViewSideLocationProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { items } = useLibrary();
|
const { items } = useLibrary();
|
||||||
const { calculateHeight } = useConceptOptions();
|
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
const canRename = (() => {
|
const canRename = (() => {
|
||||||
|
@ -56,7 +55,7 @@ function ViewSideLocation({
|
||||||
return located.length !== 0;
|
return located.length !== 0;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const maxHeight = calculateHeight('4.5rem');
|
const maxHeight = useFitHeight('4.5rem');
|
||||||
|
|
||||||
function handleClickFolder(event: CProps.EventMouse, target: FolderNode) {
|
function handleClickFolder(event: CProps.EventMouse, target: FolderNode) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
|
import { useMainHeight } from '@/stores/appLayout';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import TopicsList from './TopicsList';
|
import TopicsList from './TopicsList';
|
||||||
|
@ -17,7 +17,7 @@ function ManualsPage() {
|
||||||
const query = useQueryStrings();
|
const query = useQueryStrings();
|
||||||
const activeTopic = (query.get('topic') || HelpTopic.MAIN) as HelpTopic;
|
const activeTopic = (query.get('topic') || HelpTopic.MAIN) as HelpTopic;
|
||||||
|
|
||||||
const { mainHeight } = useConceptOptions();
|
const mainHeight = useMainHeight();
|
||||||
|
|
||||||
const onSelectTopic = useCallback(
|
const onSelectTopic = useCallback(
|
||||||
(newTopic: HelpTopic) => {
|
(newTopic: HelpTopic) => {
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { useCallback } from 'react';
|
||||||
import { IconMenuFold, IconMenuUnfold } from '@/components/Icons';
|
import { IconMenuFold, IconMenuUnfold } from '@/components/Icons';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
import SelectTree from '@/components/ui/SelectTree';
|
import SelectTree from '@/components/ui/SelectTree';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { HelpTopic, topicParent } from '@/models/miscellaneous';
|
import { HelpTopic, topicParent } from '@/models/miscellaneous';
|
||||||
|
import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ interface TopicsDropdownProps {
|
||||||
|
|
||||||
function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
|
function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
|
||||||
const menu = useDropdown();
|
const menu = useDropdown();
|
||||||
const { noNavigation, calculateHeight } = useConceptOptions();
|
const noNavigation = useAppLayoutStore(state => state.noNavigation);
|
||||||
|
const treeHeight = useFitHeight('4rem + 2px');
|
||||||
|
|
||||||
const handleSelectTopic = useCallback(
|
const handleSelectTopic = useCallback(
|
||||||
(topic: HelpTopic) => {
|
(topic: HelpTopic) => {
|
||||||
|
@ -67,7 +68,7 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
|
||||||
'bg-prim-200'
|
'bg-prim-200'
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
maxHeight: calculateHeight('4rem + 2px'),
|
maxHeight: treeHeight,
|
||||||
transitionProperty: 'clip-path',
|
transitionProperty: 'clip-path',
|
||||||
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
clipPath: menu.isOpen ? 'inset(0% 0% 0% 0%)' : 'inset(0% 100% 0% 0%)'
|
clipPath: menu.isOpen ? 'inset(0% 0% 0% 0%)' : 'inset(0% 100% 0% 0%)'
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import SelectTree from '@/components/ui/SelectTree';
|
import SelectTree from '@/components/ui/SelectTree';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { HelpTopic, topicParent } from '@/models/miscellaneous';
|
import { HelpTopic, topicParent } from '@/models/miscellaneous';
|
||||||
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ interface TopicsStaticProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
|
function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
|
||||||
const { calculateHeight } = useConceptOptions();
|
const topicsHeight = useFitHeight('1rem + 2px');
|
||||||
return (
|
return (
|
||||||
<SelectTree
|
<SelectTree
|
||||||
items={Object.values(HelpTopic).map(item => item as HelpTopic)}
|
items={Object.values(HelpTopic).map(item => item as HelpTopic)}
|
||||||
|
@ -31,7 +31,7 @@ function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
|
||||||
'text-xs sm:text-sm bg-prim-200',
|
'text-xs sm:text-sm bg-prim-200',
|
||||||
'select-none'
|
'select-none'
|
||||||
)}
|
)}
|
||||||
style={{ maxHeight: calculateHeight('1rem + 2px') }}
|
style={{ maxHeight: topicsHeight }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import TopicPage from '@/pages/ManualsPage/TopicPage';
|
import TopicPage from '@/pages/ManualsPage/TopicPage';
|
||||||
|
import { useMainHeight } from '@/stores/appLayout';
|
||||||
|
|
||||||
interface ViewTopicProps {
|
interface ViewTopicProps {
|
||||||
topic: HelpTopic;
|
topic: HelpTopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewTopic({ topic }: ViewTopicProps) {
|
function ViewTopic({ topic }: ViewTopicProps) {
|
||||||
const { mainHeight } = useConceptOptions();
|
const mainHeight = useMainHeight();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={topic}
|
key={topic}
|
||||||
|
|
|
@ -18,11 +18,11 @@ import {
|
||||||
|
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useOSS } from '@/context/OssContext';
|
import { useOSS } from '@/context/OssContext';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
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 { APP_COLORS } from '@/styling/color';
|
import { APP_COLORS } from '@/styling/color';
|
||||||
import { PARAMETER, storage } from '@/utils/constants';
|
import { PARAMETER, storage } from '@/utils/constants';
|
||||||
import { errors } from '@/utils/labels';
|
import { errors } from '@/utils/labels';
|
||||||
|
@ -41,7 +41,7 @@ interface OssFlowProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
const { mainHeight } = useConceptOptions();
|
const mainHeight = useMainHeight();
|
||||||
const model = useOSS();
|
const model = useOSS();
|
||||||
const controller = useOssEdit();
|
const controller = useOssEdit();
|
||||||
const flow = useReactFlow();
|
const flow = useReactFlow();
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { toast } from 'react-toastify';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { useOSS } from '@/context/OssContext';
|
import { useOSS } from '@/context/OssContext';
|
||||||
|
@ -32,6 +31,7 @@ import {
|
||||||
OperationType
|
OperationType
|
||||||
} from '@/models/oss';
|
} from '@/models/oss';
|
||||||
import { UserID, UserLevel } from '@/models/user';
|
import { UserID, UserLevel } from '@/models/user';
|
||||||
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { errors, information } from '@/utils/labels';
|
import { errors, information } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ interface OssEditStateProps {
|
||||||
export const OssEditState = ({ selected, setSelected, children }: React.PropsWithChildren<OssEditStateProps>) => {
|
export const OssEditState = ({ selected, setSelected, children }: React.PropsWithChildren<OssEditStateProps>) => {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { adminMode } = useConceptOptions();
|
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||||
const { accessLevel, setAccessLevel } = useAccessMode();
|
const { accessLevel, setAccessLevel } = useAccessMode();
|
||||||
const model = useOSS();
|
const model = useOSS();
|
||||||
const library = useLibrary();
|
const library = useLibrary();
|
||||||
|
|
|
@ -13,12 +13,12 @@ import Overlay from '@/components/ui/Overlay';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
|
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { useOSS } from '@/context/OssContext';
|
import { useOSS } from '@/context/OssContext';
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { OperationID } from '@/models/oss';
|
import { OperationID } from '@/models/oss';
|
||||||
|
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||||
import { information, prompts } from '@/utils/labels';
|
import { information, prompts } from '@/utils/labels';
|
||||||
|
|
||||||
import EditorRSForm from './EditorOssCard';
|
import EditorRSForm from './EditorOssCard';
|
||||||
|
@ -37,7 +37,7 @@ function OssTabs() {
|
||||||
const activeTab = query.get('tab') ? (Number(query.get('tab')) as OssTabID) : OssTabID.GRAPH;
|
const activeTab = query.get('tab') ? (Number(query.get('tab')) as OssTabID) : OssTabID.GRAPH;
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
const { setNoFooter } = useConceptOptions();
|
const hideFooter = useAppLayoutStore(state => state.hideFooter);
|
||||||
const { schema, loading, loadingError: errorLoading } = useOSS();
|
const { schema, loading, loadingError: errorLoading } = useOSS();
|
||||||
const { destroyItem } = useLibrary();
|
const { destroyItem } = useLibrary();
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ function OssTabs() {
|
||||||
}, [schema, schema?.title]);
|
}, [schema, schema?.title]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNoFooter(activeTab === OssTabID.GRAPH);
|
hideFooter(activeTab === OssTabID.GRAPH);
|
||||||
}, [activeTab, setNoFooter]);
|
}, [activeTab, hideFooter]);
|
||||||
|
|
||||||
function navigateTab(tab: OssTabID) {
|
function navigateTab(tab: OssTabID) {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { ConstituentaID, IConstituenta } from '@/models/rsform';
|
import { ConstituentaID, IConstituenta } from '@/models/rsform';
|
||||||
|
import { useMainHeight } from '@/stores/appLayout';
|
||||||
import { globals, storage } from '@/utils/constants';
|
import { globals, storage } from '@/utils/constants';
|
||||||
|
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
@ -27,7 +27,7 @@ interface EditorConstituentaProps {
|
||||||
function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }: EditorConstituentaProps) {
|
function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }: EditorConstituentaProps) {
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const { mainHeight } = useConceptOptions();
|
const mainHeight = useMainHeight();
|
||||||
|
|
||||||
const [showList, setShowList] = useLocalStorage(storage.rseditShowList, true);
|
const [showList, setShowList] = useLocalStorage(storage.rseditShowList, true);
|
||||||
const [toggleReset, setToggleReset] = useState(false);
|
const [toggleReset, setToggleReset] = useState(false);
|
||||||
|
|
|
@ -9,10 +9,10 @@ import { type RowSelectionState } from '@/components/ui/DataTable';
|
||||||
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 SearchBar from '@/components/ui/SearchBar';
|
import SearchBar from '@/components/ui/SearchBar';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { CstMatchMode } from '@/models/miscellaneous';
|
import { CstMatchMode } from '@/models/miscellaneous';
|
||||||
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
|
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
|
||||||
import { matchConstituenta } from '@/models/rsformAPI';
|
import { matchConstituenta } from '@/models/rsformAPI';
|
||||||
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
import { information } from '@/utils/labels';
|
import { information } from '@/utils/labels';
|
||||||
import { convertToCSV } from '@/utils/utils';
|
import { convertToCSV } from '@/utils/utils';
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ interface EditorRSListProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSList({ onOpenEdit }: EditorRSListProps) {
|
function EditorRSList({ onOpenEdit }: EditorRSListProps) {
|
||||||
const { calculateHeight } = useConceptOptions();
|
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
|
|
||||||
|
@ -136,7 +135,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableHeight = calculateHeight('4.05rem + 5px');
|
const tableHeight = useFitHeight('4.05rem + 5px');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -24,12 +24,12 @@ import SelectedCounter from '@/components/info/SelectedCounter';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection';
|
import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import DlgGraphParams from '@/dialogs/DlgGraphParams';
|
import DlgGraphParams from '@/dialogs/DlgGraphParams';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { GraphColoring, GraphFilterParams } from '@/models/miscellaneous';
|
import { GraphColoring, GraphFilterParams } from '@/models/miscellaneous';
|
||||||
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
|
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
|
||||||
import { isBasicConcept } from '@/models/rsformAPI';
|
import { isBasicConcept } from '@/models/rsformAPI';
|
||||||
|
import { useMainHeight } from '@/stores/appLayout';
|
||||||
import { APP_COLORS, colorBgGraphNode } from '@/styling/color';
|
import { APP_COLORS, colorBgGraphNode } from '@/styling/color';
|
||||||
import { PARAMETER, storage } from '@/utils/constants';
|
import { PARAMETER, storage } from '@/utils/constants';
|
||||||
import { errors } from '@/utils/labels';
|
import { errors } from '@/utils/labels';
|
||||||
|
@ -53,7 +53,7 @@ interface TGFlowProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function TGFlow({ onOpenEdit }: TGFlowProps) {
|
function TGFlow({ onOpenEdit }: TGFlowProps) {
|
||||||
const { mainHeight } = useConceptOptions();
|
const mainHeight = useMainHeight();
|
||||||
const controller = useRSEdit();
|
const controller = useRSEdit();
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||||
const [edges, setEdges] = useEdgesState([]);
|
const [edges, setEdges] = useEdgesState([]);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
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 { APP_COLORS, colorBgGraphNode } from '@/styling/color';
|
import { APP_COLORS, colorBgGraphNode } from '@/styling/color';
|
||||||
import { globals, PARAMETER, prefixes, storage } from '@/utils/constants';
|
import { globals, PARAMETER, prefixes, storage } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -26,11 +27,11 @@ interface ViewHiddenProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewHidden({ items, selected, toggleSelection, setFocus, schema, coloringScheme, onEdit }: ViewHiddenProps) {
|
function ViewHidden({ items, selected, toggleSelection, setFocus, schema, coloringScheme, onEdit }: ViewHiddenProps) {
|
||||||
const { calculateHeight } = useConceptOptions();
|
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const localSelected = items.filter(id => selected.includes(id));
|
const localSelected = items.filter(id => selected.includes(id));
|
||||||
const [isFolded, setIsFolded] = useLocalStorage(storage.rsgraphFoldHidden, false);
|
const [isFolded, setIsFolded] = useLocalStorage(storage.rsgraphFoldHidden, false);
|
||||||
const { setHoverCst } = useConceptOptions();
|
const { setHoverCst } = useConceptOptions();
|
||||||
|
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) {
|
||||||
if (event.ctrlKey || event.metaKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
|
@ -77,7 +78,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
|
||||||
'cc-scroll-y'
|
'cc-scroll-y'
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
maxHeight: calculateHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px'),
|
maxHeight: hiddenHeight,
|
||||||
transitionProperty: 'clip-path',
|
transitionProperty: 'clip-path',
|
||||||
transitionDuration: `${PARAMETER.fastAnimation}ms`,
|
transitionDuration: `${PARAMETER.fastAnimation}ms`,
|
||||||
transitionTimingFunction: 'ease-out',
|
transitionTimingFunction: 'ease-out',
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { toast } from 'react-toastify';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
||||||
|
@ -51,6 +50,7 @@ import {
|
||||||
} from '@/models/rsform';
|
} from '@/models/rsform';
|
||||||
import { generateAlias } from '@/models/rsformAPI';
|
import { generateAlias } from '@/models/rsformAPI';
|
||||||
import { UserID, UserLevel } from '@/models/user';
|
import { UserID, UserLevel } from '@/models/user';
|
||||||
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||||
import { information, prompts } from '@/utils/labels';
|
import { information, prompts } from '@/utils/labels';
|
||||||
import { promptUnsaved } from '@/utils/utils';
|
import { promptUnsaved } from '@/utils/utils';
|
||||||
|
@ -142,7 +142,7 @@ export const RSEditState = ({
|
||||||
}: React.PropsWithChildren<RSEditStateProps>) => {
|
}: React.PropsWithChildren<RSEditStateProps>) => {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { adminMode } = useConceptOptions();
|
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||||
const { accessLevel, setAccessLevel } = useAccessMode();
|
const { accessLevel, setAccessLevel } = useAccessMode();
|
||||||
const model = useRSForm();
|
const model = useRSForm();
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,13 @@ import Loader from '@/components/ui/Loader';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import { useGlobalOss } from '@/context/GlobalOssContext';
|
import { useGlobalOss } from '@/context/GlobalOssContext';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
|
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { ConstituentaID, IConstituenta, IConstituentaMeta } from '@/models/rsform';
|
import { ConstituentaID, IConstituenta, IConstituentaMeta } from '@/models/rsform';
|
||||||
|
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { information, labelVersion, prompts } from '@/utils/labels';
|
import { information, labelVersion, prompts } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ function RSTabs() {
|
||||||
const version = query.get('v') ? Number(query.get('v')) : undefined;
|
const version = query.get('v') ? Number(query.get('v')) : undefined;
|
||||||
const cstQuery = query.get('active');
|
const cstQuery = query.get('active');
|
||||||
|
|
||||||
const { setNoFooter } = useConceptOptions();
|
const hideFooter = useAppLayoutStore(state => state.hideFooter);
|
||||||
const { schema, loading, errorLoading, isArchive, itemID } = useRSForm();
|
const { schema, loading, errorLoading, isArchive, itemID } = useRSForm();
|
||||||
const library = useLibrary();
|
const library = useLibrary();
|
||||||
const oss = useGlobalOss();
|
const oss = useGlobalOss();
|
||||||
|
@ -73,7 +73,7 @@ function RSTabs() {
|
||||||
}, [schema, schema?.title]);
|
}, [schema, schema?.title]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNoFooter(activeTab !== RSTabID.CARD);
|
hideFooter(activeTab !== RSTabID.CARD);
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
if (activeTab === RSTabID.CST_EDIT) {
|
if (activeTab === RSTabID.CST_EDIT) {
|
||||||
const cstID = Number(cstQuery);
|
const cstID = Number(cstQuery);
|
||||||
|
@ -83,8 +83,8 @@ function RSTabs() {
|
||||||
setSelected([]);
|
setSelected([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => setNoFooter(false);
|
return () => hideFooter(false);
|
||||||
}, [activeTab, cstQuery, setSelected, schema, setNoFooter, setIsModified]);
|
}, [activeTab, cstQuery, setSelected, schema, hideFooter, setIsModified]);
|
||||||
|
|
||||||
function navigateTab(tab: RSTabID, activeID?: ConstituentaID) {
|
function navigateTab(tab: RSTabID, activeID?: ConstituentaID) {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
|
|
|
@ -4,10 +4,10 @@ import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { UserLevel } from '@/models/user';
|
import { UserLevel } from '@/models/user';
|
||||||
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import ConstituentsSearch from './ConstituentsSearch';
|
import ConstituentsSearch from './ConstituentsSearch';
|
||||||
|
@ -26,9 +26,9 @@ interface ViewConstituentsProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit, isMounted }: ViewConstituentsProps) {
|
function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit, isMounted }: ViewConstituentsProps) {
|
||||||
const { calculateHeight } = useConceptOptions();
|
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const { accessLevel } = useAccessMode();
|
const { accessLevel } = useAccessMode();
|
||||||
|
const listHeight = useFitHeight(!isBottom ? '8.2rem' : accessLevel !== UserLevel.READER ? '42rem' : '35rem', '10rem');
|
||||||
|
|
||||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
|
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
|
||||||
|
|
||||||
|
@ -57,11 +57,7 @@ function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit,
|
||||||
setFiltered={setFilteredData}
|
setFiltered={setFilteredData}
|
||||||
/>
|
/>
|
||||||
<TableSideConstituents
|
<TableSideConstituents
|
||||||
maxHeight={
|
maxHeight={listHeight}
|
||||||
isBottom
|
|
||||||
? calculateHeight(accessLevel !== UserLevel.READER ? '42rem' : '35rem', '10rem')
|
|
||||||
: calculateHeight('8.2rem')
|
|
||||||
}
|
|
||||||
items={filteredData}
|
items={filteredData}
|
||||||
activeCst={activeCst}
|
activeCst={activeCst}
|
||||||
onOpenEdit={onOpenEdit}
|
onOpenEdit={onOpenEdit}
|
||||||
|
|
68
rsconcept/frontend/src/stores/appLayout.ts
Normal file
68
rsconcept/frontend/src/stores/appLayout.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
|
/** Application layout state manager. */
|
||||||
|
interface AppLayoutStore {
|
||||||
|
noNavigation: boolean;
|
||||||
|
noNavigationAnimation: boolean;
|
||||||
|
toggleNoNavigation: () => void;
|
||||||
|
|
||||||
|
noFooter: boolean;
|
||||||
|
hideFooter: (value?: boolean) => void;
|
||||||
|
|
||||||
|
noScroll: boolean;
|
||||||
|
hideScroll: (value?: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAppLayoutStore = create<AppLayoutStore>()(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})`;
|
||||||
|
}
|
||||||
|
}
|
24
rsconcept/frontend/src/stores/preferences.ts
Normal file
24
rsconcept/frontend/src/stores/preferences.ts
Normal file
|
@ -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<PreferencesStore>()(
|
||||||
|
persist(
|
||||||
|
set => ({
|
||||||
|
showHelp: true,
|
||||||
|
adminMode: false,
|
||||||
|
toggleShowHelp: () => set(state => ({ showHelp: !state.showHelp })),
|
||||||
|
toggleAdminMode: () => set(state => ({ adminMode: !state.adminMode }))
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
version: 1,
|
||||||
|
name: 'portal.preferences'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
|
@ -109,8 +109,6 @@ export const storage = {
|
||||||
PREFIX: 'portal.',
|
PREFIX: 'portal.',
|
||||||
|
|
||||||
themeDark: 'theme.dark',
|
themeDark: 'theme.dark',
|
||||||
optionsAdmin: 'options.admin',
|
|
||||||
optionsHelp: 'options.help',
|
|
||||||
|
|
||||||
rseditShowList: 'rsedit.show_list',
|
rseditShowList: 'rsedit.show_list',
|
||||||
rseditShowControls: 'rsedit.show_controls',
|
rseditShowControls: 'rsedit.show_controls',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user