From aa4a8b3f94ea2433bfb34e42edf379c6a4d8c8af Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:57:45 +0300 Subject: [PATCH] R: Prepare to migrate to react-compiler pt1 --- README.md | 1 + rsconcept/frontend/eslint.config.js | 3 + rsconcept/frontend/package-lock.json | 184 ++++++++++++++++++ rsconcept/frontend/package.json | 1 + .../src/app/Navigation/UserDropdown.tsx | 1 - .../select/PickMultiConstituenta.tsx | 6 +- .../src/components/select/PickSchema.tsx | 4 +- .../src/components/select/SelectLocation.tsx | 4 +- .../frontend/src/components/ui/SelectTree.tsx | 4 +- .../frontend/src/context/AuthContext.tsx | 9 +- .../src/context/ConceptOptionsContext.tsx | 6 +- .../frontend/src/context/GlobalOssContext.tsx | 12 +- .../frontend/src/context/LibraryContext.tsx | 16 +- rsconcept/frontend/src/context/OssContext.tsx | 125 +++++------- .../frontend/src/context/RSFormContext.tsx | 133 ++++++------- .../DlgConstituentaTemplate.tsx | 10 +- .../dialogs/DlgCreateCst/FormCreateCst.tsx | 6 +- .../DlgCreateOperation/DlgCreateOperation.tsx | 4 +- .../DlgEditOperation/DlgEditOperation.tsx | 11 +- .../DlgEditReference/TabEntityReference.tsx | 4 +- .../TabSyntacticReference.tsx | 4 +- .../DlgEditVersions/DlgEditVersions.tsx | 4 +- .../DlgEditWordForms/DlgEditWordForms.tsx | 4 +- .../frontend/src/dialogs/DlgRenameCst.tsx | 6 +- .../src/dialogs/DlgShowAST/ASTFlow.tsx | 4 +- .../dialogs/DlgShowTypeGraph/MGraphFlow.tsx | 4 +- .../frontend/src/hooks/useClickedOutside.ts | 4 +- rsconcept/frontend/src/hooks/useOssDetails.ts | 19 +- .../frontend/src/hooks/useRSFormCache.ts | 1 + .../frontend/src/hooks/useRSFormDetails.ts | 6 +- .../pages/CreateItemPage/FormCreateItem.tsx | 6 +- .../frontend/src/pages/DatabaseSchemaPage.tsx | 4 +- rsconcept/frontend/src/pages/HomePage.tsx | 4 +- .../src/pages/LibraryPage/LibraryPage.tsx | 4 +- .../src/pages/ManualsPage/ManualsPage.tsx | 1 - .../pages/OssPage/EditorOssCard/FormOSS.tsx | 32 +-- .../EditorOssGraph/NodeContextMenu.tsx | 2 +- .../pages/OssPage/EditorOssGraph/OssFlow.tsx | 4 +- .../src/pages/OssPage/OssEditContext.tsx | 4 +- .../frontend/src/pages/OssPage/OssTabs.tsx | 6 +- .../EditorConstituenta/FormConstituenta.tsx | 34 ++-- .../EditorRSExpression/EditorRSExpression.tsx | 4 +- .../EditorRSFormCard/FormRSForm.tsx | 32 +-- .../RSFormPage/EditorRSList/EditorRSList.tsx | 6 +- .../RSFormPage/EditorTermGraph/TGFlow.tsx | 9 +- .../EditorTermGraph/useGraphFilter.ts | 4 +- .../src/pages/RSFormPage/RSEditContext.tsx | 4 +- .../frontend/src/pages/RSFormPage/RSTabs.tsx | 6 +- .../ViewConstituents/ConstituentsSearch.tsx | 4 +- .../TableSideConstituents.tsx | 4 +- .../pages/UserProfilePage/EditorProfile.tsx | 15 +- .../pages/UserProfilePage/UserContents.tsx | 4 +- 52 files changed, 477 insertions(+), 316 deletions(-) diff --git a/README.md b/README.md index 4a2a93a2..70b16093 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ This readme file is used mostly to document project dependencies and conventions - tailwindcss - postcss - autoprefixer + - eslint-plugin-react-compiler - eslint-plugin-simple-import-sort - eslint-plugin-react-hooks - eslint-plugin-tsdoc diff --git a/rsconcept/frontend/eslint.config.js b/rsconcept/frontend/eslint.config.js index bd250a39..2c660815 100644 --- a/rsconcept/frontend/eslint.config.js +++ b/rsconcept/frontend/eslint.config.js @@ -2,6 +2,7 @@ import globals from 'globals'; import typescriptPlugin from 'typescript-eslint'; import typescriptParser from '@typescript-eslint/parser'; import reactPlugin from 'eslint-plugin-react'; +import reactCompilerPlugin from 'eslint-plugin-react-compiler'; import reactHooksPlugin from 'eslint-plugin-react-hooks'; import simpleImportSort from 'eslint-plugin-simple-import-sort'; @@ -34,11 +35,13 @@ export default [ { plugins: { 'react': reactPlugin, + 'react-compiler': reactCompilerPlugin, 'react-hooks': reactHooksPlugin, 'simple-import-sort': simpleImportSort }, settings: { react: { version: 'detect' } }, rules: { + 'react-compiler/react-compiler': 'error', '@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }], '@typescript-eslint/prefer-nullish-coalescing': 'off', '@typescript-eslint/no-inferrable-types': 'off', diff --git a/rsconcept/frontend/package-lock.json b/rsconcept/frontend/package-lock.json index a772af2d..58427abe 100644 --- a/rsconcept/frontend/package-lock.json +++ b/rsconcept/frontend/package-lock.json @@ -43,6 +43,7 @@ "autoprefixer": "^10.4.20", "eslint": "^9.16.0", "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-simple-import-sort": "^12.1.1", "globals": "^15.13.0", @@ -163,6 +164,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", @@ -190,6 +204,52 @@ "semver": "bin/semver.js" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", @@ -221,6 +281,19 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-plugin-utils": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", @@ -231,6 +304,38 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", @@ -288,6 +393,24 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -5153,6 +5276,27 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-plugin-react-compiler": { + "version": "19.0.0-beta-37ed2a7-20241206", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-37ed2a7-20241206.tgz", + "integrity": "sha512-5Pex1fUCJwLwwqEJe6NkgTn45kUjjj9TZP6IrW4IcpWM/YaEe+QvcOeF60huDjBq0kz1svGeW2nw8WdY+qszAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "hermes-parser": "^0.25.1", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", @@ -6022,6 +6166,23 @@ "node": ">= 0.4" } }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -10333,6 +10494,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", + "integrity": "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.18.0" + } + }, "node_modules/zustand": { "version": "4.5.5", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz", diff --git a/rsconcept/frontend/package.json b/rsconcept/frontend/package.json index 73cc771e..94eb85d8 100644 --- a/rsconcept/frontend/package.json +++ b/rsconcept/frontend/package.json @@ -47,6 +47,7 @@ "autoprefixer": "^10.4.20", "eslint": "^9.16.0", "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-simple-import-sort": "^12.1.1", "globals": "^15.13.0", diff --git a/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx b/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx index 0e5fb7cb..c8e03b1a 100644 --- a/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx +++ b/rsconcept/frontend/src/app/Navigation/UserDropdown.tsx @@ -62,7 +62,6 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) { } function handleToggleDarkMode() { - hideDropdown(); toggleDarkMode(); } diff --git a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx index 1c24a51d..8b7d00ee 100644 --- a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx +++ b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import DataTable, { createColumnHelper, RowSelectionState } from '@/components/ui/DataTable'; import { useConceptOptions } from '@/context/ConceptOptionsContext'; @@ -68,7 +68,7 @@ function PickMultiConstituenta({ return newGraph; }, [data, schema.graph, schema.items]); - useLayoutEffect(() => { + useEffect(() => { if (filtered.length === 0) { setRowSelection({}); return; @@ -80,7 +80,7 @@ function PickMultiConstituenta({ setRowSelection(newRowSelection); }, [filtered, setRowSelection, selected]); - useLayoutEffect(() => { + useEffect(() => { if (data.length === 0) { setFiltered([]); } else if (filterText) { diff --git a/rsconcept/frontend/src/components/select/PickSchema.tsx b/rsconcept/frontend/src/components/select/PickSchema.tsx index 43058987..894d6bef 100644 --- a/rsconcept/frontend/src/components/select/PickSchema.tsx +++ b/rsconcept/frontend/src/components/select/PickSchema.tsx @@ -1,5 +1,5 @@ import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; @@ -58,7 +58,7 @@ function PickSchema({ const locationMenu = useDropdown(); - useLayoutEffect(() => { + useEffect(() => { let newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText)); if (filterLocation.length > 0) { newFiltered = newFiltered.filter( diff --git a/rsconcept/frontend/src/components/select/SelectLocation.tsx b/rsconcept/frontend/src/components/select/SelectLocation.tsx index 677ffb3c..99117e79 100644 --- a/rsconcept/frontend/src/components/select/SelectLocation.tsx +++ b/rsconcept/frontend/src/components/select/SelectLocation.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { FolderNode, FolderTree } from '@/models/FolderTree'; import { labelFolderNode } from '@/utils/labels'; @@ -24,7 +24,7 @@ function SelectLocation({ value, folderTree, dense, prefix, onClick, className, const items = useMemo(() => folderTree.getTree(), [folderTree]); const [folded, setFolded] = useState(items); - useLayoutEffect(() => { + useEffect(() => { setFolded(items.filter(item => item !== activeNode && !activeNode?.hasPredecessor(item))); }, [items, activeNode]); diff --git a/rsconcept/frontend/src/components/ui/SelectTree.tsx b/rsconcept/frontend/src/components/ui/SelectTree.tsx index 49563992..ccb9b0d2 100644 --- a/rsconcept/frontend/src/components/ui/SelectTree.tsx +++ b/rsconcept/frontend/src/components/ui/SelectTree.tsx @@ -1,5 +1,5 @@ import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { globals, PARAMETER } from '@/utils/constants'; @@ -50,7 +50,7 @@ function SelectTree({ ); const [folded, setFolded] = useState(items); - useLayoutEffect(() => { + useEffect(() => { setFolded(items.filter(item => getParent(value) !== item && getParent(getParent(value)) !== item)); }, [value, getParent, items]); diff --git a/rsconcept/frontend/src/context/AuthContext.tsx b/rsconcept/frontend/src/context/AuthContext.tsx index fc54ce85..0dfc5459 100644 --- a/rsconcept/frontend/src/context/AuthContext.tsx +++ b/rsconcept/frontend/src/context/AuthContext.tsx @@ -1,6 +1,6 @@ 'use client'; -import { createContext, useCallback, useContext, useLayoutEffect, useState } from 'react'; +import { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { DataCallback } from '@/backend/apiTransport'; import { @@ -124,10 +124,7 @@ export const AuthState = ({ children }: React.PropsWithChildren) => { showError: true, setLoading: setLoading, onError: setError, - onSuccess: () => - reload(() => { - callback?.(); - }) + onSuccess: () => reload(callback) }); }, [reload] @@ -184,7 +181,7 @@ export const AuthState = ({ children }: React.PropsWithChildren) => { [reload] ); - useLayoutEffect(() => { + useEffect(() => { reload(); }, [reload]); diff --git a/rsconcept/frontend/src/context/ConceptOptionsContext.tsx b/rsconcept/frontend/src/context/ConceptOptionsContext.tsx index d2cd078f..5f807db7 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, useLayoutEffect, useMemo, useState } from 'react'; +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import Tooltip from '@/components/ui/Tooltip'; import useLocalStorage from '@/hooks/useLocalStorage'; @@ -76,11 +76,11 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => { root.setAttribute('data-color-scheme', !isDark ? 'light' : 'dark'); } - useLayoutEffect(() => { + useEffect(() => { setDarkClass(darkMode); }, [darkMode]); - useLayoutEffect(() => { + useEffect(() => { setColors(darkMode ? darkT : lightT); }, [darkMode, setColors]); diff --git a/rsconcept/frontend/src/context/GlobalOssContext.tsx b/rsconcept/frontend/src/context/GlobalOssContext.tsx index 57e3f729..fa45c467 100644 --- a/rsconcept/frontend/src/context/GlobalOssContext.tsx +++ b/rsconcept/frontend/src/context/GlobalOssContext.tsx @@ -8,8 +8,6 @@ import { LibraryItemID } from '@/models/library'; import { IOperationSchema, IOperationSchemaData } from '@/models/oss'; import { contextOutsideScope } from '@/utils/labels'; -import { useLibrary } from './LibraryContext'; - interface IGlobalOssContext { schema: IOperationSchema | undefined; setID: (id: string | undefined) => void; @@ -20,6 +18,7 @@ interface IGlobalOssContext { invalidate: () => void; invalidateItem: (target: LibraryItemID) => void; + partialUpdate: (data: Partial) => void; reload: (callback?: () => void) => void; } @@ -33,16 +32,16 @@ export const useGlobalOss = (): IGlobalOssContext => { }; export const GlobalOssState = ({ children }: React.PropsWithChildren) => { - const library = useLibrary(); - const [isValid, setIsValid] = useState(false); + const [isValid, setIsValid] = useState(true); const [ossID, setIdInternal] = useState(undefined); const { schema: schema, // prettier: split lines error: loadingError, setSchema: setDataInternal, loading: loading, - reload: reloadInternal - } = useOssDetails({ target: ossID, items: library.items }); + reload: reloadInternal, + partialUpdate + } = useOssDetails({ target: ossID }); const reload = useCallback( (callback?: () => void) => { @@ -96,6 +95,7 @@ export const GlobalOssState = ({ children }: React.PropsWithChildren) => { loading, loadingError, reload, + partialUpdate, isValid, invalidateItem, invalidate diff --git a/rsconcept/frontend/src/context/LibraryContext.tsx b/rsconcept/frontend/src/context/LibraryContext.tsx index bbc65755..c695717c 100644 --- a/rsconcept/frontend/src/context/LibraryContext.tsx +++ b/rsconcept/frontend/src/context/LibraryContext.tsx @@ -48,7 +48,7 @@ interface ILibraryContext { destroyItem: (target: LibraryItemID, callback?: () => void) => void; renameLocation: (data: IRenameLocationData, callback?: () => void) => void; - localUpdateItem: (data: ILibraryItem) => void; + localUpdateItem: (data: Partial) => void; localUpdateTimestamp: (target: LibraryItemID) => void; } @@ -196,21 +196,17 @@ export const LibraryState = ({ children }: React.PropsWithChildren) => { }, [reloadTemplates]); const localUpdateItem = useCallback( - (data: ILibraryItem) => { - const libraryItem = items.find(item => item.id === data.id); - if (libraryItem) Object.assign(libraryItem, data); + (data: Partial) => { + setItems(prev => prev.map(item => (item.id === data.id ? { ...item, ...data } : item))); }, - [items] + [setItems] ); const localUpdateTimestamp = useCallback( (target: LibraryItemID) => { - const libraryItem = items.find(item => item.id === target); - if (libraryItem) { - libraryItem.time_update = Date(); - } + setItems(prev => prev.map(item => (item.id === target ? { ...item, time_update: Date() } : item))); }, - [items] + [setItems] ); const createItem = useCallback( diff --git a/rsconcept/frontend/src/context/OssContext.tsx b/rsconcept/frontend/src/context/OssContext.tsx index 6e8c1287..f9690499 100644 --- a/rsconcept/frontend/src/context/OssContext.tsx +++ b/rsconcept/frontend/src/context/OssContext.tsx @@ -85,23 +85,22 @@ interface OssStateProps { export const OssState = ({ itemID, children }: React.PropsWithChildren) => { const library = useLibrary(); - const oss = useGlobalOss(); - const model = oss.schema; + const ossData = useGlobalOss(); const { user } = useAuth(); const [processing, setProcessing] = useState(false); const [processingError, setProcessingError] = useState(undefined); const isOwned = useMemo(() => { - return user?.id === model?.owner || false; - }, [user, model?.owner]); + return user?.id === ossData.schema?.owner || false; + }, [user, ossData.schema?.owner]); useEffect(() => { - oss.setID(itemID); - }, [itemID, oss]); + ossData.setID(itemID); + }, [itemID, ossData]); const update = useCallback( (data: ILibraryUpdateData, callback?: DataCallback) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -111,43 +110,39 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - const fullData: IOperationSchemaData = Object.assign(model, newData); - oss.setData(fullData); + const fullData: IOperationSchemaData = Object.assign(ossData.schema!, newData); + ossData.setData(fullData); library.localUpdateItem(newData); callback?.(newData); } }); }, - [itemID, model, library, oss] + [itemID, library, ossData] ); const setOwner = useCallback( (newOwner: UserID, callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); patchSetOwner(itemID, { - data: { - user: newOwner - }, + data: { user: newOwner }, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { - model.owner = newOwner; - library.reloadItems(() => { - callback?.(); - }); + ossData.partialUpdate({ owner: newOwner }); + library.reloadItems(callback); } }); }, - [itemID, model, library] + [itemID, ossData, library] ); const setAccessPolicy = useCallback( (newPolicy: AccessPolicy, callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -159,19 +154,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - model.access_policy = newPolicy; - library.reloadItems(() => { - callback?.(); - }); + ossData.partialUpdate({ access_policy: newPolicy }); + library.reloadItems(callback); } }); }, - [itemID, model, library] + [itemID, ossData, library] ); const setLocation = useCallback( (newLocation: string, callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -183,19 +176,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - model.location = newLocation; - library.reloadItems(() => { - callback?.(); - }); + ossData.partialUpdate({ location: newLocation }); + library.reloadItems(callback); } }); }, - [itemID, model, library] + [itemID, ossData, library] ); const setEditors = useCallback( (newEditors: UserID[], callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -207,14 +198,12 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - model.editors = newEditors; - library.reloadItems(() => { - callback?.(); - }); + ossData.partialUpdate({ editors: newEditors }); + library.reloadItems(callback); } }); }, - [itemID, model, library] + [itemID, ossData, library] ); const savePositions = useCallback( @@ -243,13 +232,13 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - oss.setData(newData.oss); + ossData.setData(newData.oss); library.localUpdateTimestamp(newData.oss.id); callback?.(newData.new_operation); } }); }, - [itemID, library, oss] + [itemID, library, ossData] ); const deleteOperation = useCallback( @@ -261,14 +250,12 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - oss.setData(newData); - library.reloadItems(() => { - callback?.(); - }); + ossData.setData(newData); + library.reloadItems(callback); } }); }, - [itemID, library, oss] + [itemID, library, ossData] ); const createInput = useCallback( @@ -280,19 +267,19 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - oss.setData(newData.oss); + ossData.setData(newData.oss); library.reloadItems(() => { callback?.(newData.new_schema); }); } }); }, - [itemID, library, oss] + [itemID, library, ossData] ); const setInput = useCallback( (data: IOperationSetInputData, callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -302,19 +289,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - oss.setData(newData); - library.reloadItems(() => { - callback?.(); - }); + ossData.setData(newData); + library.reloadItems(callback); } }); }, - [itemID, model, library, oss] + [itemID, ossData, library] ); const updateOperation = useCallback( (data: IOperationUpdateData, callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -324,19 +309,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - oss.setData(newData); - library.reloadItems(() => { - callback?.(); - }); + ossData.setData(newData); + library.reloadItems(callback); } }); }, - [itemID, model, library, oss] + [itemID, library, ossData] ); const executeOperation = useCallback( (data: ITargetOperation, callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -346,19 +329,17 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - oss.setData(newData); - library.reloadItems(() => { - callback?.(); - }); + ossData.setData(newData); + library.reloadItems(callback); } }); }, - [itemID, model, library, oss] + [itemID, library, ossData] ); const relocateConstituents = useCallback( (data: ICstRelocateData, callback?: () => void) => { - if (!model) { + if (!ossData.schema) { return; } setProcessingError(undefined); @@ -368,23 +349,21 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren { - oss.reload(); - library.reloadItems(() => { - callback?.(); - }); + ossData.reload(); + library.reloadItems(callback); } }); }, - [model, library, oss] + [library, ossData] ); return ( (undefined); const isOwned = useMemo(() => { - return user?.id === schema?.owner || false; - }, [user, schema?.owner]); + return user?.id === rsData.schema?.owner || false; + }, [user, rsData.schema?.owner]); const isArchive = useMemo(() => !!versionID, [versionID]); const update = useCallback( (data: ILibraryUpdateData, callback?: DataCallback) => { - if (!schema) { + if (!rsData.schema) { return; } setProcessingError(undefined); @@ -144,19 +135,19 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(Object.assign(schema, newData)); + rsData.setSchema(Object.assign(rsData.schema!, newData)); library.localUpdateItem(newData); oss.invalidateItem(newData.id); callback?.(newData); } }); }, - [itemID, setSchema, schema, library, oss] + [itemID, rsData, library, oss] ); const upload = useCallback( (data: IRSFormUploadData, callback?: () => void) => { - if (!schema) { + if (!rsData.schema) { return; } setProcessingError(undefined); @@ -166,20 +157,17 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateItem(newData); callback?.(); } }); }, - [itemID, setSchema, schema, library] + [itemID, rsData, library] ); const setOwner = useCallback( (newOwner: UserID, callback?: () => void) => { - if (!schema) { - return; - } setProcessingError(undefined); patchSetOwner(itemID, { data: { @@ -189,18 +177,18 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { - schema.owner = newOwner; - library.localUpdateItem(schema); + rsData.partialUpdate({ owner: newOwner }); + library.localUpdateItem({ id: Number(itemID), owner: newOwner }); callback?.(); } }); }, - [itemID, schema, library] + [itemID, rsData, library] ); const setAccessPolicy = useCallback( (newPolicy: AccessPolicy, callback?: () => void) => { - if (!schema) { + if (!rsData.schema) { return; } setProcessingError(undefined); @@ -212,18 +200,18 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { - schema.access_policy = newPolicy; - library.localUpdateItem(schema); + rsData.partialUpdate({ access_policy: newPolicy }); + library.localUpdateItem({ id: Number(itemID), access_policy: newPolicy }); callback?.(); } }); }, - [itemID, schema, library] + [itemID, rsData, library] ); const setLocation = useCallback( (newLocation: string, callback?: () => void) => { - if (!schema) { + if (!rsData.schema) { return; } setProcessingError(undefined); @@ -235,17 +223,18 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { - schema.location = newLocation; - library.reloadItems(callback); + rsData.partialUpdate({ location: newLocation }); + library.localUpdateItem({ id: Number(itemID), location: newLocation }); + callback?.(); } }); }, - [itemID, schema, library] + [itemID, rsData, library] ); const setEditors = useCallback( (newEditors: UserID[], callback?: () => void) => { - if (!schema) { + if (!rsData.schema) { return; } setProcessingError(undefined); @@ -257,17 +246,17 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { - schema.editors = newEditors; + rsData.partialUpdate({ editors: newEditors }); callback?.(); } }); }, - [itemID, schema] + [itemID, rsData] ); const resetAliases = useCallback( (callback?: () => void) => { - if (!schema || !user) { + if (!rsData.schema || !user) { return; } setProcessingError(undefined); @@ -276,19 +265,19 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateTimestamp(newData.id); oss.invalidateItem(newData.id); callback?.(); } }); }, - [itemID, schema, user, setSchema, library, oss] + [itemID, rsData, user, library, oss] ); const restoreOrder = useCallback( (callback?: () => void) => { - if (!schema || !user) { + if (!rsData.schema || !user) { return; } setProcessingError(undefined); @@ -297,13 +286,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateTimestamp(newData.id); callback?.(); } }); }, - [itemID, schema, user, setSchema, library] + [itemID, rsData, user, library] ); const produceStructure = useCallback( @@ -315,27 +304,27 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData.schema); + rsData.setSchema(newData.schema); library.localUpdateTimestamp(newData.schema.id); oss.invalidateItem(newData.schema.id); callback?.(newData.cst_list); } }); }, - [setSchema, itemID, library, oss] + [rsData, itemID, library, oss] ); const download = useCallback( (callback: DataCallback) => { setProcessingError(undefined); - getTRSFile(itemID, String(schema?.version ?? ''), { + getTRSFile(itemID, String(rsData.schema?.version ?? ''), { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: callback }); }, - [itemID, schema] + [itemID, rsData] ); const cstCreate = useCallback( @@ -347,14 +336,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData.schema); + rsData.setSchema(newData.schema); library.localUpdateTimestamp(newData.schema.id); oss.invalidateItem(newData.schema.id); callback?.(newData.new_cst); } }); }, - [itemID, setSchema, library, oss] + [itemID, rsData, library, oss] ); const cstDelete = useCallback( @@ -366,14 +355,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateTimestamp(newData.id); oss.invalidateItem(newData.id); callback?.(); } }); }, - [itemID, setSchema, library, oss] + [itemID, rsData, library, oss] ); const cstUpdate = useCallback( @@ -385,14 +374,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => - reload(setProcessing, () => { + rsData.reload(setProcessing, () => { library.localUpdateTimestamp(Number(itemID)); oss.invalidateItem(Number(itemID)); callback?.(newData); }) }); }, - [itemID, reload, library, oss] + [itemID, rsData, library, oss] ); const cstRename = useCallback( @@ -404,14 +393,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData.schema); + rsData.setSchema(newData.schema); library.localUpdateTimestamp(newData.schema.id); oss.invalidateItem(newData.schema.id); callback?.(newData.new_cst); } }); }, - [setSchema, itemID, library, oss] + [rsData, itemID, library, oss] ); const cstSubstitute = useCallback( @@ -423,14 +412,14 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateTimestamp(newData.id); oss.invalidateItem(newData.id); callback?.(); } }); }, - [setSchema, itemID, library, oss] + [rsData, itemID, library, oss] ); const cstMoveTo = useCallback( @@ -442,13 +431,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateTimestamp(Number(itemID)); callback?.(); } }); }, - [itemID, setSchema, library] + [itemID, rsData, library] ); const versionCreate = useCallback( @@ -460,13 +449,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData.schema); + rsData.setSchema(newData.schema); library.localUpdateTimestamp(Number(itemID)); callback?.(newData.version); } }); }, - [itemID, setSchema, library] + [itemID, rsData, library] ); const findPredecessor = useCallback((data: ITargetCst, callback: (reference: IConstituentaReference) => void) => { @@ -489,7 +478,7 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { - schema!.versions = schema!.versions.map(prev => { + const newVersions = rsData.schema!.versions.map(prev => { if (prev.id === target) { prev.description = data.description; prev.version = data.version; @@ -498,12 +487,12 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil return prev; } }); - setSchema(schema); + rsData.partialUpdate({ versions: newVersions }); callback?.(); } }); }, - [schema, setSchema] + [rsData] ); const versionDelete = useCallback( @@ -514,13 +503,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { - schema!.versions = schema!.versions.filter(prev => prev.id !== target); - setSchema(schema); + const newVersions = rsData.schema!.versions.filter(prev => prev.id !== target); + rsData.partialUpdate({ versions: newVersions }); callback?.(); } }); }, - [schema, setSchema] + [rsData] ); const versionRestore = useCallback( @@ -531,13 +520,13 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateItem(newData); callback?.(); } }); }, - [setSchema, library] + [rsData, library] ); const inlineSynthesis = useCallback( @@ -549,24 +538,24 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { - setSchema(newData); + rsData.setSchema(newData); library.localUpdateTimestamp(newData.id); oss.invalidateItem(newData.id); callback?.(newData); } }); }, - [setSchema, library, oss] + [rsData, library, oss] ); return ( { + useEffect(() => { if (!template.templateID) { setTemplateSchema(undefined); } else { @@ -73,7 +73,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }: } }, [template.templateID, retrieveTemplate]); - useLayoutEffect(() => { + useEffect(() => { if (!template.prototype) { updateConstituenta({ definition_raw: '', @@ -103,7 +103,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }: } }, [template.prototype, updateConstituenta, updateSubstitutes, schema]); - useLayoutEffect(() => { + useEffect(() => { if (substitutes.arguments.length === 0 || !template.prototype) { return; } @@ -119,7 +119,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }: }); }, [substitutes.arguments, template.prototype, updateConstituenta, updateSubstitutes, schema]); - useLayoutEffect(() => { + useEffect(() => { setValidated(!!template.prototype && validateNewAlias(constituenta.alias, constituenta.cst_type, schema)); }, [constituenta.alias, constituenta.cst_type, schema, template.prototype]); diff --git a/rsconcept/frontend/src/dialogs/DlgCreateCst/FormCreateCst.tsx b/rsconcept/frontend/src/dialogs/DlgCreateCst/FormCreateCst.tsx index d4d063ae..45c7ab0c 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateCst/FormCreateCst.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateCst/FormCreateCst.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import BadgeHelp from '@/components/info/BadgeHelp'; import RSInput from '@/components/RSInput'; @@ -30,11 +30,11 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat const isElementary = useMemo(() => isBaseSet(state.cst_type), [state]); const showConvention = useMemo(() => !!state.convention || forceComment || isBasic, [state, forceComment, isBasic]); - useLayoutEffect(() => { + useEffect(() => { setForceComment(false); }, [state.cst_type, partialUpdate, schema]); - useLayoutEffect(() => { + useEffect(() => { if (setValidated) { setValidated(validateNewAlias(state.alias, state.cst_type, schema)); } diff --git a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx index 6bc6e600..2236173a 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import Modal from '@/components/ui/Modal'; @@ -53,7 +53,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre return true; }, [alias, activeTab, inputs, attachedID, oss.items]); - useLayoutEffect(() => { + useEffect(() => { if (attachedID) { const schema = library.items.find(value => value.id === attachedID); if (schema) { diff --git a/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx b/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx index 83880d32..64b6f65d 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import Modal from '@/components/ui/Modal'; @@ -59,6 +59,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio const cache = useRSFormCache(); const schemas = useMemo( () => schemasIDs.map(id => cache.getSchema(id)).filter(item => item !== undefined), + // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps [schemasIDs, cache.getSchema] ); @@ -86,12 +87,13 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio const canSubmit = useMemo(() => isModified && alias !== '', [isModified, alias]); - useLayoutEffect(() => { + useEffect(() => { cache.preload(schemasIDs); + // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps }, [schemasIDs]); - useLayoutEffect(() => { + useEffect(() => { if (cache.loading || schemas.length !== schemasIDs.length) { return; } @@ -108,10 +110,11 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio return true; }) ); + // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps }, [schemasIDs, schemas, cache.loading]); - useLayoutEffect(() => { + useEffect(() => { if (cache.loading || schemas.length !== schemasIDs.length) { return; } diff --git a/rsconcept/frontend/src/dialogs/DlgEditReference/TabEntityReference.tsx b/rsconcept/frontend/src/dialogs/DlgEditReference/TabEntityReference.tsx index 32129766..46f16c01 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditReference/TabEntityReference.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditReference/TabEntityReference.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useLayoutEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import PickConstituenta from '@/components/select/PickConstituenta'; import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme'; @@ -31,7 +31,7 @@ function TabEntityReference({ initial, schema, onChangeValid, onChangeReference const [selectedGrams, setSelectedGrams] = useState([]); // Initialization - useLayoutEffect(() => { + useEffect(() => { if (!!initial.refRaw && initial.type === ReferenceType.ENTITY) { const ref = parseEntityReference(initial.refRaw); setAlias(ref.entity); diff --git a/rsconcept/frontend/src/dialogs/DlgEditReference/TabSyntacticReference.tsx b/rsconcept/frontend/src/dialogs/DlgEditReference/TabSyntacticReference.tsx index caffc141..a732cb24 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditReference/TabSyntacticReference.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditReference/TabSyntacticReference.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import TextInput from '@/components/ui/TextInput'; import { ReferenceType } from '@/models/language'; @@ -27,7 +27,7 @@ function TabSyntacticReference({ initial, onChangeValid, onChangeReference }: Ta } }, [initial, offset]); - useLayoutEffect(() => { + useEffect(() => { if (initial.refRaw && initial.type === ReferenceType.SYNTACTIC) { const ref = parseSyntacticReference(initial.refRaw); setOffset(ref.offset); diff --git a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx index 6407f6aa..48408bda 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { IconReset, IconSave } from '@/components/Icons'; import MiniButton from '@/components/ui/MiniButton'; @@ -59,7 +59,7 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe setDescription(selected?.description ?? ''); } - useLayoutEffect(() => { + useEffect(() => { setVersion(selected?.version ?? ''); setDescription(selected?.description ?? ''); }, [selected]); diff --git a/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx b/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx index 3dc222b9..35c111ba 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditWordForms/DlgEditWordForms.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useLayoutEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { IconAccept, IconMoveDown, IconMoveLeft, IconMoveRight, IconRemove } from '@/components/Icons'; import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme'; @@ -33,7 +33,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps) const [inputGrams, setInputGrams] = useState([]); const [forms, setForms] = useState([]); - useLayoutEffect(() => { + useEffect(() => { const initForms: IWordForm[] = []; target.term_forms.forEach(term => initForms.push({ diff --git a/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx b/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx index 36b0044b..ad976551 100644 --- a/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx +++ b/rsconcept/frontend/src/dialogs/DlgRenameCst.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useLayoutEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import Modal, { ModalProps } from '@/components/ui/Modal'; import SelectSingle from '@/components/ui/SelectSingle'; @@ -25,13 +25,13 @@ function DlgRenameCst({ hideWindow, initial, allowChangeType, onRename }: DlgRen const [validated, setValidated] = useState(false); const [cstData, updateData] = usePartialUpdate(initial); - useLayoutEffect(() => { + useEffect(() => { if (schema && initial && cstData.cst_type !== initial.cst_type) { updateData({ alias: generateAlias(cstData.cst_type, schema) }); } }, [initial, cstData.cst_type, updateData, schema]); - useLayoutEffect(() => { + useEffect(() => { setValidated( !!schema && cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema) ); diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx b/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx index a6df36fe..b9fc590d 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx +++ b/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useLayoutEffect } from 'react'; +import { useEffect } from 'react'; import { Edge, MarkerType, Node, ReactFlow, useEdgesState, useNodesState } from 'reactflow'; import { SyntaxTree } from '@/models/rslang'; @@ -20,7 +20,7 @@ function ASTFlow({ data, onNodeEnter, onNodeLeave, onChangeDragging }: ASTFlowPr const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges] = useEdgesState([]); - useLayoutEffect(() => { + useEffect(() => { const newNodes = data.map(node => ({ id: String(node.uid), data: node, diff --git a/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/MGraphFlow.tsx b/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/MGraphFlow.tsx index d2904b61..41a1caff 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/MGraphFlow.tsx +++ b/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/MGraphFlow.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useLayoutEffect } from 'react'; +import { useEffect } from 'react'; import { Edge, ReactFlow, useEdgesState, useNodesState } from 'reactflow'; import { TMGraph } from '@/models/TMGraph'; @@ -20,7 +20,7 @@ function MGraphFlow({ data }: MGraphFlowProps) { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges] = useEdgesState([]); - useLayoutEffect(() => { + useEffect(() => { const newNodes = data.nodes.map(node => ({ id: String(node.id), data: node, diff --git a/rsconcept/frontend/src/hooks/useClickedOutside.ts b/rsconcept/frontend/src/hooks/useClickedOutside.ts index 2f3dab8a..b39b5b01 100644 --- a/rsconcept/frontend/src/hooks/useClickedOutside.ts +++ b/rsconcept/frontend/src/hooks/useClickedOutside.ts @@ -4,7 +4,7 @@ import { useEffect } from 'react'; import { assertIsNode } from '@/utils/utils'; -function useClickedOutside(enabled: boolean, ref: React.RefObject, callback?: () => void) { +function useClickedOutside(enabled: boolean, ref: React.RefObject, callback?: () => void) { useEffect(() => { if (!enabled) { return; @@ -12,7 +12,7 @@ function useClickedOutside(enabled: boolean, ref: React.RefObject, function handleClickOutside(event: MouseEvent) { assertIsNode(event.target); - if (ref.current && !ref.current.contains(event.target)) { + if (ref?.current && !ref.current.contains(event.target)) { callback?.(); } } diff --git a/rsconcept/frontend/src/hooks/useOssDetails.ts b/rsconcept/frontend/src/hooks/useOssDetails.ts index afb73b8c..f9d0b71a 100644 --- a/rsconcept/frontend/src/hooks/useOssDetails.ts +++ b/rsconcept/frontend/src/hooks/useOssDetails.ts @@ -5,12 +5,13 @@ import { useCallback, useEffect, useState } from 'react'; import { getOssDetails } from '@/backend/oss'; import { type ErrorData } from '@/components/info/InfoError'; import { useAuth } from '@/context/AuthContext'; -import { ILibraryItem } from '@/models/library'; +import { useLibrary } from '@/context/LibraryContext'; import { IOperationSchema, IOperationSchemaData } from '@/models/oss'; import { OssLoader } from '@/models/OssLoader'; -function useOssDetails({ target, items }: { target?: string; items: ILibraryItem[] }) { +function useOssDetails({ target }: { target?: string }) { const { loading: userLoading } = useAuth(); + const library = useLibrary(); const [schema, setInner] = useState(undefined); const [loading, setLoading] = useState(target != undefined); const [error, setError] = useState(undefined); @@ -21,12 +22,16 @@ function useOssDetails({ target, items }: { target?: string; items: ILibraryItem setInner(undefined); return; } - const newSchema = new OssLoader(data, items).produceOSS(); + const newSchema = new OssLoader(data, library.items).produceOSS(); setInner(newSchema); }, - [items] + [library.items] ); + function partialUpdate(data: Partial) { + setInner(prev => (prev ? { ...prev, ...data } : prev)); + } + const reload = useCallback( (setCustomLoading?: typeof setLoading, callback?: () => void) => { setError(undefined); @@ -50,12 +55,12 @@ function useOssDetails({ target, items }: { target?: string; items: ILibraryItem ); useEffect(() => { - if (!userLoading) { + if (!userLoading && !library.loading && library.items.length > 0) { reload(); } - }, [reload, userLoading]); + }, [reload, userLoading, library.loading, library.items]); - return { schema, setSchema, reload, error, setError, loading }; + return { schema, setSchema, partialUpdate, reload, error, setError, loading }; } export default useOssDetails; diff --git a/rsconcept/frontend/src/hooks/useRSFormCache.ts b/rsconcept/frontend/src/hooks/useRSFormCache.ts index 76df8302..6109d213 100644 --- a/rsconcept/frontend/src/hooks/useRSFormCache.ts +++ b/rsconcept/frontend/src/hooks/useRSFormCache.ts @@ -76,6 +76,7 @@ function useRSFormCache() { } }) ); + // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps }, [pending]); diff --git a/rsconcept/frontend/src/hooks/useRSFormDetails.ts b/rsconcept/frontend/src/hooks/useRSFormDetails.ts index ae036ad2..34308211 100644 --- a/rsconcept/frontend/src/hooks/useRSFormDetails.ts +++ b/rsconcept/frontend/src/hooks/useRSFormDetails.ts @@ -23,6 +23,10 @@ function useRSFormDetails({ target, version }: { target?: string; version?: stri setInnerSchema(schema); } + function partialUpdate(data: Partial) { + setInnerSchema(prev => (prev ? { ...prev, ...data } : prev)); + } + const reload = useCallback( (setCustomLoading?: typeof setLoading, callback?: () => void) => { setError(undefined); @@ -51,7 +55,7 @@ function useRSFormDetails({ target, version }: { target?: string; version?: stri } }, [reload, userLoading]); - return { schema, setSchema, reload, error, setError, loading }; + return { schema, setSchema, partialUpdate, reload, error, setError, loading }; } export default useRSFormDetails; diff --git a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx index c6b8e736..96a2198a 100644 --- a/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx +++ b/rsconcept/frontend/src/pages/CreateItemPage/FormCreateItem.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { toast } from 'react-toastify'; import { urls } from '@/app/urls'; @@ -107,14 +107,14 @@ function FormCreateItem() { setBody(newValue.length > 3 ? newValue.substring(3) : ''); }, []); - useLayoutEffect(() => { + useEffect(() => { if (!options.location) { return; } handleSelectLocation(options.location); }, [options.location, handleSelectLocation]); - useLayoutEffect(() => { + useEffect(() => { if (itemType !== LibraryItemType.RSFORM) { setFile(undefined); setFileName(''); diff --git a/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx b/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx index 5eac0ff4..215ad35f 100644 --- a/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx +++ b/rsconcept/frontend/src/pages/DatabaseSchemaPage.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useLayoutEffect, useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch'; import { useConceptOptions } from '@/context/ConceptOptionsContext'; @@ -11,7 +11,7 @@ function DatabaseSchemaPage() { const panelHeight = useMemo(() => calculateHeight('0px'), [calculateHeight]); - useLayoutEffect(() => { + useEffect(() => { setNoFooter(true); return () => setNoFooter(false); }, [setNoFooter]); diff --git a/rsconcept/frontend/src/pages/HomePage.tsx b/rsconcept/frontend/src/pages/HomePage.tsx index f02778e3..7c380d74 100644 --- a/rsconcept/frontend/src/pages/HomePage.tsx +++ b/rsconcept/frontend/src/pages/HomePage.tsx @@ -1,4 +1,4 @@ -import { useLayoutEffect } from 'react'; +import { useEffect } from 'react'; import { urls } from '@/app/urls'; import Loader from '@/components/ui/Loader'; @@ -10,7 +10,7 @@ function HomePage() { const router = useConceptNavigation(); const { user, loading } = useAuth(); - useLayoutEffect(() => { + useEffect(() => { if (!loading) { if (!user) { setTimeout(() => { diff --git a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx index bd2064bb..1c746908 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx @@ -1,7 +1,7 @@ 'use client'; import fileDownload from 'js-file-download'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { IconCSV } from '@/components/Icons'; @@ -82,7 +82,7 @@ function LibraryPage() { [filter] ); - useLayoutEffect(() => { + useEffect(() => { setItems(library.applyFilter(filter)); }, [library, library.items.length, filter]); diff --git a/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx b/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx index 43b037cf..2443cdf5 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx @@ -30,7 +30,6 @@ function ManualsPage() { setTimeout(() => { router.push(urls.page404); }, PARAMETER.refreshTimeout); - console.log(1); return null; } diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx index 8611988f..c38ce776 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssCard/FormOSS.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useEffect, useLayoutEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { IconSave } from '@/components/Icons'; @@ -25,11 +25,21 @@ function FormOSS({ id, isModified, setIsModified }: FormOSSProps) { const { schema, update, processing } = useOSS(); const controller = useOssEdit(); - const [title, setTitle] = useState(''); - const [alias, setAlias] = useState(''); - const [comment, setComment] = useState(''); - const [visible, setVisible] = useState(false); - const [readOnly, setReadOnly] = useState(false); + const [title, setTitle] = useState(schema?.title ?? ''); + const [alias, setAlias] = useState(schema?.alias ?? ''); + const [comment, setComment] = useState(schema?.comment ?? ''); + const [visible, setVisible] = useState(schema?.visible ?? false); + const [readOnly, setReadOnly] = useState(schema?.read_only ?? false); + + useEffect(() => { + if (schema) { + setTitle(schema.title); + setAlias(schema.alias); + setComment(schema.comment); + setVisible(schema.visible); + setReadOnly(schema.read_only); + } + }, [schema]); useEffect(() => { if (!schema) { @@ -59,16 +69,6 @@ function FormOSS({ id, isModified, setIsModified }: FormOSSProps) { setIsModified ]); - useLayoutEffect(() => { - if (schema) { - setTitle(schema.title); - setAlias(schema.alias); - setComment(schema.comment); - setVisible(schema.visible); - setReadOnly(schema.read_only); - } - }, [schema]); - const handleSubmit = (event?: React.FormEvent) => { if (event) { event.preventDefault(); diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx index 796a4970..d24abbcb 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx @@ -50,7 +50,7 @@ function NodeContextMenu({ }: NodeContextMenuProps) { const controller = useOssEdit(); const [isOpen, setIsOpen] = useState(false); - const ref = useRef(null); + const ref = useRef(null); const readyForSynthesis = useMemo(() => { if (operation.operation_type !== OperationType.SYNTHESIS) { return false; diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx index 5ac17660..449a31c2 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx @@ -1,7 +1,7 @@ 'use client'; import { toPng } from 'html-to-image'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { Background, @@ -69,7 +69,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { onChange: onSelectionChange }); - useLayoutEffect(() => { + useEffect(() => { if (!model.schema) { setNodes([]); setEdges([]); diff --git a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx index 31566448..2ba047a9 100644 --- a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx +++ b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx @@ -1,6 +1,6 @@ 'use client'; -import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react'; +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { urls } from '@/app/urls'; @@ -126,7 +126,7 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit [model, targetOperationID] ); - useLayoutEffect( + useEffect( () => setAccessLevel(prev => { if ( diff --git a/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx b/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx index bba18685..2bf6f385 100644 --- a/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx +++ b/rsconcept/frontend/src/pages/OssPage/OssTabs.tsx @@ -2,7 +2,7 @@ import axios from 'axios'; import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import { toast } from 'react-toastify'; @@ -50,7 +50,7 @@ function OssTabs() { (user.is_staff || user.id == schema.owner || schema.editors.includes(user.id)) ); - useLayoutEffect(() => { + useEffect(() => { if (schema) { const oldTitle = document.title; document.title = schema.title; @@ -60,7 +60,7 @@ function OssTabs() { } }, [schema, schema?.title]); - useLayoutEffect(() => { + useEffect(() => { setNoFooter(activeTab === OssTabID.GRAPH); }, [activeTab, setNoFooter]); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx index 31a3e2e4..41ec1f1f 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { IconChild, IconPredecessor, IconSave } from '@/components/Icons'; @@ -51,10 +51,10 @@ function FormConstituenta({ }: FormConstituentaProps) { const { schema, cstUpdate, processing } = useRSForm(); - const [term, setTerm] = useState(''); - const [textDefinition, setTextDefinition] = useState(''); - const [expression, setExpression] = useState(''); - const [convention, setConvention] = useState(''); + const [term, setTerm] = useState(state?.term_raw ?? ''); + const [textDefinition, setTextDefinition] = useState(state?.definition_raw ?? ''); + const [expression, setExpression] = useState(state?.definition_formal ?? ''); + const [convention, setConvention] = useState(state?.convention ?? ''); const [typification, setTypification] = useState('N/A'); const [showTypification, setShowTypification] = useState(false); const [localParse, setLocalParse] = useState(undefined); @@ -79,6 +79,18 @@ function FormConstituenta({ [state, forceComment, isBasic] ); + useEffect(() => { + if (state) { + setConvention(state.convention); + setTerm(state.term_raw); + setTextDefinition(state.definition_raw); + setExpression(state.definition_formal); + setTypification(state ? labelCstTypification(state) : 'N/A'); + setForceComment(false); + setLocalParse(undefined); + } + }, [state, schema, toggleReset]); + useEffect(() => { if (!state) { setIsModified(false); @@ -104,18 +116,6 @@ function FormConstituenta({ setIsModified ]); - useLayoutEffect(() => { - if (state) { - setConvention(state.convention || ''); - setTerm(state.term_raw || ''); - setTextDefinition(state.definition_raw || ''); - setExpression(state.definition_formal || ''); - setTypification(state ? labelCstTypification(state) : 'N/A'); - setForceComment(false); - setLocalParse(undefined); - } - }, [state, schema, toggleReset]); - function handleSubmit(event?: React.FormEvent) { if (event) { event.preventDefault(); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx index fa24856b..98696836 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx @@ -1,7 +1,7 @@ 'use client'; import { ReactCodeMirrorRef } from '@uiw/react-codemirror'; -import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { toast } from 'react-toastify'; import BadgeHelp from '@/components/info/BadgeHelp'; @@ -69,7 +69,7 @@ function EditorRSExpression({ const [showAST, setShowAST] = useState(false); const [showControls, setShowControls] = useLocalStorage(storage.rseditShowControls, true); - useLayoutEffect(() => { + useEffect(() => { setIsModified(false); resetParse(); }, [activeCst, resetParse, toggleReset]); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx index 8f0cc5db..f2382121 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSFormCard/FormRSForm.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useEffect, useLayoutEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { IconSave } from '@/components/Icons'; import SelectVersion from '@/components/select/SelectVersion'; @@ -25,11 +25,21 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) { const controller = useRSEdit(); const schema = controller.schema; - const [title, setTitle] = useState(''); - const [alias, setAlias] = useState(''); - const [comment, setComment] = useState(''); - const [visible, setVisible] = useState(false); - const [readOnly, setReadOnly] = useState(false); + const [title, setTitle] = useState(schema?.title ?? ''); + const [alias, setAlias] = useState(schema?.alias ?? ''); + const [comment, setComment] = useState(schema?.comment ?? ''); + const [visible, setVisible] = useState(schema?.visible ?? false); + const [readOnly, setReadOnly] = useState(schema?.read_only ?? false); + + useEffect(() => { + if (schema) { + setTitle(schema.title); + setAlias(schema.alias); + setComment(schema.comment); + setVisible(schema.visible); + setReadOnly(schema.read_only); + } + }, [schema]); useEffect(() => { if (!schema) { @@ -59,16 +69,6 @@ function FormRSForm({ id, isModified, setIsModified }: FormRSFormProps) { setIsModified ]); - useLayoutEffect(() => { - if (schema) { - setTitle(schema.title); - setAlias(schema.alias); - setComment(schema.comment); - setVisible(schema.visible); - setReadOnly(schema.read_only); - } - }, [schema]); - const handleSubmit = (event?: React.FormEvent) => { if (event) { event.preventDefault(); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx index a0310239..5d816183 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx @@ -1,7 +1,7 @@ 'use client'; import fileDownload from 'js-file-download'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { IconCSV } from '@/components/Icons'; @@ -32,7 +32,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) { const [filtered, setFiltered] = useState(controller.schema?.items ?? []); const [filterText, setFilterText] = useState(''); - useLayoutEffect(() => { + useEffect(() => { if (filtered.length === 0) { setRowSelection({}); return; @@ -44,7 +44,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) { setRowSelection(newRowSelection); }, [filtered, setRowSelection, controller.selected]); - useLayoutEffect(() => { + useEffect(() => { if (!controller.schema || controller.schema.items.length === 0) { setFiltered([]); } else if (filterText) { diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx index 4a174339..b4b2d921 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import { toPng } from 'html-to-image'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { Edge, @@ -113,7 +113,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { onChange: onSelectionChange }); - useLayoutEffect(() => { + useEffect(() => { if (!controller.schema) { return; } @@ -127,7 +127,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { setHoverID(undefined); }, [controller.schema, filteredGraph]); - useLayoutEffect(() => { + useEffect(() => { if (!controller.schema) { return; } @@ -176,10 +176,11 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { setNodes(newNodes); setEdges(newEdges); // NOTE: Do not rerender on controller.selected change because it is only needed during first load + // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps }, [filteredGraph, setNodes, setEdges, controller.schema, filterParams.noText, focusCst, coloring, colors, flow]); - useLayoutEffect(() => { + useEffect(() => { setTimeout(() => { flow.fitView({ duration: PARAMETER.zoomDuration }); }, PARAMETER.minimalTimeout); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts index fc2462a5..a89c897d 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts @@ -1,4 +1,4 @@ -import { useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { Graph } from '@/models/Graph'; import { GraphFilterParams } from '@/models/miscellaneous'; @@ -20,7 +20,7 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, return result; }, [params]); - useLayoutEffect(() => { + useEffect(() => { if (!schema) { setFiltered(new Graph()); return; diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx index b1272e91..db38cd49 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx @@ -1,7 +1,7 @@ 'use client'; import fileDownload from 'js-file-download'; -import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react'; +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { urls } from '@/app/urls'; @@ -194,7 +194,7 @@ export const RSEditState = ({ [model.schema] ); - useLayoutEffect( + useEffect( () => setAccessLevel(prev => { if ( diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx index 913c4b25..b299a084 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx @@ -2,7 +2,7 @@ import axios from 'axios'; import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import { toast } from 'react-toastify'; @@ -62,7 +62,7 @@ function RSTabs() { } }, [schema, selected]); - useLayoutEffect(() => { + useEffect(() => { if (schema) { const oldTitle = document.title; document.title = schema.title; @@ -72,7 +72,7 @@ function RSTabs() { } }, [schema, schema?.title]); - useLayoutEffect(() => { + useEffect(() => { setNoFooter(activeTab !== RSTabID.CARD); setIsModified(false); if (activeTab === RSTabID.CST_EDIT) { diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx index bb2b471c..ee016a26 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { IconChild } from '@/components/Icons'; import SelectGraphFilter from '@/components/select/SelectGraphFilter'; @@ -28,7 +28,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, setFilt const [filterText, setFilterText] = useState(''); const [showInherited, setShowInherited] = useLocalStorage(storage.cstFilterShowInherited, true); - useLayoutEffect(() => { + useEffect(() => { if (!schema || schema.items.length === 0) { setFiltered([]); return; diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx index 8c5f4788..a7c18848 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useLayoutEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import BadgeConstituenta from '@/components/info/BadgeConstituenta'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; @@ -32,7 +32,7 @@ function TableSideConstituents({ }: TableSideConstituentsProps) { const { colors } = useConceptOptions(); - useLayoutEffect(() => { + useEffect(() => { if (!activeCst) { return; } diff --git a/rsconcept/frontend/src/pages/UserProfilePage/EditorProfile.tsx b/rsconcept/frontend/src/pages/UserProfilePage/EditorProfile.tsx index f1840e80..c701fc31 100644 --- a/rsconcept/frontend/src/pages/UserProfilePage/EditorProfile.tsx +++ b/rsconcept/frontend/src/pages/UserProfilePage/EditorProfile.tsx @@ -1,8 +1,7 @@ 'use client'; import axios from 'axios'; -import clsx from 'clsx'; -import { useLayoutEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import InfoError, { ErrorData } from '@/components/info/InfoError'; @@ -16,10 +15,10 @@ import { information } from '@/utils/labels'; function EditorProfile() { const { updateUser, user, errorProcessing, processing } = useUserProfile(); - const [username, setUsername] = useState(''); - const [email, setEmail] = useState(''); - const [first_name, setFirstName] = useState(''); - const [last_name, setLastName] = useState(''); + const [username, setUsername] = useState(user?.username ?? ''); + const [email, setEmail] = useState(user?.email ?? ''); + const [first_name, setFirstName] = useState(user?.first_name ?? ''); + const [last_name, setLastName] = useState(user?.last_name ?? ''); const isModified: boolean = useMemo(() => { if (!user) { @@ -29,7 +28,7 @@ function EditorProfile() { }, [user, email, first_name, last_name]); useBlockNavigation(isModified); - useLayoutEffect(() => { + useEffect(() => { if (user) { setUsername(user.username); setEmail(user.email); @@ -50,7 +49,7 @@ function EditorProfile() { } return ( -
+ -
-

Учетные данные пользователя

+
+

Учетные данные пользователя