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