Compare commits
15 Commits
954936599e
...
939652cef0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
939652cef0 | ||
![]() |
160383b394 | ||
![]() |
02c93dfddc | ||
![]() |
7e63103d6b | ||
![]() |
d250a9fda0 | ||
![]() |
e70f7e45b9 | ||
![]() |
c7f155bba1 | ||
![]() |
6f922df227 | ||
![]() |
dc7a0025c9 | ||
![]() |
bf44b14945 | ||
![]() |
9da54d78ab | ||
![]() |
32b41620d8 | ||
![]() |
150e6498af | ||
![]() |
07919cd912 | ||
![]() |
aa4a8b3f94 |
|
@ -56,9 +56,11 @@ 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
|
||||
- babel-plugin-react-compiler
|
||||
- vite
|
||||
- jest
|
||||
- ts-jest
|
||||
|
|
|
@ -14,8 +14,7 @@ Styling conventions
|
|||
- inner layout: px-3 py-2 flex flex-col gap-3 justify-between items-center
|
||||
- overflow behavior: overflow-scroll overscroll-contain
|
||||
- border: borer-2 outline-none shadow-md
|
||||
- colors: clr-controls
|
||||
- text: text-start text-sm font-semibold whitespace-nowrap
|
||||
- text: text-start text-sm font-semibold whitespace-nowrap bg-prim-200 fg-app-100
|
||||
- behavior modifiers: select-none disabled:cursor-auto
|
||||
- transitions:
|
||||
</pre>
|
||||
|
|
|
@ -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',
|
||||
|
|
195
rsconcept/frontend/package-lock.json
generated
195
rsconcept/frontend/package-lock.json
generated
|
@ -41,8 +41,10 @@
|
|||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206",
|
||||
"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 +165,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 +205,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 +282,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 +305,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 +394,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",
|
||||
|
@ -3885,6 +4009,16 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-react-compiler": {
|
||||
"version": "19.0.0-beta-37ed2a7-20241206",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-37ed2a7-20241206.tgz",
|
||||
"integrity": "sha512-nnkrHpeDKM8A5laq9tmFvvGbbDQ7laGfQLp50cvCkCXmWrPcZdCtaQpNh8UJS/yLREJnv2R4JDL5ADfxyAn+yQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-preset-current-node-syntax": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz",
|
||||
|
@ -5153,6 +5287,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 +6177,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 +10505,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",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"dev": "vite --host",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview --port 3000"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dagrejs/dagre": "^1.1.4",
|
||||
|
@ -45,8 +45,10 @@
|
|||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-37ed2a7-20241206",
|
||||
"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",
|
||||
|
|
|
@ -13,7 +13,7 @@ function ApplicationLayout() {
|
|||
const { viewportHeight, mainHeight, showScroll } = useConceptOptions();
|
||||
return (
|
||||
<NavigationState>
|
||||
<div className='min-w-[20rem] clr-app antialiased h-full max-w-[120rem] mx-auto'>
|
||||
<div className='min-w-[20rem] antialiased h-full max-w-[120rem] mx-auto'>
|
||||
<ConceptToaster
|
||||
className='mt-[4rem] text-[14px]' // prettier: split lines
|
||||
autoClose={3000}
|
||||
|
|
|
@ -5,7 +5,7 @@ import Button from '../components/ui/Button';
|
|||
|
||||
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
||||
return (
|
||||
<div className='flex flex-col gap-3 items-center antialiased clr-app' role='alert'>
|
||||
<div className='flex flex-col gap-3 items-center antialiased' role='alert'>
|
||||
<h1>Что-то пошло не так!</h1>
|
||||
<Button onClick={resetErrorBoundary} text='Попробовать еще раз' />
|
||||
<InfoError error={error as Error} />
|
||||
|
|
|
@ -16,17 +16,17 @@ function Footer() {
|
|||
'z-navigation',
|
||||
'mx-auto',
|
||||
'px-3 py-2 flex flex-col items-center gap-1',
|
||||
'text-xs sm:text-sm select-none whitespace-nowrap'
|
||||
'text-xs sm:text-sm select-none whitespace-nowrap text-prim-600 bg-prim-100'
|
||||
)}
|
||||
>
|
||||
<div className='flex gap-3'>
|
||||
<TextURL text='Библиотека' href='/library' color='clr-footer' />
|
||||
<TextURL text='Справка' href='/manuals' color='clr-footer' />
|
||||
<TextURL text='Центр Концепт' href={external_urls.concept} color='clr-footer' />
|
||||
<TextURL text='Экстеор' href='/manuals?topic=exteor' color='clr-footer' />
|
||||
<TextURL text='Библиотека' href='/library' color='' />
|
||||
<TextURL text='Справка' href='/manuals' color='' />
|
||||
<TextURL text='Центр Концепт' href={external_urls.concept} color='' />
|
||||
<TextURL text='Экстеор' href='/manuals?topic=exteor' color='' />
|
||||
</div>
|
||||
<div>
|
||||
<p className='clr-footer'>© 2024 ЦИВТ КОНЦЕПТ</p>
|
||||
<p>© 2024 ЦИВТ КОНЦЕПТ</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
|
|
@ -29,7 +29,6 @@ function Navigation() {
|
|||
className={clsx(
|
||||
'z-navigation', // prettier: split lines
|
||||
'sticky top-0 left-0 right-0',
|
||||
'clr-app',
|
||||
'select-none'
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -31,7 +31,7 @@ function NavigationButton({
|
|||
className={clsx(
|
||||
'mr-1 h-full', // prettier: split lines
|
||||
'flex items-center gap-1',
|
||||
'clr-btn-nav',
|
||||
'clr-btn-nav cc-animate-color',
|
||||
'rounded-xl',
|
||||
'transition duration-500',
|
||||
'font-controls whitespace-nowrap',
|
||||
|
|
|
@ -21,7 +21,7 @@ function ToggleNavigation() {
|
|||
data-tooltip-id={globals.tooltip}
|
||||
data-tooltip-content={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
|
||||
style={{
|
||||
transitionProperty: 'height, width',
|
||||
transitionProperty: 'height, width, background-color',
|
||||
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||
height: noNavigationAnimation ? '1.2rem' : '3rem',
|
||||
width: noNavigationAnimation ? '3rem' : '1.2rem'
|
||||
|
|
|
@ -62,8 +62,8 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
|||
}
|
||||
|
||||
function handleToggleDarkMode() {
|
||||
hideDropdown();
|
||||
toggleDarkMode();
|
||||
hideDropdown();
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -49,9 +49,9 @@ export interface DomIconProps<RequestData> extends IconProps {
|
|||
export function ItemTypeIcon({ value, size = '1.25rem', className }: DomIconProps<LibraryItemType>) {
|
||||
switch (value) {
|
||||
case LibraryItemType.RSFORM:
|
||||
return <IconRSForm size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconRSForm size={size} className={className ?? 'text-sec-600'} />;
|
||||
case LibraryItemType.OSS:
|
||||
return <IconOSS size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconOSS size={size} className={className ?? 'text-ok-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,29 +59,29 @@ export function ItemTypeIcon({ value, size = '1.25rem', className }: DomIconProp
|
|||
export function PolicyIcon({ value, size = '1.25rem', className }: DomIconProps<AccessPolicy>) {
|
||||
switch (value) {
|
||||
case AccessPolicy.PRIVATE:
|
||||
return <IconPrivate size={size} className={className ?? 'clr-text-red'} />;
|
||||
return <IconPrivate size={size} className={className ?? 'text-warn-600'} />;
|
||||
case AccessPolicy.PROTECTED:
|
||||
return <IconProtected size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconProtected size={size} className={className ?? 'text-sec-600'} />;
|
||||
case AccessPolicy.PUBLIC:
|
||||
return <IconPublic size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconPublic size={size} className={className ?? 'text-ok-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
/** Icon for visibility. */
|
||||
export function VisibilityIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
|
||||
if (value) {
|
||||
return <IconShow size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconShow size={size} className={className ?? 'text-ok-600'} />;
|
||||
} else {
|
||||
return <IconHide size={size} className={className ?? 'clr-text-red'} />;
|
||||
return <IconHide size={size} className={className ?? 'text-warn-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
/** Icon for subfolders. */
|
||||
export function SubfoldersIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
|
||||
if (value) {
|
||||
return <IconSubfolders size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconSubfolders size={size} className={className ?? 'text-ok-600'} />;
|
||||
} else {
|
||||
return <IconSubfolders size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconSubfolders size={size} className={className ?? 'text-sec-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,13 +89,13 @@ export function SubfoldersIcon({ value, size = '1.25rem', className }: DomIconPr
|
|||
export function LocationIcon({ value, size = '1.25rem', className }: DomIconProps<string>) {
|
||||
switch (value.substring(0, 2) as LocationHead) {
|
||||
case LocationHead.COMMON:
|
||||
return <IconPublic size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconPublic size={size} className={className ?? 'text-sec-600'} />;
|
||||
case LocationHead.LIBRARY:
|
||||
return <IconTemplates size={size} className={className ?? 'clr-text-red'} />;
|
||||
return <IconTemplates size={size} className={className ?? 'text-warn-600'} />;
|
||||
case LocationHead.PROJECTS:
|
||||
return <IconBusiness size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconBusiness size={size} className={className ?? 'text-sec-600'} />;
|
||||
case LocationHead.USER:
|
||||
return <IconUser size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconUser size={size} className={className ?? 'text-ok-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,13 +105,13 @@ export function DependencyIcon({ value, size = '1.25rem', className }: DomIconPr
|
|||
case DependencyMode.ALL:
|
||||
return <IconSettings size={size} className={className} />;
|
||||
case DependencyMode.OUTPUTS:
|
||||
return <IconGraphOutputs size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconGraphOutputs size={size} className={className ?? 'text-sec-600'} />;
|
||||
case DependencyMode.INPUTS:
|
||||
return <IconGraphInputs size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconGraphInputs size={size} className={className ?? 'text-sec-600'} />;
|
||||
case DependencyMode.EXPAND_OUTPUTS:
|
||||
return <IconGraphExpand size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconGraphExpand size={size} className={className ?? 'text-sec-600'} />;
|
||||
case DependencyMode.EXPAND_INPUTS:
|
||||
return <IconGraphCollapse size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconGraphCollapse size={size} className={className ?? 'text-sec-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,13 +121,13 @@ export function MatchModeIcon({ value, size = '1.25rem', className }: DomIconPro
|
|||
case CstMatchMode.ALL:
|
||||
return <IconFilter size={size} className={className} />;
|
||||
case CstMatchMode.TEXT:
|
||||
return <IconText size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconText size={size} className={className ?? 'text-sec-600'} />;
|
||||
case CstMatchMode.EXPR:
|
||||
return <IconFormula size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconFormula size={size} className={className ?? 'text-sec-600'} />;
|
||||
case CstMatchMode.TERM:
|
||||
return <IconTerm size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconTerm size={size} className={className ?? 'text-sec-600'} />;
|
||||
case CstMatchMode.NAME:
|
||||
return <IconAlias size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconAlias size={size} className={className ?? 'text-sec-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,29 +153,29 @@ export function StatusIcon({ value, size = '1.25rem', className }: DomIconProps<
|
|||
export function CstTypeIcon({ value, size = '1.25rem', className }: DomIconProps<CstType>) {
|
||||
switch (value) {
|
||||
case CstType.BASE:
|
||||
return <IconCstBaseSet size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconCstBaseSet size={size} className={className ?? 'text-ok-600'} />;
|
||||
case CstType.CONSTANT:
|
||||
return <IconCstConstSet size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconCstConstSet size={size} className={className ?? 'text-ok-600'} />;
|
||||
case CstType.STRUCTURED:
|
||||
return <IconCstStructured size={size} className={className ?? 'clr-text-green'} />;
|
||||
return <IconCstStructured size={size} className={className ?? 'text-ok-600'} />;
|
||||
case CstType.TERM:
|
||||
return <IconCstTerm size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconCstTerm size={size} className={className ?? 'text-sec-600'} />;
|
||||
case CstType.AXIOM:
|
||||
return <IconCstAxiom size={size} className={className ?? 'clr-text-red'} />;
|
||||
return <IconCstAxiom size={size} className={className ?? 'text-warn-600'} />;
|
||||
case CstType.FUNCTION:
|
||||
return <IconCstFunction size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconCstFunction size={size} className={className ?? 'text-sec-600'} />;
|
||||
case CstType.PREDICATE:
|
||||
return <IconCstPredicate size={size} className={className ?? 'clr-text-red'} />;
|
||||
return <IconCstPredicate size={size} className={className ?? 'text-warn-600'} />;
|
||||
case CstType.THEOREM:
|
||||
return <IconCstTheorem size={size} className={className ?? 'clr-text-red'} />;
|
||||
return <IconCstTheorem size={size} className={className ?? 'text-warn-600'} />;
|
||||
}
|
||||
}
|
||||
|
||||
/** Icon for relocation direction. */
|
||||
export function RelocateUpIcon({ value, size = '1.25rem', className }: DomIconProps<boolean>) {
|
||||
if (value) {
|
||||
return <IconMoveUp size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconMoveUp size={size} className={className ?? 'text-sec-600'} />;
|
||||
} else {
|
||||
return <IconMoveDown size={size} className={className ?? 'clr-text-primary'} />;
|
||||
return <IconMoveDown size={size} className={className ?? 'text-sec-600'} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,14 @@ import { createTheme } from '@uiw/codemirror-themes';
|
|||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||
import clsx from 'clsx';
|
||||
import { EditorView } from 'codemirror';
|
||||
import { forwardRef, useCallback, useMemo, useRef } from 'react';
|
||||
import { forwardRef, useRef } from 'react';
|
||||
|
||||
import Label from '@/components/ui/Label';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||
import { generateAlias, getCstTypePrefix, guessCstType } from '@/models/rsformAPI';
|
||||
import { extractGlobals } from '@/models/rslangAPI';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
|
||||
import { ccBracketMatching } from './bracketMatching';
|
||||
import { rsNavigation } from './clickNavigation';
|
||||
|
@ -63,50 +64,42 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
},
|
||||
ref
|
||||
) => {
|
||||
const { darkMode, colors } = useConceptOptions();
|
||||
const { darkMode } = useConceptOptions();
|
||||
|
||||
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
||||
const thisRef = useMemo(() => (!ref || typeof ref === 'function' ? internalRef : ref), [internalRef, ref]);
|
||||
const thisRef = !ref || typeof ref === 'function' ? internalRef : ref;
|
||||
|
||||
const cursor = useMemo(() => (!disabled ? 'cursor-text' : 'cursor-default'), [disabled]);
|
||||
const customTheme: Extension = useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
const cursor = !disabled ? 'cursor-text' : 'cursor-default';
|
||||
const customTheme: Extension = createTheme({
|
||||
theme: darkMode ? 'dark' : 'light',
|
||||
settings: {
|
||||
fontFamily: 'inherit',
|
||||
background: !disabled ? colors.bgInput : colors.bgDefault,
|
||||
foreground: colors.fgDefault,
|
||||
selection: colors.bgHover,
|
||||
caret: colors.fgDefault
|
||||
background: !disabled ? APP_COLORS.bgInput : APP_COLORS.bgDefault,
|
||||
foreground: APP_COLORS.fgDefault,
|
||||
selection: APP_COLORS.bgHover,
|
||||
caret: APP_COLORS.fgDefault
|
||||
},
|
||||
styles: [
|
||||
{ tag: tags.name, color: colors.fgPurple, cursor: schema ? 'default' : cursor }, // GlobalID
|
||||
{ tag: tags.variableName, color: colors.fgGreen }, // LocalID
|
||||
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical
|
||||
{ tag: tags.keyword, color: colors.fgBlue }, // keywords
|
||||
{ tag: tags.literal, color: colors.fgBlue }, // literals
|
||||
{ tag: tags.name, color: APP_COLORS.fgPurple, cursor: schema ? 'default' : cursor }, // GlobalID
|
||||
{ tag: tags.variableName, color: APP_COLORS.fgGreen }, // LocalID
|
||||
{ tag: tags.propertyName, color: APP_COLORS.fgTeal }, // Radical
|
||||
{ tag: tags.keyword, color: APP_COLORS.fgBlue }, // keywords
|
||||
{ tag: tags.literal, color: APP_COLORS.fgBlue }, // literals
|
||||
{ tag: tags.controlKeyword, fontWeight: '400' }, // R | I | D
|
||||
{ tag: tags.unit, fontSize: '0.75rem' }, // indices
|
||||
{ tag: tags.brace, color: colors.fgPurple, fontWeight: '600' } // braces (curly brackets)
|
||||
{ tag: tags.brace, color: APP_COLORS.fgPurple, fontWeight: '600' } // braces (curly brackets)
|
||||
]
|
||||
}),
|
||||
[disabled, colors, darkMode, schema, cursor]
|
||||
);
|
||||
});
|
||||
|
||||
const editorExtensions = useMemo(
|
||||
() => [
|
||||
const editorExtensions = [
|
||||
EditorView.lineWrapping,
|
||||
RSLanguage,
|
||||
ccBracketMatching(darkMode),
|
||||
ccBracketMatching(),
|
||||
...(!schema || !onOpenEdit ? [] : [rsNavigation(schema, onOpenEdit)]),
|
||||
...(noTooltip || !schema ? [] : [rsHoverTooltip(schema, onOpenEdit !== undefined)])
|
||||
],
|
||||
[darkMode, schema, noTooltip, onOpenEdit]
|
||||
);
|
||||
];
|
||||
|
||||
const handleInput = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
function handleInput(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (!thisRef.current) {
|
||||
return;
|
||||
}
|
||||
|
@ -159,9 +152,7 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
[thisRef, onAnalyze, schema]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx('flex flex-col gap-2', className, cursor)} style={style}>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { bracketMatching, MatchResult } from '@codemirror/language';
|
||||
import { Decoration, EditorView } from '@codemirror/view';
|
||||
|
||||
import { bracketsDarkT, bracketsLightT } from '@/styling/color';
|
||||
import { BRACKETS_THEME } from '@/styling/color';
|
||||
|
||||
const matchingMark = Decoration.mark({ class: 'cc-matchingBracket' });
|
||||
const nonMatchingMark = Decoration.mark({ class: 'cc-nonmatchingBracket' });
|
||||
|
@ -16,16 +16,14 @@ function bracketRender(match: MatchResult) {
|
|||
return decorations;
|
||||
}
|
||||
|
||||
const darkTheme = EditorView.baseTheme(bracketsDarkT);
|
||||
const theme = EditorView.baseTheme(BRACKETS_THEME);
|
||||
|
||||
const lightTheme = EditorView.baseTheme(bracketsLightT);
|
||||
|
||||
export function ccBracketMatching(darkMode: boolean) {
|
||||
export function ccBracketMatching() {
|
||||
return [
|
||||
bracketMatching({
|
||||
renderMatch: bracketRender,
|
||||
brackets: '{}[]()'
|
||||
}),
|
||||
darkMode ? darkTheme : lightTheme
|
||||
theme
|
||||
];
|
||||
}
|
||||
|
|
|
@ -6,13 +6,14 @@ import { createTheme } from '@uiw/codemirror-themes';
|
|||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||
import clsx from 'clsx';
|
||||
import { EditorView } from 'codemirror';
|
||||
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { forwardRef, useRef, useState } from 'react';
|
||||
|
||||
import Label from '@/components/ui/Label';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import DlgEditReference from '@/dialogs/DlgEditReference';
|
||||
import { ReferenceType } from '@/models/language';
|
||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
import { CodeMirrorWrapper } from '@/utils/codemirror';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
|
@ -91,7 +92,7 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
|||
},
|
||||
ref
|
||||
) => {
|
||||
const { darkMode, colors } = useConceptOptions();
|
||||
const { darkMode } = useConceptOptions();
|
||||
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
||||
|
@ -103,39 +104,32 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
|||
const [mainRefs, setMainRefs] = useState<string[]>([]);
|
||||
|
||||
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
||||
const thisRef = useMemo(() => (!ref || typeof ref === 'function' ? internalRef : ref), [internalRef, ref]);
|
||||
const thisRef = !ref || typeof ref === 'function' ? internalRef : ref;
|
||||
|
||||
const cursor = useMemo(() => (!disabled ? 'cursor-text' : 'cursor-default'), [disabled]);
|
||||
const customTheme: Extension = useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
const cursor = !disabled ? 'cursor-text' : 'cursor-default';
|
||||
const customTheme: Extension = createTheme({
|
||||
theme: darkMode ? 'dark' : 'light',
|
||||
settings: {
|
||||
fontFamily: 'inherit',
|
||||
background: !disabled ? colors.bgInput : colors.bgDefault,
|
||||
foreground: colors.fgDefault,
|
||||
selection: colors.bgHover,
|
||||
caret: colors.fgDefault
|
||||
background: !disabled ? APP_COLORS.bgInput : APP_COLORS.bgDefault,
|
||||
foreground: APP_COLORS.fgDefault,
|
||||
selection: APP_COLORS.bgHover,
|
||||
caret: APP_COLORS.fgDefault
|
||||
},
|
||||
styles: [
|
||||
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // EntityReference
|
||||
{ tag: tags.literal, color: colors.fgTeal, cursor: 'default' }, // SyntacticReference
|
||||
{ tag: tags.comment, color: colors.fgRed } // Error
|
||||
{ tag: tags.name, color: APP_COLORS.fgPurple, cursor: 'default' }, // EntityReference
|
||||
{ tag: tags.literal, color: APP_COLORS.fgTeal, cursor: 'default' }, // SyntacticReference
|
||||
{ tag: tags.comment, color: APP_COLORS.fgRed } // Error
|
||||
]
|
||||
}),
|
||||
[disabled, colors, darkMode]
|
||||
);
|
||||
});
|
||||
|
||||
const editorExtensions = useMemo(
|
||||
() => [
|
||||
const editorExtensions = [
|
||||
EditorView.lineWrapping,
|
||||
EditorView.contentAttributes.of({ spellcheck: 'true' }),
|
||||
NaturalLanguage,
|
||||
...(!schema || !onOpenEdit ? [] : [refsNavigation(schema, onOpenEdit)]),
|
||||
...(schema ? [refsHoverTooltip(schema, colors, onOpenEdit !== undefined)] : [])
|
||||
],
|
||||
[schema, colors, onOpenEdit]
|
||||
);
|
||||
...(schema ? [refsHoverTooltip(schema, onOpenEdit !== undefined)] : [])
|
||||
];
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
if (onChange) onChange(newValue);
|
||||
|
@ -151,8 +145,7 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
|||
if (onBlur) onBlur(event);
|
||||
}
|
||||
|
||||
const handleInput = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
function handleInput(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (!thisRef.current?.view) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
@ -183,26 +176,21 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
|||
|
||||
setShowEditor(true);
|
||||
}
|
||||
},
|
||||
[thisRef]
|
||||
);
|
||||
}
|
||||
|
||||
const handleInputReference = useCallback(
|
||||
(referenceText: string) => {
|
||||
function handleInputReference(referenceText: string) {
|
||||
if (!thisRef.current?.view) {
|
||||
return;
|
||||
}
|
||||
thisRef.current.view.focus();
|
||||
const wrap = new CodeMirrorWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
||||
wrap.replaceWith(referenceText);
|
||||
},
|
||||
[thisRef]
|
||||
);
|
||||
}
|
||||
|
||||
const hideEditReference = useCallback(() => {
|
||||
function hideEditReference() {
|
||||
setShowEditor(false);
|
||||
setTimeout(() => thisRef.current?.view?.focus(), PARAMETER.refreshTimeout);
|
||||
}, [thisRef]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx('flex flex-col gap-2', cursor)}>
|
||||
|
|
|
@ -4,7 +4,6 @@ import { hoverTooltip } from '@codemirror/view';
|
|||
|
||||
import { IEntityReference, ISyntacticReference } from '@/models/language';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { IColorTheme } from '@/styling/color';
|
||||
import {
|
||||
domTooltipEntityReference,
|
||||
domTooltipSyntacticReference,
|
||||
|
@ -14,7 +13,7 @@ import {
|
|||
|
||||
import { RefEntity } from './parse/parser.terms';
|
||||
|
||||
export const tooltipProducer = (schema: IRSForm, colors: IColorTheme, canClick?: boolean) => {
|
||||
export const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||
return hoverTooltip((view, pos) => {
|
||||
const parse = findReferenceAt(pos, view.state);
|
||||
if (!parse) {
|
||||
|
@ -27,7 +26,7 @@ export const tooltipProducer = (schema: IRSForm, colors: IColorTheme, canClick?:
|
|||
pos: parse.start,
|
||||
end: parse.end,
|
||||
above: false,
|
||||
create: () => domTooltipEntityReference(parse.ref as IEntityReference, cst, colors, canClick)
|
||||
create: () => domTooltipEntityReference(parse.ref as IEntityReference, cst, canClick)
|
||||
};
|
||||
} else {
|
||||
let masterText: string | undefined = undefined;
|
||||
|
@ -54,6 +53,6 @@ export const tooltipProducer = (schema: IRSForm, colors: IColorTheme, canClick?:
|
|||
});
|
||||
};
|
||||
|
||||
export function refsHoverTooltip(schema: IRSForm, colors: IColorTheme, canClick?: boolean): Extension {
|
||||
return [tooltipProducer(schema, colors, canClick)];
|
||||
export function refsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension {
|
||||
return [tooltipProducer(schema, canClick)];
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CstClass, IConstituenta } from '@/models/rsform';
|
||||
import { colorFgCstStatus, IColorTheme } from '@/styling/color';
|
||||
import { APP_COLORS, colorFgCstStatus } from '@/styling/color';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import TooltipConstituenta from './TooltipConstituenta';
|
||||
|
@ -12,15 +12,12 @@ interface BadgeConstituentaProps extends CProps.Styling {
|
|||
|
||||
/** Constituenta to display. */
|
||||
value: IConstituenta;
|
||||
|
||||
/** Color theme to use. */
|
||||
theme: IColorTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a badge with a constituenta alias and information tooltip.
|
||||
*/
|
||||
function BadgeConstituenta({ value, prefixID, className, style, theme }: BadgeConstituentaProps) {
|
||||
function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstituentaProps) {
|
||||
return (
|
||||
<div
|
||||
id={`${prefixID}${value.id}`}
|
||||
|
@ -33,9 +30,9 @@ function BadgeConstituenta({ value, prefixID, className, style, theme }: BadgeCo
|
|||
className
|
||||
)}
|
||||
style={{
|
||||
borderColor: colorFgCstStatus(value.status, theme),
|
||||
color: colorFgCstStatus(value.status, theme),
|
||||
backgroundColor: value.cst_class === CstClass.BASIC ? theme.bgGreen25 : theme.bgInput,
|
||||
borderColor: colorFgCstStatus(value.status),
|
||||
color: colorFgCstStatus(value.status),
|
||||
backgroundColor: value.cst_class === CstClass.BASIC ? APP_COLORS.bgGreen25 : APP_COLORS.bgInput,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { GramData } from '@/models/language';
|
||||
import { colorFgGrammeme } from '@/styling/color';
|
||||
import { APP_COLORS, colorFgGrammeme } from '@/styling/color';
|
||||
import { labelGrammeme } from '@/utils/labels';
|
||||
|
||||
interface BadgeGrammemeProps {
|
||||
|
@ -14,7 +13,6 @@ interface BadgeGrammemeProps {
|
|||
* Displays a badge with a grammeme tag.
|
||||
*/
|
||||
function BadgeGrammeme({ grammeme }: BadgeGrammemeProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
|
@ -24,9 +22,9 @@ function BadgeGrammeme({ grammeme }: BadgeGrammemeProps) {
|
|||
'text-sm font-medium text-center whitespace-nowrap'
|
||||
)}
|
||||
style={{
|
||||
borderColor: colorFgGrammeme(grammeme, colors),
|
||||
color: colorFgGrammeme(grammeme, colors),
|
||||
backgroundColor: colors.bgInput
|
||||
borderColor: colorFgGrammeme(grammeme),
|
||||
color: colorFgGrammeme(grammeme),
|
||||
backgroundColor: APP_COLORS.bgInput
|
||||
}}
|
||||
>
|
||||
{labelGrammeme(grammeme)}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { CstClass } from '@/models/rsform';
|
||||
import { colorBgCstClass } from '@/styling/color';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
@ -11,8 +10,6 @@ interface InfoCstClassProps {
|
|||
}
|
||||
|
||||
function InfoCstClass({ header }: InfoCstClassProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-1 mb-2 dense'>
|
||||
{header ? <h1>{header}</h1> : null}
|
||||
|
@ -21,7 +18,7 @@ function InfoCstClass({ header }: InfoCstClassProps) {
|
|||
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||
<span
|
||||
className={clsx('inline-block', 'min-w-[7rem]', 'px-1', 'border', 'text-center text-sm font-controls')}
|
||||
style={{ backgroundColor: colorBgCstClass(cstClass, colors) }}
|
||||
style={{ backgroundColor: colorBgCstClass(cstClass) }}
|
||||
>
|
||||
{labelCstClass(cstClass)}
|
||||
</span>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { ExpressionStatus } from '@/models/rsform';
|
||||
import { colorBgCstStatus } from '@/styling/color';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
@ -11,8 +10,6 @@ interface InfoCstStatusProps {
|
|||
}
|
||||
|
||||
function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-1 mb-2 dense'>
|
||||
{title ? <h1>{title}</h1> : null}
|
||||
|
@ -28,7 +25,7 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
|||
'border',
|
||||
'text-center text-sm font-controls'
|
||||
)}
|
||||
style={{ backgroundColor: colorBgCstStatus(status, colors) }}
|
||||
style={{ backgroundColor: colorBgCstStatus(status) }}
|
||||
>
|
||||
{labelExpressionStatus(status)}
|
||||
</span>
|
||||
|
|
|
@ -63,8 +63,7 @@ function InfoError({ error }: InfoErrorProps) {
|
|||
'cc-fade-in',
|
||||
'min-w-[25rem]',
|
||||
'px-3 py-2 flex flex-col',
|
||||
'clr-text-red',
|
||||
'text-sm font-semibold',
|
||||
'text-warn-600 text-sm font-semibold',
|
||||
'select-text'
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import Tooltip from '@/components/ui/Tooltip';
|
||||
import { OssNodeInternal } from '@/models/miscellaneous';
|
||||
|
@ -19,8 +18,7 @@ interface TooltipOperationProps {
|
|||
const columnHelper = createColumnHelper<ICstSubstituteEx>();
|
||||
|
||||
function TooltipOperation({ node, anchor }: TooltipOperationProps) {
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('substitution_term', {
|
||||
id: 'substitution_term',
|
||||
size: 200
|
||||
|
@ -43,23 +41,7 @@ function TooltipOperation({ node, anchor }: TooltipOperationProps) {
|
|||
id: 'original_term',
|
||||
size: 200
|
||||
})
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const table = useMemo(
|
||||
() => (
|
||||
<DataTable
|
||||
dense
|
||||
noHeader
|
||||
noFooter
|
||||
className='text-sm border select-none mb-2'
|
||||
data={node.data.operation.substitutions}
|
||||
columns={columns}
|
||||
/>
|
||||
),
|
||||
[columns, node]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<Tooltip layer='z-modalTooltip' anchorSelect={anchor} className='max-w-[35rem] max-h-[40rem] dense'>
|
||||
|
@ -90,7 +72,14 @@ function TooltipOperation({ node, anchor }: TooltipOperationProps) {
|
|||
</p>
|
||||
) : null}
|
||||
{node.data.operation.substitutions.length > 0 ? (
|
||||
table
|
||||
<DataTable
|
||||
dense
|
||||
noHeader
|
||||
noFooter
|
||||
className='text-sm border select-none mb-2'
|
||||
data={node.data.operation.substitutions}
|
||||
columns={columns}
|
||||
/>
|
||||
) : node.data.operation.operation_type !== OperationType.INPUT ? (
|
||||
<p>
|
||||
<b>Отождествления:</b> Отсутствуют
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
|
||||
import SearchBar from '@/components/ui/SearchBar';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { CstMatchMode } from '@/models/miscellaneous';
|
||||
import { IConstituenta } from '@/models/rsform';
|
||||
import { matchConstituenta } from '@/models/rsformAPI';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
import { describeConstituenta } from '@/utils/labels';
|
||||
|
||||
|
@ -47,7 +47,6 @@ function PickConstituenta({
|
|||
className,
|
||||
...restProps
|
||||
}: PickConstituentaProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
||||
const [filterText, setFilterText] = useState(initialFilter);
|
||||
|
||||
|
@ -64,33 +63,27 @@ function PickConstituenta({
|
|||
}
|
||||
}, [data, filterText, matchFunc, onBeginFilter]);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
size: 65,
|
||||
minSize: 65,
|
||||
maxSize: 65,
|
||||
cell: props => <BadgeConstituenta theme={colors} value={props.row.original} prefixID={prefixID} />
|
||||
cell: props => <BadgeConstituenta value={props.row.original} prefixID={prefixID} />
|
||||
}),
|
||||
columnHelper.accessor(cst => describeFunc(cst), {
|
||||
id: 'description',
|
||||
size: 1000,
|
||||
minSize: 1000
|
||||
})
|
||||
],
|
||||
[colors, prefixID, describeFunc]
|
||||
);
|
||||
];
|
||||
|
||||
const conditionalRowStyles = useMemo(
|
||||
(): IConditionalStyle<IConstituenta>[] => [
|
||||
const conditionalRowStyles: IConditionalStyle<IConstituenta>[] = [
|
||||
{
|
||||
when: (cst: IConstituenta) => cst.id === value?.id,
|
||||
style: { backgroundColor: colors.bgSelected }
|
||||
style: { backgroundColor: APP_COLORS.bgSelected }
|
||||
}
|
||||
],
|
||||
[value, colors]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={clsx('border divide-y', className)} {...restProps}>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import DataTable, { createColumnHelper, RowSelectionState } from '@/components/ui/DataTable';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { Graph } from '@/models/Graph';
|
||||
import { CstMatchMode } from '@/models/miscellaneous';
|
||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
|
@ -44,12 +43,12 @@ function PickMultiConstituenta({
|
|||
className,
|
||||
...restProps
|
||||
}: PickMultiConstituentaProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
const [filtered, setFiltered] = useState<IConstituenta[]>(data);
|
||||
const [filterText, setFilterText] = useState('');
|
||||
|
||||
const foldedGraph = useMemo(() => {
|
||||
// TODO: extract graph fold logic to separate function
|
||||
const foldedGraph = (() => {
|
||||
if (data.length === schema.items.length) {
|
||||
return schema.graph;
|
||||
}
|
||||
|
@ -66,9 +65,9 @@ function PickMultiConstituenta({
|
|||
newGraph.foldNode(item.id);
|
||||
});
|
||||
return newGraph;
|
||||
}, [data, schema.graph, schema.items]);
|
||||
})();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (filtered.length === 0) {
|
||||
setRowSelection({});
|
||||
return;
|
||||
|
@ -80,7 +79,7 @@ function PickMultiConstituenta({
|
|||
setRowSelection(newRowSelection);
|
||||
}, [filtered, setRowSelection, selected]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (data.length === 0) {
|
||||
setFiltered([]);
|
||||
} else if (filterText) {
|
||||
|
@ -105,22 +104,19 @@ function PickMultiConstituenta({
|
|||
}
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
header: () => <span className='pl-3'>Имя</span>,
|
||||
size: 65,
|
||||
cell: props => <BadgeConstituenta theme={colors} value={props.row.original} prefixID={prefixID} />
|
||||
cell: props => <BadgeConstituenta value={props.row.original} prefixID={prefixID} />
|
||||
}),
|
||||
columnHelper.accessor(cst => describeConstituenta(cst), {
|
||||
id: 'description',
|
||||
size: 1000,
|
||||
header: 'Описание'
|
||||
})
|
||||
],
|
||||
[colors, prefixID]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={clsx(noBorder ? '' : 'border', className)} {...restProps}>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { IconMoveDown, IconMoveUp, IconRemove } from '@/components/Icons';
|
||||
import SelectOperation from '@/components/select/SelectOperation';
|
||||
|
@ -23,31 +23,23 @@ interface PickMultiOperationProps extends CProps.Styling {
|
|||
const columnHelper = createColumnHelper<IOperation>();
|
||||
|
||||
function PickMultiOperation({ rows, items, selected, setSelected, className, ...restProps }: PickMultiOperationProps) {
|
||||
const selectedItems = useMemo(
|
||||
() => selected.map(itemID => items.find(item => item.id === itemID)!),
|
||||
[items, selected]
|
||||
);
|
||||
const nonSelectedItems = useMemo(() => items.filter(item => !selected.includes(item.id)), [items, selected]);
|
||||
const selectedItems = selected.map(itemID => items.find(item => item.id === itemID)!);
|
||||
const nonSelectedItems = items.filter(item => !selected.includes(item.id));
|
||||
const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(operation: OperationID) => setSelected(prev => prev.filter(item => item !== operation)),
|
||||
[setSelected]
|
||||
);
|
||||
function handleDelete(operation: OperationID) {
|
||||
setSelected(prev => prev.filter(item => item !== operation));
|
||||
}
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(operation?: IOperation) => {
|
||||
function handleSelect(operation?: IOperation) {
|
||||
if (operation) {
|
||||
setLastSelected(operation);
|
||||
setSelected(prev => [...prev, operation.id]);
|
||||
setTimeout(() => setLastSelected(undefined), 1000);
|
||||
}
|
||||
},
|
||||
[setSelected]
|
||||
);
|
||||
}
|
||||
|
||||
const handleMoveUp = useCallback(
|
||||
(operation: OperationID) => {
|
||||
function handleMoveUp(operation: OperationID) {
|
||||
const index = selected.indexOf(operation);
|
||||
if (index > 0) {
|
||||
setSelected(prev => {
|
||||
|
@ -57,12 +49,9 @@ function PickMultiOperation({ rows, items, selected, setSelected, className, ...
|
|||
return newSelected;
|
||||
});
|
||||
}
|
||||
},
|
||||
[setSelected, selected]
|
||||
);
|
||||
}
|
||||
|
||||
const handleMoveDown = useCallback(
|
||||
(operation: OperationID) => {
|
||||
function handleMoveDown(operation: OperationID) {
|
||||
const index = selected.indexOf(operation);
|
||||
if (index < selected.length - 1) {
|
||||
setSelected(prev => {
|
||||
|
@ -72,12 +61,9 @@ function PickMultiOperation({ rows, items, selected, setSelected, className, ...
|
|||
return newSelected;
|
||||
});
|
||||
}
|
||||
},
|
||||
[setSelected, selected]
|
||||
);
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
header: 'Шифр',
|
||||
|
@ -122,9 +108,7 @@ function PickMultiOperation({ rows, items, selected, setSelected, className, ...
|
|||
</div>
|
||||
)
|
||||
})
|
||||
],
|
||||
[handleDelete, handleMoveUp, handleMoveDown]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
|
||||
import SearchBar from '@/components/ui/SearchBar';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import useDropdown from '@/hooks/useDropdown';
|
||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||
import { matchLibraryItem } from '@/models/libraryAPI';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
import { IconClose, IconFolderTree } from '../Icons';
|
||||
|
@ -45,20 +45,16 @@ function PickSchema({
|
|||
...restProps
|
||||
}: PickSchemaProps) {
|
||||
const intl = useIntl();
|
||||
const { colors } = useConceptOptions();
|
||||
const { folders } = useLibrary();
|
||||
|
||||
const [filterText, setFilterText] = useState(initialFilter);
|
||||
const [filterLocation, setFilterLocation] = useState('');
|
||||
const [filtered, setFiltered] = useState<ILibraryItem[]>([]);
|
||||
const baseFiltered = useMemo(
|
||||
() => items.filter(item => item.item_type === itemType && (!baseFilter || baseFilter(item))),
|
||||
[items, itemType, baseFilter]
|
||||
);
|
||||
const baseFiltered = items.filter(item => item.item_type === itemType && (!baseFilter || baseFilter(item)));
|
||||
|
||||
const locationMenu = useDropdown();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
let newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText));
|
||||
if (filterLocation.length > 0) {
|
||||
newFiltered = newFiltered.filter(
|
||||
|
@ -68,8 +64,7 @@ function PickSchema({
|
|||
setFiltered(newFiltered);
|
||||
}, [filterText, filterLocation, baseFiltered]);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
header: 'Шифр',
|
||||
|
@ -98,29 +93,21 @@ function PickSchema({
|
|||
</div>
|
||||
)
|
||||
})
|
||||
],
|
||||
[intl]
|
||||
);
|
||||
];
|
||||
|
||||
const conditionalRowStyles = useMemo(
|
||||
(): IConditionalStyle<ILibraryItem>[] => [
|
||||
const conditionalRowStyles: IConditionalStyle<ILibraryItem>[] = [
|
||||
{
|
||||
when: (item: ILibraryItem) => item.id === value,
|
||||
style: { backgroundColor: colors.bgSelected }
|
||||
style: { backgroundColor: APP_COLORS.bgSelected }
|
||||
}
|
||||
],
|
||||
[value, colors]
|
||||
);
|
||||
];
|
||||
|
||||
const handleLocationClick = useCallback(
|
||||
(event: CProps.EventMouse, newValue: string) => {
|
||||
function handleLocationClick(event: CProps.EventMouse, newValue: string) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
locationMenu.hide();
|
||||
setFilterLocation(newValue);
|
||||
},
|
||||
[locationMenu]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx('border divide-y', className)} {...restProps}>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import BadgeConstituenta from '@/components/info/BadgeConstituenta';
|
||||
import SelectConstituenta from '@/components/select/SelectConstituenta';
|
||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { ILibraryItem } from '@/models/library';
|
||||
import { ICstSubstitute, IMultiSubstitution } from '@/models/oss';
|
||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
import { errors } from '@/utils/labels';
|
||||
|
||||
import { IconAccept, IconPageLeft, IconPageRight, IconRemove, IconReplace } from '../Icons';
|
||||
|
@ -46,8 +46,6 @@ function PickSubstitutions({
|
|||
className,
|
||||
...restProps
|
||||
}: PickSubstitutionsProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
const [leftArgument, setLeftArgument] = useState<ILibraryItem | undefined>(
|
||||
schemas.length === 1 ? schemas[0] : undefined
|
||||
);
|
||||
|
@ -62,42 +60,12 @@ function PickSubstitutions({
|
|||
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||
|
||||
const [ignores, setIgnores] = useState<ICstSubstitute[]>([]);
|
||||
const filteredSuggestions = useMemo(
|
||||
() =>
|
||||
const filteredSuggestions =
|
||||
suggestions?.filter(
|
||||
item => !ignores.find(ignore => ignore.original === item.original && ignore.substitution === item.substitution)
|
||||
) ?? [],
|
||||
[ignores, suggestions]
|
||||
);
|
||||
) ?? [];
|
||||
|
||||
const getSchemaByCst = useCallback(
|
||||
(id: ConstituentaID): IRSForm | undefined => {
|
||||
for (const schema of schemas) {
|
||||
const cst = schema.cstByID.get(id);
|
||||
if (cst) {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
[schemas]
|
||||
);
|
||||
|
||||
const getConstituenta = useCallback(
|
||||
(id: ConstituentaID): IConstituenta | undefined => {
|
||||
for (const schema of schemas) {
|
||||
const cst = schema.cstByID.get(id);
|
||||
if (cst) {
|
||||
return cst;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
[schemas]
|
||||
);
|
||||
|
||||
const substitutionData: IMultiSubstitution[] = useMemo(
|
||||
() => [
|
||||
const substitutionData: IMultiSubstitution[] = [
|
||||
...substitutions.map(item => ({
|
||||
original_source: getSchemaByCst(item.original)!,
|
||||
original: getConstituenta(item.original)!,
|
||||
|
@ -112,9 +80,27 @@ function PickSubstitutions({
|
|||
substitution_source: getSchemaByCst(item.substitution)!,
|
||||
is_suggestion: true
|
||||
}))
|
||||
],
|
||||
[getConstituenta, getSchemaByCst, substitutions, filteredSuggestions]
|
||||
);
|
||||
];
|
||||
|
||||
function getSchemaByCst(id: ConstituentaID): IRSForm | undefined {
|
||||
for (const schema of schemas) {
|
||||
const cst = schema.cstByID.get(id);
|
||||
if (cst) {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getConstituenta(id: ConstituentaID): IConstituenta | undefined {
|
||||
for (const schema of schemas) {
|
||||
const cst = schema.cstByID.get(id);
|
||||
if (cst) {
|
||||
return cst;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function addSubstitution() {
|
||||
if (!leftCst || !rightCst) {
|
||||
|
@ -145,22 +131,15 @@ function PickSubstitutions({
|
|||
setRightCst(undefined);
|
||||
}
|
||||
|
||||
const handleDeclineSuggestion = useCallback(
|
||||
(item: IMultiSubstitution) => {
|
||||
function handleDeclineSuggestion(item: IMultiSubstitution) {
|
||||
setIgnores(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]);
|
||||
},
|
||||
[setIgnores]
|
||||
);
|
||||
}
|
||||
|
||||
const handleAcceptSuggestion = useCallback(
|
||||
(item: IMultiSubstitution) => {
|
||||
function handleAcceptSuggestion(item: IMultiSubstitution) {
|
||||
setSubstitutions(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]);
|
||||
},
|
||||
[setSubstitutions]
|
||||
);
|
||||
}
|
||||
|
||||
const handleDeleteSubstitution = useCallback(
|
||||
(target: IMultiSubstitution) => {
|
||||
function handleDeleteSubstitution(target: IMultiSubstitution) {
|
||||
handleDeclineSuggestion(target);
|
||||
setSubstitutions(prev => {
|
||||
const newItems: ICstSubstitute[] = [];
|
||||
|
@ -171,12 +150,9 @@ function PickSubstitutions({
|
|||
});
|
||||
return newItems;
|
||||
});
|
||||
},
|
||||
[setSubstitutions, handleDeclineSuggestion]
|
||||
);
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor(item => item.substitution_source.alias, {
|
||||
id: 'left_schema',
|
||||
size: 100,
|
||||
|
@ -186,11 +162,7 @@ function PickSubstitutions({
|
|||
id: 'left_alias',
|
||||
size: 65,
|
||||
cell: props => (
|
||||
<BadgeConstituenta
|
||||
theme={colors}
|
||||
value={props.row.original.substitution}
|
||||
prefixID={`${prefixID}_${props.row.index}_1_`}
|
||||
/>
|
||||
<BadgeConstituenta value={props.row.original.substitution} prefixID={`${prefixID}_${props.row.index}_1_`} />
|
||||
)
|
||||
}),
|
||||
columnHelper.display({
|
||||
|
@ -202,11 +174,7 @@ function PickSubstitutions({
|
|||
id: 'right_alias',
|
||||
size: 65,
|
||||
cell: props => (
|
||||
<BadgeConstituenta
|
||||
theme={colors}
|
||||
value={props.row.original.original}
|
||||
prefixID={`${prefixID}_${props.row.index}_2_`}
|
||||
/>
|
||||
<BadgeConstituenta value={props.row.original.original} prefixID={`${prefixID}_${props.row.index}_2_`} />
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor(item => item.original_source.alias, {
|
||||
|
@ -244,21 +212,14 @@ function PickSubstitutions({
|
|||
</div>
|
||||
)
|
||||
})
|
||||
],
|
||||
[handleDeleteSubstitution, handleDeclineSuggestion, handleAcceptSuggestion, colors, prefixID]
|
||||
);
|
||||
];
|
||||
|
||||
const conditionalRowStyles = useMemo(
|
||||
(): IConditionalStyle<IMultiSubstitution>[] => [
|
||||
const conditionalRowStyles: IConditionalStyle<IMultiSubstitution>[] = [
|
||||
{
|
||||
when: (item: IMultiSubstitution) => item.is_suggestion,
|
||||
style: {
|
||||
backgroundColor: colors.bgOrange50
|
||||
style: { backgroundColor: APP_COLORS.bgOrange50 }
|
||||
}
|
||||
}
|
||||
],
|
||||
[colors]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={clsx('flex flex-col', className)} {...restProps}>
|
||||
|
@ -286,9 +247,9 @@ function PickSubstitutions({
|
|||
onClick={toggleDelete}
|
||||
icon={
|
||||
deleteRight ? (
|
||||
<IconPageRight size='1.5rem' className='clr-text-primary' />
|
||||
<IconPageRight size='1.5rem' className='text-sec-600' />
|
||||
) : (
|
||||
<IconPageLeft size='1.5rem' className='clr-text-primary' />
|
||||
<IconPageLeft size='1.5rem' className='text-sec-600' />
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { CstMatchMode } from '@/models/miscellaneous';
|
||||
import { ConstituentaID, IConstituenta } from '@/models/rsform';
|
||||
|
@ -28,22 +27,16 @@ function SelectConstituenta({
|
|||
placeholder = 'Выберите конституенту',
|
||||
...restProps
|
||||
}: SelectConstituentaProps) {
|
||||
const options = useMemo(() => {
|
||||
return (
|
||||
const options =
|
||||
items?.map(cst => ({
|
||||
value: cst.id,
|
||||
label: `${cst.alias}${cst.is_inherited ? '*' : ''}: ${describeConstituenta(cst)}`
|
||||
})) ?? []
|
||||
);
|
||||
}, [items]);
|
||||
})) ?? [];
|
||||
|
||||
const filter = useCallback(
|
||||
(option: { value: ConstituentaID | undefined; label: string }, inputValue: string) => {
|
||||
function filter(option: { value: ConstituentaID | undefined; label: string }, inputValue: string) {
|
||||
const cst = items?.find(item => item.id === option.value);
|
||||
return !cst ? false : matchConstituenta(cst, inputValue, CstMatchMode.ALL);
|
||||
},
|
||||
[items]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectSingle
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||
import { matchLibraryItem } from '@/models/libraryAPI';
|
||||
|
@ -26,22 +25,16 @@ function SelectLibraryItem({
|
|||
placeholder = 'Выберите схему',
|
||||
...restProps
|
||||
}: SelectLibraryItemProps) {
|
||||
const options = useMemo(() => {
|
||||
return (
|
||||
const options =
|
||||
items?.map(cst => ({
|
||||
value: cst.id,
|
||||
label: `${cst.alias}: ${cst.title}`
|
||||
})) ?? []
|
||||
);
|
||||
}, [items]);
|
||||
})) ?? [];
|
||||
|
||||
const filter = useCallback(
|
||||
(option: { value: LibraryItemID | undefined; label: string }, inputValue: string) => {
|
||||
function filter(option: { value: LibraryItemID | undefined; label: string }, inputValue: string) {
|
||||
const item = items?.find(item => item.id === option.value);
|
||||
return !item ? false : matchLibraryItem(item, inputValue);
|
||||
},
|
||||
[items]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectSingle
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { FolderNode, FolderTree } from '@/models/FolderTree';
|
||||
import { labelFolderNode } from '@/utils/labels';
|
||||
|
@ -19,17 +19,15 @@ interface SelectLocationProps extends CProps.Styling {
|
|||
}
|
||||
|
||||
function SelectLocation({ value, folderTree, dense, prefix, onClick, className, style }: SelectLocationProps) {
|
||||
const activeNode = useMemo(() => folderTree.at(value), [folderTree, value]);
|
||||
|
||||
const items = useMemo(() => folderTree.getTree(), [folderTree]);
|
||||
const activeNode = folderTree.at(value);
|
||||
const items = folderTree.getTree();
|
||||
const [folded, setFolded] = useState<FolderNode[]>(items);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
setFolded(items.filter(item => item !== activeNode && !activeNode?.hasPredecessor(item)));
|
||||
}, [items, activeNode]);
|
||||
|
||||
const onFoldItem = useCallback(
|
||||
(target: FolderNode, showChildren: boolean) => {
|
||||
function onFoldItem(target: FolderNode, showChildren: boolean) {
|
||||
setFolded(prev =>
|
||||
items.filter(item => {
|
||||
if (item === target) {
|
||||
|
@ -42,18 +40,13 @@ function SelectLocation({ value, folderTree, dense, prefix, onClick, className,
|
|||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
[items]
|
||||
);
|
||||
}
|
||||
|
||||
const handleClickFold = useCallback(
|
||||
(event: CProps.EventMouse, target: FolderNode, showChildren: boolean) => {
|
||||
function handleClickFold(event: CProps.EventMouse, target: FolderNode, showChildren: boolean) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onFoldItem(target, showChildren);
|
||||
},
|
||||
[onFoldItem]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx('flex flex-col', 'cc-scroll-y', className)} style={style}>
|
||||
|
@ -66,7 +59,7 @@ function SelectLocation({ value, folderTree, dense, prefix, onClick, className,
|
|||
!dense && 'min-h-[2.0825rem] sm:min-h-[2.3125rem]',
|
||||
'pr-3 py-1 flex items-center gap-2',
|
||||
'cc-scroll-row',
|
||||
'clr-hover',
|
||||
'clr-hover cc-animate-color',
|
||||
'cursor-pointer',
|
||||
'leading-3 sm:leading-4',
|
||||
activeNode === item && 'clr-selected'
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { IOperation, OperationID } from '@/models/oss';
|
||||
import { matchOperation } from '@/models/ossAPI';
|
||||
|
@ -26,22 +25,16 @@ function SelectOperation({
|
|||
placeholder = 'Выберите операцию',
|
||||
...restProps
|
||||
}: SelectOperationProps) {
|
||||
const options = useMemo(() => {
|
||||
return (
|
||||
const options =
|
||||
items?.map(cst => ({
|
||||
value: cst.id,
|
||||
label: `${cst.alias}: ${cst.title}`
|
||||
})) ?? []
|
||||
);
|
||||
}, [items]);
|
||||
})) ?? [];
|
||||
|
||||
const filter = useCallback(
|
||||
(option: { value: OperationID | undefined; label: string }, inputValue: string) => {
|
||||
function filter(option: { value: OperationID | undefined; label: string }, inputValue: string) {
|
||||
const operation = items?.find(item => item.id === option.value);
|
||||
return !operation ? false : matchOperation(operation, inputValue);
|
||||
},
|
||||
[items]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectSingle
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { useUsers } from '@/context/UsersContext';
|
||||
import { IUserInfo, UserID } from '@/models/user';
|
||||
|
@ -28,22 +27,16 @@ function SelectUser({
|
|||
...restProps
|
||||
}: SelectUserProps) {
|
||||
const { getUserLabel } = useUsers();
|
||||
const options = useMemo(() => {
|
||||
return (
|
||||
const options =
|
||||
items?.map(user => ({
|
||||
value: user.id,
|
||||
label: getUserLabel(user.id)
|
||||
})) ?? []
|
||||
);
|
||||
}, [items, getUserLabel]);
|
||||
})) ?? [];
|
||||
|
||||
const filter = useCallback(
|
||||
(option: { value: UserID | undefined; label: string }, inputValue: string) => {
|
||||
function filter(option: { value: UserID | undefined; label: string }, inputValue: string) {
|
||||
const user = items?.find(item => item.id === option.value);
|
||||
return !user ? false : matchUser(user, inputValue);
|
||||
},
|
||||
[items]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectSingle
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { IVersionInfo, VersionID } from '@/models/library';
|
||||
import { labelVersion } from '@/utils/labels';
|
||||
|
@ -20,8 +19,7 @@ interface SelectVersionProps extends CProps.Styling {
|
|||
}
|
||||
|
||||
function SelectVersion({ id, className, items, value, onSelectValue, ...restProps }: SelectVersionProps) {
|
||||
const options = useMemo(() => {
|
||||
return [
|
||||
const options = [
|
||||
{
|
||||
value: undefined,
|
||||
label: labelVersion(undefined)
|
||||
|
@ -31,11 +29,11 @@ function SelectVersion({ id, className, items, value, onSelectValue, ...restProp
|
|||
label: version.version
|
||||
})) ?? [])
|
||||
];
|
||||
}, [items]);
|
||||
const valueLabel = useMemo(() => {
|
||||
|
||||
const valueLabel = (() => {
|
||||
const version = items?.find(ver => ver.id === value);
|
||||
return version ? version.version : labelVersion(undefined);
|
||||
}, [items, value]);
|
||||
})();
|
||||
|
||||
return (
|
||||
<SelectSingle
|
||||
|
|
|
@ -43,6 +43,7 @@ function Button({
|
|||
className={clsx(
|
||||
'inline-flex gap-2 items-center justify-center',
|
||||
'select-none disabled:cursor-auto',
|
||||
'cc-animate-color',
|
||||
{
|
||||
'border rounded': !noBorder,
|
||||
'px-1': dense,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
|
||||
|
@ -34,15 +33,7 @@ function Checkbox({
|
|||
setValue,
|
||||
...restProps
|
||||
}: CheckboxProps) {
|
||||
const cursor = useMemo(() => {
|
||||
if (disabled) {
|
||||
return 'cursor-arrow';
|
||||
} else if (setValue) {
|
||||
return 'cursor-pointer';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}, [disabled, setValue]);
|
||||
const cursor = disabled ? 'cursor-arrow' : setValue ? 'cursor-pointer' : '';
|
||||
|
||||
function handleClick(event: CProps.EventMouse): void {
|
||||
event.preventDefault();
|
||||
|
@ -75,9 +66,10 @@ function Checkbox({
|
|||
className={clsx(
|
||||
'max-w-[1rem] min-w-[1rem] h-4', // prettier: split lines
|
||||
'border rounded-sm ',
|
||||
'cc-animate-color',
|
||||
{
|
||||
'clr-primary': value !== false,
|
||||
'clr-app': value === false
|
||||
'bg-sec-600 text-sec-0': value !== false,
|
||||
'bg-prim-100': value === false
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
|
||||
|
@ -29,15 +28,7 @@ function CheckboxTristate({
|
|||
setValue,
|
||||
...restProps
|
||||
}: CheckboxTristateProps) {
|
||||
const cursor = useMemo(() => {
|
||||
if (disabled) {
|
||||
return 'cursor-arrow';
|
||||
} else if (setValue) {
|
||||
return 'cursor-pointer';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}, [disabled, setValue]);
|
||||
const cursor = disabled ? 'cursor-arrow' : setValue ? 'cursor-pointer' : '';
|
||||
|
||||
function handleClick(event: CProps.EventMouse): void {
|
||||
event.preventDefault();
|
||||
|
@ -76,9 +67,10 @@ function CheckboxTristate({
|
|||
className={clsx(
|
||||
'w-4 h-4', // prettier: split lines
|
||||
'border rounded-sm',
|
||||
'cc-animate-color',
|
||||
{
|
||||
'clr-primary': value !== false,
|
||||
'clr-app': value === false
|
||||
'bg-sec-600 text-sec-0': value !== false,
|
||||
'bg-prim-100': value === false
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
'use client';
|
||||
'use no memo';
|
||||
|
||||
import {
|
||||
ColumnSort,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
'use client';
|
||||
'use no memo';
|
||||
|
||||
import { Table } from '@tanstack/react-table';
|
||||
import clsx from 'clsx';
|
||||
|
@ -46,7 +47,7 @@ function PaginationTools<TData>({
|
|||
<div className='flex'>
|
||||
<button
|
||||
type='button'
|
||||
className='clr-hover clr-text-controls'
|
||||
className='clr-hover clr-text-controls cc-animate-color'
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
|
@ -54,7 +55,7 @@ function PaginationTools<TData>({
|
|||
</button>
|
||||
<button
|
||||
type='button'
|
||||
className='clr-hover clr-text-controls'
|
||||
className='clr-hover clr-text-controls cc-animate-color'
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
|
@ -63,7 +64,7 @@ function PaginationTools<TData>({
|
|||
<input
|
||||
id={id ? `${id}__page` : undefined}
|
||||
title='Номер страницы. Выделите для ручного ввода'
|
||||
className='w-6 text-center clr-app'
|
||||
className='w-6 text-center bg-prim-100'
|
||||
value={table.getState().pagination.pageIndex + 1}
|
||||
onChange={event => {
|
||||
const page = event.target.value ? Number(event.target.value) - 1 : 0;
|
||||
|
@ -74,7 +75,7 @@ function PaginationTools<TData>({
|
|||
/>
|
||||
<button
|
||||
type='button'
|
||||
className='clr-hover clr-text-controls'
|
||||
className='clr-hover clr-text-controls cc-animate-color'
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
|
@ -82,7 +83,7 @@ function PaginationTools<TData>({
|
|||
</button>
|
||||
<button
|
||||
type='button'
|
||||
className='clr-hover clr-text-controls'
|
||||
className='clr-hover clr-text-controls cc-animate-color'
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
|
@ -93,7 +94,7 @@ function PaginationTools<TData>({
|
|||
id={id ? `${id}__per_page` : undefined}
|
||||
value={table.getState().pagination.pageSize}
|
||||
onChange={handlePaginationOptionsChange}
|
||||
className='mx-2 cursor-pointer clr-app'
|
||||
className='mx-2 cursor-pointer bg-prim-100'
|
||||
>
|
||||
{paginationOptions.map(pageSize => (
|
||||
<option key={`${prefixes.page_size}${pageSize}`} value={pageSize}>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use no memo';
|
||||
|
||||
import { Table } from '@tanstack/react-table';
|
||||
|
||||
import CheckboxTristate from '@/components/ui/CheckboxTristate';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use no memo';
|
||||
|
||||
import { Row } from '@tanstack/react-table';
|
||||
|
||||
import Checkbox from '@/components/ui/Checkbox';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use no memo';
|
||||
|
||||
import { Column } from '@tanstack/react-table';
|
||||
|
||||
import { IconSortAsc, IconSortDesc } from '@/components/Icons';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use no memo';
|
||||
|
||||
import { Cell, flexRender, Row, Table } from '@tanstack/react-table';
|
||||
import clsx from 'clsx';
|
||||
|
||||
|
@ -70,12 +72,9 @@ function TableBody<TData>({
|
|||
key={row.id}
|
||||
className={clsx(
|
||||
'cc-scroll-row',
|
||||
'clr-hover cc-animate-color',
|
||||
!noHeader && 'scroll-mt-[calc(2px+2rem)]',
|
||||
row.getIsSelected()
|
||||
? 'clr-selected clr-hover'
|
||||
: index % 2 === 0
|
||||
? 'clr-controls clr-hover'
|
||||
: 'clr-app clr-hover'
|
||||
row.getIsSelected() ? 'clr-selected' : index % 2 === 0 ? 'bg-prim-200' : 'bg-prim-100'
|
||||
)}
|
||||
style={{ ...(conditionalRowStyles ? getRowStyles(row) : []) }}
|
||||
>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use no memo';
|
||||
|
||||
import { flexRender, Header, HeaderGroup, Table } from '@tanstack/react-table';
|
||||
|
||||
interface TableFooterProps<TData> {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use no memo';
|
||||
|
||||
import { flexRender, Header, HeaderGroup, Table } from '@tanstack/react-table';
|
||||
|
||||
import SelectAll from './SelectAll';
|
||||
|
@ -20,7 +22,7 @@ function TableHeader<TData>({
|
|||
}: TableHeaderProps<TData>) {
|
||||
return (
|
||||
<thead
|
||||
className='clr-app cc-shadow-border'
|
||||
className='bg-prim-100 cc-shadow-border'
|
||||
style={{
|
||||
top: headPosition,
|
||||
position: 'sticky'
|
||||
|
|
|
@ -39,6 +39,7 @@ function DropdownButton({
|
|||
'px-3 py-1 inline-flex items-center gap-2',
|
||||
'text-left text-sm overflow-ellipsis whitespace-nowrap',
|
||||
'disabled:clr-text-controls',
|
||||
'cc-animate-color',
|
||||
{
|
||||
'clr-hover': onClick,
|
||||
'cursor-pointer disabled:cursor-auto': onClick,
|
||||
|
|
|
@ -9,7 +9,7 @@ function DropdownCheckbox({ setValue, disabled, ...restProps }: CheckboxProps) {
|
|||
className={clsx(
|
||||
'px-3 py-1',
|
||||
'text-left overflow-ellipsis whitespace-nowrap',
|
||||
'disabled:clr-text-controls',
|
||||
'disabled:clr-text-controls cc-animate-color',
|
||||
!!setValue && !disabled && 'clr-hover'
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -19,7 +19,7 @@ function Indicator({ icon, title, titleHtml, hideTitle, noPadding, className, ..
|
|||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'clr-btn-clear',
|
||||
'clr-text-controls',
|
||||
'outline-none',
|
||||
{
|
||||
'px-1 py-1': !noPadding
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
|
||||
interface LoaderProps {
|
||||
/** Scale of the loader from 1 to 10. */
|
||||
|
@ -55,11 +55,10 @@ const animatePulse = (startBig: boolean, duration: string) => {
|
|||
* Displays animated loader.
|
||||
*/
|
||||
function Loader({ scale = 5, circular }: LoaderProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
if (circular) {
|
||||
return (
|
||||
<div className='flex justify-center' aria-label='three-circles-loading' aria-busy='true' role='progressbar'>
|
||||
<svg height={`${scale * 20}`} width={`${scale * 20}`} viewBox='0 0 100 100' fill={colors.bgPrimary}>
|
||||
<svg height={`${scale * 20}`} width={`${scale * 20}`} viewBox='0 0 100 100' fill={APP_COLORS.bgPrimary}>
|
||||
<path d='M31.6,3.5C5.9,13.6-6.6,42.7,3.5,68.4c10.1,25.7,39.2,38.3,64.9,28.1l-3.1-7.9c-21.3,8.4-45.4-2-53.8-23.3 c-8.4-21.3,2-45.4,23.3-53.8L31.6,3.5z'>
|
||||
{animateRotation('2.25s')}
|
||||
</path>
|
||||
|
@ -75,7 +74,7 @@ function Loader({ scale = 5, circular }: LoaderProps) {
|
|||
} else {
|
||||
return (
|
||||
<div className='flex justify-center' aria-busy='true' role='progressbar'>
|
||||
<svg height={`${scale * 20}`} width={`${scale * 20}`} viewBox='0 0 120 30' fill={colors.bgPrimary}>
|
||||
<svg height={`${scale * 20}`} width={`${scale * 20}`} viewBox='0 0 120 30' fill={APP_COLORS.bgPrimary}>
|
||||
<circle cx='15' cy='15' r='16'>
|
||||
{animatePulse(true, '0.8s')}
|
||||
</circle>
|
||||
|
|
|
@ -35,7 +35,7 @@ function MiniButton({
|
|||
tabIndex={tabIndex ?? -1}
|
||||
className={clsx(
|
||||
'rounded-lg',
|
||||
'clr-btn-clear',
|
||||
'clr-text-controls cc-animate-color',
|
||||
'cursor-pointer disabled:cursor-auto',
|
||||
{
|
||||
'px-1 py-1': !noPadding,
|
||||
|
|
|
@ -93,16 +93,16 @@ function Modal({
|
|||
|
||||
return (
|
||||
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
|
||||
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'cc-modal-blur')} />
|
||||
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'backdrop-blur-[3px] opacity-50')} />
|
||||
<div
|
||||
className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'cc-modal-backdrop')}
|
||||
className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')}
|
||||
onClick={hideWindow}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'cc-animate-modal',
|
||||
'z-modal absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
|
||||
'border rounded-xl clr-app'
|
||||
'border rounded-xl bg-prim-100'
|
||||
)}
|
||||
>
|
||||
<Overlay position='right-2 top-2'>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
|
||||
|
@ -29,10 +27,8 @@ function PDFViewer({ file, offsetXpx, minWidth = MINIMUM_WIDTH }: PDFViewerProps
|
|||
const windowSize = useWindowSize();
|
||||
const { calculateHeight } = useConceptOptions();
|
||||
|
||||
const pageWidth = useMemo(() => {
|
||||
return Math.max(minWidth, Math.min((windowSize?.width ?? 0) - (offsetXpx ?? 0) - 10, MAXIMUM_WIDTH));
|
||||
}, [windowSize, offsetXpx, minWidth]);
|
||||
const pageHeight = useMemo(() => calculateHeight('1rem'), [calculateHeight]);
|
||||
const pageWidth = Math.max(minWidth, Math.min((windowSize?.width ?? 0) - (offsetXpx ?? 0) - 10, MAXIMUM_WIDTH));
|
||||
const pageHeight = calculateHeight('1rem');
|
||||
|
||||
return <embed src={`${file}#toolbar=0`} className='p-3' style={{ width: pageWidth, height: pageHeight }} />;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import Select, {
|
||||
ClearIndicatorProps,
|
||||
components,
|
||||
|
@ -10,9 +9,8 @@ import Select, {
|
|||
StylesConfig
|
||||
} from 'react-select';
|
||||
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { selectDarkT, selectLightT } from '@/styling/color';
|
||||
import { APP_COLORS, SELECT_THEME } from '@/styling/color';
|
||||
|
||||
import { IconClose, IconDropArrow, IconDropArrowUp } from '../Icons';
|
||||
|
||||
|
@ -52,12 +50,9 @@ function SelectMulti<Option, Group extends GroupBase<Option> = GroupBase<Option>
|
|||
noPortal,
|
||||
...restProps
|
||||
}: SelectMultiProps<Option, Group>) {
|
||||
const { darkMode, colors } = useConceptOptions();
|
||||
const size = useWindowSize();
|
||||
const themeColors = useMemo(() => (!darkMode ? selectLightT : selectDarkT), [darkMode]);
|
||||
|
||||
const adjustedStyles: StylesConfig<Option, true, Group> = useMemo(
|
||||
() => ({
|
||||
const adjustedStyles: StylesConfig<Option, true, Group> = {
|
||||
container: defaultStyles => ({
|
||||
...defaultStyles,
|
||||
borderRadius: '0.25rem'
|
||||
|
@ -73,10 +68,10 @@ function SelectMulti<Option, Group extends GroupBase<Option> = GroupBase<Option>
|
|||
padding: '0.25rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '1.25rem',
|
||||
backgroundColor: isSelected ? colors.bgSelected : styles.backgroundColor,
|
||||
color: isSelected ? colors.fgSelected : styles.color,
|
||||
backgroundColor: isSelected ? APP_COLORS.bgSelected : styles.backgroundColor,
|
||||
color: isSelected ? APP_COLORS.fgSelected : styles.color,
|
||||
borderWidth: '1px',
|
||||
borderColor: colors.border
|
||||
borderColor: APP_COLORS.border
|
||||
}),
|
||||
menuPortal: styles => ({
|
||||
...styles,
|
||||
|
@ -91,7 +86,7 @@ function SelectMulti<Option, Group extends GroupBase<Option> = GroupBase<Option>
|
|||
multiValue: styles => ({
|
||||
...styles,
|
||||
borderRadius: '0.5rem',
|
||||
backgroundColor: colors.bgSelected
|
||||
backgroundColor: APP_COLORS.bgSelected
|
||||
}),
|
||||
dropdownIndicator: base => ({
|
||||
...base,
|
||||
|
@ -103,9 +98,7 @@ function SelectMulti<Option, Group extends GroupBase<Option> = GroupBase<Option>
|
|||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
})
|
||||
}),
|
||||
[colors]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
|
@ -123,7 +116,7 @@ function SelectMulti<Option, Group extends GroupBase<Option> = GroupBase<Option>
|
|||
},
|
||||
colors: {
|
||||
...theme.colors,
|
||||
...themeColors
|
||||
...SELECT_THEME
|
||||
}
|
||||
})}
|
||||
menuPortalTarget={!noPortal ? document.body : null}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import Select, {
|
||||
ClearIndicatorProps,
|
||||
components,
|
||||
|
@ -10,9 +9,8 @@ import Select, {
|
|||
StylesConfig
|
||||
} from 'react-select';
|
||||
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { selectDarkT, selectLightT } from '@/styling/color';
|
||||
import { APP_COLORS, SELECT_THEME } from '@/styling/color';
|
||||
|
||||
import { IconClose, IconDropArrow, IconDropArrowUp } from '../Icons';
|
||||
|
||||
|
@ -54,12 +52,9 @@ function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option
|
|||
noBorder,
|
||||
...restProps
|
||||
}: SelectSingleProps<Option, Group>) {
|
||||
const { darkMode, colors } = useConceptOptions();
|
||||
const size = useWindowSize();
|
||||
const themeColors = useMemo(() => (!darkMode ? selectLightT : selectDarkT), [darkMode]);
|
||||
|
||||
const adjustedStyles: StylesConfig<Option, false, Group> = useMemo(
|
||||
() => ({
|
||||
const adjustedStyles: StylesConfig<Option, false, Group> = {
|
||||
container: defaultStyles => ({
|
||||
...defaultStyles,
|
||||
borderRadius: '0.25rem'
|
||||
|
@ -84,10 +79,10 @@ function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option
|
|||
padding: '0.25rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '1.25rem',
|
||||
backgroundColor: isSelected ? colors.bgSelected : defaultStyles.backgroundColor,
|
||||
color: isSelected ? colors.fgSelected : defaultStyles.color,
|
||||
backgroundColor: isSelected ? APP_COLORS.bgSelected : defaultStyles.backgroundColor,
|
||||
color: isSelected ? APP_COLORS.fgSelected : defaultStyles.color,
|
||||
borderWidth: '1px',
|
||||
borderColor: colors.border
|
||||
borderColor: APP_COLORS.border
|
||||
}),
|
||||
input: defaultStyles => ({ ...defaultStyles }),
|
||||
placeholder: defaultStyles => ({ ...defaultStyles }),
|
||||
|
@ -102,9 +97,7 @@ function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option
|
|||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
})
|
||||
}),
|
||||
[colors, noBorder]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
|
@ -121,7 +114,7 @@ function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option
|
|||
},
|
||||
colors: {
|
||||
...theme.colors,
|
||||
...themeColors
|
||||
...SELECT_THEME
|
||||
}
|
||||
})}
|
||||
menuPortalTarget={!noPortal ? document.body : null}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { globals, PARAMETER } from '@/utils/constants';
|
||||
|
||||
|
@ -44,18 +44,14 @@ function SelectTree<ItemType>({
|
|||
prefix,
|
||||
...restProps
|
||||
}: SelectTreeProps<ItemType>) {
|
||||
const foldable = useMemo(
|
||||
() => new Set(items.filter(item => getParent(item) !== item).map(item => getParent(item))),
|
||||
[items, getParent]
|
||||
);
|
||||
const foldable = new Set(items.filter(item => getParent(item) !== item).map(item => getParent(item)));
|
||||
const [folded, setFolded] = useState<ItemType[]>(items);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
setFolded(items.filter(item => getParent(value) !== item && getParent(getParent(value)) !== item));
|
||||
}, [value, getParent, items]);
|
||||
|
||||
const onFoldItem = useCallback(
|
||||
(target: ItemType, showChildren: boolean) => {
|
||||
function onFoldItem(target: ItemType, showChildren: boolean) {
|
||||
setFolded(prev =>
|
||||
items.filter(item => {
|
||||
if (item === target) {
|
||||
|
@ -68,27 +64,19 @@ function SelectTree<ItemType>({
|
|||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
[items, getParent]
|
||||
);
|
||||
}
|
||||
|
||||
const handleClickFold = useCallback(
|
||||
(event: CProps.EventMouse, target: ItemType, showChildren: boolean) => {
|
||||
function handleClickFold(event: CProps.EventMouse, target: ItemType, showChildren: boolean) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onFoldItem(target, showChildren);
|
||||
},
|
||||
[onFoldItem]
|
||||
);
|
||||
}
|
||||
|
||||
const handleSetValue = useCallback(
|
||||
(event: CProps.EventMouse, target: ItemType) => {
|
||||
function handleSetValue(event: CProps.EventMouse, target: ItemType) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onChangeValue(target);
|
||||
},
|
||||
[onChangeValue]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...restProps}>
|
||||
|
@ -101,7 +89,7 @@ function SelectTree<ItemType>({
|
|||
className={clsx(
|
||||
'pr-3 pl-6 border-b',
|
||||
'cc-scroll-row',
|
||||
'clr-controls clr-hover',
|
||||
'bg-prim-200 clr-hover cc-animate-color',
|
||||
'cursor-pointer',
|
||||
value === item && 'clr-selected'
|
||||
)}
|
||||
|
|
|
@ -41,6 +41,7 @@ function SelectorButton({
|
|||
'text-sm font-controls select-none',
|
||||
'text-btn clr-text-controls',
|
||||
'disabled:cursor-auto cursor-pointer',
|
||||
'cc-animate-color',
|
||||
{
|
||||
'clr-hover': transparent,
|
||||
'border': !transparent
|
||||
|
|
|
@ -24,7 +24,7 @@ function SubmitButton({ text = 'ОК', icon, disabled, loading, className, ...re
|
|||
'px-3 py-1 flex gap-2 items-center justify-center',
|
||||
'border',
|
||||
'font-medium',
|
||||
'clr-btn-primary',
|
||||
'clr-btn-primary cc-animate-color',
|
||||
'select-none disabled:cursor-auto',
|
||||
loading && 'cursor-progress',
|
||||
className
|
||||
|
|
|
@ -20,7 +20,7 @@ function TabLabel({ label, title, titleHtml, hideTitle, className, ...otherProps
|
|||
className={clsx(
|
||||
'min-w-[5.5rem] h-full',
|
||||
'px-2 py-1 flex justify-center',
|
||||
'clr-tab',
|
||||
'clr-hover bg-prim-200 cc-animate-color',
|
||||
'text-sm whitespace-nowrap font-controls',
|
||||
'select-none hover:cursor-pointer',
|
||||
'outline-none',
|
||||
|
|
|
@ -20,7 +20,7 @@ interface TextURLProps {
|
|||
/**
|
||||
* Displays a text with a clickable link.
|
||||
*/
|
||||
function TextURL({ text, href, title, color = 'clr-text-url', onClick }: TextURLProps) {
|
||||
function TextURL({ text, href, title, color = 'text-sec-600', onClick }: TextURLProps) {
|
||||
const design = `cursor-pointer hover:underline ${color}`;
|
||||
if (href) {
|
||||
return (
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
|
||||
|
@ -50,7 +49,8 @@ function ValueIcon({
|
|||
onClick,
|
||||
...restProps
|
||||
}: ValueIconProps) {
|
||||
const isSmall = useMemo(() => !smallThreshold || String(value).length < smallThreshold, [value, smallThreshold]);
|
||||
// TODO: use CSS instead of threshold
|
||||
const isSmall = !smallThreshold || String(value).length < smallThreshold;
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
|
|
|
@ -22,7 +22,7 @@ function ExpectedAnonymous() {
|
|||
<span> | </span>
|
||||
<TextURL text='Справка' href='/manuals' />
|
||||
<span> | </span>
|
||||
<span className='cursor-pointer hover:underline clr-text-url' onClick={logoutAndRedirect}>
|
||||
<span className='cursor-pointer hover:underline text-sec-600' onClick={logoutAndRedirect}>
|
||||
Выйти
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -21,5 +21,5 @@ export const useAccessMode = () => {
|
|||
|
||||
export const AccessModeState = ({ children }: React.PropsWithChildren) => {
|
||||
const [accessLevel, setAccessLevel] = useState<UserLevel>(UserLevel.READER);
|
||||
return <AccessContext.Provider value={{ accessLevel, setAccessLevel }}>{children}</AccessContext.Provider>;
|
||||
return <AccessContext value={{ accessLevel, setAccessLevel }}>{children}</AccessContext>;
|
||||
};
|
||||
|
|
|
@ -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,12 +181,12 @@ export const AuthState = ({ children }: React.PropsWithChildren) => {
|
|||
[reload]
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, [reload]);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
<AuthContext
|
||||
value={{
|
||||
user,
|
||||
login,
|
||||
|
@ -205,6 +202,6 @@ export const AuthState = ({ children }: React.PropsWithChildren) => {
|
|||
}}
|
||||
>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
</AuthContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
'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';
|
||||
import { darkT, IColorTheme, lightT } from '@/styling/color';
|
||||
import { globals, PARAMETER, storage } from '@/utils/constants';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
|
@ -12,8 +11,6 @@ interface IOptionsContext {
|
|||
viewportHeight: string;
|
||||
mainHeight: string;
|
||||
|
||||
colors: IColorTheme;
|
||||
|
||||
darkMode: boolean;
|
||||
toggleDarkMode: () => void;
|
||||
|
||||
|
@ -60,8 +57,6 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
|||
const [folderMode, setFolderMode] = useLocalStorage<boolean>(storage.librarySearchFolderMode, true);
|
||||
const [location, setLocation] = useLocalStorage<string>(storage.librarySearchLocation, '');
|
||||
|
||||
const [colors, setColors] = useState<IColorTheme>(lightT);
|
||||
|
||||
const [noNavigationAnimation, setNoNavigationAnimation] = useState(false);
|
||||
const [noFooter, setNoFooter] = useState(false);
|
||||
const [showScroll, setShowScroll] = useState(false);
|
||||
|
@ -76,14 +71,10 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
|||
root.setAttribute('data-color-scheme', !isDark ? 'light' : 'dark');
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
setDarkClass(darkMode);
|
||||
}, [darkMode]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setColors(darkMode ? darkT : lightT);
|
||||
}, [darkMode, setColors]);
|
||||
|
||||
const toggleNoNavigation = useCallback(() => {
|
||||
if (noNavigation) {
|
||||
setNoNavigationAnimation(false);
|
||||
|
@ -109,7 +100,6 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
|||
|
||||
const toggleDarkMode = useCallback(() => {
|
||||
setDarkMode(prev => !prev);
|
||||
window.location.reload();
|
||||
}, [setDarkMode]);
|
||||
|
||||
const mainHeight = useMemo(() => {
|
||||
|
@ -127,11 +117,10 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
|||
}, [noNavigation]);
|
||||
|
||||
return (
|
||||
<OptionsContext.Provider
|
||||
<OptionsContext
|
||||
value={{
|
||||
darkMode,
|
||||
adminMode,
|
||||
colors,
|
||||
noNavigationAnimation,
|
||||
noNavigation,
|
||||
noFooter,
|
||||
|
@ -169,6 +158,6 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
|||
|
||||
{children}
|
||||
</>
|
||||
</OptionsContext.Provider>
|
||||
</OptionsContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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<IOperationSchema>) => 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<string | undefined>(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) => {
|
||||
|
@ -88,7 +87,7 @@ export const GlobalOssState = ({ children }: React.PropsWithChildren) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<GlobalOssContext.Provider
|
||||
<GlobalOssContext
|
||||
value={{
|
||||
schema,
|
||||
setID,
|
||||
|
@ -96,12 +95,13 @@ export const GlobalOssState = ({ children }: React.PropsWithChildren) => {
|
|||
loading,
|
||||
loadingError,
|
||||
reload,
|
||||
partialUpdate,
|
||||
isValid,
|
||||
invalidateItem,
|
||||
invalidate
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</GlobalOssContext.Provider>
|
||||
</GlobalOssContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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<ILibraryItem>) => 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<ILibraryItem>) => {
|
||||
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(
|
||||
|
@ -295,7 +291,7 @@ export const LibraryState = ({ children }: React.PropsWithChildren) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<LibraryContext.Provider
|
||||
<LibraryContext
|
||||
value={{
|
||||
items,
|
||||
folders,
|
||||
|
@ -322,6 +318,6 @@ export const LibraryState = ({ children }: React.PropsWithChildren) => {
|
|||
}}
|
||||
>
|
||||
{children}
|
||||
</LibraryContext.Provider>
|
||||
</LibraryContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -93,7 +93,7 @@ export const NavigationState = ({ children }: React.PropsWithChildren) => {
|
|||
}, [pathname, scrollTop]);
|
||||
|
||||
return (
|
||||
<NavigationContext.Provider
|
||||
<NavigationContext
|
||||
value={{
|
||||
push,
|
||||
replace,
|
||||
|
@ -105,7 +105,7 @@ export const NavigationState = ({ children }: React.PropsWithChildren) => {
|
|||
}}
|
||||
>
|
||||
{children}
|
||||
</NavigationContext.Provider>
|
||||
</NavigationContext>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -85,23 +85,22 @@ interface OssStateProps {
|
|||
|
||||
export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateProps>) => {
|
||||
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<ErrorData>(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<ILibraryItem>) => {
|
||||
if (!model) {
|
||||
if (!ossData.schema) {
|
||||
return;
|
||||
}
|
||||
setProcessingError(undefined);
|
||||
|
@ -111,43 +110,39 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: () => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: () => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: () => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
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<OssStateP
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: () => {
|
||||
oss.reload();
|
||||
library.reloadItems(() => {
|
||||
callback?.();
|
||||
});
|
||||
ossData.reload();
|
||||
library.reloadItems(callback);
|
||||
}
|
||||
});
|
||||
},
|
||||
[model, library, oss]
|
||||
[library, ossData]
|
||||
);
|
||||
|
||||
return (
|
||||
<OssContext.Provider
|
||||
<OssContext
|
||||
value={{
|
||||
schema: model,
|
||||
schema: ossData.schema,
|
||||
itemID,
|
||||
loading: oss.loading,
|
||||
loadingError: oss.loadingError,
|
||||
loading: ossData.loading,
|
||||
loadingError: ossData.loadingError,
|
||||
processing,
|
||||
processingError,
|
||||
isOwned,
|
||||
|
@ -406,6 +385,6 @@ export const OssState = ({ itemID, children }: React.PropsWithChildren<OssStateP
|
|||
}}
|
||||
>
|
||||
{children}
|
||||
</OssContext.Provider>
|
||||
</OssContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -113,28 +113,19 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
|
|||
const library = useLibrary();
|
||||
const oss = useGlobalOss();
|
||||
const { user } = useAuth();
|
||||
const {
|
||||
schema, // prettier: split lines
|
||||
reload,
|
||||
error: errorLoading,
|
||||
setSchema,
|
||||
loading
|
||||
} = useRSFormDetails({
|
||||
target: itemID,
|
||||
version: versionID
|
||||
});
|
||||
const rsData = useRSFormDetails({ target: itemID, version: versionID });
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const [processingError, setProcessingError] = useState<ErrorData>(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<ILibraryItem>) => {
|
||||
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<Blob>) => {
|
||||
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 (
|
||||
<RSFormContext.Provider
|
||||
<RSFormContext
|
||||
value={{
|
||||
schema,
|
||||
schema: rsData.schema,
|
||||
itemID,
|
||||
versionID,
|
||||
loading,
|
||||
errorLoading,
|
||||
loading: rsData.loading,
|
||||
errorLoading: rsData.error,
|
||||
processing,
|
||||
processingError,
|
||||
isOwned,
|
||||
|
@ -598,6 +587,6 @@ export const RSFormState = ({ itemID, versionID, children }: React.PropsWithChil
|
|||
}}
|
||||
>
|
||||
{children}
|
||||
</RSFormContext.Provider>
|
||||
</RSFormContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -76,8 +76,8 @@ export const UserProfileState = ({ children }: React.PropsWithChildren) => {
|
|||
}, [reload]);
|
||||
|
||||
return (
|
||||
<ProfileContext.Provider value={{ user, updateUser, error, loading, setError, processing, errorProcessing }}>
|
||||
<ProfileContext value={{ user, updateUser, error, loading, setError, processing, errorProcessing }}>
|
||||
{children}
|
||||
</ProfileContext.Provider>
|
||||
</ProfileContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -81,7 +81,7 @@ export const UsersState = ({ children }: React.PropsWithChildren) => {
|
|||
}, [reload]);
|
||||
|
||||
return (
|
||||
<UsersContext.Provider
|
||||
<UsersContext
|
||||
value={{
|
||||
users,
|
||||
reload,
|
||||
|
@ -89,6 +89,6 @@ export const UsersState = ({ children }: React.PropsWithChildren) => {
|
|||
}}
|
||||
>
|
||||
{children}
|
||||
</UsersContext.Provider>
|
||||
</UsersContext>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { IconReset } from '@/components/Icons';
|
||||
import PickSchema from '@/components/select/PickSchema';
|
||||
|
@ -22,18 +22,16 @@ interface DlgChangeInputSchemaProps extends Pick<ModalProps, 'hideWindow'> {
|
|||
function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeInputSchemaProps) {
|
||||
const [selected, setSelected] = useState<LibraryItemID | undefined>(target.result ?? undefined);
|
||||
const library = useLibrary();
|
||||
const sortedItems = useMemo(() => sortItemsForOSS(oss, library.items), [oss, library.items]);
|
||||
const sortedItems = sortItemsForOSS(oss, library.items);
|
||||
const isValid = target.result !== selected;
|
||||
|
||||
const baseFilter = useCallback(
|
||||
(item: ILibraryItem) => !oss.schemas.includes(item.id) || item.id === selected || item.id === target.result,
|
||||
[oss, selected, target]
|
||||
);
|
||||
function baseFilter(item: ILibraryItem) {
|
||||
return !oss.schemas.includes(item.id) || item.id === selected || item.id === target.result;
|
||||
}
|
||||
|
||||
const isValid = useMemo(() => target.result !== selected, [target, selected]);
|
||||
|
||||
const handleSelectLocation = useCallback((newValue: LibraryItemID) => {
|
||||
function handleSelectLocation(newValue: LibraryItemID) {
|
||||
setSelected(newValue);
|
||||
}, []);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import SelectLocationContext from '@/components/select/SelectLocationContext';
|
||||
import SelectLocationHead from '@/components/select/SelectLocationHead';
|
||||
|
@ -26,13 +26,13 @@ function DlgChangeLocation({ hideWindow, initial, onChangeLocation }: DlgChangeL
|
|||
|
||||
const { folders } = useLibrary();
|
||||
|
||||
const location = useMemo(() => combineLocation(head, body), [head, body]);
|
||||
const isValid = useMemo(() => initial !== location && validateLocation(location), [initial, location]);
|
||||
const location = combineLocation(head, body);
|
||||
const isValid = initial !== location && validateLocation(location);
|
||||
|
||||
const handleSelectLocation = useCallback((newValue: string) => {
|
||||
function handleSelectLocation(newValue: string) {
|
||||
setHead(newValue.substring(0, 2) as LocationHead);
|
||||
setBody(newValue.length > 3 ? newValue.substring(3) : '');
|
||||
}, []);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
|
@ -43,16 +43,16 @@ function DlgCloneLibraryItem({ hideWindow, base, initialLocation, selected, tota
|
|||
|
||||
const [head, setHead] = useState(initialLocation.substring(0, 2) as LocationHead);
|
||||
const [body, setBody] = useState(initialLocation.substring(3));
|
||||
const location = useMemo(() => combineLocation(head, body), [head, body]);
|
||||
const location = combineLocation(head, body);
|
||||
|
||||
const { cloneItem, folders } = useLibrary();
|
||||
|
||||
const canSubmit = useMemo(() => title !== '' && alias !== '' && validateLocation(location), [title, alias, location]);
|
||||
const canSubmit = title !== '' && alias !== '' && validateLocation(location);
|
||||
|
||||
const handleSelectLocation = useCallback((newValue: string) => {
|
||||
function handleSelectLocation(newValue: string) {
|
||||
setHead(newValue.substring(0, 2) as LocationHead);
|
||||
setBody(newValue.length > 3 ? newValue.substring(3) : '');
|
||||
}, []);
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
const data: IRSFormCloneData = {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
|
@ -65,7 +65,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
|||
return true;
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
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,39 +119,10 @@ 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]);
|
||||
|
||||
const templatePanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabTemplate state={template} partialUpdate={updateTemplate} templateSchema={templateSchema} />
|
||||
</TabPanel>
|
||||
),
|
||||
[template, templateSchema, updateTemplate]
|
||||
);
|
||||
|
||||
const argumentsPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabArguments schema={schema} state={substitutes} partialUpdate={updateSubstitutes} />
|
||||
</TabPanel>
|
||||
),
|
||||
[schema, substitutes, updateSubstitutes]
|
||||
);
|
||||
|
||||
const editorPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<div className='cc-fade-in cc-column'>
|
||||
<FormCreateCst state={constituenta} partialUpdate={updateConstituenta} schema={schema} />
|
||||
</div>
|
||||
</TabPanel>
|
||||
),
|
||||
[constituenta, updateConstituenta, schema]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header='Создание конституенты из шаблона'
|
||||
|
@ -175,9 +146,19 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
|||
<TabLabel label='Конституента' title='Редактирование конституенты' className='w-[8rem]' />
|
||||
</TabList>
|
||||
|
||||
{templatePanel}
|
||||
{argumentsPanel}
|
||||
{editorPanel}
|
||||
<TabPanel>
|
||||
<TabTemplate state={template} partialUpdate={updateTemplate} templateSchema={templateSchema} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TabArguments schema={schema} state={substitutes} partialUpdate={updateSubstitutes} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<div className='cc-fade-in cc-column'>
|
||||
<FormCreateCst state={constituenta} partialUpdate={updateConstituenta} schema={schema} />
|
||||
</div>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { IconAccept, IconRemove, IconReset } from '@/components/Icons';
|
||||
import RSInput from '@/components/RSInput';
|
||||
|
@ -10,9 +10,9 @@ import PickConstituenta from '@/components/select/PickConstituenta';
|
|||
import DataTable, { IConditionalStyle } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import NoData from '@/components/ui/NoData';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { IConstituenta, IRSForm } from '@/models/rsform';
|
||||
import { IArgumentValue } from '@/models/rslang';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
interface TabArgumentsProps {
|
||||
|
@ -29,17 +29,10 @@ export interface IArgumentsState {
|
|||
const argumentsHelper = createColumnHelper<IArgumentValue>();
|
||||
|
||||
function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
|
||||
const [selectedArgument, setSelectedArgument] = useState<IArgumentValue | undefined>(undefined);
|
||||
|
||||
const [argumentValue, setArgumentValue] = useState('');
|
||||
|
||||
const isModified = useMemo(
|
||||
() => selectedArgument && argumentValue !== selectedArgument.value,
|
||||
[selectedArgument, argumentValue]
|
||||
);
|
||||
const isModified = selectedArgument && argumentValue !== selectedArgument.value;
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedArgument && state.arguments.length > 0) {
|
||||
|
@ -47,46 +40,39 @@ function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
|
|||
}
|
||||
}, [state.arguments, selectedArgument]);
|
||||
|
||||
const handleSelectArgument = useCallback((arg: IArgumentValue) => {
|
||||
function handleSelectArgument(arg: IArgumentValue) {
|
||||
setSelectedArgument(arg);
|
||||
if (arg.value) {
|
||||
setArgumentValue(arg.value);
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
const handleSelectConstituenta = useCallback((cst: IConstituenta) => {
|
||||
function handleSelectConstituenta(cst: IConstituenta) {
|
||||
setSelectedCst(cst);
|
||||
setArgumentValue(cst.alias);
|
||||
}, []);
|
||||
}
|
||||
|
||||
const handleClearArgument = useCallback(
|
||||
(target: IArgumentValue) => {
|
||||
function handleClearArgument(target: IArgumentValue) {
|
||||
const newArg = { ...target, value: '' };
|
||||
partialUpdate({
|
||||
arguments: state.arguments.map(arg => (arg.alias !== target.alias ? arg : newArg))
|
||||
});
|
||||
setSelectedArgument(newArg);
|
||||
},
|
||||
[partialUpdate, state.arguments]
|
||||
);
|
||||
}
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
function handleReset() {
|
||||
setArgumentValue(selectedArgument?.value ?? '');
|
||||
}, [selectedArgument]);
|
||||
}
|
||||
|
||||
const handleAssignArgument = useCallback(
|
||||
(target: IArgumentValue, value: string) => {
|
||||
function handleAssignArgument(target: IArgumentValue, value: string) {
|
||||
const newArg = { ...target, value: value };
|
||||
partialUpdate({
|
||||
arguments: state.arguments.map(arg => (arg.alias !== target.alias ? arg : newArg))
|
||||
});
|
||||
setSelectedArgument(newArg);
|
||||
},
|
||||
[partialUpdate, state.arguments]
|
||||
);
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
argumentsHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
size: 40,
|
||||
|
@ -131,19 +117,14 @@ function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
|
|||
</div>
|
||||
)
|
||||
})
|
||||
],
|
||||
[handleClearArgument]
|
||||
);
|
||||
];
|
||||
|
||||
const conditionalRowStyles = useMemo(
|
||||
(): IConditionalStyle<IArgumentValue>[] => [
|
||||
const conditionalRowStyles: IConditionalStyle<IArgumentValue>[] = [
|
||||
{
|
||||
when: (arg: IArgumentValue) => arg.alias === selectedArgument?.alias,
|
||||
style: { backgroundColor: colors.bgSelected }
|
||||
style: { backgroundColor: APP_COLORS.bgSelected }
|
||||
}
|
||||
],
|
||||
[selectedArgument, colors]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<div className='cc-fade-in'>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
||||
import { Dispatch, useEffect, useState } from 'react';
|
||||
|
||||
import RSInput from '@/components/RSInput';
|
||||
import PickConstituenta from '@/components/select/PickConstituenta';
|
||||
|
@ -28,36 +28,23 @@ function TabTemplate({ state, partialUpdate, templateSchema }: TabTemplateProps)
|
|||
|
||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
||||
|
||||
const prototypeInfo = useMemo(() => {
|
||||
if (!state.prototype) {
|
||||
return '';
|
||||
} else {
|
||||
return `${state.prototype?.term_raw}${
|
||||
state.prototype?.definition_raw ? ` — ${state.prototype?.definition_raw}` : ''
|
||||
}`;
|
||||
}
|
||||
}, [state.prototype]);
|
||||
const prototypeInfo = !state.prototype
|
||||
? ''
|
||||
: `${state.prototype?.term_raw}${state.prototype?.definition_raw ? ` — ${state.prototype?.definition_raw}` : ''}`;
|
||||
|
||||
const templateSelector = useMemo(
|
||||
() =>
|
||||
templates.map(template => ({
|
||||
const templateSelector = templates.map(template => ({
|
||||
value: template.id,
|
||||
label: template.title
|
||||
})),
|
||||
[templates]
|
||||
);
|
||||
}));
|
||||
|
||||
const categorySelector = useMemo((): { value: number; label: string }[] => {
|
||||
if (!templateSchema) {
|
||||
return [];
|
||||
}
|
||||
return templateSchema.items
|
||||
const categorySelector: { value: number; label: string }[] = !templateSchema
|
||||
? []
|
||||
: templateSchema.items
|
||||
.filter(cst => cst.cst_type === CATEGORY_CST_TYPE)
|
||||
.map(cst => ({
|
||||
value: cst.id,
|
||||
label: cst.term_raw
|
||||
}));
|
||||
}, [templateSchema]);
|
||||
|
||||
useEffect(() => {
|
||||
if (templates.length > 0 && !state.templateID) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||
import RSInput from '@/components/RSInput';
|
||||
|
@ -26,24 +26,23 @@ interface FormCreateCstProps {
|
|||
function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreateCstProps) {
|
||||
const [forceComment, setForceComment] = useState(false);
|
||||
|
||||
const isBasic = useMemo(() => isBasicConcept(state.cst_type), [state]);
|
||||
const isElementary = useMemo(() => isBaseSet(state.cst_type), [state]);
|
||||
const showConvention = useMemo(() => !!state.convention || forceComment || isBasic, [state, forceComment, isBasic]);
|
||||
const isBasic = isBasicConcept(state.cst_type);
|
||||
const isElementary = isBaseSet(state.cst_type);
|
||||
const showConvention = !!state.convention || forceComment || isBasic;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
setForceComment(false);
|
||||
}, [state.cst_type, partialUpdate, schema]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (setValidated) {
|
||||
setValidated(validateNewAlias(state.alias, state.cst_type, schema));
|
||||
}
|
||||
}, [state.alias, state.cst_type, schema, setValidated]);
|
||||
|
||||
const handleTypeChange = useCallback(
|
||||
(target: CstType) => partialUpdate({ cst_type: target, alias: generateAlias(target, schema) }),
|
||||
[partialUpdate, schema]
|
||||
);
|
||||
function handleTypeChange(target: CstType) {
|
||||
return partialUpdate({ cst_type: target, alias: generateAlias(target, schema) });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -120,7 +119,7 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
|||
id='dlg_cst_show_comment'
|
||||
tabIndex={-1}
|
||||
type='button'
|
||||
className='self-start cc-label clr-text-url hover:underline'
|
||||
className='self-start cc-label text-sec-600 hover:underline'
|
||||
onClick={() => setForceComment(true)}
|
||||
>
|
||||
Добавить комментарий
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
|
||||
import Modal from '@/components/ui/Modal';
|
||||
|
@ -38,7 +38,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
|||
const [attachedID, setAttachedID] = useState<LibraryItemID | undefined>(undefined);
|
||||
const [createSchema, setCreateSchema] = useState(false);
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
const isValid = (() => {
|
||||
if (alias === '') {
|
||||
return false;
|
||||
}
|
||||
|
@ -51,9 +51,9 @@ 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) {
|
||||
|
@ -82,8 +82,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
|||
onCreate(data);
|
||||
};
|
||||
|
||||
const handleSelectTab = useCallback(
|
||||
(newTab: TabID, last: TabID) => {
|
||||
function handleSelectTab(newTab: TabID, last: TabID) {
|
||||
if (last === newTab) {
|
||||
return;
|
||||
}
|
||||
|
@ -93,49 +92,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
|||
setInputs(initialInputs);
|
||||
}
|
||||
setActiveTab(newTab);
|
||||
},
|
||||
[setActiveTab, initialInputs]
|
||||
);
|
||||
|
||||
const inputPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabInputOperation
|
||||
oss={oss}
|
||||
alias={alias}
|
||||
onChangeAlias={setAlias}
|
||||
comment={comment}
|
||||
onChangeComment={setComment}
|
||||
title={title}
|
||||
onChangeTitle={setTitle}
|
||||
attachedID={attachedID}
|
||||
onChangeAttachedID={setAttachedID}
|
||||
createSchema={createSchema}
|
||||
onChangeCreateSchema={setCreateSchema}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[alias, comment, title, attachedID, oss, createSchema, setAlias]
|
||||
);
|
||||
|
||||
const synthesisPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabSynthesisOperation
|
||||
oss={oss}
|
||||
alias={alias}
|
||||
onChangeAlias={setAlias}
|
||||
comment={comment}
|
||||
onChangeComment={setComment}
|
||||
title={title}
|
||||
onChangeTitle={setTitle}
|
||||
inputs={inputs}
|
||||
setInputs={setInputs}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[oss, alias, comment, title, inputs, setAlias]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
@ -164,8 +121,35 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
|||
/>
|
||||
</TabList>
|
||||
|
||||
{inputPanel}
|
||||
{synthesisPanel}
|
||||
<TabPanel>
|
||||
<TabInputOperation
|
||||
oss={oss}
|
||||
alias={alias}
|
||||
onChangeAlias={setAlias}
|
||||
comment={comment}
|
||||
onChangeComment={setComment}
|
||||
title={title}
|
||||
onChangeTitle={setTitle}
|
||||
attachedID={attachedID}
|
||||
onChangeAttachedID={setAttachedID}
|
||||
createSchema={createSchema}
|
||||
onChangeCreateSchema={setCreateSchema}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TabSynthesisOperation
|
||||
oss={oss}
|
||||
alias={alias}
|
||||
onChangeAlias={setAlias}
|
||||
comment={comment}
|
||||
onChangeComment={setComment}
|
||||
title={title}
|
||||
onChangeTitle={setTitle}
|
||||
inputs={inputs}
|
||||
setInputs={setInputs}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { IconReset } from '@/components/Icons';
|
||||
import PickSchema from '@/components/select/PickSchema';
|
||||
|
@ -41,9 +41,12 @@ function TabInputOperation({
|
|||
createSchema,
|
||||
onChangeCreateSchema
|
||||
}: TabInputOperationProps) {
|
||||
const baseFilter = useCallback((item: ILibraryItem) => !oss.schemas.includes(item.id), [oss]);
|
||||
const library = useLibrary();
|
||||
const sortedItems = useMemo(() => sortItemsForOSS(oss, library.items), [oss, library.items]);
|
||||
const sortedItems = sortItemsForOSS(oss, library.items);
|
||||
|
||||
function baseFilter(item: ILibraryItem) {
|
||||
return !oss.schemas.includes(item.id);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (createSchema) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Checkbox from '@/components/ui/Checkbox';
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
|
@ -23,9 +23,7 @@ function DlgCreateVersion({ hideWindow, versions, selected, totalCount, onCreate
|
|||
const [description, setDescription] = useState('');
|
||||
const [onlySelected, setOnlySelected] = useState(false);
|
||||
|
||||
const canSubmit = useMemo(() => {
|
||||
return !versions.find(ver => ver.version === version);
|
||||
}, [versions, version]);
|
||||
const canSubmit = !versions.find(ver => ver.version === version);
|
||||
|
||||
function handleSubmit() {
|
||||
const data: IVersionCreateData = {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Checkbox from '@/components/ui/Checkbox';
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
|
@ -18,12 +18,9 @@ interface DlgDeleteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
|||
|
||||
function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstProps) {
|
||||
const [expandOut, setExpandOut] = useState(false);
|
||||
const expansion: ConstituentaID[] = useMemo(
|
||||
() => schema.graph.expandAllOutputs(selected), // prettier: split-lines
|
||||
[selected, schema.graph]
|
||||
);
|
||||
const hasInherited = useMemo(
|
||||
() => selected.some(id => schema.inheritance.find(item => item.parent === id), [selected, schema.inheritance]),
|
||||
const expansion: ConstituentaID[] = schema.graph.expandAllOutputs(selected);
|
||||
const hasInherited = selected.some(
|
||||
id => schema.inheritance.find(item => item.parent === id),
|
||||
[selected, schema.inheritance]
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { IconRemove } from '@/components/Icons';
|
||||
import SelectUser from '@/components/select/SelectUser';
|
||||
|
@ -22,20 +22,19 @@ interface DlgEditEditorsProps {
|
|||
function DlgEditEditors({ hideWindow, editors, setEditors }: DlgEditEditorsProps) {
|
||||
const [selected, setSelected] = useState<UserID[]>(editors);
|
||||
const { users } = useUsers();
|
||||
const filtered = useMemo(() => users.filter(user => !selected.includes(user.id)), [users, selected]);
|
||||
const filtered = users.filter(user => !selected.includes(user.id));
|
||||
|
||||
function handleSubmit() {
|
||||
setEditors(selected);
|
||||
}
|
||||
|
||||
const onDeleteEditor = useCallback((target: UserID) => setSelected(prev => prev.filter(id => id !== target)), []);
|
||||
function onDeleteEditor(target: UserID) {
|
||||
setSelected(prev => prev.filter(id => id !== target));
|
||||
}
|
||||
|
||||
const onAddEditor = useCallback((target: UserID) => setSelected(prev => [...prev, target]), []);
|
||||
|
||||
const usersTable = useMemo(
|
||||
() => <TableUsers items={users.filter(user => selected.includes(user.id))} onDelete={onDeleteEditor} />,
|
||||
[users, selected, onDeleteEditor]
|
||||
);
|
||||
function onAddEditor(target: UserID) {
|
||||
setSelected(prev => [...prev, target]);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
@ -58,7 +57,7 @@ function DlgEditEditors({ hideWindow, editors, setEditors }: DlgEditEditorsProps
|
|||
/>
|
||||
</div>
|
||||
|
||||
{usersTable}
|
||||
<TableUsers items={users.filter(user => selected.includes(user.id))} onDelete={onDeleteEditor} />
|
||||
|
||||
<div className='flex items-center gap-3'>
|
||||
<Label text='Добавить' />
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { IconRemove } from '@/components/Icons';
|
||||
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
|
@ -15,8 +13,7 @@ interface TableUsersProps {
|
|||
const columnHelper = createColumnHelper<IUserInfo>();
|
||||
|
||||
function TableUsers({ items, onDelete }: TableUsersProps) {
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('last_name', {
|
||||
id: 'last_name',
|
||||
size: 400,
|
||||
|
@ -42,9 +39,7 @@ function TableUsers({ items, onDelete }: TableUsersProps) {
|
|||
</div>
|
||||
)
|
||||
})
|
||||
],
|
||||
[onDelete]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
|
||||
import Modal from '@/components/ui/Modal';
|
||||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import useRSFormCache from '@/hooks/useRSFormCache';
|
||||
import { LibraryItemID } from '@/models/library';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import {
|
||||
ICstSubstitute,
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
OperationType
|
||||
} from '@/models/oss';
|
||||
import { SubstitutionValidator } from '@/models/ossAPI';
|
||||
import { ConstituentaID } from '@/models/rsform';
|
||||
|
||||
import TabArguments from './TabArguments';
|
||||
import TabOperation from './TabOperation';
|
||||
|
@ -45,73 +47,73 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
const [isCorrect, setIsCorrect] = useState(true);
|
||||
const [validationText, setValidationText] = useState('');
|
||||
|
||||
const initialInputs = useMemo(() => oss.graph.expandInputs([target.id]), [oss.graph, target.id]);
|
||||
const initialInputs = oss.graph.expandInputs([target.id]);
|
||||
const [inputs, setInputs] = useState<OperationID[]>(initialInputs);
|
||||
const inputOperations = useMemo(() => inputs.map(id => oss.operationByID.get(id)!), [inputs, oss.operationByID]);
|
||||
const schemasIDs = useMemo(
|
||||
() => inputOperations.map(operation => operation.result).filter(id => id !== null),
|
||||
[inputOperations]
|
||||
);
|
||||
const inputOperations = inputs.map(id => oss.operationByID.get(id)!);
|
||||
|
||||
const [needPreload, setNeedPreload] = useState(false);
|
||||
const [schemasIDs, setSchemaIDs] = useState<LibraryItemID[]>([]);
|
||||
|
||||
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>(target.substitutions);
|
||||
const [suggestions, setSuggestions] = useState<ICstSubstitute[]>([]);
|
||||
|
||||
const cache = useRSFormCache();
|
||||
const schemas = useMemo(
|
||||
() => schemasIDs.map(id => cache.getSchema(id)).filter(item => item !== undefined),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[schemasIDs, cache.getSchema]
|
||||
);
|
||||
const schemas = schemasIDs.map(id => cache.data.find(item => item.id === id)).filter(item => item !== undefined);
|
||||
|
||||
const isModified = useMemo(
|
||||
() =>
|
||||
const isModified =
|
||||
alias !== target.alias ||
|
||||
title !== target.title ||
|
||||
comment !== target.comment ||
|
||||
JSON.stringify(initialInputs) !== JSON.stringify(inputs) ||
|
||||
JSON.stringify(substitutions) !== JSON.stringify(target.substitutions),
|
||||
[
|
||||
alias,
|
||||
title,
|
||||
comment,
|
||||
target.alias,
|
||||
target.title,
|
||||
target.comment,
|
||||
initialInputs,
|
||||
inputs,
|
||||
substitutions,
|
||||
target.substitutions
|
||||
]
|
||||
JSON.stringify(substitutions) !== JSON.stringify(target.substitutions);
|
||||
|
||||
const canSubmit = isModified && alias !== '';
|
||||
|
||||
const getSchemaByCst = useCallback(
|
||||
(id: ConstituentaID) => {
|
||||
for (const schema of cache.data) {
|
||||
const cst = schema.items.find(cst => cst.id === id);
|
||||
if (cst) {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
[cache.data]
|
||||
);
|
||||
|
||||
const canSubmit = useMemo(() => isModified && alias !== '', [isModified, alias]);
|
||||
useEffect(() => {
|
||||
setNeedPreload(true);
|
||||
setSchemaIDs(inputOperations.map(operation => operation.result).filter(id => id !== null));
|
||||
}, [inputOperations]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (needPreload) {
|
||||
setNeedPreload(false);
|
||||
cache.preload(schemasIDs);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [schemasIDs]);
|
||||
}
|
||||
}, [schemasIDs, needPreload, cache]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (cache.loading || schemas.length !== schemasIDs.length) {
|
||||
return;
|
||||
}
|
||||
setSubstitutions(prev =>
|
||||
prev.filter(sub => {
|
||||
const original = cache.getSchemaByCst(sub.original);
|
||||
const original = getSchemaByCst(sub.original);
|
||||
if (!original || !schemasIDs.includes(original.id)) {
|
||||
return false;
|
||||
}
|
||||
const substitution = cache.getSchemaByCst(sub.substitution);
|
||||
const substitution = getSchemaByCst(sub.substitution);
|
||||
if (!substitution || !schemasIDs.includes(substitution.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [schemasIDs, schemas, cache.loading]);
|
||||
}, [schemasIDs, schemas, cache.loading, getSchemaByCst]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (cache.loading || schemas.length !== schemasIDs.length) {
|
||||
return;
|
||||
}
|
||||
|
@ -121,7 +123,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
setSuggestions(validator.suggestions);
|
||||
}, [substitutions, cache.loading, schemas, schemasIDs.length]);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
function handleSubmit() {
|
||||
const data: IOperationUpdateData = {
|
||||
target: target.id,
|
||||
item_data: {
|
||||
|
@ -134,55 +136,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
substitutions: target.operation_type !== OperationType.SYNTHESIS ? undefined : substitutions
|
||||
};
|
||||
onSubmit(data);
|
||||
}, [alias, comment, title, inputs, substitutions, target, onSubmit]);
|
||||
|
||||
const cardPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabOperation
|
||||
alias={alias}
|
||||
onChangeAlias={setAlias}
|
||||
comment={comment}
|
||||
onChangeComment={setComment}
|
||||
title={title}
|
||||
onChangeTitle={setTitle}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[alias, comment, title, setAlias]
|
||||
);
|
||||
|
||||
const argumentsPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabArguments
|
||||
target={target.id} // prettier: split-lines
|
||||
oss={oss}
|
||||
inputs={inputs}
|
||||
setInputs={setInputs}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[oss, target, inputs, setInputs]
|
||||
);
|
||||
|
||||
const synthesisPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabSynthesis
|
||||
schemas={schemas}
|
||||
loading={cache.loading}
|
||||
error={cache.error}
|
||||
validationText={validationText}
|
||||
isCorrect={isCorrect}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
suggestions={suggestions}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[cache.loading, cache.error, substitutions, suggestions, schemas, validationText, isCorrect]
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
@ -215,9 +169,41 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
) : null}
|
||||
</TabList>
|
||||
|
||||
{cardPanel}
|
||||
{target.operation_type === OperationType.SYNTHESIS ? argumentsPanel : null}
|
||||
{target.operation_type === OperationType.SYNTHESIS ? synthesisPanel : null}
|
||||
<TabPanel>
|
||||
<TabOperation
|
||||
alias={alias}
|
||||
onChangeAlias={setAlias}
|
||||
comment={comment}
|
||||
onChangeComment={setComment}
|
||||
title={title}
|
||||
onChangeTitle={setTitle}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabPanel>
|
||||
<TabArguments
|
||||
target={target.id} // prettier: split-lines
|
||||
oss={oss}
|
||||
inputs={inputs}
|
||||
setInputs={setInputs}
|
||||
/>
|
||||
</TabPanel>
|
||||
) : null}
|
||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabPanel>
|
||||
<TabSynthesis
|
||||
schemas={schemas}
|
||||
loading={cache.loading}
|
||||
error={cache.error}
|
||||
validationText={validationText}
|
||||
isCorrect={isCorrect}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
suggestions={suggestions}
|
||||
/>
|
||||
</TabPanel>
|
||||
) : null}
|
||||
</Tabs>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import PickMultiOperation from '@/components/select/PickMultiOperation';
|
||||
import FlexColumn from '@/components/ui/FlexColumn';
|
||||
import Label from '@/components/ui/Label';
|
||||
|
@ -15,11 +13,8 @@ interface TabArgumentsProps {
|
|||
}
|
||||
|
||||
function TabArguments({ oss, inputs, target, setInputs }: TabArgumentsProps) {
|
||||
const potentialCycle = useMemo(() => [target, ...oss.graph.expandAllOutputs([target])], [target, oss.graph]);
|
||||
const filtered = useMemo(
|
||||
() => oss.items.filter(item => !potentialCycle.includes(item.id)),
|
||||
[oss.items, potentialCycle]
|
||||
);
|
||||
const potentialCycle = [target, ...oss.graph.expandAllOutputs([target])];
|
||||
const filtered = oss.items.filter(item => !potentialCycle.includes(item.id));
|
||||
return (
|
||||
<div className='cc-fade-in cc-column'>
|
||||
<FlexColumn>
|
||||
|
|
|
@ -2,9 +2,9 @@ import { ErrorData } from '@/components/info/InfoError';
|
|||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||
import TextArea from '@/components/ui/TextArea';
|
||||
import DataLoader from '@/components/wrap/DataLoader';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { ICstSubstitute } from '@/models/oss';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
interface TabSynthesisProps {
|
||||
|
@ -29,7 +29,6 @@ function TabSynthesis({
|
|||
setSubstitutions,
|
||||
suggestions
|
||||
}: TabSynthesisProps) {
|
||||
const { colors } = useConceptOptions();
|
||||
return (
|
||||
<DataLoader isLoading={loading} error={error}>
|
||||
<div className='cc-fade-in cc-column mt-3'>
|
||||
|
@ -45,7 +44,7 @@ function TabSynthesis({
|
|||
disabled
|
||||
value={validationText}
|
||||
rows={4}
|
||||
style={{ borderColor: isCorrect ? undefined : colors.fgRed }}
|
||||
style={{ borderColor: isCorrect ? undefined : APP_COLORS.fgRed }}
|
||||
/>
|
||||
</div>
|
||||
</DataLoader>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
|
||||
import Modal from '@/components/ui/Modal';
|
||||
|
@ -36,42 +36,16 @@ export enum TabID {
|
|||
|
||||
function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditReferenceProps) {
|
||||
const [activeTab, setActiveTab] = useState(initial.type === ReferenceType.ENTITY ? TabID.ENTITY : TabID.SYNTACTIC);
|
||||
|
||||
const [reference, setReference] = useState('');
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
|
||||
const handleSubmit = () => onSave(reference);
|
||||
|
||||
const entityPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabEntityReference
|
||||
initial={initial}
|
||||
schema={schema}
|
||||
onChangeReference={setReference}
|
||||
onChangeValid={setIsValid}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[initial, schema]
|
||||
);
|
||||
|
||||
const syntacticPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabSyntacticReference initial={initial} onChangeReference={setReference} onChangeValid={setIsValid} />
|
||||
</TabPanel>
|
||||
),
|
||||
[initial]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header='Редактирование ссылки'
|
||||
submitText='Сохранить ссылку'
|
||||
hideWindow={hideWindow}
|
||||
canSubmit={isValid}
|
||||
onSubmit={handleSubmit}
|
||||
onSubmit={() => onSave(reference)}
|
||||
className='w-[40rem] px-6 h-[32rem]'
|
||||
helpTopic={HelpTopic.TERM_CONTROL}
|
||||
>
|
||||
|
@ -89,8 +63,18 @@ function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditRefere
|
|||
/>
|
||||
</TabList>
|
||||
|
||||
{entityPanel}
|
||||
{syntacticPanel}
|
||||
<TabPanel>
|
||||
<TabEntityReference
|
||||
initial={initial}
|
||||
schema={schema}
|
||||
onChangeReference={setReference}
|
||||
onChangeValid={setIsValid}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TabSyntacticReference initial={initial} onChangeReference={setReference} onChangeValid={setIsValid} />
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -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<IGrammemeOption[]>([]);
|
||||
|
||||
// Initialization
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!!initial.refRaw && initial.type === ReferenceType.ENTITY) {
|
||||
const ref = parseEntityReference(initial.refRaw);
|
||||
setAlias(ref.entity);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import TextInput from '@/components/ui/TextInput';
|
||||
import { ReferenceType } from '@/models/language';
|
||||
|
@ -18,16 +18,16 @@ function TabSyntacticReference({ initial, onChangeValid, onChangeReference }: Ta
|
|||
const [nominal, setNominal] = useState('');
|
||||
const [offset, setOffset] = useState(1);
|
||||
|
||||
const mainLink = useMemo(() => {
|
||||
const mainLink = (() => {
|
||||
const position = offset > 0 ? initial.basePosition + (offset - 1) : initial.basePosition + offset;
|
||||
if (offset === 0 || position < 0 || position >= initial.mainRefs.length) {
|
||||
return 'Некорректное значение смещения';
|
||||
} else {
|
||||
return initial.mainRefs[position];
|
||||
}
|
||||
}, [initial, offset]);
|
||||
})();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
if (initial.refRaw && initial.type === ReferenceType.SYNTACTIC) {
|
||||
const ref = parseSyntacticReference(initial.refRaw);
|
||||
setOffset(ref.offset);
|
||||
|
|
|
@ -21,7 +21,7 @@ function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...re
|
|||
'p-1',
|
||||
'border rounded-none',
|
||||
'cursor-pointer',
|
||||
'clr-btn-clear clr-hover',
|
||||
'clr-text-controls clr-hover cc-animate-color',
|
||||
isSelected && 'clr-selected'
|
||||
)}
|
||||
{...restProps}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { IconReset, IconSave } from '@/components/Icons';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
|
@ -26,19 +26,8 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
|
|||
const [version, setVersion] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
if (!selected) {
|
||||
return false;
|
||||
}
|
||||
return versions.every(ver => ver.id === selected.id || ver.version != version);
|
||||
}, [selected, version, versions]);
|
||||
|
||||
const isModified = useMemo(() => {
|
||||
if (!selected) {
|
||||
return false;
|
||||
}
|
||||
return selected.version != version || selected.description != description;
|
||||
}, [version, description, selected]);
|
||||
const isValid = selected && versions.every(ver => ver.id === selected.id || ver.version != version);
|
||||
const isModified = selected && (selected.version != version || selected.description != description);
|
||||
|
||||
function handleUpdate() {
|
||||
if (!isModified || !selected || processing || !isValid) {
|
||||
|
@ -59,24 +48,11 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
|
|||
setDescription(selected?.description ?? '');
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
setVersion(selected?.version ?? '');
|
||||
setDescription(selected?.description ?? '');
|
||||
}, [selected]);
|
||||
|
||||
const versionsTable = useMemo(
|
||||
() => (
|
||||
<TableVersions
|
||||
processing={processing}
|
||||
items={versions}
|
||||
onDelete={onDelete}
|
||||
onSelect={versionID => setSelected(versions.find(ver => ver.id === versionID))}
|
||||
selected={selected?.id}
|
||||
/>
|
||||
),
|
||||
[processing, versions, onDelete, selected?.id]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
readonly
|
||||
|
@ -84,7 +60,14 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
|
|||
hideWindow={hideWindow}
|
||||
className='flex flex-col w-[40rem] px-6 gap-3 pb-6'
|
||||
>
|
||||
{versionsTable}
|
||||
<TableVersions
|
||||
processing={processing}
|
||||
items={versions}
|
||||
onDelete={onDelete}
|
||||
onSelect={versionID => setSelected(versions.find(ver => ver.id === versionID))}
|
||||
selected={selected?.id}
|
||||
/>
|
||||
|
||||
<div className='flex'>
|
||||
<TextInput
|
||||
id='dlg_version'
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { IconRemove } from '@/components/Icons';
|
||||
import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { IVersionInfo, VersionID } from '@/models/library';
|
||||
import { APP_COLORS } from '@/styling/color';
|
||||
|
||||
interface TableVersionsProps {
|
||||
processing: boolean;
|
||||
|
@ -22,10 +21,7 @@ const columnHelper = createColumnHelper<IVersionInfo>();
|
|||
|
||||
function TableVersions({ processing, items, onDelete, selected, onSelect }: TableVersionsProps) {
|
||||
const intl = useIntl();
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('version', {
|
||||
id: 'version',
|
||||
header: 'Версия',
|
||||
|
@ -70,21 +66,16 @@ function TableVersions({ processing, items, onDelete, selected, onSelect }: Tabl
|
|||
</div>
|
||||
)
|
||||
})
|
||||
],
|
||||
[onDelete, intl, processing]
|
||||
);
|
||||
];
|
||||
|
||||
const conditionalRowStyles = useMemo(
|
||||
(): IConditionalStyle<IVersionInfo>[] => [
|
||||
const conditionalRowStyles: IConditionalStyle<IVersionInfo>[] = [
|
||||
{
|
||||
when: (version: IVersionInfo) => version.id === selected,
|
||||
style: {
|
||||
backgroundColor: colors.bgSelected
|
||||
backgroundColor: APP_COLORS.bgSelected
|
||||
}
|
||||
}
|
||||
],
|
||||
[selected, colors]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
|
|
|
@ -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<IGrammemeOption[]>([]);
|
||||
const [forms, setForms] = useState<IWordForm[]>([]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
const initForms: IWordForm[] = [];
|
||||
target.term_forms.forEach(term =>
|
||||
initForms.push({
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { IconRemove } from '@/components/Icons';
|
||||
import BadgeWordForm from '@/components/info/BadgeWordForm';
|
||||
|
@ -19,8 +18,7 @@ interface TableWordFormsProps {
|
|||
const columnHelper = createColumnHelper<IWordForm>();
|
||||
|
||||
function TableWordForms({ forms, setForms, onFormSelect }: TableWordFormsProps) {
|
||||
const handleDeleteRow = useCallback(
|
||||
(row: number) => {
|
||||
function handleDeleteRow(row: number) {
|
||||
setForms(prev => {
|
||||
const newForms: IWordForm[] = [];
|
||||
prev.forEach((form, index) => {
|
||||
|
@ -30,12 +28,9 @@ function TableWordForms({ forms, setForms, onFormSelect }: TableWordFormsProps)
|
|||
});
|
||||
return newForms;
|
||||
});
|
||||
},
|
||||
[setForms]
|
||||
);
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
const columns = [
|
||||
columnHelper.accessor('text', {
|
||||
id: 'text',
|
||||
size: 350,
|
||||
|
@ -63,9 +58,7 @@ function TableWordForms({ forms, setForms, onFormSelect }: TableWordFormsProps)
|
|||
</div>
|
||||
)
|
||||
})
|
||||
],
|
||||
[handleDeleteRow]
|
||||
);
|
||||
];
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
|
@ -35,7 +35,7 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
|||
|
||||
const source = useRSFormDetails({ target: donorID ? String(donorID) : undefined });
|
||||
|
||||
const validated = useMemo(() => !!source.schema && selected.length > 0, [source.schema, selected]);
|
||||
const validated = !!source.schema && selected.length > 0;
|
||||
|
||||
function handleSubmit() {
|
||||
if (!source.schema) {
|
||||
|
@ -55,43 +55,6 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
|||
setSubstitutions([]);
|
||||
}, [source.schema]);
|
||||
|
||||
const schemaPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabSchema selected={donorID} setSelected={setDonorID} receiver={receiver} />
|
||||
</TabPanel>
|
||||
),
|
||||
[donorID, receiver]
|
||||
);
|
||||
const itemsPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabConstituents
|
||||
schema={source.schema}
|
||||
loading={source.loading}
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[source.schema, source.loading, selected]
|
||||
);
|
||||
const substitutesPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
<TabSubstitutions
|
||||
receiver={receiver}
|
||||
source={source.schema}
|
||||
selected={selected}
|
||||
loading={source.loading}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
/>
|
||||
</TabPanel>
|
||||
),
|
||||
[source.schema, source.loading, receiver, selected, substitutions]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header='Импорт концептуальной схем'
|
||||
|
@ -113,9 +76,29 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
|||
<TabLabel label='Отождествления' title='Таблица отождествлений' className='w-[8rem]' />
|
||||
</TabList>
|
||||
|
||||
{schemaPanel}
|
||||
{itemsPanel}
|
||||
{substitutesPanel}
|
||||
<TabPanel>
|
||||
<TabSchema selected={donorID} setSelected={setDonorID} receiver={receiver} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TabConstituents
|
||||
schema={source.schema}
|
||||
loading={source.loading}
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TabSubstitutions
|
||||
receiver={receiver}
|
||||
source={source.schema}
|
||||
selected={selected}
|
||||
loading={source.loading}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import PickSchema from '@/components/select/PickSchema';
|
||||
import TextInput from '@/components/ui/TextInput';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
|
@ -17,8 +15,8 @@ interface TabSchemaProps {
|
|||
|
||||
function TabSchema({ selected, receiver, setSelected }: TabSchemaProps) {
|
||||
const library = useLibrary();
|
||||
const selectedInfo = useMemo(() => library.items.find(item => item.id === selected), [selected, library.items]);
|
||||
const sortedItems = useMemo(() => sortItemsForInlineSynthesis(receiver, library.items), [receiver, library.items]);
|
||||
const selectedInfo = library.items.find(item => item.id === selected);
|
||||
const sortedItems = sortItemsForInlineSynthesis(receiver, library.items);
|
||||
|
||||
return (
|
||||
<div className='cc-fade-in flex flex-col'>
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||
import DataLoader from '@/components/wrap/DataLoader';
|
||||
import { ICstSubstitute } from '@/models/oss';
|
||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
interface TabSubstitutionsProps {
|
||||
|
@ -32,12 +30,7 @@ function TabSubstitutions({
|
|||
substitutions,
|
||||
setSubstitutions
|
||||
}: TabSubstitutionsProps) {
|
||||
const filter = useCallback(
|
||||
(cst: IConstituenta) => cst.id !== source?.id || selected.includes(cst.id),
|
||||
[selected, source]
|
||||
);
|
||||
|
||||
const schemas = useMemo(() => [...(source ? [source] : []), ...(receiver ? [receiver] : [])], [source, receiver]);
|
||||
const schemas = [...(source ? [source] : []), ...(receiver ? [receiver] : [])];
|
||||
|
||||
return (
|
||||
<DataLoader isLoading={loading} error={error} hasNoData={!source}>
|
||||
|
@ -47,7 +40,7 @@ function TabSubstitutions({
|
|||
rows={10}
|
||||
prefixID={prefixes.cst_inline_synth_substitutes}
|
||||
schemas={schemas}
|
||||
filter={filter}
|
||||
filter={cst => cst.id !== source?.id || selected.includes(cst.id)}
|
||||
/>
|
||||
</DataLoader>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { RelocateUpIcon } from '@/components/DomainIcons';
|
||||
import PickMultiConstituenta from '@/components/select/PickMultiConstituenta';
|
||||
|
@ -33,10 +33,11 @@ function DlgRelocateConstituents({ oss, hideWindow, initialTarget, onSubmit }: D
|
|||
const [source, setSource] = useState<ILibraryItem | undefined>(
|
||||
library.items.find(item => item.id === initialTarget?.result)
|
||||
);
|
||||
const isValid = !!destination && selected.length > 0;
|
||||
|
||||
const operation = useMemo(() => oss.items.find(item => item.result === source?.id), [oss, source]);
|
||||
const sourceSchemas = useMemo(() => library.items.filter(item => oss.schemas.includes(item.id)), [library, oss]);
|
||||
const destinationSchemas = useMemo(() => {
|
||||
const operation = oss.items.find(item => item.result === source?.id);
|
||||
const sourceSchemas = library.items.filter(item => oss.schemas.includes(item.id));
|
||||
const destinationSchemas = (() => {
|
||||
if (!operation) {
|
||||
return [];
|
||||
}
|
||||
|
@ -45,35 +46,34 @@ function DlgRelocateConstituents({ oss, hideWindow, initialTarget, onSubmit }: D
|
|||
? node.inputs.map(id => oss.operationByID.get(id)!.result).filter(id => id !== null)
|
||||
: node.outputs.map(id => oss.operationByID.get(id)!.result).filter(id => id !== null);
|
||||
return ids.map(id => library.items.find(item => item.id === id)).filter(item => item !== undefined);
|
||||
}, [oss, library.items, operation, directionUp]);
|
||||
})();
|
||||
|
||||
const sourceData = useRSFormDetails({ target: source ? String(source.id) : undefined });
|
||||
const filteredConstituents = useMemo(() => {
|
||||
const filteredConstituents = (() => {
|
||||
if (!sourceData.schema || !destination || !operation) {
|
||||
return [];
|
||||
}
|
||||
const destinationOperation = oss.items.find(item => item.result === destination.id);
|
||||
return getRelocateCandidates(operation.id, destinationOperation!.id, sourceData.schema, oss);
|
||||
}, [destination, operation, sourceData.schema, oss]);
|
||||
})();
|
||||
|
||||
const isValid = useMemo(() => !!destination && selected.length > 0, [destination, selected]);
|
||||
const toggleDirection = useCallback(() => {
|
||||
function toggleDirection() {
|
||||
setDirectionUp(prev => !prev);
|
||||
setDestination(undefined);
|
||||
}, []);
|
||||
}
|
||||
|
||||
const handleSelectSource = useCallback((newValue: ILibraryItem | undefined) => {
|
||||
function handleSelectSource(newValue: ILibraryItem | undefined) {
|
||||
setSource(newValue);
|
||||
setDestination(undefined);
|
||||
setSelected([]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
const handleSelectDestination = useCallback((newValue: ILibraryItem | undefined) => {
|
||||
function handleSelectDestination(newValue: ILibraryItem | undefined) {
|
||||
setDestination(newValue);
|
||||
setSelected([]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
function handleSubmit() {
|
||||
if (!destination) {
|
||||
return;
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ function DlgRelocateConstituents({ oss, hideWindow, initialTarget, onSubmit }: D
|
|||
items: selected
|
||||
};
|
||||
onSubmit(data);
|
||||
}, [destination, onSubmit, selected]);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user