Compare commits
18 Commits
e2ab676ec2
...
c05759901a
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c05759901a | ||
![]() |
ac50a4fb05 | ||
![]() |
2b7acfbd27 | ||
![]() |
313d5de706 | ||
![]() |
c166a4478a | ||
![]() |
5dcaae4f51 | ||
![]() |
f0f41a4dcb | ||
![]() |
2b65b19d08 | ||
![]() |
53d8dfc3d6 | ||
![]() |
ece60e565d | ||
![]() |
841a72af4e | ||
![]() |
8452bf4d55 | ||
![]() |
32eee7ca0b | ||
![]() |
addcd52cb6 | ||
![]() |
c442ca8094 | ||
![]() |
a26dd4bc7b | ||
![]() |
16a74bf4f8 | ||
![]() |
5f2239dc26 |
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
|
@ -58,14 +58,6 @@
|
||||||
"depth": 2
|
"depth": 2
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"colorize.include": [".tsx", ".jsx", ".ts", ".js"],
|
|
||||||
"colorize.languages": [
|
|
||||||
"typescript",
|
|
||||||
"javascript",
|
|
||||||
"css",
|
|
||||||
"typescriptreact",
|
|
||||||
"javascriptreact"
|
|
||||||
],
|
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"ablt",
|
"ablt",
|
||||||
"acconcept",
|
"acconcept",
|
||||||
|
|
|
@ -28,8 +28,8 @@ RUN apt-get update -qq && \
|
||||||
FROM python-base AS builder
|
FROM python-base AS builder
|
||||||
|
|
||||||
# Set env variables
|
# Set env variables
|
||||||
ENV PYTHONDONTWRITEBYTECODE 1
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
ENV PYTHONUNBUFFERED 1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
COPY ./requirements.txt ./
|
COPY ./requirements.txt ./
|
||||||
RUN python3.12 -m pip wheel \
|
RUN python3.12 -m pip wheel \
|
||||||
|
|
|
@ -7410,13 +7410,12 @@
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"fields": {
|
"fields": {
|
||||||
"oss": 41,
|
"oss": 41,
|
||||||
|
"parent": null,
|
||||||
"operation_type": "input",
|
"operation_type": "input",
|
||||||
"result": 38,
|
"result": 38,
|
||||||
"alias": "КС Вещества",
|
"alias": "КС Вещества",
|
||||||
"title": "Вещества и смеси",
|
"title": "Вещества и смеси",
|
||||||
"description": "",
|
"description": ""
|
||||||
"position_x": 530.0,
|
|
||||||
"position_y": 370.0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -7424,13 +7423,12 @@
|
||||||
"pk": 2,
|
"pk": 2,
|
||||||
"fields": {
|
"fields": {
|
||||||
"oss": 41,
|
"oss": 41,
|
||||||
|
"parent": null,
|
||||||
"operation_type": "input",
|
"operation_type": "input",
|
||||||
"result": 39,
|
"result": 39,
|
||||||
"alias": "КС ООО",
|
"alias": "КС ООО",
|
||||||
"title": "Объект-объектные отношения",
|
"title": "Объект-объектные отношения",
|
||||||
"description": "",
|
"description": ""
|
||||||
"position_x": 710.0,
|
|
||||||
"position_y": 370.0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -7438,13 +7436,12 @@
|
||||||
"pk": 4,
|
"pk": 4,
|
||||||
"fields": {
|
"fields": {
|
||||||
"oss": 41,
|
"oss": 41,
|
||||||
|
"parent": null,
|
||||||
"operation_type": "input",
|
"operation_type": "input",
|
||||||
"result": 40,
|
"result": 40,
|
||||||
"alias": "КС Процессы",
|
"alias": "КС Процессы",
|
||||||
"title": "Процессы",
|
"title": "Процессы",
|
||||||
"description": "",
|
"description": ""
|
||||||
"position_x": 890.0,
|
|
||||||
"position_y": 370.0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -7452,13 +7449,12 @@
|
||||||
"pk": 9,
|
"pk": 9,
|
||||||
"fields": {
|
"fields": {
|
||||||
"oss": 41,
|
"oss": 41,
|
||||||
|
"parent": null,
|
||||||
"operation_type": "synthesis",
|
"operation_type": "synthesis",
|
||||||
"result": 43,
|
"result": 43,
|
||||||
"alias": "КС Объект-сред",
|
"alias": "КС Объект-сред",
|
||||||
"title": "Объектная среда",
|
"title": "Объектная среда",
|
||||||
"description": "",
|
"description": ""
|
||||||
"position_x": 620.0,
|
|
||||||
"position_y": 470.0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -7466,13 +7462,49 @@
|
||||||
"pk": 10,
|
"pk": 10,
|
||||||
"fields": {
|
"fields": {
|
||||||
"oss": 41,
|
"oss": 41,
|
||||||
|
"parent": null,
|
||||||
"operation_type": "synthesis",
|
"operation_type": "synthesis",
|
||||||
"result": 44,
|
"result": 44,
|
||||||
"alias": "КС Проц-сред",
|
"alias": "КС Проц-сред",
|
||||||
"title": "Процессные среды",
|
"title": "Процессные среды",
|
||||||
"description": "",
|
"description": ""
|
||||||
"position_x": 760.0,
|
}
|
||||||
"position_y": 570.0
|
},
|
||||||
|
{
|
||||||
|
"model": "oss.layout",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"oss": 41,
|
||||||
|
"data": {
|
||||||
|
"blocks": [],
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"x": 530.0,
|
||||||
|
"y": 370.0,
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 710.0,
|
||||||
|
"y": 370.0,
|
||||||
|
"id": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 890.0,
|
||||||
|
"y": 370.0,
|
||||||
|
"id": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 620.0,
|
||||||
|
"y": 470.0,
|
||||||
|
"id": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 760.0,
|
||||||
|
"y": 570.0,
|
||||||
|
"id": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
tzdata==2025.2
|
tzdata==2025.2
|
||||||
Django==5.1.7
|
Django==5.2.1
|
||||||
djangorestframework==3.15.2
|
djangorestframework==3.16.0
|
||||||
django-cors-headers==4.7.0
|
django-cors-headers==4.7.0
|
||||||
django-filter==25.1
|
django-filter==25.1
|
||||||
drf-spectacular==0.28.0
|
drf-spectacular==0.28.0
|
||||||
drf-spectacular-sidecar==2025.3.1
|
drf-spectacular-sidecar==2025.5.1
|
||||||
coreapi==2.3.3
|
coreapi==2.3.3
|
||||||
django-rest-passwordreset==1.5.0
|
django-rest-passwordreset==1.5.0
|
||||||
cctext==0.1.4
|
cctext==0.1.4
|
||||||
|
@ -13,9 +13,9 @@ pyconcept==0.1.12
|
||||||
psycopg2-binary==2.9.10
|
psycopg2-binary==2.9.10
|
||||||
gunicorn==23.0.0
|
gunicorn==23.0.0
|
||||||
|
|
||||||
djangorestframework-stubs==3.15.3
|
djangorestframework-stubs==3.16.0
|
||||||
django-extensions==3.2.3
|
django-extensions==4.1
|
||||||
django-stubs==5.1.3
|
django-stubs==5.2.0
|
||||||
mypy==1.15.0
|
mypy==1.15.0
|
||||||
pylint==3.3.6
|
pylint==3.3.7
|
||||||
coverage==7.7.1
|
coverage==7.8.2
|
|
@ -1,10 +1,10 @@
|
||||||
tzdata==2025.2
|
tzdata==2025.2
|
||||||
Django==5.1.7
|
Django==5.2.1
|
||||||
djangorestframework==3.15.2
|
djangorestframework==3.16.0
|
||||||
django-cors-headers==4.7.0
|
django-cors-headers==4.7.0
|
||||||
django-filter==25.1
|
django-filter==25.1
|
||||||
drf-spectacular==0.28.0
|
drf-spectacular==0.28.0
|
||||||
drf-spectacular-sidecar==2025.3.1
|
drf-spectacular-sidecar==2025.5.1
|
||||||
coreapi==2.3.3
|
coreapi==2.3.3
|
||||||
django-rest-passwordreset==1.5.0
|
django-rest-passwordreset==1.5.0
|
||||||
cctext==0.1.4
|
cctext==0.1.4
|
||||||
|
|
3035
rsconcept/frontend/package-lock.json
generated
3035
rsconcept/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"generate": "lezer-generator src/components/rs-input/rslang/rslang-fast.grammar -o src/components/rs-input/rslang/parser.ts && lezer-generator src/components/rs-input/rslang/rslang-ast.grammar -o src/components/rs-input/rslang/parserAST.ts && lezer-generator src/components/refs-input/parse/refs-text.grammar -o src/components/refs-input/parse/parser.ts",
|
"generate": "lezer-generator src/features/rsform/components/rs-input/rslang/rslang-fast.grammar -o src/features/rsform/components/rs-input/rslang/parser.ts && lezer-generator src/features/rsform/components/rs-input/rslang/rslang-ast.grammar -o src/features/rsform/components/rs-input/rslang/parser-ast.ts && lezer-generator src/features/rsform/components/refs-input/parse/refs-text.grammar -o src/features/rsform/components/refs-input/parse/parser.ts",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "playwright test",
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
|
@ -17,10 +17,10 @@
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.0.1",
|
||||||
"@lezer/lr": "^1.4.2",
|
"@lezer/lr": "^1.4.2",
|
||||||
"@radix-ui/react-popover": "^1.1.13",
|
"@radix-ui/react-popover": "^1.1.14",
|
||||||
"@radix-ui/react-select": "^2.2.4",
|
"@radix-ui/react-select": "^2.2.5",
|
||||||
"@tanstack/react-query": "^5.76.0",
|
"@tanstack/react-query": "^5.80.5",
|
||||||
"@tanstack/react-query-devtools": "^5.76.0",
|
"@tanstack/react-query-devtools": "^5.80.5",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@uiw/codemirror-themes": "^4.23.12",
|
"@uiw/codemirror-themes": "^4.23.12",
|
||||||
"@uiw/react-codemirror": "^4.23.12",
|
"@uiw/react-codemirror": "^4.23.12",
|
||||||
|
@ -30,56 +30,56 @@
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"global": "^4.4.0",
|
"global": "^4.4.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"lucide-react": "^0.510.0",
|
"lucide-react": "^0.511.0",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-error-boundary": "^6.0.0",
|
"react-error-boundary": "^6.0.0",
|
||||||
"react-hook-form": "^7.56.3",
|
"react-hook-form": "^7.57.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-intl": "^7.1.11",
|
"react-intl": "^7.1.11",
|
||||||
"react-router": "^7.6.0",
|
"react-router": "^7.6.2",
|
||||||
"react-scan": "^0.3.3",
|
"react-scan": "^0.3.4",
|
||||||
"react-tabs": "^6.1.0",
|
"react-tabs": "^6.1.0",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"react-tooltip": "^5.28.1",
|
"react-tooltip": "^5.28.1",
|
||||||
"react-zoom-pan-pinch": "^3.7.0",
|
"react-zoom-pan-pinch": "^3.7.0",
|
||||||
"reactflow": "^11.11.4",
|
"reactflow": "^11.11.4",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.0",
|
||||||
"tw-animate-css": "^1.2.9",
|
"tw-animate-css": "^1.3.4",
|
||||||
"use-debounce": "^10.0.4",
|
"use-debounce": "^10.0.4",
|
||||||
"zod": "^3.24.4",
|
"zod": "^3.25.51",
|
||||||
"zustand": "^5.0.4"
|
"zustand": "^5.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lezer/generator": "^1.7.3",
|
"@lezer/generator": "^1.7.3",
|
||||||
"@playwright/test": "^1.52.0",
|
"@playwright/test": "^1.52.0",
|
||||||
"@tailwindcss/vite": "^4.1.6",
|
"@tailwindcss/vite": "^4.1.8",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.15.17",
|
"@types/node": "^22.15.29",
|
||||||
"@types/react": "^19.1.4",
|
"@types/react": "^19.1.6",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||||
"@typescript-eslint/parser": "^8.0.1",
|
"@typescript-eslint/parser": "^8.0.1",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.5.1",
|
||||||
"babel-plugin-react-compiler": "^19.1.0-rc.1",
|
"babel-plugin-react-compiler": "^19.1.0-rc.1",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-playwright": "^2.2.0",
|
"eslint-plugin-playwright": "^2.2.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-compiler": "^19.1.0-rc.1",
|
"eslint-plugin-react-compiler": "^19.1.0-rc.1",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"globals": "^16.1.0",
|
"globals": "^16.2.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"stylelint": "^16.19.1",
|
"stylelint": "^16.20.0",
|
||||||
"stylelint-config-recommended": "^16.0.0",
|
"stylelint-config-recommended": "^16.0.0",
|
||||||
"stylelint-config-standard": "^38.0.0",
|
"stylelint-config-standard": "^38.0.0",
|
||||||
"stylelint-config-tailwindcss": "^1.0.0",
|
"stylelint-config-tailwindcss": "^1.0.0",
|
||||||
"tailwindcss": "^4.0.7",
|
"tailwindcss": "^4.0.7",
|
||||||
"ts-jest": "^29.3.2",
|
"ts-jest": "^29.3.4",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.32.1",
|
"typescript-eslint": "^8.33.1",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.3.5"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
|
|
@ -47,6 +47,7 @@ export function TextArea({
|
||||||
<textarea
|
<textarea
|
||||||
id={id}
|
id={id}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
'min-h-0',
|
||||||
'px-3 py-2',
|
'px-3 py-2',
|
||||||
'leading-tight',
|
'leading-tight',
|
||||||
'overflow-x-hidden overflow-y-auto',
|
'overflow-x-hidden overflow-y-auto',
|
||||||
|
|
|
@ -19,8 +19,8 @@ function useTokenValidation(token: string, isPending: boolean) {
|
||||||
|
|
||||||
const validate = async () => {
|
const validate = async () => {
|
||||||
if (!isTokenValidating && !isPending) {
|
if (!isTokenValidating && !isPending) {
|
||||||
await validateToken({ token });
|
|
||||||
setIsTokenValidating(true);
|
setIsTokenValidating(true);
|
||||||
|
await validateToken({ token });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return { isTokenValidating, validate };
|
return { isTokenValidating, validate };
|
||||||
|
@ -44,10 +44,7 @@ export function Component() {
|
||||||
void resetPassword({
|
void resetPassword({
|
||||||
password: newPassword,
|
password: newPassword,
|
||||||
token: token
|
token: token
|
||||||
}).then(() => {
|
}).then(() => router.replace({ path: urls.login }));
|
||||||
router.replace({ path: urls.home });
|
|
||||||
router.push({ path: urls.login });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +80,9 @@ export function Component() {
|
||||||
setNewPasswordRepeat(event.target.value);
|
setNewPasswordRepeat(event.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{newPasswordRepeat && newPassword !== newPasswordRepeat ? (
|
||||||
|
<div className='text-sm text-destructive'>Пароли не совпадают</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Установить пароль'
|
text='Установить пароль'
|
||||||
|
|
|
@ -35,6 +35,7 @@ export function Component() {
|
||||||
<form className='cc-column w-96 mx-auto px-6 mt-3' onSubmit={handleSubmit} onChange={clearServerError}>
|
<form className='cc-column w-96 mx-auto px-6 mt-3' onSubmit={handleSubmit} onChange={clearServerError}>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='email'
|
id='email'
|
||||||
|
type='email'
|
||||||
autoComplete='email'
|
autoComplete='email'
|
||||||
required
|
required
|
||||||
allowEnter
|
allowEnter
|
||||||
|
|
|
@ -20,6 +20,7 @@ export function HelpConceptSynthesis() {
|
||||||
<p>
|
<p>
|
||||||
Расширение выразительной способности достигается несколькими способами в зависимости от соотношения
|
Расширение выразительной способности достигается несколькими способами в зависимости от соотношения
|
||||||
синтезируемых точек зрения:
|
синтезируемых точек зрения:
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<b>аспектный синтез</b> характеризуется отождествлением общих понятий в случае, когда часть неопределяемых
|
<b>аспектный синтез</b> характеризуется отождествлением общих понятий в случае, когда часть неопределяемых
|
||||||
понятий является общей для двух точек зрения;
|
понятий является общей для двух точек зрения;
|
||||||
|
@ -33,11 +34,12 @@ export function HelpConceptSynthesis() {
|
||||||
интерпретации) схему для соединения понятий из двух операндов путем введения нового неопределяемого понятия,
|
интерпретации) схему для соединения понятий из двух операндов путем введения нового неопределяемого понятия,
|
||||||
моделирующего отношения между синтезируемыми схемами.
|
моделирующего отношения между синтезируемыми схемами.
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Возможно использование комбинации описанных подходов в рамках одного синтеза. Более подробно про реализацию
|
Поддерживается использование комбинации описанных подходов в рамках одного синтеза. Более подробно про
|
||||||
операций в родоструктурной форме можно прочитать в{' '}
|
реализацию операций в родоструктурной форме можно прочитать в{' '}
|
||||||
<LinkTopic text='разделе Операции' topic={HelpTopic.RSL_OPERATIONS} />
|
<LinkTopic text='разделе Операции' topic={HelpTopic.RSL_OPERATIONS} />.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Для управления совокупностью синтезов используются{' '}
|
Для управления совокупностью синтезов используются{' '}
|
||||||
|
|
|
@ -9,12 +9,13 @@ export function HelpAccess() {
|
||||||
<p>
|
<p>
|
||||||
Доступ к контенту на Портале может быть ограничен владельцем каждой схемы в рамках <b>политики доступа</b>.
|
Доступ к контенту на Портале может быть ограничен владельцем каждой схемы в рамках <b>политики доступа</b>.
|
||||||
</p>
|
</p>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconPublic className='inline-icon icon-green' /> публичная политика не ограничивает чтение схемы
|
<IconPublic className='inline-icon icon-green' /> публичная политика не ограничивает чтение схемы
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconProtected className='inline-icon icon-blue' /> защитная политика запрещает доступ для всех кроме редакторов
|
<IconProtected className='inline-icon icon-blue' /> защитная политика запрещает доступ для всех кроме
|
||||||
и владельца схемы
|
редакторов и владельца схемы
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconPrivate className='inline-icon icon-red' /> личная политика оставляет доступ к схеме только владельцу
|
<IconPrivate className='inline-icon icon-red' /> личная политика оставляет доступ к схеме только владельцу
|
||||||
|
@ -26,6 +27,7 @@ export function HelpAccess() {
|
||||||
<li>
|
<li>
|
||||||
<IconImmutable className='inline-icon' /> режим защиты от редактирования предохраняет от случайных изменений
|
<IconImmutable className='inline-icon' /> режим защиты от редактирования предохраняет от случайных изменений
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ export function HelpExteor() {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Основные функции</h2>
|
<h2>Основные функции</h2>
|
||||||
|
<ul>
|
||||||
<li>Работа с РС-формой системы понятий</li>
|
<li>Работа с РС-формой системы понятий</li>
|
||||||
<li>Автоматическое определение типизации выражений</li>
|
<li>Автоматическое определение типизации выражений</li>
|
||||||
<li>Проверка корректности РС-формы</li>
|
<li>Проверка корректности РС-формы</li>
|
||||||
|
@ -35,6 +36,7 @@ export function HelpExteor() {
|
||||||
<li>Вычисление объектной интерпретации</li>
|
<li>Вычисление объектной интерпретации</li>
|
||||||
<li>Выгрузка концептуальных схем в Word</li>
|
<li>Выгрузка концептуальных схем в Word</li>
|
||||||
<li>Импорт/экспорт интерпретаций через Excel</li>
|
<li>Импорт/экспорт интерпретаций через Excel</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ export function HelpInterface() {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Навигация и настройки</h2>
|
<h2>Навигация и настройки</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<kbd>Ctrl + клик</kbd> на объект навигации откроет новую вкладку
|
<kbd>Ctrl + клик</kbd> на объект навигации откроет новую вкладку
|
||||||
</li>
|
</li>
|
||||||
|
@ -46,8 +47,8 @@ export function HelpInterface() {
|
||||||
<IconLogin size='1.25rem' className='inline-icon' /> вход в систему / регистрация нового пользователя
|
<IconLogin size='1.25rem' className='inline-icon' /> вход в систему / регистрация нового пользователя
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconUser2 size='1.25rem' className='inline-icon' /> меню пользователя содержит ряд настроек и переход к профилю
|
<IconUser2 size='1.25rem' className='inline-icon' /> меню пользователя содержит ряд настроек и переход к
|
||||||
пользователя
|
профилю пользователя
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
@ -57,6 +58,7 @@ export function HelpInterface() {
|
||||||
<li>
|
<li>
|
||||||
<IconLogout className='inline-icon' /> выход из системы
|
<IconLogout className='inline-icon' /> выход из системы
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<Subtopics headTopic={HelpTopic.INTERFACE} />
|
<Subtopics headTopic={HelpTopic.INTERFACE} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,6 +22,7 @@ export function HelpMain() {
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary className='text-center font-semibold'>Разделы Справки</summary>
|
<summary className='text-center font-semibold'>Разделы Справки</summary>
|
||||||
|
<ul>
|
||||||
{[
|
{[
|
||||||
HelpTopic.THESAURUS,
|
HelpTopic.THESAURUS,
|
||||||
HelpTopic.INTERFACE,
|
HelpTopic.INTERFACE,
|
||||||
|
@ -35,9 +36,11 @@ export function HelpMain() {
|
||||||
].map(topic => (
|
].map(topic => (
|
||||||
<TopicItem key={`${prefixes.topic_item}${topic}`} topic={topic} />
|
<TopicItem key={`${prefixes.topic_item}${topic}`} topic={topic} />
|
||||||
))}
|
))}
|
||||||
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<h2>Лицензирование и раскрытие информации</h2>
|
<h2>Лицензирование и раскрытие информации</h2>
|
||||||
|
<ul>
|
||||||
<li>Пользователи Портала сохраняют авторские права на создаваемый ими контент</li>
|
<li>Пользователи Портала сохраняют авторские права на создаваемый ими контент</li>
|
||||||
<li>
|
<li>
|
||||||
Политика обработки данных доступна по <LinkTopic text='ссылке' topic={HelpTopic.INFO_PRIVACY} />
|
Политика обработки данных доступна по <LinkTopic text='ссылке' topic={HelpTopic.INFO_PRIVACY} />
|
||||||
|
@ -50,6 +53,7 @@ export function HelpMain() {
|
||||||
Данный сайт использует доменное имя и серверные мощности{' '}
|
Данный сайт использует доменное имя и серверные мощности{' '}
|
||||||
<TextURL text='Центра Концепт' href={external_urls.concept} />
|
<TextURL text='Центра Концепт' href={external_urls.concept} />
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Поддержка</h2>
|
<h2>Поддержка</h2>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -11,6 +11,7 @@ export function HelpVersions() {
|
||||||
<p>После создания версии ее содержание изменить нельзя.</p>
|
<p>После создания версии ее содержание изменить нельзя.</p>
|
||||||
|
|
||||||
<h2>Действия</h2>
|
<h2>Действия</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconShare size='1.25rem' className='inline-icon' /> Поделиться включает версию в ссылку
|
<IconShare size='1.25rem' className='inline-icon' /> Поделиться включает версию в ссылку
|
||||||
</li>
|
</li>
|
||||||
|
@ -25,6 +26,7 @@ export function HelpVersions() {
|
||||||
<li>
|
<li>
|
||||||
<IconVersions size='1.25rem' className='inline-icon' /> Редактировать атрибуты версий
|
<IconVersions size='1.25rem' className='inline-icon' /> Редактировать атрибуты версий
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ export function HelpContributors() {
|
||||||
В списке указан год окончания работ над соответствующим результатом или год публикации соответствующей статьи.
|
В списке указан год окончания работ над соответствующим результатом или год публикации соответствующей статьи.
|
||||||
Курсивом выделены комментарии к значимости указанного результата.
|
Курсивом выделены комментарии к значимости указанного результата.
|
||||||
</p>
|
</p>
|
||||||
<p>Любые добавления и поправки приветствуются.</p>
|
<p>Добавления и корректировки приветствуются.</p>
|
||||||
<div className='flex flex-col gap-3'>
|
<ul className='flex flex-col gap-3'>
|
||||||
<li>1973 Никаноров С.П., Персиц Д.Б. Формальное проектирование целостных СОУ.</li>
|
<li>1973 Никаноров С.П., Персиц Д.Б. Формальное проектирование целостных СОУ.</li>
|
||||||
<li>
|
<li>
|
||||||
1975–1981 Никаноров С.П., Персиц Д.Б., Айзенштат А.В., Закс Б.А. Экспериментальная система пакетов прикладных
|
1975–1981 Никаноров С.П., Персиц Д.Б., Айзенштат А.В., Закс Б.А. Экспериментальная система пакетов прикладных
|
||||||
|
@ -48,18 +48,18 @@ export function HelpContributors() {
|
||||||
решений.
|
решений.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1989 Кучкаров З.А., Остапов А.В. Методические вопросы концептуализации предметных областей,{' '}
|
1989 Остапов А.В., Кучкаров З.А. Методические вопросы концептуализации предметных областей,{' '}
|
||||||
<i>
|
<i>
|
||||||
как пример одной из работ Остапова, значительно расширившего технику экспликации и практику применения
|
как пример одной из работ Остапова, значительно расширившего технику экспликации и практику применения
|
||||||
"бескванторных" выражений.
|
"бескванторных" выражений.
|
||||||
</i>
|
</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1990 Никитина Н.К., Постников В.В. Синтаксический анализатор текста рода структуры для МАКС,{' '}
|
1990 Постников В.В., Никитина Н.К. Синтаксический анализатор текста рода структуры для МАКС,{' '}
|
||||||
<i>являющийся первой попыткой реализовать автоматизированную проверку синтаксиса родов структур.</i>
|
<i>являющийся первой попыткой реализовать автоматизированную проверку синтаксиса родов структур.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1993 Костюк А.В., Никитина Н.К., Юдкин Ю.Ю. Программа визуализации М-графов, представляющих родовую структуру.
|
1993 Юдкин Ю.Ю., Костюк А.В., Никитина Н.К. Программа визуализации М-графов, представляющих родовую структуру.
|
||||||
</li>
|
</li>
|
||||||
<li>1993 Никитина Н.К., Чувашов Е.В. Система проектирования баз данных по их концептуальной модели.</li>
|
<li>1993 Никитина Н.К., Чувашов Е.В. Система проектирования баз данных по их концептуальной модели.</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -72,11 +72,11 @@ export function HelpContributors() {
|
||||||
PROLOG-программ, формирующих предметные интерпретации родоструктурных экспликаций Инттеор.
|
PROLOG-программ, формирующих предметные интерпретации родоструктурных экспликаций Инттеор.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1994 Кучкаров З.А., Ким В.Л. Разработка родоструктурных конструктов для библиотеки моделей и исследование
|
1994 Ким В.Л., Кучкаров З.А. Разработка родоструктурных конструктов для библиотеки моделей и исследование
|
||||||
возможностей их развития.
|
возможностей их развития.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1994 Коваль А.Г., Воробей П.Н. Редактор Программного комплекса Экстеор 1.5,{' '}
|
1994 Воробей П.Н., Коваль А.Г. Редактор Программного комплекса Экстеор 1.5,{' '}
|
||||||
<i>упростивший механизм печати экспликаций и улучшивший синтаксический анализ формального выражения.</i>
|
<i>упростивший механизм печати экспликаций и улучшивший синтаксический анализ формального выражения.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -84,16 +84,17 @@ export function HelpContributors() {
|
||||||
родоструктурного синтеза операционализированных терминальных концептуальных моделей Экстеор 2,{' '}
|
родоструктурного синтеза операционализированных терминальных концептуальных моделей Экстеор 2,{' '}
|
||||||
<i>ставшая первой версией реализации родоструктурного аппарата на C++ под Windows.</i>
|
<i>ставшая первой версией реализации родоструктурного аппарата на C++ под Windows.</i>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
1996 Никаноров С.П., Никитина Н.К., Климишин В.В. Автоматизированная система "Библиотека концептуальных схем",{' '}
|
1996 Климишин В.В., Никаноров С.П., Никитина Н.К. Автоматизированная система "Библиотека концептуальных схем",{' '}
|
||||||
<i>впервые определившая паспорт концептуальной схемы.</i>
|
<i>впервые определившая паспорт концептуальной схемы.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1997 Никитина Н.К., Юрьев О.И. Система поддержки процессов концептуального анализа и проектирования ПРОКСИМА
|
1997 Юрьев О.И., Никитина Н.К. Система поддержки процессов концептуального анализа и проектирования ПРОКСИМА
|
||||||
1.
|
1.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1998 Никитина Н.К., Гараева Ю.Р. Синтаксический анализатор выражений на языке родоструктурной экспликации для
|
1998 Гараева Ю.Р., Никитина Н.К. Синтаксический анализатор выражений на языке родоструктурной экспликации для
|
||||||
ПРОКСИМА 1.
|
ПРОКСИМА 1.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -101,12 +102,12 @@ export function HelpContributors() {
|
||||||
концептуального проектирования.
|
концептуального проектирования.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1999 Кучкаров З.А., Кононенко А.А. Программа преобразования родоструктурного синтеза операционализированных
|
1999 Кононенко А.А., Кучкаров З.А. Программа преобразования родоструктурного синтеза операционализированных
|
||||||
терминальных концептуальных моделей Экстеор 3,{' '}
|
терминальных концептуальных моделей Экстеор 3,{' '}
|
||||||
<i>впервые включившая операционную схему синтеза (дерево синтеза).</i>
|
<i>впервые включившая операционную схему синтеза (дерево синтеза).</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
1999 Никитина Н.К., Ландин Н.А. Разработка автоматизированной подсистемы, реализующей операции отслоения и
|
1999 Ландин Н.А., Никитина Н.К. Разработка автоматизированной подсистемы, реализующей операции отслоения и
|
||||||
рассечения над концептуальными схемами.
|
рассечения над концептуальными схемами.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -122,7 +123,7 @@ export function HelpContributors() {
|
||||||
</i>
|
</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2000 Кононенко А.А., Майоров В.А. Программа автоматизированной генерации структуры данных и их визуализации по
|
2000 Майоров В.А., Кононенко А.А. Программа автоматизированной генерации структуры данных и их визуализации по
|
||||||
концептуальной модели БДтеор,{' '}
|
концептуальной модели БДтеор,{' '}
|
||||||
<i>
|
<i>
|
||||||
определившая проблемы интерфейса наполнения концептуальной модели в сложных ступенях и предложившая
|
определившая проблемы интерфейса наполнения концептуальной модели в сложных ступенях и предложившая
|
||||||
|
@ -136,11 +137,11 @@ export function HelpContributors() {
|
||||||
</li>
|
</li>
|
||||||
<li>2000 Ключников А.В. Эквивалентность теорий родов структур.</li>
|
<li>2000 Ключников А.В. Эквивалентность теорий родов структур.</li>
|
||||||
<li>
|
<li>
|
||||||
2001 Кучкаров З.А., Никитин А.В. Исследование и построение типологии изменений теоретико-множественных
|
2001 Никитин А.В., Кучкаров З.А. Исследование и построение типологии изменений теоретико-множественных
|
||||||
интерпретаций класса декартового произведения.
|
интерпретаций класса декартового произведения.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2001 Кононенко А.А., Майоров В.А. Программа преобразования сети процедур из формата Оргтеор в формат BPWin
|
2001 Майоров В.А., Кононенко А.А. Программа преобразования сети процедур из формата Оргтеор в формат BPWin
|
||||||
(IDEF0).
|
(IDEF0).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -158,6 +159,7 @@ export function HelpContributors() {
|
||||||
2003 Юдкин Ю.Ю., Кудюкин Д.А. Разработка и испытание компьютерной программы, формирующей
|
2003 Юдкин Ю.Ю., Кудюкин Д.А. Разработка и испытание компьютерной программы, формирующей
|
||||||
теоретико-множественную интерпретацию терма частной родоструктурной теории.
|
теоретико-множественную интерпретацию терма частной родоструктурной теории.
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
2004 Кононенко А.А. Генерация кода на языке программирования C++ по тексту концептуальной схемы,
|
2004 Кононенко А.А. Генерация кода на языке программирования C++ по тексту концептуальной схемы,
|
||||||
эксплицированной в родах структур.
|
эксплицированной в родах структур.
|
||||||
|
@ -182,25 +184,25 @@ export function HelpContributors() {
|
||||||
2008 Пономарев И.Н. Об эквивалентной представимости рода структуры с помощью заданной типовой характеристики.
|
2008 Пономарев И.Н. Об эквивалентной представимости рода структуры с помощью заданной типовой характеристики.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2010 Кононенко А.А., Грязнов А.Д. Исследование и построение транслятора концептуальной схемы в концептуальную
|
2010 Грязнов А.Д., Кононенко А.А. Исследование и построение транслятора концептуальной схемы в концептуальную
|
||||||
модель.
|
модель.
|
||||||
</li>
|
</li>
|
||||||
<li>2010 Никаноров С.П. Введение в аппарат ступеней.</li>
|
<li>2010 Никаноров С.П. Введение в аппарат ступеней.</li>
|
||||||
<li>
|
<li>
|
||||||
2012 Кононенко А.А., Елисов Д.Н. Использование механизма XSD-схем для хранения и операционализации
|
2012 Елисов Д.Н., Кононенко А.А. Использование механизма XSD-схем для хранения и операционализации
|
||||||
концептуальных схем и концептуальных моделей с помощью XML.
|
концептуальных схем и концептуальных моделей с помощью XML.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2013 Кононенко А.А., Борисов И.Р. Исследование, разработка и экспериментальная программная реализация операций
|
2013 Борисов И.Р., Кононенко А.А. Исследование, разработка и экспериментальная программная реализация операций
|
||||||
над концептуальными моделями,{' '}
|
над концептуальными моделями,{' '}
|
||||||
<i>
|
<i>
|
||||||
впервые реализовавшая модуль прямого вычисления интерпретации формального выражения, встроенный в Экстеор
|
впервые реализовавшая модуль прямого вычисления интерпретации формального выражения, встроенный в Экстеор
|
||||||
3.5.
|
3.5.
|
||||||
</i>
|
</i>
|
||||||
</li>
|
</li>
|
||||||
<li>2013 Пономарев И.Н., Липатов А.А. Операции над родами структур и пример автоматизации их выполнения.</li>
|
<li>2013 Липатов А.А., Пономарев И.Н. Операции над родами структур и пример автоматизации их выполнения.</li>
|
||||||
<li>
|
<li>
|
||||||
2014 Борисов И.Р., Баширов Р.М. Исследования и программная реализации оптимальной структуры данных для
|
2014 Баширов Р.М., Борисов И.Р. Исследования и программная реализации оптимальной структуры данных для
|
||||||
вычисления интерпретации концептуальных схем.
|
вычисления интерпретации концептуальных схем.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -220,7 +222,7 @@ export function HelpContributors() {
|
||||||
</li>
|
</li>
|
||||||
<li>2015 Иванов А.Ю. Аппарат ступеней С.П. Никанорова и возможное развитие идей по его использованию.</li>
|
<li>2015 Иванов А.Ю. Аппарат ступеней С.П. Никанорова и возможное развитие идей по его использованию.</li>
|
||||||
<li>
|
<li>
|
||||||
2016 Борисов И.Р., Баширов Р.М. Исследование области компьютерной лингвистики и разработка модулей
|
2016 Баширов Р.М., Борисов И.Р. Исследование области компьютерной лингвистики и разработка модулей
|
||||||
терминологического контроля в Экстеор 4 и Microsoft Office Word,{' '}
|
терминологического контроля в Экстеор 4 и Microsoft Office Word,{' '}
|
||||||
<i>
|
<i>
|
||||||
являющееся основой библиотеки <TextURL text='cctext' href={external_urls.git_cctext} />.
|
являющееся основой библиотеки <TextURL text='cctext' href={external_urls.git_cctext} />.
|
||||||
|
@ -239,25 +241,25 @@ export function HelpContributors() {
|
||||||
теории (на примере родственных отношений).
|
теории (на примере родственных отношений).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2017 Борисов И.Р., Мурадов А.К. Организация операций над системами понятий посредством графических
|
2017 Мурадов А.К., Борисов И.Р. Организация операций над системами понятий посредством графических
|
||||||
интерфейсов, <i>заложивший основу для технологии Концепт.Блоки и блока графического синтеза.</i>
|
интерфейсов, <i>заложивший основу для технологии Концепт.Блоки и блока графического синтеза.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2018 Борисов И.Р., Князев А.В. Изучение методов концептуальной расчистки, разметки текстов и разработка
|
2018 Князев А.В., Борисов И.Р. Изучение методов концептуальной расчистки, разметки текстов и разработка
|
||||||
программных средств их автоматизации,{' '}
|
программных средств их автоматизации,{' '}
|
||||||
<i> — диплом, сформировавший основу для технологий Концепт.Разметка и Концепт.Майнинг.</i>
|
<i> — диплом, сформировавший основу для технологий Концепт.Разметка и Концепт.Майнинг.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2018 Никитин А.В., Болотин П.В. Исследование типологии изменения теоретико-множественной интерпретации класса
|
2018 Болотин П.В., Никитин А.В. Исследование типологии изменения теоретико-множественной интерпретации класса
|
||||||
множества подмножеств.
|
множества подмножеств.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2019 Борисов И.Р., Широкова Л.Р. Исследование возможностей применения методов машинного обучения для решения
|
2019 Широкова Л.Р., Борисов И.Р. Исследование возможностей применения методов машинного обучения для решения
|
||||||
задач расчистки текстов. Разработка прототипа программного модуля, —{' '}
|
задач расчистки текстов. Разработка прототипа программного модуля, —{' '}
|
||||||
<i>первая попытка внедрения технологий ИИ в текстовый модуль.</i>
|
<i>первая попытка внедрения технологий ИИ в текстовый модуль.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2020 Борисов И.Р., Пакулина Т.А. Исследование применения методов машинного обучения для выделения именованных
|
2020 Пакулина Т.А., Борисов И.Р. Исследование применения методов машинного обучения для выделения именованных
|
||||||
сущностей в текстах интервью. Экспериментальная разработка программного модуля расчистки текстов,{' '}
|
сущностей в текстах интервью. Экспериментальная разработка программного модуля расчистки текстов,{' '}
|
||||||
<i>ставшего расширением технологии Концепт.Расчистка.</i>
|
<i>ставшего расширением технологии Концепт.Расчистка.</i>
|
||||||
</li>
|
</li>
|
||||||
|
@ -266,12 +268,12 @@ export function HelpContributors() {
|
||||||
структур (рекурсивные и императивные выражения, фильтры, ASCII синтаксис).
|
структур (рекурсивные и императивные выражения, фильтры, ASCII синтаксис).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2021 Борисов И.Р., Демешко А.Б. Исследование и разработка программного модуля формирования текстов функций на
|
2021 Демешко А.Б., Борисов И.Р. Исследование и разработка программного модуля формирования текстов функций на
|
||||||
основе концепта функциональная структура,{' '}
|
основе концепта функциональная структура,{' '}
|
||||||
<i>дополнившего текстовый модуль возможностью работы с глагольными формами.</i>
|
<i>дополнившего текстовый модуль возможностью работы с глагольными формами.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2023 Борисов И.Р., Тулисов А.В. Разработка инструмента экспликации концептуальных схем в родоструктурной форме
|
2023 Тулисов А.В., Борисов И.Р. Разработка инструмента экспликации концептуальных схем в родоструктурной форме
|
||||||
через веб-интерфейс, — <i>разработка прототипа интерфейса КонцептПортал.</i>
|
через веб-интерфейс, — <i>разработка прототипа интерфейса КонцептПортал.</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -283,11 +285,16 @@ export function HelpContributors() {
|
||||||
</i>
|
</i>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
2024 Борисов И.Р., Хаданович Б.А. Исследование механизмов проведения сквозных изменений в операционной схеме
|
2024 Хаданович Б.А., Борисов И.Р. Исследование механизмов проведения сквозных изменений в операционной схеме
|
||||||
синтеза. Разработка прототипа веб-интерфейса синтеза концептуальных схем.
|
синтеза. Разработка прототипа веб-интерфейса синтеза концептуальных схем.
|
||||||
<i> Прототип графического интерфейса для синтеза концептуальных схем.</i>
|
<i> Прототип графического интерфейса для синтеза концептуальных схем.</i>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
<li>
|
||||||
|
2024 Викентьев М.И., Борисов И.Р. Исследование использования современных web-интерфейсов для визуализации
|
||||||
|
отношений для применения в рамках концептуального синтеза.{' '}
|
||||||
|
<i> Визуализации смешанных представлений концептуальной схемы.</i>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ export function HelpRules() {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Ожидаемое поведение</h2>
|
<h2>Ожидаемое поведение</h2>
|
||||||
|
<ul>
|
||||||
<li>взаимное уважением, поддержка в отношениях с участниками Портала.</li>
|
<li>взаимное уважением, поддержка в отношениях с участниками Портала.</li>
|
||||||
<li>
|
<li>
|
||||||
пожелания по доработке, найденные ошибки и иные предложения следует направлять по адресу email:{' '}
|
пожелания по доработке, найденные ошибки и иные предложения следует направлять по адресу email:{' '}
|
||||||
|
@ -50,6 +51,7 @@ export function HelpRules() {
|
||||||
осложнение создания (и/или поддержания) контента, созданного другими участниками.
|
осложнение создания (и/или поддержания) контента, созданного другими участниками.
|
||||||
</li>
|
</li>
|
||||||
<li>нарушение работоспособности Портала, в том числе путем использования уязвимостей и ошибок в коде.</li>
|
<li>нарушение работоспособности Портала, в том числе путем использования уязвимостей и ошибок в коде.</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ export function HelpRSLangOperations() {
|
||||||
<h2>
|
<h2>
|
||||||
<IconSortList size='1.25rem' className='inline-icon' /> Упорядочение
|
<IconSortList size='1.25rem' className='inline-icon' /> Упорядочение
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<ul>
|
||||||
Упорядочение списка конституент по следующим правилам
|
Упорядочение списка конституент по следующим правилам
|
||||||
<li>базисные и константные множества объявляются первыми</li>
|
<li>базисные и константные множества объявляются первыми</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -32,7 +32,7 @@ export function HelpRSLangOperations() {
|
||||||
<LinkTopic text='порожденные' topic={HelpTopic.CC_RELATIONS} /> конституенты следуют сразу за исходной
|
<LinkTopic text='порожденные' topic={HelpTopic.CC_RELATIONS} /> конституенты следуют сразу за исходной
|
||||||
</li>
|
</li>
|
||||||
<li>максимальное сохранение исходного порядка при выполнении предыдущих правил</li>
|
<li>максимальное сохранение исходного порядка при выполнении предыдущих правил</li>
|
||||||
</p>
|
</ul>
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
<IconGenerateNames size='1.25rem' className='inline-icon' /> Порядковые имена
|
<IconGenerateNames size='1.25rem' className='inline-icon' /> Порядковые имена
|
||||||
|
|
|
@ -10,6 +10,7 @@ export function HelpRSLangTemplates() {
|
||||||
Источником шаблонов является <b>Банк выражений</b>, содержащий параметризованные понятия и утверждения,
|
Источником шаблонов является <b>Банк выражений</b>, содержащий параметризованные понятия и утверждения,
|
||||||
сгруппированные по разделам
|
сгруппированные по разделам
|
||||||
</p>
|
</p>
|
||||||
|
<ul>
|
||||||
<li>Сначала выбирается шаблон выражения (вкладка Шаблон)</li>
|
<li>Сначала выбирается шаблон выражения (вкладка Шаблон)</li>
|
||||||
<li>
|
<li>
|
||||||
Далее для аргументов можно зафиксировать значения, выбрав из конституент текущей схемы или указав выражения
|
Далее для аргументов можно зафиксировать значения, выбрав из конституент текущей схемы или указав выражения
|
||||||
|
@ -21,6 +22,7 @@ export function HelpRSLangTemplates() {
|
||||||
<li>
|
<li>
|
||||||
Кнопка <b>Создать</b> инициирует добавление выбранной конституенты в схему
|
Кнопка <b>Создать</b> инициирует добавление выбранной конституенты в схему
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ export function HelpRSLangTypes() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Типизация</h1>
|
<h1>Типизация</h1>
|
||||||
<p>
|
<ul>
|
||||||
Родоструктурное выражение <code>ξ</code> обладает типизацией (структурой), если выполнено ξ∈H,
|
Родоструктурное выражение <code>ξ</code> обладает типизацией (структурой), если выполнено ξ∈H,
|
||||||
<br />
|
<br />
|
||||||
где <code>H</code> – корректное выражение <b>ступени</b>, задаваемой следующими правилами:
|
где <code>H</code> – корректное выражение <b>ступени</b>, задаваемой следующими правилами:
|
||||||
|
@ -18,7 +18,7 @@ export function HelpRSLangTypes() {
|
||||||
<li>
|
<li>
|
||||||
<code>ℬ(H)</code> – ступень, называемая <b>множеством</b>.
|
<code>ℬ(H)</code> – ступень, называемая <b>множеством</b>.
|
||||||
</li>
|
</li>
|
||||||
</p>
|
</ul>
|
||||||
<p>Пустое множество ∅ имеет типизацию ℬ(R0) – множество с произвольной структурой элемента</p>
|
<p>Пустое множество ∅ имеет типизацию ℬ(R0) – множество с произвольной структурой элемента</p>
|
||||||
<p>
|
<p>
|
||||||
Для обобщения понятия типизация на логические и параметризованные выражения вводится ряд дополнительных
|
Для обобщения понятия типизация на логические и параметризованные выражения вводится ряд дополнительных
|
||||||
|
|
|
@ -4,11 +4,15 @@ export function HelpFormulaTree() {
|
||||||
<h1>Дерево разбора выражения</h1>
|
<h1>Дерево разбора выражения</h1>
|
||||||
<p>Дерево получено путем семантических преобразований дерева синтаксического разбора.</p>
|
<p>Дерево получено путем семантических преобразований дерева синтаксического разбора.</p>
|
||||||
<p>Оно отражает структуру грамматически корректного выражения языка родов структур.</p>
|
<p>Оно отражает структуру грамматически корректного выражения языка родов структур.</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
<li>Порядок узлов в рамках одного уровня может отличаться от их порядка в выражении</li>
|
<li>Порядок узлов в рамках одного уровня может отличаться от их порядка в выражении</li>
|
||||||
<li>При наведении курсора на узел в тексте выделяется соответствующий ему фрагмент</li>
|
<li>При наведении курсора на узел в тексте выделяется соответствующий ему фрагмент</li>
|
||||||
<li>Текст в узле дерева соответствует элементу языка</li>
|
<li>Текст в узле дерева соответствует элементу языка</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Виды узлов</h2>
|
<h2>Виды узлов</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<span className='bg-(--acc-bg-green)'>объявление идентификатора</span>
|
<span className='bg-(--acc-bg-green)'>объявление идентификатора</span>
|
||||||
</li>
|
</li>
|
||||||
|
@ -27,6 +31,14 @@ export function HelpFormulaTree() {
|
||||||
<li>
|
<li>
|
||||||
<span className='bg-secondary'>составные выражения</span>
|
<span className='bg-secondary'>составные выражения</span>
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Команды</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<kbd>Space</kbd> – перемещение экрана
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ export function HelpLibrary() {
|
||||||
<LinkTopic text='операционные схемы синтеза' topic={HelpTopic.CC_OSS} /> (ОСС).
|
<LinkTopic text='операционные схемы синтеза' topic={HelpTopic.CC_OSS} /> (ОСС).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<span className='text-(--acc-fg-green)'>зеленым текстом</span> выделены ОСС
|
<span className='text-(--acc-fg-green)'>зеленым текстом</span> выделены ОСС
|
||||||
</li>
|
</li>
|
||||||
|
@ -63,8 +64,10 @@ export function HelpLibrary() {
|
||||||
<li>
|
<li>
|
||||||
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Поиск
|
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Поиск
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Режим: Проводник</h2>
|
<h2>Режим: Проводник</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconFolderEdit size='1rem' className='inline-icon' /> переименовать выбранную
|
<IconFolderEdit size='1rem' className='inline-icon' /> переименовать выбранную
|
||||||
</li>
|
</li>
|
||||||
|
@ -95,6 +98,7 @@ export function HelpLibrary() {
|
||||||
<li>
|
<li>
|
||||||
<IconFolderOpened size='1rem' className='inline-icon icon-green' /> развернутая папка
|
<IconFolderOpened size='1rem' className='inline-icon icon-green' /> развернутая папка
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,8 @@ export function HelpOssGraph() {
|
||||||
<h1 className='sm:pr-24'>Граф синтеза</h1>
|
<h1 className='sm:pr-24'>Граф синтеза</h1>
|
||||||
<div className='flex flex-col sm:flex-row'>
|
<div className='flex flex-col sm:flex-row'>
|
||||||
<div className='sm:w-56'>
|
<div className='sm:w-56'>
|
||||||
<h1>Настройка графа</h1>
|
<h2>Настройка графа</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconReset className='inline-icon' /> Сбросить изменения
|
<IconReset className='inline-icon' /> Сбросить изменения
|
||||||
</li>
|
</li>
|
||||||
|
@ -64,12 +65,14 @@ export function HelpOssGraph() {
|
||||||
<li>
|
<li>
|
||||||
черта слева - КС <LinkTopic text='внешняя' topic={HelpTopic.CC_OSS} />
|
черта слева - КС <LinkTopic text='внешняя' topic={HelpTopic.CC_OSS} />
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider vertical margins='mx-3 mt-3' className='hidden sm:block' />
|
<Divider vertical margins='mx-3 mt-3' className='hidden sm:block' />
|
||||||
|
|
||||||
<div className='sm:w-84'>
|
<div className='sm:w-84'>
|
||||||
<h1>Изменение узлов</h1>
|
<h2>Изменение узлов</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<kbd>Клик</kbd> на операцию – выделение
|
<kbd>Клик</kbd> на операцию – выделение
|
||||||
</li>
|
</li>
|
||||||
|
@ -91,14 +94,16 @@ export function HelpOssGraph() {
|
||||||
<li>
|
<li>
|
||||||
<IconDestroy className='inline-icon icon-red' /> <kbd>Delete</kbd> – удалить выбранные
|
<IconDestroy className='inline-icon icon-red' /> <kbd>Delete</kbd> – удалить выбранные
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider margins='my-3' className='hidden sm:block' />
|
<Divider margins='my-2' className='hidden sm:block' />
|
||||||
|
|
||||||
<div className='flex flex-col-reverse mb-3 sm:flex-row'>
|
<div className='flex flex-col-reverse mb-3 sm:flex-row'>
|
||||||
<div className='sm:w-56'>
|
<div className='sm:w-56'>
|
||||||
<h1>Общие</h1>
|
<h2>Общие</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconSave className='inline-icon' /> Сохранить положения
|
<IconSave className='inline-icon' /> Сохранить положения
|
||||||
</li>
|
</li>
|
||||||
|
@ -108,12 +113,14 @@ export function HelpOssGraph() {
|
||||||
<li>
|
<li>
|
||||||
<kbd>Shift</kbd> – перемещение выделенных элементов в границах родителя
|
<kbd>Shift</kbd> – перемещение выделенных элементов в границах родителя
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||||
|
|
||||||
<div className='dense w-84'>
|
<div className='dense w-84'>
|
||||||
<h1>Контекстное меню</h1>
|
<h2>Контекстное меню</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconRSForm className='inline-icon icon-green' /> Статус связанной{' '}
|
<IconRSForm className='inline-icon icon-green' /> Статус связанной{' '}
|
||||||
<LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
|
<LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
|
||||||
|
@ -135,6 +142,7 @@ export function HelpOssGraph() {
|
||||||
<li>
|
<li>
|
||||||
<IconExecute className='inline-icon icon-green' /> Активировать операцию
|
<IconExecute className='inline-icon icon-green' /> Активировать операцию
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,6 +12,7 @@ export function HelpRelocateCst() {
|
||||||
другую КС (целевую) в рамках одной <IconOSS size='1rem' className='inline-icon' />{' '}
|
другую КС (целевую) в рамках одной <IconOSS size='1rem' className='inline-icon' />{' '}
|
||||||
<LinkTopic text='операционной схемы синтеза' topic={HelpTopic.CC_OSS} />.
|
<LinkTopic text='операционной схемы синтеза' topic={HelpTopic.CC_OSS} />.
|
||||||
</p>
|
</p>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
только для <IconPredecessor size='1rem' className='inline-icon' /> собственных конституент источника
|
только для <IconPredecessor size='1rem' className='inline-icon' /> собственных конституент источника
|
||||||
</li>
|
</li>
|
||||||
|
@ -19,16 +20,21 @@ export function HelpRelocateCst() {
|
||||||
<IconMoveUp size='1rem' className='inline-icon' />
|
<IconMoveUp size='1rem' className='inline-icon' />
|
||||||
<IconMoveDown size='1rem' className='inline-icon' /> направление переноса - вверх или вниз по дереву синтеза
|
<IconMoveDown size='1rem' className='inline-icon' /> направление переноса - вверх или вниз по дереву синтеза
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Перенос вверх</h2>
|
<h2>Перенос вверх</h2>
|
||||||
|
<ul>
|
||||||
<li>выбранные конституенты становятся наследованными, а их копии добавляются в целевую КС</li>
|
<li>выбранные конституенты становятся наследованными, а их копии добавляются в целевую КС</li>
|
||||||
<li>нельзя выбирать конституенты, зависящие от конституент других концептуальных схем</li>
|
<li>нельзя выбирать конституенты, зависящие от конституент других концептуальных схем</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Перенос вниз</h2>
|
<h2>Перенос вниз</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
выбранные конституенты становятся собственными конституентами целевой КС, удаляются из исходной КС и ее
|
выбранные конституенты становятся собственными конституентами целевой КС, удаляются из исходной КС и ее
|
||||||
наследников
|
наследников
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ export function HelpRSCard() {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Управление</h2>
|
<h2>Управление</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -57,6 +58,7 @@ export function HelpRSCard() {
|
||||||
<li>
|
<li>
|
||||||
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,9 @@ export function HelpRSEditor() {
|
||||||
<div className='dense'>
|
<div className='dense'>
|
||||||
<h1>Редактор конституенты</h1>
|
<h1>Редактор конституенты</h1>
|
||||||
<div className='flex flex-col sm:flex-row sm:gap-3'>
|
<div className='flex flex-col sm:flex-row sm:gap-3'>
|
||||||
<div className='flex flex-col'>
|
<div>
|
||||||
|
<h2>Команды</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconOSS className='inline-icon' /> переход к <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
<IconOSS className='inline-icon' /> переход к <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -52,10 +54,12 @@ export function HelpRSEditor() {
|
||||||
<li>
|
<li>
|
||||||
<IconDestroy className='inline-icon icon-red' /> удалить
|
<IconDestroy className='inline-icon icon-red' /> удалить
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col'>
|
<div>
|
||||||
<h2>Список конституент</h2>
|
<h2>Список конституент</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconMoveDown className='inline-icon' />
|
<IconMoveDown className='inline-icon' />
|
||||||
<IconMoveUp className='inline-icon' /> <kbd>Alt + вверх/вниз</kbd>
|
<IconMoveUp className='inline-icon' /> <kbd>Alt + вверх/вниз</kbd>
|
||||||
|
@ -80,10 +84,12 @@ export function HelpRSEditor() {
|
||||||
<LinkTopic text='порожденные' topic={HelpTopic.CC_RELATIONS} /> текущей
|
<LinkTopic text='порожденные' topic={HelpTopic.CC_RELATIONS} /> текущей
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Формальное определение</h2>
|
<h2>Формальное определение</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconStatusOK className='inline-icon' /> индикатор статуса определения сверху
|
<IconStatusOK className='inline-icon' /> индикатор статуса определения сверху
|
||||||
</li>
|
</li>
|
||||||
|
@ -101,15 +107,19 @@ export function HelpRSEditor() {
|
||||||
<li>
|
<li>
|
||||||
<kbd>Ctrl + Пробел</kbd> вставка незанятого имени / замена проекции
|
<kbd>Ctrl + Пробел</kbd> вставка незанятого имени / замена проекции
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Термин и Текстовое определение</h2>
|
<h2>Термин и Текстовое определение</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconEdit className='inline-icon' /> редактирование <LinkTopic text='Имени' topic={HelpTopic.CC_CONSTITUENTA} />{' '}
|
<IconEdit className='inline-icon' /> редактирование{' '}
|
||||||
/ <LinkTopic text='Термина' topic={HelpTopic.CC_CONSTITUENTA} />
|
<LinkTopic text='Имени' topic={HelpTopic.CC_CONSTITUENTA} /> /{' '}
|
||||||
|
<LinkTopic text='Термина' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<kbd>Ctrl + Пробел</kbd> открывает редактирование отсылок
|
<kbd>Ctrl + Пробел</kbd> открывает редактирование отсылок
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ export function HelpRSGraphTerm() {
|
||||||
<h1>Граф термов</h1>
|
<h1>Граф термов</h1>
|
||||||
<div className='flex flex-col sm:flex-row'>
|
<div className='flex flex-col sm:flex-row'>
|
||||||
<div className='sm:w-56'>
|
<div className='sm:w-56'>
|
||||||
<h1>Настройка графа</h1>
|
<h2>Настройка графа</h2>
|
||||||
|
<ul>
|
||||||
<li>Цвет – покраска узлов</li>
|
<li>Цвет – покраска узлов</li>
|
||||||
<li>
|
<li>
|
||||||
<IconText className='inline-icon' /> Отображение текста
|
<IconText className='inline-icon' /> Отображение текста
|
||||||
|
@ -41,12 +42,14 @@ export function HelpRSGraphTerm() {
|
||||||
<li>
|
<li>
|
||||||
<IconRotate3D className='inline-icon' /> Вращение 3D
|
<IconRotate3D className='inline-icon' /> Вращение 3D
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider vertical margins='mx-3 mt-3' className='hidden sm:block' />
|
<Divider vertical margins='mx-3 mt-3' className='hidden sm:block' />
|
||||||
|
|
||||||
<div className='sm:w-84'>
|
<div className='sm:w-84'>
|
||||||
<h1>Изменение узлов</h1>
|
<h2>Изменение узлов</h2>
|
||||||
|
<ul>
|
||||||
<li>Клик на узел – выделение</li>
|
<li>Клик на узел – выделение</li>
|
||||||
<li>Левый клик – выбор фокус-конституенты</li>
|
<li>Левый клик – выбор фокус-конституенты</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -61,6 +64,7 @@ export function HelpRSGraphTerm() {
|
||||||
<li>
|
<li>
|
||||||
<IconNewItem className='inline-icon icon-green' /> Новая со ссылками на выделенные
|
<IconNewItem className='inline-icon icon-green' /> Новая со ссылками на выделенные
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -68,7 +72,11 @@ export function HelpRSGraphTerm() {
|
||||||
|
|
||||||
<div className='flex flex-col-reverse mb-3 sm:flex-row'>
|
<div className='flex flex-col-reverse mb-3 sm:flex-row'>
|
||||||
<div className='sm:w-56'>
|
<div className='sm:w-56'>
|
||||||
<h1>Общие</h1>
|
<h2>Общие</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<kbd>Space</kbd> – перемещение экрана
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -85,12 +93,14 @@ export function HelpRSGraphTerm() {
|
||||||
<IconTypeGraph className='inline-icon' /> Открыть{' '}
|
<IconTypeGraph className='inline-icon' /> Открыть{' '}
|
||||||
<LinkTopic text='граф ступеней' topic={HelpTopic.UI_TYPE_GRAPH} />
|
<LinkTopic text='граф ступеней' topic={HelpTopic.UI_TYPE_GRAPH} />
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||||
|
|
||||||
<div className='dense w-84'>
|
<div className='dense w-84'>
|
||||||
<h1>Выделение</h1>
|
<h2>Выделение</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconGraphCollapse className='inline-icon' /> все влияющие
|
<IconGraphCollapse className='inline-icon' /> все влияющие
|
||||||
</li>
|
</li>
|
||||||
|
@ -113,6 +123,7 @@ export function HelpRSGraphTerm() {
|
||||||
<IconPredecessor className='inline-icon' /> выделить{' '}
|
<IconPredecessor className='inline-icon' /> выделить{' '}
|
||||||
<LinkTopic text='собственные' topic={HelpTopic.CC_PROPAGATION} />
|
<LinkTopic text='собственные' topic={HelpTopic.CC_PROPAGATION} />
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,6 +19,7 @@ export function HelpRSList() {
|
||||||
return (
|
return (
|
||||||
<div className='dense'>
|
<div className='dense'>
|
||||||
<h1>Список конституент</h1>
|
<h1>Список конституент</h1>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconAlias className='inline-icon' />
|
<IconAlias className='inline-icon' />
|
||||||
Конституенты обладают уникальным <LinkTopic text='Именем' topic={HelpTopic.CC_CONSTITUENTA} />
|
Конституенты обладают уникальным <LinkTopic text='Именем' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||||
|
@ -27,8 +28,10 @@ export function HelpRSList() {
|
||||||
<li>
|
<li>
|
||||||
пунктиром отображаются <LinkTopic text='наследованные' topic={HelpTopic.CC_OSS} /> конституенты
|
пунктиром отображаются <LinkTopic text='наследованные' topic={HelpTopic.CC_OSS} /> конституенты
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Управление списком</h2>
|
<h2>Управление списком</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -62,6 +65,7 @@ export function HelpRSList() {
|
||||||
<li>
|
<li>
|
||||||
<IconDestroy className='inline-icon icon-red' /> удаление выделенных: <kbd>Delete</kbd>
|
<IconDestroy className='inline-icon icon-red' /> удаление выделенных: <kbd>Delete</kbd>
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<Divider margins='my-2' />
|
<Divider margins='my-2' />
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ export function HelpRSMenu() {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Вкладки</h2>
|
<h2>Вкладки</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<LinkTopic text='Карточка' topic={HelpTopic.UI_RS_CARD} /> – редактирование атрибутов схемы и версии
|
<LinkTopic text='Карточка' topic={HelpTopic.UI_RS_CARD} /> – редактирование атрибутов схемы и версии
|
||||||
</li>
|
</li>
|
||||||
|
@ -41,12 +42,15 @@ export function HelpRSMenu() {
|
||||||
<LinkTopic text='Конституенты' topic={HelpTopic.CC_CONSTITUENTA} />
|
<LinkTopic text='Конституенты' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<LinkTopic text='Граф термов' topic={HelpTopic.UI_GRAPH_TERM} /> – графическое представление связей конституент
|
<LinkTopic text='Граф термов' topic={HelpTopic.UI_GRAPH_TERM} /> – графическое представление связей
|
||||||
|
конституент
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div className='flex my-3'>
|
<div className='flex my-3'>
|
||||||
<div>
|
<div>
|
||||||
<h2>Меню схемы</h2>
|
<h2>Меню схемы</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconMenu size='1.25rem' className='inline-icon' /> Меню схемы – выпадающее меню с общими функциями
|
<IconMenu size='1.25rem' className='inline-icon' /> Меню схемы – выпадающее меню с общими функциями
|
||||||
</li>
|
</li>
|
||||||
|
@ -71,18 +75,21 @@ export function HelpRSMenu() {
|
||||||
<li>
|
<li>
|
||||||
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider vertical margins='mx-3' />
|
<Divider vertical margins='mx-3' />
|
||||||
|
|
||||||
<div className='w-72'>
|
<div className='w-72'>
|
||||||
<h2>Режимы работы</h2>
|
<h2>Режимы работы</h2>
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconAlert size='1.25rem' className='inline-icon icon-red' /> работа в анонимном режиме. Переход на страницу
|
<IconAlert size='1.25rem' className='inline-icon icon-red' /> работа в анонимном режиме. Переход на
|
||||||
логина
|
страницу логина
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconArchive size='1.25rem' className='inline-icon' /> просмотр архивной версии. Переход к актуальной версии
|
<IconArchive size='1.25rem' className='inline-icon' /> просмотр архивной версии. Переход к актуальной
|
||||||
|
версии
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconReader size='1.25rem' className='inline-icon' /> режим Читатель
|
<IconReader size='1.25rem' className='inline-icon' /> режим Читатель
|
||||||
|
@ -96,6 +103,7 @@ export function HelpRSMenu() {
|
||||||
<li>
|
<li>
|
||||||
<IconAdmin size='1.25rem' className='inline-icon' /> режим Администратор
|
<IconAdmin size='1.25rem' className='inline-icon' /> режим Администратор
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ export function HelpSubstitutions() {
|
||||||
<p>Пара отождествлений, обозначает замену вхождений одной конституенты на другую.</p>
|
<p>Пара отождествлений, обозначает замену вхождений одной конституенты на другую.</p>
|
||||||
<p>
|
<p>
|
||||||
Таблица отождествлений накладывает следующие ограничения:
|
Таблица отождествлений накладывает следующие ограничения:
|
||||||
|
<ul>
|
||||||
<li>конституента может быть удаляемой только в одном отождествлении</li>
|
<li>конституента может быть удаляемой только в одном отождествлении</li>
|
||||||
<li>удаляемые конституенты не могут быть замещающими в отождествлениях</li>
|
<li>удаляемые конституенты не могут быть замещающими в отождествлениях</li>
|
||||||
<li>базисные множества могут замещать только другие базисные множества</li>
|
<li>базисные множества могут замещать только другие базисные множества</li>
|
||||||
|
@ -15,6 +16,7 @@ export function HelpSubstitutions() {
|
||||||
</li>
|
</li>
|
||||||
<li>логические выражения могут замещать только другие логические выражения</li>
|
<li>логические выражения могут замещать только другие логические выражения</li>
|
||||||
<li>при отождествлении параметризованных конституент количество и типизации аргументов должно совпадать</li>
|
<li>при отождествлении параметризованных конституент количество и типизации аргументов должно совпадать</li>
|
||||||
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,17 +7,23 @@ export function HelpTypeGraph() {
|
||||||
<h1>Граф ступеней</h1>
|
<h1>Граф ступеней</h1>
|
||||||
<p>
|
<p>
|
||||||
Граф связей между ступенями, используемыми в данном выражении или{' '}
|
Граф связей между ступенями, используемыми в данном выражении или{' '}
|
||||||
<LinkTopic text='КС' topic={HelpTopic.CC_OSS} />. Исторически отображался в форме мультиграфа (М-граф). В
|
<LinkTopic text='КС' topic={HelpTopic.CC_OSS} />.<br />
|
||||||
Портале кратные ребра представлены перечислением индексов компонент произведения.
|
Исторически отображался в форме мультиграфа (М-граф).
|
||||||
|
<br />
|
||||||
|
Кратные ребра представлены перечислением индексов компонент произведения.
|
||||||
</p>
|
</p>
|
||||||
|
<ul>
|
||||||
<li>ребра без надписей означают взятие булеана</li>
|
<li>ребра без надписей означают взятие булеана</li>
|
||||||
<li>цифры на ребрах означают номера компонент декартова произведения</li>
|
<li>цифры на ребрах означают номера компонент декартова произведения</li>
|
||||||
<li>цифры на узлах означают количество конституент в данной ступени</li>
|
<li>цифры на узлах означают количество конституент в данной ступени</li>
|
||||||
<li>основаниями дерева являются ступени базисных, константных множеств</li>
|
<li>основаниями дерева являются ступени базисных, константных множеств</li>
|
||||||
<li>ступень терм-функции - произведение ступеней результата и аргументов</li>
|
<li>ступень терм-функции - произведение ступеней результата и аргументов</li>
|
||||||
<li>ступень предикат-функции - произведение ступеней аргументов</li>
|
<li>ступень предикат-функции - произведение ступеней аргументов</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>Виды узлов</h2>
|
<h2>Цвета узлов</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<span className='bg-secondary'>ступень-основание</span>
|
<span className='bg-secondary'>ступень-основание</span>
|
||||||
</li>
|
</li>
|
||||||
|
@ -27,6 +33,14 @@ export function HelpTypeGraph() {
|
||||||
<li>
|
<li>
|
||||||
<span className='bg-accent-orange'>ступень декартова произведения</span>
|
<span className='bg-accent-orange'>ступень декартова произведения</span>
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Команды</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<kbd>Space</kbd> – перемещение экрана
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,13 @@ export const useRenameLocation = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [libraryApi.baseKey, 'rename-location'],
|
mutationKey: [libraryApi.baseKey, 'rename-location'],
|
||||||
mutationFn: libraryApi.renameLocation,
|
mutationFn: libraryApi.renameLocation,
|
||||||
onSuccess: () =>
|
onSuccess: async () => {
|
||||||
Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.library] }),
|
client.invalidateQueries({ queryKey: [KEYS.library] }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] }),
|
client.invalidateQueries({ queryKey: [KEYS.rsform] }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] })
|
client.invalidateQueries({ queryKey: [KEYS.oss] })
|
||||||
]),
|
]);
|
||||||
|
},
|
||||||
onError: () => client.invalidateQueries()
|
onError: () => client.invalidateQueries()
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -16,12 +16,12 @@ export const useSetAccessPolicy = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setAccessPolicy,
|
mutationFn: libraryApi.setAccessPolicy,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: async (_, variables) => {
|
||||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, access_policy: variables.policy });
|
client.setQueryData(ossKey, { ...ossData, access_policy: variables.policy });
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
...ossData.operations
|
...ossData.operations
|
||||||
.map(item => {
|
.map(item => {
|
||||||
|
@ -33,6 +33,7 @@ export const useSetAccessPolicy = () => {
|
||||||
})
|
})
|
||||||
.filter(item => !!item)
|
.filter(item => !!item)
|
||||||
]);
|
]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||||
|
|
|
@ -12,12 +12,12 @@ export const useSetEditors = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setEditors,
|
mutationFn: libraryApi.setEditors,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: async (_, variables) => {
|
||||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, editors: variables.editors });
|
client.setQueryData(ossKey, { ...ossData, editors: variables.editors });
|
||||||
return Promise.allSettled(
|
await Promise.allSettled(
|
||||||
ossData.operations
|
ossData.operations
|
||||||
.map(item => {
|
.map(item => {
|
||||||
if (!item.result) {
|
if (!item.result) {
|
||||||
|
@ -28,6 +28,7 @@ export const useSetEditors = () => {
|
||||||
})
|
})
|
||||||
.filter(item => !!item)
|
.filter(item => !!item)
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||||
|
|
|
@ -16,12 +16,12 @@ export const useSetLocation = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
||||||
mutationFn: libraryApi.setLocation,
|
mutationFn: libraryApi.setLocation,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: async (_, variables) => {
|
||||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, location: variables.location });
|
client.setQueryData(ossKey, { ...ossData, location: variables.location });
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
|
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
|
||||||
...ossData.operations
|
...ossData.operations
|
||||||
.map(item => {
|
.map(item => {
|
||||||
|
@ -33,6 +33,7 @@ export const useSetLocation = () => {
|
||||||
})
|
})
|
||||||
.filter(item => !!item)
|
.filter(item => !!item)
|
||||||
]);
|
]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||||
|
|
|
@ -16,12 +16,12 @@ export const useSetOwner = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-owner'],
|
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-owner'],
|
||||||
mutationFn: libraryApi.setOwner,
|
mutationFn: libraryApi.setOwner,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: async (_, variables) => {
|
||||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||||
if (ossData) {
|
if (ossData) {
|
||||||
client.setQueryData(ossKey, { ...ossData, owner: variables.owner });
|
client.setQueryData(ossKey, { ...ossData, owner: variables.owner });
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
|
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
|
||||||
...ossData.operations
|
...ossData.operations
|
||||||
.map(item => {
|
.map(item => {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const useUpdateItem = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'update-item'],
|
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'update-item'],
|
||||||
mutationFn: libraryApi.updateItem,
|
mutationFn: libraryApi.updateItem,
|
||||||
onSuccess: (data: ILibraryItem) => {
|
onSuccess: async (data: ILibraryItem) => {
|
||||||
const itemKey =
|
const itemKey =
|
||||||
data.item_type === LibraryItemType.RSFORM
|
data.item_type === LibraryItemType.RSFORM
|
||||||
? KEYS.composite.rsItem({ itemID: data.id })
|
? KEYS.composite.rsItem({ itemID: data.id })
|
||||||
|
@ -30,7 +30,7 @@ export const useUpdateItem = () => {
|
||||||
if (data.item_type === LibraryItemType.RSFORM) {
|
if (data.item_type === LibraryItemType.RSFORM) {
|
||||||
const schema: IRSFormDTO | undefined = client.getQueryData(itemKey);
|
const schema: IRSFormDTO | undefined = client.getQueryData(itemKey);
|
||||||
if (schema) {
|
if (schema) {
|
||||||
return Promise.allSettled(
|
await Promise.allSettled(
|
||||||
schema.oss.map(item => client.invalidateQueries({ queryKey: KEYS.composite.ossItem({ itemID: item.id }) }))
|
schema.oss.map(item => client.invalidateQueries({ queryKey: KEYS.composite.ossItem({ itemID: item.id }) }))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { cn } from '@/components/utils';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { tripleToggleColor } from '@/utils/utils';
|
import { tripleToggleColor } from '@/utils/utils';
|
||||||
|
|
||||||
|
import { useLibrarySuspense } from '../../backend/use-library';
|
||||||
import { IconItemVisibility } from '../../components/icon-item-visibility';
|
import { IconItemVisibility } from '../../components/icon-item-visibility';
|
||||||
import { IconLocationHead } from '../../components/icon-location-head';
|
import { IconLocationHead } from '../../components/icon-location-head';
|
||||||
import { describeLocationHead, labelLocationHead } from '../../labels';
|
import { describeLocationHead, labelLocationHead } from '../../labels';
|
||||||
|
@ -33,6 +34,7 @@ interface ToolbarSearchProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
||||||
|
const { items } = useLibrarySuspense();
|
||||||
const userMenu = useDropdown();
|
const userMenu = useDropdown();
|
||||||
const headMenu = useDropdown();
|
const headMenu = useDropdown();
|
||||||
|
|
||||||
|
@ -58,6 +60,10 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
||||||
|
|
||||||
const userActive = isOwned !== null || isEditor !== null || filterUser !== null;
|
const userActive = isOwned !== null || isEditor !== null || filterUser !== null;
|
||||||
|
|
||||||
|
function filterNonEmptyUsers(userID: number): boolean {
|
||||||
|
return items.some(item => item.owner === userID);
|
||||||
|
}
|
||||||
|
|
||||||
function handleChange(newValue: LocationHead | null) {
|
function handleChange(newValue: LocationHead | null) {
|
||||||
headMenu.hide();
|
headMenu.hide();
|
||||||
setHead(newValue);
|
setHead(newValue);
|
||||||
|
@ -114,6 +120,7 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
||||||
placeholder='Выберите владельца'
|
placeholder='Выберите владельца'
|
||||||
noBorder
|
noBorder
|
||||||
className='min-w-60 mx-1 mb-1'
|
className='min-w-60 mx-1 mb-1'
|
||||||
|
filter={filterNonEmptyUsers}
|
||||||
value={filterUser}
|
value={filterUser}
|
||||||
onChange={setFilterUser}
|
onChange={setFilterUser}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,7 +7,15 @@ import { type ILibraryItem } from '@/features/library';
|
||||||
import { Graph } from '@/models/graph';
|
import { Graph } from '@/models/graph';
|
||||||
import { type RO } from '@/utils/meta';
|
import { type RO } from '@/utils/meta';
|
||||||
|
|
||||||
import { type IBlock, type IOperation, type IOperationSchema, type IOperationSchemaStats } from '../models/oss';
|
import {
|
||||||
|
type IBlock,
|
||||||
|
type IOperation,
|
||||||
|
type IOperationSchema,
|
||||||
|
type IOperationSchemaStats,
|
||||||
|
type IOssItem,
|
||||||
|
NodeType
|
||||||
|
} from '../models/oss';
|
||||||
|
import { constructNodeID } from '../models/oss-api';
|
||||||
import { BLOCK_NODE_MIN_HEIGHT, BLOCK_NODE_MIN_WIDTH } from '../pages/oss-page/editor-oss-graph/graph/block-node';
|
import { BLOCK_NODE_MIN_HEIGHT, BLOCK_NODE_MIN_WIDTH } from '../pages/oss-page/editor-oss-graph/graph/block-node';
|
||||||
|
|
||||||
import { type IOperationSchemaDTO, OperationType } from './types';
|
import { type IOperationSchemaDTO, OperationType } from './types';
|
||||||
|
@ -16,8 +24,9 @@ import { type IOperationSchemaDTO, OperationType } from './types';
|
||||||
export class OssLoader {
|
export class OssLoader {
|
||||||
private oss: IOperationSchema;
|
private oss: IOperationSchema;
|
||||||
private graph: Graph = new Graph();
|
private graph: Graph = new Graph();
|
||||||
private hierarchy: Graph = new Graph();
|
private hierarchy: Graph<string> = new Graph<string>();
|
||||||
private operationByID = new Map<number, IOperation>();
|
private operationByID = new Map<number, IOperation>();
|
||||||
|
private itemByNodeID = new Map<string, IOssItem>();
|
||||||
private blockByID = new Map<number, IBlock>();
|
private blockByID = new Map<number, IBlock>();
|
||||||
private schemaIDs: number[] = [];
|
private schemaIDs: number[] = [];
|
||||||
private items: RO<ILibraryItem[]>;
|
private items: RO<ILibraryItem[]>;
|
||||||
|
@ -37,6 +46,7 @@ export class OssLoader {
|
||||||
|
|
||||||
result.operationByID = this.operationByID;
|
result.operationByID = this.operationByID;
|
||||||
result.blockByID = this.blockByID;
|
result.blockByID = this.blockByID;
|
||||||
|
result.itemByNodeID = this.itemByNodeID;
|
||||||
result.graph = this.graph;
|
result.graph = this.graph;
|
||||||
result.hierarchy = this.hierarchy;
|
result.hierarchy = this.hierarchy;
|
||||||
result.schemas = this.schemaIDs;
|
result.schemas = this.schemaIDs;
|
||||||
|
@ -46,18 +56,24 @@ export class OssLoader {
|
||||||
|
|
||||||
private prepareLookups() {
|
private prepareLookups() {
|
||||||
this.oss.operations.forEach(operation => {
|
this.oss.operations.forEach(operation => {
|
||||||
|
operation.nodeID = constructNodeID(NodeType.OPERATION, operation.id);
|
||||||
|
operation.nodeType = NodeType.OPERATION;
|
||||||
|
this.itemByNodeID.set(operation.nodeID, operation);
|
||||||
this.operationByID.set(operation.id, operation);
|
this.operationByID.set(operation.id, operation);
|
||||||
this.graph.addNode(operation.id);
|
this.graph.addNode(operation.id);
|
||||||
this.hierarchy.addNode(operation.id);
|
this.hierarchy.addNode(operation.nodeID);
|
||||||
if (operation.parent) {
|
if (operation.parent) {
|
||||||
this.hierarchy.addEdge(-operation.parent, operation.id);
|
this.hierarchy.addEdge(constructNodeID(NodeType.BLOCK, operation.parent), operation.nodeID);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.oss.blocks.forEach(block => {
|
this.oss.blocks.forEach(block => {
|
||||||
|
block.nodeID = constructNodeID(NodeType.BLOCK, block.id);
|
||||||
|
block.nodeType = NodeType.BLOCK;
|
||||||
|
this.itemByNodeID.set(block.nodeID, block);
|
||||||
this.blockByID.set(block.id, block);
|
this.blockByID.set(block.id, block);
|
||||||
this.hierarchy.addNode(-block.id);
|
this.hierarchy.addNode(block.nodeID);
|
||||||
if (block.parent) {
|
if (block.parent) {
|
||||||
this.hierarchy.addEdge(-block.parent, -block.id);
|
this.hierarchy.addEdge(constructNodeID(NodeType.BLOCK, block.parent), block.nodeID);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,12 @@ export type IRelocateConstituentsDTO = z.infer<typeof schemaRelocateConstituents
|
||||||
/** Represents {@link IConstituenta} reference. */
|
/** Represents {@link IConstituenta} reference. */
|
||||||
export type IConstituentaReference = z.infer<typeof schemaConstituentaReference>;
|
export type IConstituentaReference = z.infer<typeof schemaConstituentaReference>;
|
||||||
|
|
||||||
|
/** Represents {@link IOperation} position. */
|
||||||
|
export type IOperationPosition = z.infer<typeof schemaOperationPosition>;
|
||||||
|
|
||||||
|
/** Represents {@link IBlock} position. */
|
||||||
|
export type IBlockPosition = z.infer<typeof schemaBlockPosition>;
|
||||||
|
|
||||||
// ====== Schemas ======
|
// ====== Schemas ======
|
||||||
export const schemaOperationType = z.enum(Object.values(OperationType) as [OperationType, ...OperationType[]]);
|
export const schemaOperationType = z.enum(Object.values(OperationType) as [OperationType, ...OperationType[]]);
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ export const useCreateInput = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-input'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-input'],
|
||||||
mutationFn: ossApi.createInput,
|
mutationFn: ossApi.createInput,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,9 +10,9 @@ export const useDeleteBlock = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-block'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-block'],
|
||||||
mutationFn: ossApi.deleteBlock,
|
mutationFn: ossApi.deleteBlock,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,9 +10,9 @@ export const useDeleteOperation = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-operation'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-operation'],
|
||||||
mutationFn: ossApi.deleteOperation,
|
mutationFn: ossApi.deleteOperation,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,9 +10,9 @@ export const useExecuteOperation = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'execute-operation'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'execute-operation'],
|
||||||
mutationFn: ossApi.executeOperation,
|
mutationFn: ossApi.executeOperation,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,9 +10,9 @@ export const useMoveItems = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'move-items'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'move-items'],
|
||||||
mutationFn: ossApi.moveItems,
|
mutationFn: ossApi.moveItems,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,9 +10,9 @@ export const useRelocateConstituents = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'relocate-constituents'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'relocate-constituents'],
|
||||||
mutationFn: ossApi.relocateConstituents,
|
mutationFn: ossApi.relocateConstituents,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,9 +10,9 @@ export const useUpdateInput = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-input'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-input'],
|
||||||
mutationFn: ossApi.updateInput,
|
mutationFn: ossApi.updateInput,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -9,24 +9,22 @@ import { ComboBox } from '@/components/input/combo-box';
|
||||||
import { type Styling } from '@/components/props';
|
import { type Styling } from '@/components/props';
|
||||||
import { cn } from '@/components/utils';
|
import { cn } from '@/components/utils';
|
||||||
import { NoData } from '@/components/view';
|
import { NoData } from '@/components/view';
|
||||||
import { type RO } from '@/utils/meta';
|
|
||||||
|
|
||||||
import { labelOssItem } from '../labels';
|
import { labelOssItem } from '../labels';
|
||||||
import { type IOperationSchema, type IOssItem } from '../models/oss';
|
import { type IOperationSchema, type IOssItem, NodeType } from '../models/oss';
|
||||||
import { getItemID, isOperation } from '../models/oss-api';
|
|
||||||
|
|
||||||
const SELECTION_CLEAR_TIMEOUT = 1000;
|
const SELECTION_CLEAR_TIMEOUT = 1000;
|
||||||
|
|
||||||
interface PickMultiOperationProps extends Styling {
|
interface PickContentsProps extends Styling {
|
||||||
value: number[];
|
value: IOssItem[];
|
||||||
onChange: (newValue: number[]) => void;
|
onChange: (newValue: IOssItem[]) => void;
|
||||||
schema: IOperationSchema;
|
schema: IOperationSchema;
|
||||||
rows?: number;
|
rows?: number;
|
||||||
exclude?: number[];
|
exclude?: IOssItem[];
|
||||||
disallowBlocks?: boolean;
|
disallowBlocks?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<RO<IOssItem>>();
|
const columnHelper = createColumnHelper<IOssItem>();
|
||||||
|
|
||||||
export function PickContents({
|
export function PickContents({
|
||||||
rows,
|
rows,
|
||||||
|
@ -37,29 +35,26 @@ export function PickContents({
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
...restProps
|
...restProps
|
||||||
}: PickMultiOperationProps) {
|
}: PickContentsProps) {
|
||||||
const selectedItems = value
|
const [lastSelected, setLastSelected] = useState<IOssItem | null>(null);
|
||||||
.map(itemID => (itemID > 0 ? schema.operationByID.get(itemID) : schema.blockByID.get(-itemID)))
|
const items: IOssItem[] = [
|
||||||
.filter(item => item !== undefined);
|
...(disallowBlocks ? [] : schema.blocks.filter(item => !value.includes(item) && !exclude?.includes(item))),
|
||||||
const [lastSelected, setLastSelected] = useState<RO<IOssItem> | null>(null);
|
...schema.operations.filter(item => !value.includes(item) && !exclude?.includes(item))
|
||||||
const items = [
|
|
||||||
...(disallowBlocks ? [] : schema.blocks.filter(item => !value.includes(-item.id) && !exclude?.includes(-item.id))),
|
|
||||||
...schema.operations.filter(item => !value.includes(item.id) && !exclude?.includes(item.id))
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function handleDelete(target: number) {
|
function handleDelete(target: IOssItem) {
|
||||||
onChange(value.filter(item => item !== target));
|
onChange(value.filter(item => item !== target));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelect(target: RO<IOssItem> | null) {
|
function handleSelect(target: IOssItem | null) {
|
||||||
if (target) {
|
if (target) {
|
||||||
setLastSelected(target);
|
setLastSelected(target);
|
||||||
onChange([...value, getItemID(target)]);
|
onChange([...value, target]);
|
||||||
setTimeout(() => setLastSelected(null), SELECTION_CLEAR_TIMEOUT);
|
setTimeout(() => setLastSelected(null), SELECTION_CLEAR_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMoveUp(target: number) {
|
function handleMoveUp(target: IOssItem) {
|
||||||
const index = value.indexOf(target);
|
const index = value.indexOf(target);
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
const newSelected = [...value];
|
const newSelected = [...value];
|
||||||
|
@ -69,7 +64,7 @@ export function PickContents({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMoveDown(target: number) {
|
function handleMoveDown(target: IOssItem) {
|
||||||
const index = value.indexOf(target);
|
const index = value.indexOf(target);
|
||||||
if (index < value.length - 1) {
|
if (index < value.length - 1) {
|
||||||
const newSelected = [...value];
|
const newSelected = [...value];
|
||||||
|
@ -80,13 +75,13 @@ export function PickContents({
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
columnHelper.accessor(item => isOperation(item), {
|
columnHelper.accessor(item => item.nodeType === NodeType.OPERATION, {
|
||||||
id: 'type',
|
id: 'type',
|
||||||
header: 'Тип',
|
header: 'Тип',
|
||||||
size: 150,
|
size: 150,
|
||||||
minSize: 150,
|
minSize: 150,
|
||||||
maxSize: 150,
|
maxSize: 150,
|
||||||
cell: props => <div>{isOperation(props.row.original) ? 'Операция' : 'Блок'}</div>
|
cell: props => <div>{props.getValue() ? 'Операция' : 'Блок'}</div>
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('title', {
|
columnHelper.accessor('title', {
|
||||||
id: 'title',
|
id: 'title',
|
||||||
|
@ -106,21 +101,21 @@ export function PickContents({
|
||||||
noHover
|
noHover
|
||||||
className='px-0'
|
className='px-0'
|
||||||
icon={<IconRemove size='1rem' className='icon-red' />}
|
icon={<IconRemove size='1rem' className='icon-red' />}
|
||||||
onClick={() => handleDelete(getItemID(props.row.original))}
|
onClick={() => handleDelete(props.row.original)}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Переместить выше'
|
title='Переместить выше'
|
||||||
noHover
|
noHover
|
||||||
className='px-0'
|
className='px-0'
|
||||||
icon={<IconMoveUp size='1rem' className='icon-primary' />}
|
icon={<IconMoveUp size='1rem' className='icon-primary' />}
|
||||||
onClick={() => handleMoveUp(getItemID(props.row.original))}
|
onClick={() => handleMoveUp(props.row.original)}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Переместить ниже'
|
title='Переместить ниже'
|
||||||
noHover
|
noHover
|
||||||
className='px-0'
|
className='px-0'
|
||||||
icon={<IconMoveDown size='1rem' className='icon-primary' />}
|
icon={<IconMoveDown size='1rem' className='icon-primary' />}
|
||||||
onClick={() => handleMoveDown(getItemID(props.row.original))}
|
onClick={() => handleMoveDown(props.row.original)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -134,7 +129,7 @@ export function PickContents({
|
||||||
items={items}
|
items={items}
|
||||||
value={lastSelected}
|
value={lastSelected}
|
||||||
placeholder='Выберите операцию или блок'
|
placeholder='Выберите операцию или блок'
|
||||||
idFunc={item => String(getItemID(item))}
|
idFunc={item => item.nodeID}
|
||||||
labelValueFunc={item => labelOssItem(item)}
|
labelValueFunc={item => labelOssItem(item)}
|
||||||
labelOptionFunc={item => labelOssItem(item)}
|
labelOptionFunc={item => labelOssItem(item)}
|
||||||
onChange={handleSelect}
|
onChange={handleSelect}
|
||||||
|
@ -145,7 +140,7 @@ export function PickContents({
|
||||||
rows={rows}
|
rows={rows}
|
||||||
contentHeight='1.3rem'
|
contentHeight='1.3rem'
|
||||||
className='cc-scroll-y text-sm select-none border-y rounded-b-md'
|
className='cc-scroll-y text-sm select-none border-y rounded-b-md'
|
||||||
data={selectedItems}
|
data={value}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
headPosition='0rem'
|
headPosition='0rem'
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { Tooltip } from '@/components/container';
|
import { Tooltip } from '@/components/container';
|
||||||
import { globalIDs } from '@/utils/constants';
|
import { globalIDs } from '@/utils/constants';
|
||||||
|
|
||||||
import { type IBlock, type IOperation } from '../models/oss';
|
import { NodeType } from '../models/oss';
|
||||||
import { isOperation } from '../models/oss-api';
|
|
||||||
import { useOperationTooltipStore } from '../stores/operation-tooltip';
|
import { useOperationTooltipStore } from '../stores/operation-tooltip';
|
||||||
|
|
||||||
import { InfoBlock } from './info-block';
|
import { InfoBlock } from './info-block';
|
||||||
|
@ -10,7 +9,7 @@ import { InfoOperation } from './info-operation';
|
||||||
|
|
||||||
export function OperationTooltip() {
|
export function OperationTooltip() {
|
||||||
const hoverItem = useOperationTooltipStore(state => state.hoverItem);
|
const hoverItem = useOperationTooltipStore(state => state.hoverItem);
|
||||||
const isOperationNode = isOperation(hoverItem);
|
const isOperationNode = hoverItem?.nodeType === NodeType.OPERATION;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -20,8 +19,8 @@ export function OperationTooltip() {
|
||||||
className='max-w-140 dense max-h-120! overflow-y-auto!'
|
className='max-w-140 dense max-h-120! overflow-y-auto!'
|
||||||
hidden={!hoverItem}
|
hidden={!hoverItem}
|
||||||
>
|
>
|
||||||
{hoverItem && isOperationNode ? <InfoOperation operation={hoverItem as IOperation} /> : null}
|
{hoverItem && isOperationNode ? <InfoOperation operation={hoverItem} /> : null}
|
||||||
{hoverItem && !isOperationNode ? <InfoBlock block={hoverItem as IBlock} /> : null}
|
{hoverItem && !isOperationNode ? <InfoBlock block={hoverItem} /> : null}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type ICreateBlockDTO, schemaCreateBlock } from '../../backend/types';
|
import { type ICreateBlockDTO, schemaCreateBlock } from '../../backend/types';
|
||||||
import { useCreateBlock } from '../../backend/use-create-block';
|
import { useCreateBlock } from '../../backend/use-create-block';
|
||||||
|
import { type IOssItem, NodeType } from '../../models/oss';
|
||||||
import { type LayoutManager } from '../../models/oss-layout-api';
|
import { type LayoutManager } from '../../models/oss-layout-api';
|
||||||
import { BLOCK_NODE_MIN_HEIGHT, BLOCK_NODE_MIN_WIDTH } from '../../pages/oss-page/editor-oss-graph/graph/block-node';
|
import { BLOCK_NODE_MIN_HEIGHT, BLOCK_NODE_MIN_WIDTH } from '../../pages/oss-page/editor-oss-graph/graph/block-node';
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ import { TabBlockChildren } from './tab-block-children';
|
||||||
|
|
||||||
export interface DlgCreateBlockProps {
|
export interface DlgCreateBlockProps {
|
||||||
manager: LayoutManager;
|
manager: LayoutManager;
|
||||||
initialChildren: number[];
|
initialChildren: IOssItem[];
|
||||||
initialParent: number | null;
|
initialParent: number | null;
|
||||||
defaultX: number;
|
defaultX: number;
|
||||||
defaultY: number;
|
defaultY: number;
|
||||||
|
@ -52,8 +53,8 @@ export function DlgCreateBlock() {
|
||||||
position_y: defaultY,
|
position_y: defaultY,
|
||||||
width: BLOCK_NODE_MIN_WIDTH,
|
width: BLOCK_NODE_MIN_WIDTH,
|
||||||
height: BLOCK_NODE_MIN_HEIGHT,
|
height: BLOCK_NODE_MIN_HEIGHT,
|
||||||
children_blocks: initialChildren.filter(id => id < 0).map(id => -id),
|
children_blocks: initialChildren.filter(item => item.nodeType === NodeType.BLOCK).map(item => item.id),
|
||||||
children_operations: initialChildren.filter(id => id > 0),
|
children_operations: initialChildren.filter(item => item.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||||
layout: manager.layout
|
layout: manager.layout
|
||||||
},
|
},
|
||||||
mode: 'onChange'
|
mode: 'onChange'
|
||||||
|
@ -65,11 +66,12 @@ export function DlgCreateBlock() {
|
||||||
const isValid = !!title && !manager.oss.blocks.some(block => block.title === title);
|
const isValid = !!title && !manager.oss.blocks.some(block => block.title === title);
|
||||||
|
|
||||||
function onSubmit(data: ICreateBlockDTO) {
|
function onSubmit(data: ICreateBlockDTO) {
|
||||||
const rectangle = manager.calculateNewBlockPosition(data);
|
const rectangle = manager.newBlockPosition(data);
|
||||||
data.position_x = rectangle.x;
|
data.position_x = rectangle.x;
|
||||||
data.position_y = rectangle.y;
|
data.position_y = rectangle.y;
|
||||||
data.width = rectangle.width;
|
data.width = rectangle.width;
|
||||||
data.height = rectangle.height;
|
data.height = rectangle.height;
|
||||||
|
data.layout = manager.layout;
|
||||||
void createBlock({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_block.id));
|
void createBlock({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_block.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type ICreateBlockDTO } from '../../backend/types';
|
import { type ICreateBlockDTO } from '../../backend/types';
|
||||||
import { SelectParent } from '../../components/select-parent';
|
import { SelectParent } from '../../components/select-parent';
|
||||||
|
import { NodeType } from '../../models/oss';
|
||||||
|
import { constructNodeID } from '../../models/oss-api';
|
||||||
|
|
||||||
import { type DlgCreateBlockProps } from './dlg-create-block';
|
import { type DlgCreateBlockProps } from './dlg-create-block';
|
||||||
|
|
||||||
|
@ -18,10 +20,8 @@ export function TabBlockCard() {
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useFormContext<ICreateBlockDTO>();
|
} = useFormContext<ICreateBlockDTO>();
|
||||||
const children_blocks = useWatch({ control, name: 'children_blocks' });
|
const children_blocks = useWatch({ control, name: 'children_blocks' });
|
||||||
const all_children = [
|
const block_ids = children_blocks.map(id => constructNodeID(NodeType.BLOCK, id));
|
||||||
...children_blocks,
|
const all_children = [...block_ids, ...manager.oss.hierarchy.expandAllOutputs(block_ids)];
|
||||||
...manager.oss.hierarchy.expandAllOutputs(children_blocks.filter(id => id < 0).map(id => -id)).map(id => -id)
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-fade-in cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
|
@ -36,7 +36,7 @@ export function TabBlockCard() {
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<SelectParent
|
<SelectParent
|
||||||
items={manager.oss.blocks.filter(block => !all_children.includes(block.id))}
|
items={manager.oss.blocks.filter(block => !all_children.includes(block.nodeID))}
|
||||||
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
||||||
placeholder='Блок содержания не выбран'
|
placeholder='Блок содержания не выбран'
|
||||||
onChange={value => field.onChange(value ? value.id : null)}
|
onChange={value => field.onChange(value ? value.id : null)}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type ICreateBlockDTO } from '../../backend/types';
|
import { type ICreateBlockDTO } from '../../backend/types';
|
||||||
import { PickContents } from '../../components/pick-contents';
|
import { PickContents } from '../../components/pick-contents';
|
||||||
|
import { type IOssItem, NodeType } from '../../models/oss';
|
||||||
|
|
||||||
import { type DlgCreateBlockProps } from './dlg-create-block';
|
import { type DlgCreateBlockProps } from './dlg-create-block';
|
||||||
|
|
||||||
|
@ -15,19 +16,31 @@ export function TabBlockChildren() {
|
||||||
const parent = useWatch({ control, name: 'item_data.parent' });
|
const parent = useWatch({ control, name: 'item_data.parent' });
|
||||||
const children_blocks = useWatch({ control, name: 'children_blocks' });
|
const children_blocks = useWatch({ control, name: 'children_blocks' });
|
||||||
const children_operations = useWatch({ control, name: 'children_operations' });
|
const children_operations = useWatch({ control, name: 'children_operations' });
|
||||||
const exclude = parent ? [-parent, ...manager.oss.hierarchy.expandAllInputs([-parent]).filter(id => id < 0)] : [];
|
|
||||||
|
|
||||||
const value = [...children_blocks.map(id => -id), ...children_operations];
|
const parentItem = parent ? manager.oss.blockByID.get(parent) : null;
|
||||||
|
const internalBlocks = parentItem
|
||||||
|
? manager.oss.hierarchy
|
||||||
|
.expandAllInputs([parentItem.nodeID])
|
||||||
|
.map(id => manager.oss.itemByNodeID.get(id))
|
||||||
|
.filter(item => item !== null && item?.nodeType === NodeType.BLOCK)
|
||||||
|
: [];
|
||||||
|
|
||||||
function handleChangeSelected(newValue: number[]) {
|
const exclude = parentItem ? [parentItem, ...internalBlocks] : [];
|
||||||
|
|
||||||
|
const value = [
|
||||||
|
...children_blocks.map(id => manager.oss.blockByID.get(id)!),
|
||||||
|
...children_operations.map(id => manager.oss.operationByID.get(id)!)
|
||||||
|
];
|
||||||
|
|
||||||
|
function handleChangeSelected(newValue: IOssItem[]) {
|
||||||
setValue(
|
setValue(
|
||||||
'children_blocks',
|
'children_blocks',
|
||||||
newValue.filter(id => id < 0).map(id => -id),
|
newValue.filter(item => item.nodeType === NodeType.BLOCK).map(item => item.id),
|
||||||
{ shouldValidate: true }
|
{ shouldValidate: true }
|
||||||
);
|
);
|
||||||
setValue(
|
setValue(
|
||||||
'children_operations',
|
'children_operations',
|
||||||
newValue.filter(id => id > 0),
|
newValue.filter(item => item.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||||
{ shouldValidate: true }
|
{ shouldValidate: true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,9 +64,10 @@ export function DlgCreateOperation() {
|
||||||
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
|
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
|
||||||
|
|
||||||
function onSubmit(data: ICreateOperationDTO) {
|
function onSubmit(data: ICreateOperationDTO) {
|
||||||
const target = manager.calculateNewOperationPosition(data);
|
const target = manager.newOperationPosition(data);
|
||||||
data.position_x = target.x;
|
data.position_x = target.x;
|
||||||
data.position_y = target.y;
|
data.position_y = target.y;
|
||||||
|
data.layout = manager.layout;
|
||||||
void createOperation({ itemID: manager.oss.id, data: data }).then(response =>
|
void createOperation({ itemID: manager.oss.id, data: data }).then(response =>
|
||||||
onCreate?.(response.new_operation.id)
|
onCreate?.(response.new_operation.id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,6 +42,10 @@ export function DlgEditBlock() {
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(data: IUpdateBlockDTO) {
|
function onSubmit(data: IUpdateBlockDTO) {
|
||||||
|
if (data.item_data.parent !== target.parent) {
|
||||||
|
manager.onBlockChangeParent(data.target, data.item_data.parent);
|
||||||
|
data.layout = manager.layout;
|
||||||
|
}
|
||||||
return updateBlock({ itemID: manager.oss.id, data });
|
return updateBlock({ itemID: manager.oss.id, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,9 +58,10 @@ export function DlgEditOperation() {
|
||||||
const [activeTab, setActiveTab] = useState<TabID>(TabID.CARD);
|
const [activeTab, setActiveTab] = useState<TabID>(TabID.CARD);
|
||||||
|
|
||||||
function onSubmit(data: IUpdateOperationDTO) {
|
function onSubmit(data: IUpdateOperationDTO) {
|
||||||
// if (data.item_data.parent !== target.parent) {
|
if (data.item_data.parent !== target.parent) {
|
||||||
// data.layout = updateLayoutOnOperationChange(data.target, data.item_data.parent, data.layout);
|
manager.onOperationChangeParent(data.target, data.item_data.parent);
|
||||||
// }
|
data.layout = manager.layout;
|
||||||
|
}
|
||||||
return updateOperation({ itemID: manager.oss.id, data });
|
return updateOperation({ itemID: manager.oss.id, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ import {
|
||||||
type IOperation,
|
type IOperation,
|
||||||
type IOssItem,
|
type IOssItem,
|
||||||
type ISubstitutionErrorDescription,
|
type ISubstitutionErrorDescription,
|
||||||
|
NodeType,
|
||||||
SubstitutionErrorType
|
SubstitutionErrorType
|
||||||
} from './models/oss';
|
} from './models/oss';
|
||||||
import { isOperation } from './models/oss-api';
|
|
||||||
|
|
||||||
/** Retrieves label for {@link OperationType}. */
|
/** Retrieves label for {@link OperationType}. */
|
||||||
export function labelOperationType(itemType: OperationType): string {
|
export function labelOperationType(itemType: OperationType): string {
|
||||||
|
@ -58,7 +58,7 @@ export function describeSubstitutionError(error: RO<ISubstitutionErrorDescriptio
|
||||||
|
|
||||||
/** Retrieves label for {@link IOssItem}. */
|
/** Retrieves label for {@link IOssItem}. */
|
||||||
export function labelOssItem(item: RO<IOssItem>): string {
|
export function labelOssItem(item: RO<IOssItem>): string {
|
||||||
if (isOperation(item)) {
|
if (item.nodeType === NodeType.OPERATION) {
|
||||||
return `${(item as IOperation).alias}: ${item.title}`;
|
return `${(item as IOperation).alias}: ${item.title}`;
|
||||||
} else {
|
} else {
|
||||||
return `Блок: ${item.title}`;
|
return `Блок: ${item.title}`;
|
||||||
|
|
|
@ -20,23 +20,16 @@ import {
|
||||||
} from '@/features/rsform/models/rslang-api';
|
} from '@/features/rsform/models/rslang-api';
|
||||||
|
|
||||||
import { infoMsg } from '@/utils/labels';
|
import { infoMsg } from '@/utils/labels';
|
||||||
import { type RO } from '@/utils/meta';
|
|
||||||
|
|
||||||
import { Graph } from '../../../models/graph';
|
import { Graph } from '../../../models/graph';
|
||||||
import { describeSubstitutionError } from '../labels';
|
import { describeSubstitutionError } from '../labels';
|
||||||
|
|
||||||
import { type IOperationSchema, type IOssItem, SubstitutionErrorType } from './oss';
|
import { type IOperationSchema, NodeType, SubstitutionErrorType } from './oss';
|
||||||
|
|
||||||
const STARTING_SUB_INDEX = 900; // max semantic index for starting substitution
|
const STARTING_SUB_INDEX = 900; // max semantic index for starting substitution
|
||||||
|
|
||||||
/** Checks if element is {@link IOperation} or {@link IBlock}. */
|
export function constructNodeID(type: NodeType, itemID: number): string {
|
||||||
export function isOperation(item: RO<IOssItem> | null): boolean {
|
return type === NodeType.OPERATION ? 'o' + String(itemID) : 'b' + String(itemID);
|
||||||
return !!item && 'arguments' in item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Extract contiguous ID of {@link IOperation} or {@link IBlock}. */
|
|
||||||
export function getItemID(item: RO<IOssItem>): number {
|
|
||||||
return isOperation(item) ? item.id : -item.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sorts library items relevant for the specified {@link IOperationSchema}. */
|
/** Sorts library items relevant for the specified {@link IOperationSchema}. */
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
import { type ICreateBlockDTO, type ICreateOperationDTO, type IOssLayout } from '../backend/types';
|
import {
|
||||||
|
type IBlockPosition,
|
||||||
|
type ICreateBlockDTO,
|
||||||
|
type ICreateOperationDTO,
|
||||||
|
type IOperationPosition,
|
||||||
|
type IOssLayout
|
||||||
|
} from '../backend/types';
|
||||||
|
|
||||||
import { type IOperationSchema } from './oss';
|
import { type IOperationSchema } from './oss';
|
||||||
import { type Position2D, type Rectangle2D } from './oss-layout';
|
import { type Position2D, type Rectangle2D } from './oss-layout';
|
||||||
|
|
||||||
export const GRID_SIZE = 10; // pixels - size of OSS grid
|
export const GRID_SIZE = 10; // pixels - size of OSS grid
|
||||||
const MIN_DISTANCE = 2 * GRID_SIZE; // pixels - minimum distance between nodes
|
const MIN_DISTANCE = 2 * GRID_SIZE; // pixels - minimum distance between nodes
|
||||||
const DISTANCE_X = 180; // pixels - insert x-distance between node centers
|
|
||||||
const DISTANCE_Y = 100; // pixels - insert y-distance between node centers
|
|
||||||
|
|
||||||
const OPERATION_NODE_WIDTH = 150;
|
const OPERATION_NODE_WIDTH = 150;
|
||||||
const OPERATION_NODE_HEIGHT = 40;
|
const OPERATION_NODE_HEIGHT = 40;
|
||||||
|
@ -26,93 +30,236 @@ export class LayoutManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calculate insert position for a new {@link IOperation} */
|
/** Calculate insert position for a new {@link IOperation} */
|
||||||
calculateNewOperationPosition(data: ICreateOperationDTO): Position2D {
|
newOperationPosition(data: ICreateOperationDTO): Position2D {
|
||||||
// TODO: check parent node
|
let result = { x: data.position_x, y: data.position_y };
|
||||||
|
|
||||||
const result = { x: data.position_x, y: data.position_y };
|
|
||||||
const operations = this.layout.operations;
|
const operations = this.layout.operations;
|
||||||
|
const parentNode = this.layout.blocks.find(pos => pos.id === data.item_data.parent);
|
||||||
if (operations.length === 0) {
|
if (operations.length === 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.arguments.length === 0) {
|
if (data.arguments.length !== 0) {
|
||||||
let inputsPositions = operations.filter(pos =>
|
result = calculatePositionFromArgs(data.arguments, operations);
|
||||||
this.oss.operations.find(operation => operation.arguments.length === 0 && operation.id === pos.id)
|
} else if (parentNode) {
|
||||||
);
|
result.x = parentNode.x + MIN_DISTANCE;
|
||||||
if (inputsPositions.length === 0) {
|
result.y = parentNode.y + MIN_DISTANCE;
|
||||||
inputsPositions = operations;
|
|
||||||
}
|
|
||||||
const maxX = Math.max(...inputsPositions.map(node => node.x));
|
|
||||||
const minY = Math.min(...inputsPositions.map(node => node.y));
|
|
||||||
result.x = maxX + DISTANCE_X;
|
|
||||||
result.y = minY;
|
|
||||||
} else {
|
} else {
|
||||||
const argNodes = operations.filter(pos => data.arguments.includes(pos.id));
|
result = this.calculatePositionForFreeOperation(result);
|
||||||
const maxY = Math.max(...argNodes.map(node => node.y));
|
|
||||||
const minX = Math.min(...argNodes.map(node => node.x));
|
|
||||||
const maxX = Math.max(...argNodes.map(node => node.x));
|
|
||||||
result.x = Math.ceil((maxX + minX) / 2 / GRID_SIZE) * GRID_SIZE;
|
|
||||||
result.y = maxY + DISTANCE_Y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let flagIntersect = false;
|
result = preventOverlap(
|
||||||
do {
|
{ ...result, width: OPERATION_NODE_WIDTH, height: OPERATION_NODE_HEIGHT },
|
||||||
flagIntersect = operations.some(
|
operations.map(node => ({ ...node, width: OPERATION_NODE_WIDTH, height: OPERATION_NODE_HEIGHT }))
|
||||||
position => Math.abs(position.x - result.x) < MIN_DISTANCE && Math.abs(position.y - result.y) < MIN_DISTANCE
|
|
||||||
);
|
);
|
||||||
if (flagIntersect) {
|
|
||||||
result.x += MIN_DISTANCE;
|
if (parentNode) {
|
||||||
result.y += MIN_DISTANCE;
|
const borderX = result.x + OPERATION_NODE_WIDTH + MIN_DISTANCE;
|
||||||
|
const borderY = result.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE;
|
||||||
|
if (borderX > parentNode.x + parentNode.width) {
|
||||||
|
parentNode.width = borderX - parentNode.x;
|
||||||
}
|
}
|
||||||
} while (flagIntersect);
|
if (borderY > parentNode.y + parentNode.height) {
|
||||||
return result;
|
parentNode.height = borderY - parentNode.y;
|
||||||
|
}
|
||||||
|
// TODO: trigger cascading updates
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: result.x, y: result.y };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calculate insert position for a new {@link IBlock} */
|
/** Calculate insert position for a new {@link IBlock} */
|
||||||
calculateNewBlockPosition(data: ICreateBlockDTO): Rectangle2D {
|
newBlockPosition(data: ICreateBlockDTO): Rectangle2D {
|
||||||
const block_nodes = data.children_blocks
|
const block_nodes = data.children_blocks
|
||||||
.map(id => this.layout.blocks.find(block => block.id === id))
|
.map(id => this.layout.blocks.find(block => block.id === id))
|
||||||
.filter(node => !!node);
|
.filter(node => !!node);
|
||||||
const operation_nodes = data.children_operations
|
const operation_nodes = data.children_operations
|
||||||
.map(id => this.layout.operations.find(operation => operation.id === id))
|
.map(id => this.layout.operations.find(operation => operation.id === id))
|
||||||
.filter(node => !!node);
|
.filter(node => !!node);
|
||||||
|
const parentNode = this.layout.blocks.find(pos => pos.id === data.item_data.parent);
|
||||||
|
|
||||||
if (block_nodes.length === 0 && operation_nodes.length === 0) {
|
let result: Rectangle2D = { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
||||||
return { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
|
||||||
|
if (block_nodes.length !== 0 || operation_nodes.length !== 0) {
|
||||||
|
result = calculatePositionFromChildren(
|
||||||
|
{ x: data.position_x, y: data.position_y, width: data.width, height: data.height },
|
||||||
|
operation_nodes,
|
||||||
|
block_nodes
|
||||||
|
);
|
||||||
|
} else if (parentNode) {
|
||||||
|
result = {
|
||||||
|
x: parentNode.x + MIN_DISTANCE,
|
||||||
|
y: parentNode.y + MIN_DISTANCE,
|
||||||
|
width: data.width,
|
||||||
|
height: data.height
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
result = this.calculatePositionForFreeBlock(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (block_nodes.length === 0 && operation_nodes.length === 0) {
|
||||||
|
if (parentNode) {
|
||||||
|
const siblings = this.oss.blocks.filter(block => block.parent === parentNode.id).map(block => block.id);
|
||||||
|
if (siblings.length > 0) {
|
||||||
|
result = preventOverlap(
|
||||||
|
result,
|
||||||
|
this.layout.blocks.filter(block => siblings.includes(block.id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.id);
|
||||||
|
if (rootBlocks.length > 0) {
|
||||||
|
result = preventOverlap(
|
||||||
|
result,
|
||||||
|
this.layout.blocks.filter(block => rootBlocks.includes(block.id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentNode) {
|
||||||
|
const borderX = result.x + result.width + MIN_DISTANCE;
|
||||||
|
const borderY = result.y + result.height + MIN_DISTANCE;
|
||||||
|
if (borderX > parentNode.x + parentNode.width) {
|
||||||
|
parentNode.width = borderX - parentNode.x;
|
||||||
|
}
|
||||||
|
if (borderY > parentNode.y + parentNode.height) {
|
||||||
|
parentNode.height = borderY - parentNode.y;
|
||||||
|
}
|
||||||
|
// TODO: trigger cascading updates
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update layout when parent changes */
|
||||||
|
onOperationChangeParent(targetID: number, newParent: number | null) {
|
||||||
|
console.error('not implemented', targetID, newParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update layout when parent changes */
|
||||||
|
onBlockChangeParent(targetID: number, newParent: number | null) {
|
||||||
|
console.error('not implemented', targetID, newParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculatePositionForFreeOperation(initial: Position2D): Position2D {
|
||||||
|
const operations = this.layout.operations;
|
||||||
|
if (operations.length === 0) {
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
const freeInputs = this.oss.operations
|
||||||
|
.filter(operation => operation.arguments.length === 0 && operation.parent === null)
|
||||||
|
.map(operation => operation.id);
|
||||||
|
let inputsPositions = operations.filter(pos => freeInputs.includes(pos.id));
|
||||||
|
if (inputsPositions.length === 0) {
|
||||||
|
inputsPositions = operations;
|
||||||
|
}
|
||||||
|
const maxX = Math.max(...inputsPositions.map(node => node.x));
|
||||||
|
const minY = Math.min(...inputsPositions.map(node => node.y));
|
||||||
|
return {
|
||||||
|
x: maxX + OPERATION_NODE_WIDTH + MIN_DISTANCE + GRID_SIZE,
|
||||||
|
y: minY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculatePositionForFreeBlock(initial: Rectangle2D): Rectangle2D {
|
||||||
|
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.id);
|
||||||
|
const blocksPositions = this.layout.blocks.filter(pos => rootBlocks.includes(pos.id));
|
||||||
|
if (blocksPositions.length === 0) {
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
const maxX = Math.max(...blocksPositions.map(node => node.x + node.width));
|
||||||
|
const minY = Math.min(...blocksPositions.map(node => node.y));
|
||||||
|
return { ...initial, x: maxX + MIN_DISTANCE, y: minY };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= Internals =======
|
||||||
|
function rectanglesOverlap(a: Rectangle2D, b: Rectangle2D): boolean {
|
||||||
|
return !(
|
||||||
|
a.x + a.width + MIN_DISTANCE <= b.x ||
|
||||||
|
b.x + b.width + MIN_DISTANCE <= a.x ||
|
||||||
|
a.y + a.height + MIN_DISTANCE <= b.y ||
|
||||||
|
b.y + b.height + MIN_DISTANCE <= a.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOverlapAmount(a: Rectangle2D, b: Rectangle2D): Position2D {
|
||||||
|
const xOverlap = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x));
|
||||||
|
const yOverlap = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
|
||||||
|
return { x: xOverlap, y: yOverlap };
|
||||||
|
}
|
||||||
|
|
||||||
|
function preventOverlap(target: Rectangle2D, fixedRectangles: Rectangle2D[]): Rectangle2D {
|
||||||
|
let hasOverlap: boolean;
|
||||||
|
do {
|
||||||
|
hasOverlap = false;
|
||||||
|
for (const fixed of fixedRectangles) {
|
||||||
|
if (rectanglesOverlap(target, fixed)) {
|
||||||
|
hasOverlap = true;
|
||||||
|
const overlap = getOverlapAmount(target, fixed);
|
||||||
|
if (overlap.x >= overlap.y) {
|
||||||
|
target.x += overlap.x + MIN_DISTANCE;
|
||||||
|
} else {
|
||||||
|
target.y += overlap.y + MIN_DISTANCE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (hasOverlap);
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculatePositionFromArgs(args: number[], operations: IOperationPosition[]): Position2D {
|
||||||
|
const argNodes = operations.filter(pos => args.includes(pos.id));
|
||||||
|
const maxY = Math.max(...argNodes.map(node => node.y));
|
||||||
|
const minX = Math.min(...argNodes.map(node => node.x));
|
||||||
|
const maxX = Math.max(...argNodes.map(node => node.x));
|
||||||
|
return {
|
||||||
|
x: Math.ceil((maxX + minX) / 2 / GRID_SIZE) * GRID_SIZE,
|
||||||
|
y: maxY + 2 * OPERATION_NODE_HEIGHT + MIN_DISTANCE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculatePositionFromChildren(
|
||||||
|
initial: Rectangle2D,
|
||||||
|
operations: IOperationPosition[],
|
||||||
|
blocks: IBlockPosition[]
|
||||||
|
): Rectangle2D {
|
||||||
let left = undefined;
|
let left = undefined;
|
||||||
let top = undefined;
|
let top = undefined;
|
||||||
let right = undefined;
|
let right = undefined;
|
||||||
let bottom = undefined;
|
let bottom = undefined;
|
||||||
|
|
||||||
for (const block of block_nodes) {
|
for (const block of blocks) {
|
||||||
left = !left ? block.x - MIN_DISTANCE : Math.min(left, block.x - MIN_DISTANCE);
|
left = left === undefined ? block.x - MIN_DISTANCE : Math.min(left, block.x - MIN_DISTANCE);
|
||||||
top = !top ? block.y - MIN_DISTANCE : Math.min(top, block.y - MIN_DISTANCE);
|
top = top === undefined ? block.y - MIN_DISTANCE : Math.min(top, block.y - MIN_DISTANCE);
|
||||||
right = !right
|
right =
|
||||||
? Math.max(left + data.width, block.x + block.width + MIN_DISTANCE)
|
right === undefined
|
||||||
|
? Math.max(left + initial.width, block.x + block.width + MIN_DISTANCE)
|
||||||
: Math.max(right, block.x + block.width + MIN_DISTANCE);
|
: Math.max(right, block.x + block.width + MIN_DISTANCE);
|
||||||
bottom = !bottom
|
bottom = !bottom
|
||||||
? Math.max(top + data.height, block.y + block.height + MIN_DISTANCE)
|
? Math.max(top + initial.height, block.y + block.height + MIN_DISTANCE)
|
||||||
: Math.max(bottom, block.y + block.height + MIN_DISTANCE);
|
: Math.max(bottom, block.y + block.height + MIN_DISTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const operation of operation_nodes) {
|
for (const operation of operations) {
|
||||||
left = !left ? operation.x - MIN_DISTANCE : Math.min(left, operation.x - MIN_DISTANCE);
|
left = left === undefined ? operation.x - MIN_DISTANCE : Math.min(left, operation.x - MIN_DISTANCE);
|
||||||
top = !top ? operation.y - MIN_DISTANCE : Math.min(top, operation.y - MIN_DISTANCE);
|
top = top === undefined ? operation.y - MIN_DISTANCE : Math.min(top, operation.y - MIN_DISTANCE);
|
||||||
right = !right
|
right =
|
||||||
? Math.max(left + data.width, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE)
|
right === undefined
|
||||||
|
? Math.max(left + initial.width, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE)
|
||||||
: Math.max(right, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE);
|
: Math.max(right, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE);
|
||||||
bottom = !bottom
|
bottom = !bottom
|
||||||
? Math.max(top + data.height, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE)
|
? Math.max(top + initial.height, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE)
|
||||||
: Math.max(bottom, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE);
|
: Math.max(bottom, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: left ?? data.position_x,
|
x: left ?? initial.x,
|
||||||
y: top ?? data.position_y,
|
y: top ?? initial.y,
|
||||||
width: right && left ? right - left : data.width,
|
width: right !== undefined && left !== undefined ? right - left : initial.width,
|
||||||
height: bottom && top ? bottom - top : data.height
|
height: bottom !== undefined && top !== undefined ? bottom - top : initial.height
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,17 @@ import {
|
||||||
type IOperationSchemaDTO
|
type IOperationSchemaDTO
|
||||||
} from '../backend/types';
|
} from '../backend/types';
|
||||||
|
|
||||||
|
/** Represents OSS node type. */
|
||||||
|
export const NodeType = {
|
||||||
|
OPERATION: 1,
|
||||||
|
BLOCK: 2
|
||||||
|
} as const;
|
||||||
|
export type NodeType = (typeof NodeType)[keyof typeof NodeType];
|
||||||
|
|
||||||
/** Represents Operation. */
|
/** Represents Operation. */
|
||||||
export interface IOperation extends IOperationDTO {
|
export interface IOperation extends IOperationDTO {
|
||||||
|
nodeID: string;
|
||||||
|
nodeType: typeof NodeType.OPERATION;
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
is_owned: boolean;
|
is_owned: boolean;
|
||||||
|
@ -23,12 +32,17 @@ export interface IOperation extends IOperationDTO {
|
||||||
|
|
||||||
/** Represents Block. */
|
/** Represents Block. */
|
||||||
export interface IBlock extends IBlockDTO {
|
export interface IBlock extends IBlockDTO {
|
||||||
|
nodeID: string;
|
||||||
|
nodeType: typeof NodeType.BLOCK;
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Represents item of OperationSchema. */
|
||||||
|
export type IOssItem = IOperation | IBlock;
|
||||||
|
|
||||||
/** Represents {@link IOperationSchema} statistics. */
|
/** Represents {@link IOperationSchema} statistics. */
|
||||||
export interface IOperationSchemaStats {
|
export interface IOperationSchemaStats {
|
||||||
count_all: number;
|
count_all: number;
|
||||||
|
@ -45,16 +59,14 @@ export interface IOperationSchema extends IOperationSchemaDTO {
|
||||||
blocks: IBlock[];
|
blocks: IBlock[];
|
||||||
|
|
||||||
graph: Graph;
|
graph: Graph;
|
||||||
hierarchy: Graph;
|
hierarchy: Graph<string>;
|
||||||
schemas: number[];
|
schemas: number[];
|
||||||
stats: IOperationSchemaStats;
|
stats: IOperationSchemaStats;
|
||||||
operationByID: Map<number, IOperation>;
|
operationByID: Map<number, IOperation>;
|
||||||
blockByID: Map<number, IBlock>;
|
blockByID: Map<number, IBlock>;
|
||||||
|
itemByNodeID: Map<string, IOssItem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents item of OperationSchema. */
|
|
||||||
export type IOssItem = IOperation | IBlock;
|
|
||||||
|
|
||||||
/** Represents substitution error description. */
|
/** Represents substitution error description. */
|
||||||
export interface ISubstitutionErrorDescription {
|
export interface ISubstitutionErrorDescription {
|
||||||
errorType: SubstitutionErrorType;
|
errorType: SubstitutionErrorType;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
|
@ -47,10 +47,12 @@ export function FormOSS() {
|
||||||
const readOnly = useWatch({ control, name: 'read_only' });
|
const readOnly = useWatch({ control, name: 'read_only' });
|
||||||
|
|
||||||
const prevDirty = useRef(isDirty);
|
const prevDirty = useRef(isDirty);
|
||||||
|
useEffect(() => {
|
||||||
if (prevDirty.current !== isDirty) {
|
if (prevDirty.current !== isDirty) {
|
||||||
prevDirty.current = isDirty;
|
prevDirty.current = isDirty;
|
||||||
setIsModified(isDirty);
|
setIsModified(isDirty);
|
||||||
}
|
}
|
||||||
|
}, [isDirty, setIsModified]);
|
||||||
|
|
||||||
function onSubmit(data: IUpdateLibraryItemDTO) {
|
function onSubmit(data: IUpdateLibraryItemDTO) {
|
||||||
return updateOss(data).then(() => reset({ ...data }));
|
return updateOss(data).then(() => reset({ ...data }));
|
||||||
|
|
|
@ -4,8 +4,7 @@ import { useRef } from 'react';
|
||||||
|
|
||||||
import { Dropdown } from '@/components/dropdown';
|
import { Dropdown } from '@/components/dropdown';
|
||||||
|
|
||||||
import { type IBlock, type IOperation, type IOssItem } from '../../../../models/oss';
|
import { type IOssItem, NodeType } from '../../../../models/oss';
|
||||||
import { isOperation } from '../../../../models/oss-api';
|
|
||||||
|
|
||||||
import { MenuBlock } from './menu-block';
|
import { MenuBlock } from './menu-block';
|
||||||
import { MenuOperation } from './menu-operation';
|
import { MenuOperation } from './menu-operation';
|
||||||
|
@ -27,7 +26,6 @@ interface ContextMenuProps extends ContextMenuData {
|
||||||
|
|
||||||
export function ContextMenu({ isOpen, item, cursorX, cursorY, onHide }: ContextMenuProps) {
|
export function ContextMenu({ isOpen, item, cursorX, cursorY, onHide }: ContextMenuProps) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const isOperationNode = isOperation(item);
|
|
||||||
|
|
||||||
function handleBlur(event: React.FocusEvent<HTMLDivElement>) {
|
function handleBlur(event: React.FocusEvent<HTMLDivElement>) {
|
||||||
if (!ref.current?.contains(event.relatedTarget as Node)) {
|
if (!ref.current?.contains(event.relatedTarget as Node)) {
|
||||||
|
@ -39,21 +37,20 @@ export function ContextMenu({ isOpen, item, cursorX, cursorY, onHide }: ContextM
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
className='relative'
|
className='fixed z-tooltip'
|
||||||
style={{ top: `calc(${cursorY}px - 2.5rem)`, left: cursorX }}
|
style={{ top: `calc(${cursorY}px + 0.5rem)`, left: cursorX }}
|
||||||
>
|
>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className='z-navigation!'
|
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
stretchLeft={cursorX >= window.innerWidth - MENU_WIDTH}
|
stretchLeft={cursorX >= window.innerWidth - MENU_WIDTH}
|
||||||
stretchTop={cursorY >= window.innerHeight - MENU_HEIGHT}
|
stretchTop={cursorY >= window.innerHeight - MENU_HEIGHT}
|
||||||
margin={cursorY >= window.innerHeight - MENU_HEIGHT ? 'mb-3' : 'mt-3'}
|
margin={cursorY >= window.innerHeight - MENU_HEIGHT ? 'mb-3' : 'mt-3'}
|
||||||
>
|
>
|
||||||
{!!item ? (
|
{!!item ? (
|
||||||
isOperationNode ? (
|
item.nodeType === NodeType.OPERATION ? (
|
||||||
<MenuOperation operation={item as IOperation} onHide={onHide} />
|
<MenuOperation operation={item} onHide={onHide} />
|
||||||
) : (
|
) : (
|
||||||
<MenuBlock block={item as IBlock} onHide={onHide} />
|
<MenuBlock block={item} onHide={onHide} />
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
|
@ -16,15 +16,15 @@ export const BLOCK_NODE_MIN_WIDTH = 160;
|
||||||
export const BLOCK_NODE_MIN_HEIGHT = 100;
|
export const BLOCK_NODE_MIN_HEIGHT = 100;
|
||||||
|
|
||||||
export function BlockNode(node: BlockInternalNode) {
|
export function BlockNode(node: BlockInternalNode) {
|
||||||
const { selected, schema } = useOssEdit();
|
const { selectedItems, schema } = useOssEdit();
|
||||||
const dropTarget = useDraggingStore(state => state.dropTarget);
|
const dropTarget = useDraggingStore(state => state.dropTarget);
|
||||||
const isDragging = useDraggingStore(state => state.isDragging);
|
const isDragging = useDraggingStore(state => state.isDragging);
|
||||||
const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
|
const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
|
||||||
const setHover = useOperationTooltipStore(state => state.setHoverItem);
|
const setHover = useOperationTooltipStore(state => state.setHoverItem);
|
||||||
|
|
||||||
const focus = selected.length === 1 ? selected[0] : null;
|
const focus = selectedItems.length === 1 ? selectedItems[0] : null;
|
||||||
const isParent = (!!focus && schema.hierarchy.at(focus)?.inputs.includes(-node.data.block.id)) ?? false;
|
const isParent = (!!focus && schema.hierarchy.at(focus.nodeID)?.inputs.includes(node.data.block.nodeID)) ?? false;
|
||||||
const isChild = (!!focus && schema.hierarchy.at(focus)?.outputs.includes(-node.data.block.id)) ?? false;
|
const isChild = (!!focus && schema.hierarchy.at(focus.nodeID)?.outputs.includes(node.data.block.nodeID)) ?? false;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NodeResizeControl minWidth={BLOCK_NODE_MIN_WIDTH} minHeight={BLOCK_NODE_MIN_HEIGHT}>
|
<NodeResizeControl minWidth={BLOCK_NODE_MIN_WIDTH} minHeight={BLOCK_NODE_MIN_HEIGHT}>
|
||||||
|
|
|
@ -21,9 +21,9 @@ interface NodeCoreProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NodeCore({ node }: NodeCoreProps) {
|
export function NodeCore({ node }: NodeCoreProps) {
|
||||||
const { selected, schema } = useOssEdit();
|
const { selectedItems, schema } = useOssEdit();
|
||||||
const focus = selected.length === 1 ? selected[0] : null;
|
const focus = selectedItems.length === 1 ? selectedItems[0] : null;
|
||||||
const isChild = (!!focus && schema.hierarchy.at(focus)?.outputs.includes(node.data.operation.id)) ?? false;
|
const isChild = (!!focus && schema.hierarchy.at(focus.nodeID)?.outputs.includes(node.data.operation.nodeID)) ?? false;
|
||||||
|
|
||||||
const setHover = useOperationTooltipStore(state => state.setHoverItem);
|
const setHover = useOperationTooltipStore(state => state.setHoverItem);
|
||||||
const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
|
const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { type Edge, type Node, useEdgesState, useNodesState, useOnSelectionChange, useReactFlow } from 'reactflow';
|
import { type Edge, type Node, useEdgesState, useNodesState, useOnSelectionChange, useReactFlow } from 'reactflow';
|
||||||
|
|
||||||
import { type IOperationSchema } from '@/features/oss/models/oss';
|
|
||||||
import { type Position2D } from '@/features/oss/models/oss-layout';
|
|
||||||
import { useOSSGraphStore } from '@/features/oss/stores/oss-graph';
|
|
||||||
|
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
|
import { type IOperationSchema, NodeType } from '../../../models/oss';
|
||||||
|
import { constructNodeID } from '../../../models/oss-api';
|
||||||
|
import { type Position2D } from '../../../models/oss-layout';
|
||||||
|
import { useOSSGraphStore } from '../../../stores/oss-graph';
|
||||||
import { useOssEdit } from '../oss-edit-context';
|
import { useOssEdit } from '../oss-edit-context';
|
||||||
|
|
||||||
import { flowOptions } from './oss-flow';
|
import { flowOptions } from './oss-flow';
|
||||||
|
@ -29,10 +29,10 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
|
||||||
|
|
||||||
function onSelectionChange({ nodes }: { nodes: Node[] }) {
|
function onSelectionChange({ nodes }: { nodes: Node[] }) {
|
||||||
const ids = nodes.map(node => Number(node.id));
|
const ids = nodes.map(node => node.id);
|
||||||
setSelected(prev => [
|
setSelected(prev => [
|
||||||
...prev.filter(nodeID => ids.includes(nodeID)),
|
...prev.filter(nodeID => ids.includes(nodeID)),
|
||||||
...ids.filter(nodeID => !prev.includes(Number(nodeID)))
|
...ids.filter(nodeID => !prev.includes(nodeID))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,46 +41,45 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const resetGraph = useCallback(() => {
|
const resetGraph = useCallback(() => {
|
||||||
const newNodes: Node[] = [
|
const newNodes: Node[] = schema.hierarchy.topologicalOrder().map(nodeID => {
|
||||||
...schema.hierarchy
|
const item = schema.itemByNodeID.get(nodeID)!;
|
||||||
.topologicalOrder()
|
if (item.nodeType === NodeType.BLOCK) {
|
||||||
.filter(id => id < 0)
|
|
||||||
.map(id => {
|
|
||||||
const block = schema.blockByID.get(-id)!;
|
|
||||||
return {
|
return {
|
||||||
id: String(id),
|
id: nodeID,
|
||||||
type: 'block',
|
type: 'block',
|
||||||
data: { label: block.title, block: block },
|
data: { label: item.title, block: item },
|
||||||
position: computeRelativePosition(schema, { x: block.x, y: block.y }, block.parent),
|
position: computeRelativePosition(schema, { x: item.x, y: item.y }, item.parent),
|
||||||
style: {
|
style: {
|
||||||
width: block.width,
|
width: item.width,
|
||||||
height: block.height
|
height: item.height
|
||||||
},
|
},
|
||||||
parentId: block.parent ? `-${block.parent}` : undefined,
|
parentId: item.parent ? constructNodeID(NodeType.BLOCK, item.parent) : undefined,
|
||||||
zIndex: Z_BLOCK
|
zIndex: Z_BLOCK
|
||||||
};
|
};
|
||||||
}),
|
} else {
|
||||||
...schema.operations.map(operation => ({
|
return {
|
||||||
id: String(operation.id),
|
id: item.nodeID,
|
||||||
type: operation.operation_type.toString(),
|
type: item.operation_type.toString(),
|
||||||
data: { label: operation.alias, operation: operation },
|
data: { label: item.alias, operation: item },
|
||||||
position: computeRelativePosition(schema, { x: operation.x, y: operation.y }, operation.parent),
|
position: computeRelativePosition(schema, { x: item.x, y: item.y }, item.parent),
|
||||||
parentId: operation.parent ? `-${operation.parent}` : undefined,
|
parentId: item.parent ? constructNodeID(NodeType.BLOCK, item.parent) : undefined,
|
||||||
zIndex: Z_SCHEMA
|
zIndex: Z_SCHEMA
|
||||||
}))
|
};
|
||||||
];
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const newEdges: Edge[] = schema.arguments.map((argument, index) => ({
|
const newEdges: Edge[] = schema.arguments.map((argument, index) => {
|
||||||
|
const source = schema.operationByID.get(argument.argument)!;
|
||||||
|
const target = schema.operationByID.get(argument.operation)!;
|
||||||
|
return {
|
||||||
id: String(index),
|
id: String(index),
|
||||||
source: String(argument.argument),
|
source: source.nodeID,
|
||||||
target: String(argument.operation),
|
target: target.nodeID,
|
||||||
type: edgeStraight ? 'straight' : 'simplebezier',
|
type: edgeStraight ? 'straight' : 'simplebezier',
|
||||||
animated: edgeAnimate,
|
animated: edgeAnimate,
|
||||||
targetHandle:
|
targetHandle: source.x > target.x ? 'right' : 'left'
|
||||||
schema.operationByID.get(argument.argument)!.x > schema.operationByID.get(argument.operation)!.x
|
};
|
||||||
? 'right'
|
});
|
||||||
: 'left'
|
|
||||||
}));
|
|
||||||
|
|
||||||
setNodes(newNodes);
|
setNodes(newNodes);
|
||||||
setEdges(newEdges);
|
setEdges(newEdges);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { promptText } from '@/utils/labels';
|
||||||
import { useDeleteBlock } from '../../../backend/use-delete-block';
|
import { useDeleteBlock } from '../../../backend/use-delete-block';
|
||||||
import { useMutatingOss } from '../../../backend/use-mutating-oss';
|
import { useMutatingOss } from '../../../backend/use-mutating-oss';
|
||||||
import { useUpdateLayout } from '../../../backend/use-update-layout';
|
import { useUpdateLayout } from '../../../backend/use-update-layout';
|
||||||
|
import { type IOssItem, NodeType } from '../../../models/oss';
|
||||||
import { type OssNode, type Position2D } from '../../../models/oss-layout';
|
import { type OssNode, type Position2D } from '../../../models/oss-layout';
|
||||||
import { GRID_SIZE, LayoutManager } from '../../../models/oss-layout-api';
|
import { GRID_SIZE, LayoutManager } from '../../../models/oss-layout-api';
|
||||||
import { useOSSGraphStore } from '../../../stores/oss-graph';
|
import { useOSSGraphStore } from '../../../stores/oss-graph';
|
||||||
|
@ -41,7 +42,7 @@ export const flowOptions = {
|
||||||
|
|
||||||
export function OssFlow() {
|
export function OssFlow() {
|
||||||
const mainHeight = useMainHeight();
|
const mainHeight = useMainHeight();
|
||||||
const { navigateOperationSchema, schema, selected, isMutable, canDeleteOperation } = useOssEdit();
|
const { navigateOperationSchema, schema, selected, selectedItems, isMutable, canDeleteOperation } = useOssEdit();
|
||||||
const { screenToFlowPosition } = useReactFlow();
|
const { screenToFlowPosition } = useReactFlow();
|
||||||
const { containMovement, nodes, onNodesChange, edges, onEdgesChange, resetGraph, resetView } = useOssFlow();
|
const { containMovement, nodes, onNodesChange, edges, onEdgesChange, resetGraph, resetView } = useOssFlow();
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
@ -76,20 +77,21 @@ export function OssFlow() {
|
||||||
manager: new LayoutManager(schema, getLayout()),
|
manager: new LayoutManager(schema, getLayout()),
|
||||||
defaultX: targetPosition.x,
|
defaultX: targetPosition.x,
|
||||||
defaultY: targetPosition.y,
|
defaultY: targetPosition.y,
|
||||||
initialInputs: selected.filter(id => id > 0),
|
initialInputs: selectedItems.filter(item => item?.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||||
initialParent: extractSingleBlock(selected),
|
initialParent: extractBlockParent(selectedItems),
|
||||||
onCreate: resetView
|
onCreate: resetView
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCreateBlock() {
|
function handleCreateBlock() {
|
||||||
const targetPosition = screenToFlowPosition({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
const targetPosition = screenToFlowPosition({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
||||||
const parent = extractSingleBlock(selected);
|
const parent = extractBlockParent(selectedItems);
|
||||||
showCreateBlock({
|
showCreateBlock({
|
||||||
manager: new LayoutManager(schema, getLayout()),
|
manager: new LayoutManager(schema, getLayout()),
|
||||||
defaultX: targetPosition.x,
|
defaultX: targetPosition.x,
|
||||||
defaultY: targetPosition.y,
|
defaultY: targetPosition.y,
|
||||||
initialChildren: parent !== null ? [] : selected,
|
initialChildren:
|
||||||
|
parent !== null && selectedItems.length === 1 && parent === selectedItems[0].id ? [] : selectedItems,
|
||||||
initialParent: parent,
|
initialParent: parent,
|
||||||
onCreate: resetView
|
onCreate: resetView
|
||||||
});
|
});
|
||||||
|
@ -99,25 +101,24 @@ export function OssFlow() {
|
||||||
if (selected.length !== 1) {
|
if (selected.length !== 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selected[0] > 0) {
|
const item = schema.itemByNodeID.get(selected[0]);
|
||||||
const operation = schema.operationByID.get(selected[0]);
|
if (!item) {
|
||||||
if (!operation || !canDeleteOperation(operation)) {
|
return;
|
||||||
|
}
|
||||||
|
if (item.nodeType === NodeType.OPERATION) {
|
||||||
|
if (!canDeleteOperation(item)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showDeleteOperation({
|
showDeleteOperation({
|
||||||
oss: schema,
|
oss: schema,
|
||||||
target: operation,
|
target: item,
|
||||||
layout: getLayout()
|
layout: getLayout()
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const block = schema.blockByID.get(-selected[0]);
|
|
||||||
if (!block) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!window.confirm(promptText.deleteBlock)) {
|
if (!window.confirm(promptText.deleteBlock)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void deleteBlock({ itemID: schema.id, data: { target: block.id, layout: getLayout() } });
|
void deleteBlock({ itemID: schema.id, data: { target: item.id, layout: getLayout() } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +220,10 @@ export function OssFlow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- Internals --------
|
// -------- Internals --------
|
||||||
function extractSingleBlock(selected: number[]): number | null {
|
function extractBlockParent(selectedItems: IOssItem[]): number | null {
|
||||||
const blocks = selected.filter(id => id < 0);
|
if (selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK) {
|
||||||
return blocks.length === 1 ? -blocks[0] : null;
|
return selectedItems[0].id;
|
||||||
|
}
|
||||||
|
const parents = selectedItems.map(item => item.parent).filter(id => id !== null);
|
||||||
|
return parents.length === 0 ? null : parents[0];
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { OperationType } from '../../../backend/types';
|
||||||
import { useExecuteOperation } from '../../../backend/use-execute-operation';
|
import { useExecuteOperation } from '../../../backend/use-execute-operation';
|
||||||
import { useMutatingOss } from '../../../backend/use-mutating-oss';
|
import { useMutatingOss } from '../../../backend/use-mutating-oss';
|
||||||
import { useUpdateLayout } from '../../../backend/use-update-layout';
|
import { useUpdateLayout } from '../../../backend/use-update-layout';
|
||||||
|
import { NodeType } from '../../../models/oss';
|
||||||
import { LayoutManager } from '../../../models/oss-layout-api';
|
import { LayoutManager } from '../../../models/oss-layout-api';
|
||||||
import { useOssEdit } from '../oss-edit-context';
|
import { useOssEdit } from '../oss-edit-context';
|
||||||
|
|
||||||
|
@ -48,11 +49,13 @@ export function ToolbarOssGraph({
|
||||||
className,
|
className,
|
||||||
...restProps
|
...restProps
|
||||||
}: ToolbarOssGraphProps) {
|
}: ToolbarOssGraphProps) {
|
||||||
const { schema, selected, isMutable, canDeleteOperation: canDelete } = useOssEdit();
|
const { schema, selectedItems, isMutable, canDeleteOperation: canDelete } = useOssEdit();
|
||||||
const isProcessing = useMutatingOss();
|
const isProcessing = useMutatingOss();
|
||||||
const { resetView } = useOssFlow();
|
const { resetView } = useOssFlow();
|
||||||
const selectedOperation = selected.length !== 1 ? null : schema.operationByID.get(selected[0]) ?? null;
|
const selectedOperation =
|
||||||
const selectedBlock = selected.length !== 1 ? null : schema.blockByID.get(-selected[0]) ?? null;
|
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.OPERATION ? selectedItems[0] : null;
|
||||||
|
const selectedBlock =
|
||||||
|
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK ? selectedItems[0] : null;
|
||||||
const getLayout = useGetLayout();
|
const getLayout = useGetLayout();
|
||||||
|
|
||||||
const { updateLayout } = useUpdateLayout();
|
const { updateLayout } = useUpdateLayout();
|
||||||
|
@ -145,7 +148,9 @@ export function ToolbarOssGraph({
|
||||||
title='Исправить позиции узлов'
|
title='Исправить позиции узлов'
|
||||||
icon={<IconFixLayout size='1.25rem' className='icon-primary' />}
|
icon={<IconFixLayout size='1.25rem' className='icon-primary' />}
|
||||||
onClick={handleFixLayout}
|
onClick={handleFixLayout}
|
||||||
disabled={selected.length > 1 || selected[0] > 0}
|
disabled={
|
||||||
|
selectedItems.length > 1 || (selectedItems.length > 0 && selectedItems[0].nodeType === NodeType.OPERATION)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Настройки отображения'
|
title='Настройки отображения'
|
||||||
|
@ -181,14 +186,14 @@ export function ToolbarOssGraph({
|
||||||
title='Активировать операцию'
|
title='Активировать операцию'
|
||||||
icon={<IconExecute size='1.25rem' className='icon-green' />}
|
icon={<IconExecute size='1.25rem' className='icon-green' />}
|
||||||
onClick={handleOperationExecute}
|
onClick={handleOperationExecute}
|
||||||
disabled={isProcessing || selected.length !== 1 || !readyForSynthesis}
|
disabled={isProcessing || selectedItems.length !== 1 || !readyForSynthesis}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Редактировать выбранную', 'Двойной клик')}
|
titleHtml={prepareTooltip('Редактировать выбранную', 'Двойной клик')}
|
||||||
aria-label='Редактировать выбранную'
|
aria-label='Редактировать выбранную'
|
||||||
icon={<IconEdit2 size='1.25rem' className='icon-primary' />}
|
icon={<IconEdit2 size='1.25rem' className='icon-primary' />}
|
||||||
onClick={handleEditItem}
|
onClick={handleEditItem}
|
||||||
disabled={selected.length !== 1 || isProcessing}
|
disabled={selectedItems.length !== 1 || isProcessing}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Удалить выбранную', 'Delete')}
|
titleHtml={prepareTooltip('Удалить выбранную', 'Delete')}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { type Node } from 'reactflow';
|
import { type Node } from 'reactflow';
|
||||||
|
|
||||||
import { useMoveItems } from '@/features/oss/backend/use-move-items';
|
|
||||||
|
|
||||||
import { useThrottleCallback } from '@/hooks/use-throttle-callback';
|
import { useThrottleCallback } from '@/hooks/use-throttle-callback';
|
||||||
import { useDraggingStore } from '@/stores/dragging';
|
import { useDraggingStore } from '@/stores/dragging';
|
||||||
|
|
||||||
|
import { useMoveItems } from '../../../backend/use-move-items';
|
||||||
|
import { NodeType } from '../../../models/oss';
|
||||||
import { useOssEdit } from '../oss-edit-context';
|
import { useOssEdit } from '../oss-edit-context';
|
||||||
|
|
||||||
import { useOssFlow } from './oss-flow-context';
|
import { useOssFlow } from './oss-flow-context';
|
||||||
|
@ -21,6 +21,7 @@ interface DraggingProps {
|
||||||
export function useDragging({ hideContextMenu }: DraggingProps) {
|
export function useDragging({ hideContextMenu }: DraggingProps) {
|
||||||
const { setContainMovement, containMovement, setNodes } = useOssFlow();
|
const { setContainMovement, containMovement, setNodes } = useOssFlow();
|
||||||
const setIsDragging = useDraggingStore(state => state.setIsDragging);
|
const setIsDragging = useDraggingStore(state => state.setIsDragging);
|
||||||
|
const isDragging = useDraggingStore(state => state.isDragging);
|
||||||
const getLayout = useGetLayout();
|
const getLayout = useGetLayout();
|
||||||
const { selected, schema } = useOssEdit();
|
const { selected, schema } = useOssEdit();
|
||||||
const dropTarget = useDropTarget();
|
const dropTarget = useDropTarget();
|
||||||
|
@ -43,7 +44,7 @@ export function useDragging({ hideContextMenu }: DraggingProps) {
|
||||||
function handleDragStart(event: React.MouseEvent, target: Node) {
|
function handleDragStart(event: React.MouseEvent, target: Node) {
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
setContainMovement(true);
|
setContainMovement(true);
|
||||||
applyContainMovement([target.id, ...selected.map(id => String(id))], true);
|
applyContainMovement([target.id, ...selected], true);
|
||||||
} else {
|
} else {
|
||||||
setContainMovement(false);
|
setContainMovement(false);
|
||||||
dropTarget.update(event);
|
dropTarget.update(event);
|
||||||
|
@ -61,24 +62,20 @@ export function useDragging({ hideContextMenu }: DraggingProps) {
|
||||||
|
|
||||||
function handleDragStop(event: React.MouseEvent, target: Node) {
|
function handleDragStop(event: React.MouseEvent, target: Node) {
|
||||||
if (containMovement) {
|
if (containMovement) {
|
||||||
applyContainMovement([target.id, ...selected.map(id => String(id))], false);
|
applyContainMovement([target.id, ...selected], false);
|
||||||
} else {
|
} else {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (isDragging) {
|
||||||
|
setIsDragging(false);
|
||||||
const new_parent = dropTarget.evaluate(event);
|
const new_parent = dropTarget.evaluate(event);
|
||||||
const allSelected = [...selected.filter(id => id != Number(target.id)), Number(target.id)];
|
const allSelected = [...selected.filter(id => id != target.id), target.id].map(id =>
|
||||||
const operations = allSelected
|
schema.itemByNodeID.get(id)
|
||||||
.filter(id => id > 0)
|
|
||||||
.map(id => schema.operationByID.get(id))
|
|
||||||
.filter(operation => !!operation);
|
|
||||||
const blocks = allSelected
|
|
||||||
.filter(id => id < 0)
|
|
||||||
.map(id => schema.blockByID.get(-id))
|
|
||||||
.filter(operation => !!operation);
|
|
||||||
const parents = new Set(
|
|
||||||
[...blocks.map(block => block.parent), ...operations.map(operation => operation.parent)].filter(id => !!id)
|
|
||||||
);
|
);
|
||||||
|
const parents = new Set(allSelected.map(item => item?.parent).filter(id => !!id));
|
||||||
|
const operations = allSelected.filter(item => item?.nodeType === NodeType.OPERATION);
|
||||||
|
const blocks = allSelected.filter(item => item?.nodeType === NodeType.BLOCK);
|
||||||
if (
|
if (
|
||||||
(parents.size !== 1 || parents.values().next().value !== new_parent) &&
|
(parents.size !== 1 || parents.values().next().value !== new_parent) &&
|
||||||
(parents.size !== 0 || new_parent !== null)
|
(parents.size !== 0 || new_parent !== null)
|
||||||
|
@ -94,8 +91,8 @@ export function useDragging({ hideContextMenu }: DraggingProps) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setIsDragging(false);
|
|
||||||
setContainMovement(false);
|
setContainMovement(false);
|
||||||
dropTarget.reset();
|
dropTarget.reset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { useReactFlow } from 'reactflow';
|
import { useReactFlow } from 'reactflow';
|
||||||
|
|
||||||
|
import { NodeType } from '@/features/oss/models/oss';
|
||||||
|
|
||||||
import { useDraggingStore } from '@/stores/dragging';
|
import { useDraggingStore } from '@/stores/dragging';
|
||||||
|
|
||||||
import { useOssEdit } from '../oss-edit-context';
|
import { useOssEdit } from '../oss-edit-context';
|
||||||
|
@ -7,7 +9,7 @@ import { useOssEdit } from '../oss-edit-context';
|
||||||
/** Hook to encapsulate drop target logic. */
|
/** Hook to encapsulate drop target logic. */
|
||||||
export function useDropTarget() {
|
export function useDropTarget() {
|
||||||
const { getIntersectingNodes, screenToFlowPosition } = useReactFlow();
|
const { getIntersectingNodes, screenToFlowPosition } = useReactFlow();
|
||||||
const { selected, schema } = useOssEdit();
|
const { selectedItems, selected, schema } = useOssEdit();
|
||||||
const dropTarget = useDraggingStore(state => state.dropTarget);
|
const dropTarget = useDraggingStore(state => state.dropTarget);
|
||||||
const setDropTarget = useDraggingStore(state => state.setDropTarget);
|
const setDropTarget = useDraggingStore(state => state.setDropTarget);
|
||||||
|
|
||||||
|
@ -19,17 +21,16 @@ export function useDropTarget() {
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1
|
height: 1
|
||||||
})
|
})
|
||||||
.map(node => Number(node.id))
|
.filter(node => !selected.includes(node.id))
|
||||||
.filter(id => id < 0 && !selected.includes(id))
|
.map(node => schema.itemByNodeID.get(node.id))
|
||||||
.map(id => schema.blockByID.get(-id))
|
.filter(item => item?.nodeType === NodeType.BLOCK);
|
||||||
.filter(block => !!block);
|
|
||||||
|
|
||||||
if (blocks.length === 0) {
|
if (blocks.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const successors = schema.hierarchy.expandAllOutputs([...selected]).filter(id => id < 0);
|
const successors = schema.hierarchy.expandAllOutputs(selectedItems.map(item => item.nodeID));
|
||||||
blocks = blocks.filter(block => !successors.includes(-block.id));
|
blocks = blocks.filter(block => !successors.includes(block.nodeID));
|
||||||
if (blocks.length === 0) {
|
if (blocks.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,13 @@ export function useGetLayout() {
|
||||||
operations: nodes
|
operations: nodes
|
||||||
.filter(node => node.type !== 'block')
|
.filter(node => node.type !== 'block')
|
||||||
.map(node => ({
|
.map(node => ({
|
||||||
id: Number(node.id),
|
id: schema.itemByNodeID.get(node.id)!.id,
|
||||||
...computeAbsolutePosition(node, schema, nodeById)
|
...computeAbsolutePosition(node, schema, nodeById)
|
||||||
})),
|
})),
|
||||||
blocks: nodes
|
blocks: nodes
|
||||||
.filter(node => node.type === 'block')
|
.filter(node => node.type === 'block')
|
||||||
.map(node => ({
|
.map(node => ({
|
||||||
id: -Number(node.id),
|
id: schema.itemByNodeID.get(node.id)!.id,
|
||||||
...computeAbsolutePosition(node, schema, nodeById),
|
...computeAbsolutePosition(node, schema, nodeById),
|
||||||
width: node.width ?? BLOCK_NODE_MIN_WIDTH,
|
width: node.width ?? BLOCK_NODE_MIN_WIDTH,
|
||||||
height: node.height ?? BLOCK_NODE_MIN_HEIGHT
|
height: node.height ?? BLOCK_NODE_MIN_HEIGHT
|
||||||
|
@ -35,7 +35,7 @@ export function useGetLayout() {
|
||||||
|
|
||||||
// ------- Internals -------
|
// ------- Internals -------
|
||||||
function computeAbsolutePosition(target: Node, schema: IOperationSchema, nodeById: Map<string, Node>): Position2D {
|
function computeAbsolutePosition(target: Node, schema: IOperationSchema, nodeById: Map<string, Node>): Position2D {
|
||||||
const nodes = schema.hierarchy.expandAllInputs([Number(target.id)]);
|
const nodes = schema.hierarchy.expandAllInputs([target.id]);
|
||||||
let x = target.position.x;
|
let x = target.position.x;
|
||||||
let y = target.position.y;
|
let y = target.position.y;
|
||||||
for (const nodeID of nodes) {
|
for (const nodeID of nodes) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { createContext, use } from 'react';
|
import { createContext, use } from 'react';
|
||||||
|
|
||||||
import { type IOperation, type IOperationSchema } from '../../models/oss';
|
import { type IOperation, type IOperationSchema, type IOssItem } from '../../models/oss';
|
||||||
|
|
||||||
export const OssTabID = {
|
export const OssTabID = {
|
||||||
CARD: 0,
|
CARD: 0,
|
||||||
|
@ -12,7 +12,8 @@ export type OssTabID = (typeof OssTabID)[keyof typeof OssTabID];
|
||||||
|
|
||||||
interface IOssEditContext {
|
interface IOssEditContext {
|
||||||
schema: IOperationSchema;
|
schema: IOperationSchema;
|
||||||
selected: number[];
|
selected: string[];
|
||||||
|
selectedItems: IOssItem[];
|
||||||
|
|
||||||
isOwned: boolean;
|
isOwned: boolean;
|
||||||
isMutable: boolean;
|
isMutable: boolean;
|
||||||
|
@ -22,7 +23,7 @@ interface IOssEditContext {
|
||||||
|
|
||||||
canDeleteOperation: (target: IOperation) => boolean;
|
canDeleteOperation: (target: IOperation) => boolean;
|
||||||
deleteSchema: () => void;
|
deleteSchema: () => void;
|
||||||
setSelected: React.Dispatch<React.SetStateAction<number[]>>;
|
setSelected: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OssEditContext = createContext<IOssEditContext | null>(null);
|
export const OssEditContext = createContext<IOssEditContext | null>(null);
|
||||||
|
|
|
@ -38,7 +38,8 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
const isMutable = role > UserRole.READER && !schema.read_only;
|
const isMutable = role > UserRole.READER && !schema.read_only;
|
||||||
const isEditor = !!user.id && schema.editors.includes(user.id);
|
const isEditor = !!user.id && schema.editors.includes(user.id);
|
||||||
|
|
||||||
const [selected, setSelected] = useState<number[]>([]);
|
const [selected, setSelected] = useState<string[]>([]);
|
||||||
|
const selectedItems = selected.map(id => schema.itemByNodeID.get(id)).filter(item => !!item);
|
||||||
|
|
||||||
const { deleteItem } = useDeleteItem();
|
const { deleteItem } = useDeleteItem();
|
||||||
|
|
||||||
|
@ -92,6 +93,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
value={{
|
value={{
|
||||||
schema,
|
schema,
|
||||||
selected,
|
selected,
|
||||||
|
selectedItems,
|
||||||
|
|
||||||
isOwned,
|
isOwned,
|
||||||
isMutable,
|
isMutable,
|
||||||
|
|
|
@ -13,11 +13,11 @@ export const useCreateConstituenta = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'create-constituenta'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'create-constituenta'],
|
||||||
mutationFn: rsformsApi.createConstituenta,
|
mutationFn: rsformsApi.createConstituenta,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
||||||
updateTimestamp(data.schema.id);
|
updateTimestamp(data.schema.id);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -13,11 +13,11 @@ export const useDeleteConstituents = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'delete-constituents'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'delete-constituents'],
|
||||||
mutationFn: rsformsApi.deleteConstituents,
|
mutationFn: rsformsApi.deleteConstituents,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -13,11 +13,11 @@ export const useInlineSynthesis = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'inline-synthesis'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'inline-synthesis'],
|
||||||
mutationFn: rsformsApi.inlineSynthesis,
|
mutationFn: rsformsApi.inlineSynthesis,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -12,11 +12,11 @@ export const useProduceStructure = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'produce-structure'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'produce-structure'],
|
||||||
mutationFn: rsformsApi.produceStructure,
|
mutationFn: rsformsApi.produceStructure,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
||||||
updateTimestamp(data.schema.id);
|
updateTimestamp(data.schema.id);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -13,11 +13,11 @@ export const useRenameConstituenta = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'rename-constituenta'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'rename-constituenta'],
|
||||||
mutationFn: rsformsApi.renameConstituenta,
|
mutationFn: rsformsApi.renameConstituenta,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
||||||
updateTimestamp(data.schema.id);
|
updateTimestamp(data.schema.id);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -12,11 +12,11 @@ export const useResetAliases = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'reset-aliases'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'reset-aliases'],
|
||||||
mutationFn: rsformsApi.resetAliases,
|
mutationFn: rsformsApi.resetAliases,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -13,11 +13,11 @@ export const useSubstituteConstituents = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'substitute-constituents'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'substitute-constituents'],
|
||||||
mutationFn: rsformsApi.substituteConstituents,
|
mutationFn: rsformsApi.substituteConstituents,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||||
updateTimestamp(data.id);
|
updateTimestamp(data.id);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -13,10 +13,10 @@ export const useUpdateConstituenta = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'update-constituenta'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'update-constituenta'],
|
||||||
mutationFn: rsformsApi.updateConstituenta,
|
mutationFn: rsformsApi.updateConstituenta,
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: async (_, variables) => {
|
||||||
updateTimestamp(variables.itemID);
|
updateTimestamp(variables.itemID);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
|
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -12,13 +12,13 @@ export const useUploadTRS = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'load-trs'],
|
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'load-trs'],
|
||||||
mutationFn: rsformsApi.upload,
|
mutationFn: rsformsApi.upload,
|
||||||
onSuccess: data => {
|
onSuccess: async data => {
|
||||||
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
|
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
|
||||||
client.setQueryData(KEYS.composite.libraryList, (prev: ILibraryItem[] | undefined) =>
|
client.setQueryData(KEYS.composite.libraryList, (prev: ILibraryItem[] | undefined) =>
|
||||||
prev?.map(item => (item.id === data.id ? data : item))
|
prev?.map(item => (item.id === data.id ? data : item))
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.allSettled([
|
await Promise.allSettled([
|
||||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||||
client.invalidateQueries({
|
client.invalidateQueries({
|
||||||
queryKey: [rsformsApi.baseKey],
|
queryKey: [rsformsApi.baseKey],
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
import { LRParser } from '@lezer/lr';
|
import {LRParser} from "@lezer/lr"
|
||||||
import { highlighting } from './highlight';
|
import {highlighting} from "./highlight"
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states:
|
states: "$nQVQPOOObQQO'#C^OOQO'#Cl'#ClOjQPO'#CuOxQPO'#CdOOQO'#Ce'#CeOOQO'#Ck'#CkOOQO'#Cf'#CfQVQPOOO}QPO,58xO!SQPO,58{OOQO,59a,59aO!XQPO,59OOOQO-E6d-E6dO!^QSO1G.dO!cQPO1G.gOOQO1G.j1G.jO!hQQO'#CpOOQO'#C`'#C`O!pQPO7+$OOOQO'#Cg'#CgO!uQPO'#CcO!}QPO7+$RO!^QSO,59[OOQO<<Gj<<GjOOQO-E6e-E6eOOQO<<Gm<<GmOOQO1G.v1G.v",
|
||||||
"$nQVQPOOObQQO'#C^OOQO'#Cl'#ClOjQPO'#CuOxQPO'#CdOOQO'#Ce'#CeOOQO'#Ck'#CkOOQO'#Cf'#CfQVQPOOO}QPO,58xO!SQPO,58{OOQO,59a,59aO!XQPO,59OOOQO-E6d-E6dO!^QSO1G.dO!cQPO1G.gOOQO1G.j1G.jO!hQQO'#CpOOQO'#C`'#C`O!pQPO7+$OOOQO'#Cg'#CgO!uQPO'#CcO!}QPO7+$RO!^QSO,59[OOQO<<Gj<<GjOOQO-E6e-E6eOOQO<<Gm<<GmOOQO1G.v1G.v",
|
stateData: "#V~O^OS~ObPOgROhSO~ORXOUYO~OgRO[iXbiXhiX~OgRO~Oc^O~Oc_O~Oh`O~OeaO~OgdO~OfgOadX~OahO~OgdOaVX~OajO~Og^~",
|
||||||
stateData: '#V~O^OS~ObPOgROhSO~ORXOUYO~OgRO[iXbiXhiX~OgRO~Oc^O~Oc_O~Oh`O~OeaO~OgdO~OfgOadX~OahO~OgdOaVX~OajO~Og^~',
|
goto: "!kjPPkPokPruuy!PPPP!VuPPP!ZPPPP!aTQOWRc^Rf_TUOWQWOR]WQe_RieTVOWQb^RkgSTOWQZRR[S",
|
||||||
goto: '!kjPPkPokPruuy!PPPP!VuPPP!ZPPPP!aTQOWRc^Rf_TUOWQWOR]WQe_RieTVOWQb^RkgSTOWQZRR[S',
|
nodeNames: "⚠ Text RefEntity Global Grams RefSyntactic Offset Nominal Error Filler",
|
||||||
nodeNames: '⚠ Text RefEntity Global Grams RefSyntactic Offset Nominal Error Filler',
|
|
||||||
maxTerm: 25,
|
maxTerm: 25,
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 2,
|
repeatNodeCount: 2,
|
||||||
tokenData:
|
tokenData: ".V~R!UOX$eXZ%VZ^%z^p$epq%Vqr'sr|$e|}'x}!O(f!O!Q$e!Q!R)i!R![*i![!b$e!b!c+k!c!d+v!d!e)i!e!f+v!f!g+v!g!h)i!h!i+v!i!r)i!r!s+v!s!t)i!t!u+v!u!v+v!v!w+v!w!z)i!z!{+v!{!})i!}#T$e#T#o)i#p#q-{#q#r.Q#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$eP$jVgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eP%SP;=`<%l$e~%[Y^~X^%Vpq%V#y#z%V$f$g%V#BY#BZ%V$IS$I_%V$I|$JO%V$JT$JU%V$KV$KW%V&FU&FV%V~&RjgP^~OX$eXZ%VZ^%z^p$epq%Vr!b$e!c#o$e#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$e~'xOh~R(PVfQgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eV(m^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eT)p]eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![)i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV*r]UQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~+nP#o#p+q~+vOb~V+}^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV-S]RQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~.QOc~~.VOa~",
|
||||||
".V~R!UOX$eXZ%VZ^%z^p$epq%Vqr'sr|$e|}'x}!O(f!O!Q$e!Q!R)i!R![*i![!b$e!b!c+k!c!d+v!d!e)i!e!f+v!f!g+v!g!h)i!h!i+v!i!r)i!r!s+v!s!t)i!t!u+v!u!v+v!v!w+v!w!z)i!z!{+v!{!})i!}#T$e#T#o)i#p#q-{#q#r.Q#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$eP$jVgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eP%SP;=`<%l$e~%[Y^~X^%Vpq%V#y#z%V$f$g%V#BY#BZ%V$IS$I_%V$I|$JO%V$JT$JU%V$KV$KW%V&FU&FV%V~&RjgP^~OX$eXZ%VZ^%z^p$epq%Vr!b$e!c#o$e#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$e~'xOh~R(PVfQgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eV(m^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eT)p]eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![)i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV*r]UQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~+nP#o#p+q~+vOb~V+}^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV-S]RQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~.QOc~~.VOa~",
|
|
||||||
tokenizers: [0, 1, 2],
|
tokenizers: [0, 1, 2],
|
||||||
topRules: { Text: [0, 1] },
|
topRules: {"Text":[0,1]},
|
||||||
tokenPrec: 96
|
tokenPrec: 96
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
export const
|
export const Expression = 1,
|
||||||
Expression = 1,
|
|
||||||
Logic = 2,
|
Logic = 2,
|
||||||
Logic_predicates = 3,
|
Logic_predicates = 3,
|
||||||
Variable = 4,
|
Variable = 4,
|
||||||
|
@ -43,4 +42,4 @@ export const
|
||||||
Logic_binary = 76,
|
Logic_binary = 76,
|
||||||
Function_decl = 81,
|
Function_decl = 81,
|
||||||
Arguments = 82,
|
Arguments = 82,
|
||||||
Declaration = 83
|
Declaration = 83;
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
import {LRParser} from "@lezer/lr"
|
import { LRParser } from '@lezer/lr';
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: "2xO!sQPOOOOQO'#Ch'#ChO#lQPO'#EWOOQO'#EW'#EWO%yQPO'#EVOVQPO'#CnO'_QPO'#CzO'fQPO'#C{O'nQPO'#C}O'sQPO'#DRO'xQPO'#DWO'}QPO'#D[OOQO'#Cw'#CwO(SQPO'#CwOOQO'#D_'#D_OOQO'#Cg'#CgO)pQPO'#CgO)uQPO'#CgO)zQPO'#C_OVQPO'#DsO*SQPO'#DvOOQO'#Dr'#DrO*[QPO'#DrO*aQPO'#EVOOQO'#C^'#C^O*rQPO'#EPQOQPOOO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,58yO+OQPO'#CfO+VQPO,59YO,jQPO,59OO,rQPO'#CeO,wQPO,58xO-YQPO,59mO-{QPO'#CfO*wQPO'#CnO.YQPO,59fOOQO'#Cf'#CfO*wQPO,59gOOQO,59g,59gO*wQPO,59iO*SQPO,59mO*wQPO,59rO*SQPO,59vO*wQPO,59RO*wQPO,59RO$lQPO'#C_OOQO,5:_,5:_O*wQPO'#CdOOQO'#C`'#C`OOQO'#Dx'#DxO.bQPO,5:bO*wQPO,5:^OVQPO,5:fOVQPO,5:fOVQPO,5:fOVQPO,5:fO.jQPO'#EROOQO'#EQ'#EQO.oQPO,5:kOOQO1G.t1G.tO2`QPO1G.tO2gQPO1G.tO6VQPO1G.tO9uQPO1G.tO9|QPO1G.tO:TQPO1G.tO:[QPO1G.tO=vQPO1G.eOOQO1G.j1G.jO*wQPO,59POOQO1G.d1G.dO*wQPO1G/XOOQO1G/Q1G/QO>gQPO1G/RO>nQPO1G/TO>vQPO1G/XO>{QPO1G/^O?SQPO1G/bO?XQPO1G.mO?aQPO1G.mO*SQPO,5:dO*wQPO1G/|O?hQPO1G/xOOQO1G0Q1G0QO@[QPO1G0QO@cQPO1G0QO@jQPO1G0QO*wQPO,5:mO*rQPO,5:lOVQPO1G0VOAXQPO1G.kOAiQPO7+$sOOQO7+$m7+$mOApQPO7+$oO*wQPO7+$sOVQPO7+$xO*wQPO7+$|OOQO7+$X7+$XOOQO1G0O1G0OOAuQPO7+%hOOQO7+%d7+%dOBcQPO1G0XOOQO1G0W1G0WOOQO7+%q7+%qOVQPO<<H_O*wQPO<<HZOBmQPO<<H_OBtQPO'#DXOCYQPO<<HdOCbQPO<<HhOCiQPO<<ISODWQPOAN=yODiQPOAN=uOVQPOAN=yOVQPO,59sOOQOAN>OAN>OOVQPOAN>SOOQOG23eG23eOOQOG23aG23aODpQPOG23eOERQPO1G/_OEgQPOG23nOEnQPOG23nOOQOLD)PLD)POOQOLD)YLD)YO*wQPOLD)YOFPQPO!$'LtOOQO!)9B`!)9B`",
|
states:
|
||||||
stateData: "Fi~O!xOS~OTQOVTO]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O!hcO!ifO!kdO!mdO~OsiO~PVOc!zXd!zXe!zXf!zXg!zXh!zXi!zXj!zX!Y!zXl!zX~Ov!zX!PSX!ZSX![!zX!]!zX!^!zX!_!zX!`!zX!a!zX!b!zX!c!zX!d!zX!e!zX!v!zX~P!zOckOdlOemOfnOgoOhpOiqOjrOvsO![sO!]sO!^sO!_sO!`sO!asO!bsO!csO!dsO!esO~O!v!yX~P$lOV{O]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O~OTyO~P&QOV!OOpVO~Os!QO~Om!RO~Om!SO~Om!TO~OckXdkXekXfkXgkXhkXikXjkXvkX!PSX!ZSX![kX!]kX!^kX!_kX!`kX!akX!bkX!ckX!dkX!ekX!vkX!YkXlkX~Os!UO~OV!VO~O!PsO!ZsO~OT!ZOV!YO~Os!^O~O!o!_O!p!`O!q!aO!r!bO!v!yX~OT!cO~OTRO~P&QO!YYX~P$lOU!fOcZXdZXeZXfZXgZXhZXiZXjZXvZX!YZX![ZX!]ZX!^ZX!_ZX!`ZX!aZX!bZX!cZX!dZX!eZX~OU!oO!YYX~O!Y!pO~OU!qO!o!_O!p!`O!q!aO!r!bO~Ov!rO~P!zOckOdlOemOfnOgoOhpOiqOjrO~OlYX!YYXtYX~P-aOl!sO!Y!pO~Ov!|O!Y!{O~Ov#SO~Ot#UO!Y#TO~OckOebifbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Odbi~P.wOdlO~P.wOckOdlOemOjrOgbihbiibivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ofbi~P2nOckOdlOemOfnOiqOjrOhbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ogbi~P6^OgoO~P6^OfnO~P2nOckOdlOemOfbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~O!oRi!pRi!qRi!rRi!vRiURilRi|RiwRi~P-aOU#XO~P-aOt#YO!Y!pO~Ov#ZO~Ow#[O~P-aO!P#]O~Ot#^O!Y!pO~OU#^O~P-aOt#aO!Y!pO~O!o!_O!q!ni!r!ni!v!niU!nil!ni|!niw!ni~O!p!ni~P?pO!p!`O~P?pO!o!_O!p!`O!q!aO!r!ni!v!niU!nil!ni|!niw!ni~OUXi!YXilXitXi~P-aOw#eO~P-aOV#fO~OckOdlOemOfnOgoOhpOiqOjrO~PVOt!ui!Y!ui~P-aOw#nO~P-aO!o!_O!p!`O!q!aO!r!bOl{X|{X~Ol#pO|#oO~Ow#qO~P-aO!o!_O!p!`O!q!aO!r!bO!v!jyU!jyl!jy|!jyw!jy~Ol#rO!o!_O!p!`O!q!aO!r!bO~OU#sO~P-aOl#xO!o!_O!p!`O!q!aO!r!bO~O!o!_O!p!`O!q!aO!r!bOl{i|{i~Ol#yO~P$lOw#zO!o!_O!p!`O!q!aO!r!bO~Ol#|O~P-aOr!S!i!Q`a!U!V!W!X!TT}yxy~",
|
"2xO!sQPOOOOQO'#Ch'#ChO#lQPO'#EWOOQO'#EW'#EWO%yQPO'#EVOVQPO'#CnO'_QPO'#CzO'fQPO'#C{O'nQPO'#C}O'sQPO'#DRO'xQPO'#DWO'}QPO'#D[OOQO'#Cw'#CwO(SQPO'#CwOOQO'#D_'#D_OOQO'#Cg'#CgO)pQPO'#CgO)uQPO'#CgO)zQPO'#C_OVQPO'#DsO*SQPO'#DvOOQO'#Dr'#DrO*[QPO'#DrO*aQPO'#EVOOQO'#C^'#C^O*rQPO'#EPQOQPOOO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,58yO+OQPO'#CfO+VQPO,59YO,jQPO,59OO,rQPO'#CeO,wQPO,58xO-YQPO,59mO-{QPO'#CfO*wQPO'#CnO.YQPO,59fOOQO'#Cf'#CfO*wQPO,59gOOQO,59g,59gO*wQPO,59iO*SQPO,59mO*wQPO,59rO*SQPO,59vO*wQPO,59RO*wQPO,59RO$lQPO'#C_O.bQPO,5:_O*wQPO'#CdOOQO'#C`'#C`OOQO'#Dx'#DxO/PQPO,5:bO*wQPO,5:^OVQPO,5:fOVQPO,5:fOVQPO,5:fOVQPO,5:fO/XQPO'#EROOQO'#EQ'#EQO/^QPO,5:kOOQO1G.t1G.tO2}QPO1G.tO3UQPO1G.tO6tQPO1G.tO:dQPO1G.tO:kQPO1G.tO:rQPO1G.tO:yQPO1G.tO>eQPO1G.eOOQO1G.j1G.jO*wQPO,59POOQO1G.d1G.dO*wQPO1G/XOOQO1G/Q1G/QO?UQPO1G/RO?]QPO1G/TO?eQPO1G/XO?jQPO1G/^O?qQPO1G/bO?vQPO1G.mO@OQPO1G.mO*SQPO,5:dO*wQPO1G/|O@VQPO1G/xO@yQPO1G0QOAQQPO1G0QOAXQPO1G0QOOQO1G0Q1G0QO*wQPO,5:mO*rQPO,5:lOVQPO1G0VOAvQPO1G.kOBWQPO7+$sOOQO7+$m7+$mOB_QPO7+$oO*wQPO7+$sOVQPO7+$xO*wQPO7+$|OOQO7+$X7+$XOOQO1G0O1G0OOBdQPO7+%hOOQO7+%d7+%dOCQQPO1G0XOOQO1G0W1G0WOOQO7+%q7+%qOVQPO<<H_O*wQPO<<HZOC[QPO<<H_OCcQPO'#DXOCwQPO<<HdODPQPO<<HhODWQPO<<ISODuQPOAN=yOEWQPOAN=uOVQPOAN=yOVQPO,59sOOQOAN>OAN>OOVQPOAN>SOOQOG23eG23eOOQOG23aG23aOE_QPOG23eOEpQPO1G/_OFUQPOG23nOF]QPOG23nOOQOLD)PLD)POOQOLD)YLD)YO*wQPOLD)YOFnQPO!$'LtOOQO!)9B`!)9B`",
|
||||||
goto: "-}!{PP!|#v$WPPP$t%x&T&f(dPPPPP)`PPPPPPPP(dPP*^+YP*^PPP*^PPPP*^,XPP*^PP,[PPPPPPPPPPPPPPPPPP#v-WPP-WP-hP#vPPPP-k-n-qPPP-w(dSgO#UQxTQ!XcQ#O!_Q#P!`Q#Q!aQ#R!bQ#h#[Q#k#`Q#l#eQ#t#nQ#u#oR#w#qmhOTc!_!`!a!b#U#[#`#e#n#o#qlbOTc!_!`!a!b#U#[#`#e#n#o#qQ![dQ!v!RQ!x!TR#_!{!U[Uklmnopqrs{!O!Q!S!U!V!Y!^!p!r!|#S#Z#]#f#zl]OTc!_!`!a!b#U#[#`#e#n#o#qX!Zd!R!T!{UvT{!YX}U!Q!U!^UwT{!YQ|UQ!u!QQ!y!UR!}!^SSO#UQtT[zU{!Q!U!Y!^d!Wc!_!`!a!b#[#`#e#n#oQ!fkQ!glQ!hmQ!inQ!joQ!kpQ!lqQ!mrQ!nsQ!t!OQ!w!SQ!z!VQ#V!pQ#W!rQ#`!|Q#b#SQ#g#ZQ#j#]Q#m#fQ#v#qR#{#z!s_OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!n_OUcklmnopqrs!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zTuT{!s[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!r[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zR!PVR#i#[!saOTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zmeOTc!_!`!a!b#U#[#`#e#n#o#qR!]dRjOR!eiQ!diR#c#TQjOR#d#U",
|
stateData:
|
||||||
nodeNames: "⚠ Expression Logic Logic_predicates Variable Local ) ( Tuple Setexpr_enum_min2 Setexpr_enum Setexpr Literal Integer EmptySet IntegerSet Global Radical Setexpr_binary + - * ∪ \\ ∆ ∩ × Setexpr_generators } { Enumeration Boolean ℬ Filter_expression Filter [ ] Declarative ∈ | PrefixD PrefixI Imperative Imp_blocks ; PrefixR Recursion := Function TextFunction BigPr SmallPr Card Bool Debool Red , :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = Logic_unary Negation ¬ Predicate Logic_quantor ∀ Variable_pack ∃ Logic_binary ⇔ ⇒ ∨ & Function_decl Arguments Declaration",
|
'GW~O!xOS~OTQOVTO]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O!hcO!ifO!kdO!mdO~OsiO~PVOc!zXd!zXe!zXf!zXg!zXh!zXi!zXj!zX!Y!zXl!zX~Ov!zX!PSX!ZSX![!zX!]!zX!^!zX!_!zX!`!zX!a!zX!b!zX!c!zX!d!zX!e!zX!v!zX~P!zOckOdlOemOfnOgoOhpOiqOjrOvsO![sO!]sO!^sO!_sO!`sO!asO!bsO!csO!dsO!esO~O!v!yX~P$lOV{O]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O~OTyO~P&QOV!OOpVO~Os!QO~Om!RO~Om!SO~Om!TO~OckXdkXekXfkXgkXhkXikXjkXvkX!PSX!ZSX![kX!]kX!^kX!_kX!`kX!akX!bkX!ckX!dkX!ekX!vkX!YkXlkX~Os!UO~OV!VO~O!PsO!ZsO~OT!ZOV!YO~Os!^O~O!o!_O!p!`O!q!aO!r!bO!v!yX~OT!cO~OTRO~P&QO!YYX~P$lOU!fOcZXdZXeZXfZXgZXhZXiZXjZXvZX!YZX![ZX!]ZX!^ZX!_ZX!`ZX!aZX!bZX!cZX!dZX!eZX~OU!oO!YYX~O!Y!pO~OU!qO!o!_O!p!`O!q!aO!r!bO~Ov!rO~P!zOckOdlOemOfnOgoOhpOiqOjrO~OlYX!YYXtYX~P-aOl!sO!Y!pO~O!o!_O!p!`O!q!aO!r!bO!v!gaU!gal!ga|!gaw!ga~Ov!|O!Y!{O~Ov#SO~Ot#UO!Y#TO~OckOebifbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Odbi~P/fOdlO~P/fOckOdlOemOjrOgbihbiibivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ofbi~P3]OckOdlOemOfnOiqOjrOhbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ogbi~P6{OgoO~P6{OfnO~P3]OckOdlOemOfbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~O!oRi!pRi!qRi!rRi!vRiURilRi|RiwRi~P-aOU#XO~P-aOt#YO!Y!pO~Ov#ZO~Ow#[O~P-aO!P#]O~Ot#^O!Y!pO~OU#^O~P-aOt#aO!Y!pO~O!q!aO!r!bO!o!ni!v!niU!nil!ni|!niw!ni~O!p!`O~P@_O!p!ni~P@_O!r!bO!o!ni!p!ni!q!ni!v!niU!nil!ni|!niw!ni~OUXi!YXilXitXi~P-aOw#eO~P-aOV#fO~OckOdlOemOfnOgoOhpOiqOjrO~PVOt!ui!Y!ui~P-aOw#nO~P-aO!o!_O!p!`O!q!aO!r!bOl{X|{X~Ol#pO|#oO~Ow#qO~P-aO!o!_O!p!`O!q!aO!r!bO!v!jyU!jyl!jy|!jyw!jy~Ol#rO!o!_O!p!`O!q!aO!r!bO~OU#sO~P-aOl#xO!o!_O!p!`O!q!aO!r!bO~O!o!_O!p!`O!q!aO!r!bOl{i|{i~Ol#yO~P$lOw#zO!o!_O!p!`O!q!aO!r!bO~Ol#|O~P-aOr!S!i!Q`a!U!V!W!X!TT}yxy~',
|
||||||
|
goto: '-}!{PP!|#v$WPPP$t%x&T&f(dPPPPP)`PPPPPPPP(dPP*^+YP*^PPP*^PPPP*^,XPP*^PP,[PPPPPPPPPPPPPPPPPP#v-WPP-WP-hP#vPPPP-k-n-qPPP-w(dSgO#UQxTQ!XcQ#O!_Q#P!`Q#Q!aQ#R!bQ#h#[Q#k#`Q#l#eQ#t#nQ#u#oR#w#qmhOTc!_!`!a!b#U#[#`#e#n#o#qlbOTc!_!`!a!b#U#[#`#e#n#o#qQ![dQ!v!RQ!x!TR#_!{!U[Uklmnopqrs{!O!Q!S!U!V!Y!^!p!r!|#S#Z#]#f#zl]OTc!_!`!a!b#U#[#`#e#n#o#qX!Zd!R!T!{UvT{!YX}U!Q!U!^UwT{!YQ|UQ!u!QQ!y!UR!}!^SSO#UQtT[zU{!Q!U!Y!^d!Wc!_!`!a!b#[#`#e#n#oQ!fkQ!glQ!hmQ!inQ!joQ!kpQ!lqQ!mrQ!nsQ!t!OQ!w!SQ!z!VQ#V!pQ#W!rQ#`!|Q#b#SQ#g#ZQ#j#]Q#m#fQ#v#qR#{#z!s_OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!n_OUcklmnopqrs!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zTuT{!s[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!r[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zR!PVR#i#[!saOTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zmeOTc!_!`!a!b#U#[#`#e#n#o#qR!]dRjOR!eiQ!diR#c#TQjOR#d#U',
|
||||||
|
nodeNames:
|
||||||
|
'⚠ Expression Logic Logic_predicates Variable Local ) ( Tuple Setexpr_enum_min2 Setexpr_enum Setexpr Literal Integer EmptySet IntegerSet Global Radical Setexpr_binary * + - ∪ \\ ∆ ∩ × Setexpr_generators } { Enumeration Boolean ℬ Filter_expression Filter [ ] Declarative ∈ | PrefixD PrefixI Imperative Imp_blocks ; PrefixR Recursion := Function TextFunction BigPr SmallPr Card Bool Debool Red , :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = Logic_unary Negation ¬ Predicate Logic_quantor ∀ Variable_pack ∃ Logic_binary ⇔ ⇒ ∨ & Function_decl Arguments Declaration',
|
||||||
maxTerm: 88,
|
maxTerm: 88,
|
||||||
nodeProps: [
|
nodeProps: [
|
||||||
["openedBy", 6,"(",28,"{"],
|
['openedBy', 6, '(', 28, '{'],
|
||||||
["closedBy", 7,")",29,"}"]
|
['closedBy', 7, ')', 29, '}']
|
||||||
],
|
],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 0,
|
repeatNodeCount: 0,
|
||||||
tokenData: "6g~R!iX^%ppq%pvw&exy&jyz&oz{&t{|&y|}'O}!O'T!Q!['Y![!]'b!]!^'u!^!_'z!_!`(P!`!a(U!c!d(Z!e!f(Z!f!g(i!h!i(q!k!l)e!r!s)j!t!u*^!u!v(Z!v!w(Z!z!{(Z!|!}*n!}#O*s#O#P*x#P#Q*}#R#S+S#T#U+S#U#V+j#V#W-Y#W#X.u#X#d+S#d#e1_#e#f+S#f#g2t#g#o+S#o#p4O#p#q4T#q#r4Y#y#z%p$f$g%p$r$s4_%o%p4d5i6S+S#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p% l% m4i%%Y%%Z4n%%[%%]4s%&Y%&Z4x%&]%&^4}%&_%&`5S%&`%&a5X%&b%&c5^%&c%&d5c%'S%'T5h%'T%'U5m%'U%'V5r%(^%(_5w%(b%(c5|%(c%(d6R%)Q%)R6W%)S%)T6]%)U%)V6b&FU&FV%p~%uY!x~X^%ppq%p#y#z%p$f$g%p#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p&FU&FV%p~&jO!r~~&oOV~~&tOU~~&yOe~~'OOc~~'TO!Y~~'YOd~~'_P]~!Q!['Y~'eQ!_!`'k%&b%&c'p~'pO!P~~'uO!Z~~'zO|~~(PO!b~~(UO!e~~(ZO!`~~(^P!Q![(a~(fP`~!Q![(a~(nPx~!Q![(a~(tQ!Q![(z#]#^)S~)PP!Q~!Q![(z~)VP!R![)Y~)_Qr~|})S!Q![)Y~)jOy~~)mQ!Q![)s#f#g){~)xP!i~!Q![)s~*OP!R![*R~*WQ!S~|}){!Q![*R~*cP}~!Q![*f~*kPa~!Q![*f~*sO_~~*xOs~~*}Og~~+SOt~~+XRT~!Q![+b#T#o+S5i6S+S~+gPT~!Q![+b~+oTT~!Q![+b#T#c+S#c#d,O#d#o+S5i6S+S~,TTT~!Q![+b#T#c+S#c#d,d#d#o+S5i6S+S~,iTT~!Q![+b#T#`+S#`#a,x#a#o+S5i6S+S~-PR!V~T~!Q![+b#T#o+S5i6S+S~-_ST~!Q![+b#T#U-k#U#o+S5i6S+S~-pTT~!Q![+b#T#f+S#f#g.P#g#o+S5i6S+S~.UTT~!Q![+b#T#W+S#W#X.e#X#o+S5i6S+S~.lR!U~T~!Q![+b#T#o+S5i6S+S~.zTT~!Q![+b#T#X+S#X#Y/Z#Y#o+S5i6S+S~/`TT~!Q![+b#T#U+S#U#V/o#V#o+S5i6S+S~/tTT~!Q![+b#T#c+S#c#d0T#d#o+S5i6S+S~0YTT~!Q![+b#T#c+S#c#d0i#d#o+S5i6S+S~0nTT~!Q![+b#T#`+S#`#a0}#a#o+S5i6S+S~1UR!W~T~!Q![+b#T#o+S5i6S+S~1dTT~!Q![+b#T#f+S#f#g1s#g#o+S5i6S+S~1xST~!Q!R+b!R![2U#T#o+S5i6S+S~2]Q!T~T~|}2c!Q![2U~2fP!R![2i~2nQ!T~|}2c!Q![2i~2yTT~!Q![+b#T#X+S#X#Y3Y#Y#o+S5i6S+S~3_TT~!Q![+b#T#W+S#W#X3n#X#o+S5i6S+S~3uR!X~T~!Q![+b#T#o+S5i6S+S~4TOm~~4YOw~~4_Ol~~4dO!h~~4iOj~~4nOp~~4sO!p~~4xO!o~~4}O!k~~5SO!m~~5XO^~~5^Oh~~5cOv~~5hO![~~5mO!q~~5rOi~~5wOf~~5|O!d~~6RO!c~~6WO!a~~6]O!_~~6bO!^~~6gO!]~",
|
tokenData:
|
||||||
|
"6g~R!iX^%ppq%pvw&exy&jyz&oz{&t{|&y|}'O}!O'T!Q!['Y![!]'b!]!^'u!^!_'z!_!`(P!`!a(U!c!d(Z!e!f(Z!f!g(i!h!i(q!k!l)e!r!s)j!t!u*^!u!v(Z!v!w(Z!z!{(Z!|!}*n!}#O*s#O#P*x#P#Q*}#R#S+S#T#U+S#U#V+j#V#W-Y#W#X.u#X#d+S#d#e1_#e#f+S#f#g2t#g#o+S#o#p4O#p#q4T#q#r4Y#y#z%p$f$g%p$r$s4_%o%p4d5i6S+S#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p% l% m4i%%Y%%Z4n%%[%%]4s%&Y%&Z4x%&]%&^4}%&_%&`5S%&`%&a5X%&b%&c5^%&c%&d5c%'S%'T5h%'T%'U5m%'U%'V5r%(^%(_5w%(b%(c5|%(c%(d6R%)Q%)R6W%)S%)T6]%)U%)V6b&FU&FV%p~%uY!x~X^%ppq%p#y#z%p$f$g%p#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p&FU&FV%p~&jO!r~~&oOV~~&tOU~~&yOc~~'OOd~~'TO!Y~~'YOe~~'_P]~!Q!['Y~'eQ!_!`'k%&b%&c'p~'pO!P~~'uO!Z~~'zO|~~(PO!b~~(UO!e~~(ZO!`~~(^P!Q![(a~(fP`~!Q![(a~(nPx~!Q![(a~(tQ!Q![(z#]#^)S~)PP!Q~!Q![(z~)VP!R![)Y~)_Qr~|})S!Q![)Y~)jOy~~)mQ!Q![)s#f#g){~)xP!i~!Q![)s~*OP!R![*R~*WQ!S~|}){!Q![*R~*cP}~!Q![*f~*kPa~!Q![*f~*sO_~~*xOs~~*}Og~~+SOt~~+XRT~!Q![+b#T#o+S5i6S+S~+gPT~!Q![+b~+oTT~!Q![+b#T#c+S#c#d,O#d#o+S5i6S+S~,TTT~!Q![+b#T#c+S#c#d,d#d#o+S5i6S+S~,iTT~!Q![+b#T#`+S#`#a,x#a#o+S5i6S+S~-PR!V~T~!Q![+b#T#o+S5i6S+S~-_ST~!Q![+b#T#U-k#U#o+S5i6S+S~-pTT~!Q![+b#T#f+S#f#g.P#g#o+S5i6S+S~.UTT~!Q![+b#T#W+S#W#X.e#X#o+S5i6S+S~.lR!U~T~!Q![+b#T#o+S5i6S+S~.zTT~!Q![+b#T#X+S#X#Y/Z#Y#o+S5i6S+S~/`TT~!Q![+b#T#U+S#U#V/o#V#o+S5i6S+S~/tTT~!Q![+b#T#c+S#c#d0T#d#o+S5i6S+S~0YTT~!Q![+b#T#c+S#c#d0i#d#o+S5i6S+S~0nTT~!Q![+b#T#`+S#`#a0}#a#o+S5i6S+S~1UR!W~T~!Q![+b#T#o+S5i6S+S~1dTT~!Q![+b#T#f+S#f#g1s#g#o+S5i6S+S~1xST~!Q!R+b!R![2U#T#o+S5i6S+S~2]Q!T~T~|}2c!Q![2U~2fP!R![2i~2nQ!T~|}2c!Q![2i~2yTT~!Q![+b#T#X+S#X#Y3Y#Y#o+S5i6S+S~3_TT~!Q![+b#T#W+S#W#X3n#X#o+S5i6S+S~3uR!X~T~!Q![+b#T#o+S5i6S+S~4TOm~~4YOw~~4_Ol~~4dO!h~~4iOj~~4nOp~~4sO!p~~4xO!o~~4}O!k~~5SO!m~~5XO^~~5^Oh~~5cOv~~5hO![~~5mO!q~~5rOi~~5wOf~~5|O!d~~6RO!c~~6WO!a~~6]O!_~~6bO!^~~6gO!]~",
|
||||||
tokenizers: [0],
|
tokenizers: [0],
|
||||||
topRules: {"Expression":[0,1]},
|
topRules: { Expression: [0, 1] },
|
||||||
tokenPrec: 1710
|
tokenPrec: 1739
|
||||||
})
|
});
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
export const
|
export const Expression = 1,
|
||||||
Expression = 1,
|
|
||||||
Local = 2,
|
Local = 2,
|
||||||
Literal = 4,
|
Literal = 4,
|
||||||
Global = 5,
|
Global = 5,
|
||||||
|
@ -12,4 +11,4 @@ export const
|
||||||
PrefixR = 28,
|
PrefixR = 28,
|
||||||
Function = 30,
|
Function = 30,
|
||||||
TextFunction = 31,
|
TextFunction = 31,
|
||||||
Predicate = 44
|
Predicate = 44;
|
||||||
|
|
|
@ -4,19 +4,19 @@ import { highlighting } from './highlight';
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states:
|
states:
|
||||||
"3UO!sQPOOOOQO'#C`'#C`O#lQPO'#DoOOQO'#Do'#DoO%yQPO'#DdOVQPO'#DpO'_QPO'#DrO'fQPO'#DsO'nQQO'#DtO'sQPO'#DuO'xQPO'#DvO'}QPO'#DxOOQO'#Dq'#DqO(SQPO'#DqO)pQQO'#C{OOQO'#C{'#C{OOQO'#Dk'#DkO)uQPO'#DkO)zQPO'#DkO*PQPO'#DfOVQPO'#EQO*XQPO'#EQO*^QPO'#EQO*fQPO'#DdOOQO'#De'#DeO*wQPO'#ETQOQPOOO*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:QO+TQPO'#DjO+[QPO,5:[O,oQPO,5:SO,wQPO'#DiO,|QPO,5:PO-_QPO,5:aO.QQPO'#DjO*|QPO'#DpO._QPO,5:^OOQO'#Dj'#DjO*|QPO,5:_OOQO,5:_,5:_O.gQPO,5:`O*^QPO,5:aO*|QPO,5:bO*^QPO,5:dOOQO,59g,59gO*|QPO,5:VO*|QPO,5:VO$lQPO'#DfOOQO,5:l,5:lO*|QPO,5:lO*|QPO'#DhOOQO'#Dg'#DgOOQO'#ER'#ERO.lQPO,5:lOVQPO,5:nOVQPO,5:nOVQPO,5:nOVQPO,5:nO.tQPO'#EVOOQO'#EU'#EUO.yQPO,5:oOOQO1G/v1G/vO2jQPO1G/vO2qQPO1G/vO6aQPO1G/vO:PQPO1G/vO:WQPO1G/vO:_QPO1G/vO:fQPO1G/vO>QQPO1G/lOOQO1G/n1G/nO*|QPO,5:TOOQO1G/k1G/kO*|QPO1G/{OOQO1G/x1G/xO>qQPO1G/yO*|QPO1G/zO>xQPO1G/{O>}QPO1G/|O?UQPO1G0OO?ZQPO1G/qO?cQPO1G/qO?jQPO1G0WO*^QPO,5:mO*|QPO1G0WOOQO1G0Y1G0YO@^QPO1G0YO@eQPO1G0YO@lQPO1G0YO*|QPO,5:qO*wQPO,5:pOVQPO1G0ZOAZQPO1G/oOAkQPO7+%gOOQO7+%e7+%eOArQPO7+%fO*|QPO7+%gOVQPO7+%hO*|QPO7+%jOOQO7+%]7+%]OOQO7+%r7+%rOOQO1G0X1G0XOAzQPO7+%rOBhQPO1G0]OOQO1G0[1G0[OOQO7+%u7+%uOVQPO<<IROBrQPO<<IQOBwQPO<<IROCOQPO'#DwOCdQPO<<ISOClQPO<<IUOCsQPO<<I^ODbQPOAN>mO*|QPOAN>lOVQPOAN>mOVQPO,5:cOOQOAN>nAN>nOVQPOAN>pOOQOG24XG24XODsQPOG24WODzQPOG24XOE]QPO1G/}OEqQPOG24[OExQPOG24[OOQOLD)rLD)rOOQOLD)sLD)sOOQOLD)vLD)vO*|QPOLD)vOFZQPO!$'MbOOQO!)9B|!)9B|",
|
"3UO!sQPOOOOQO'#C`'#C`O#lQPO'#DoOOQO'#Do'#DoO%yQPO'#DdOVQPO'#DpO'_QPO'#DrO'fQPO'#DsO'nQQO'#DtO'sQPO'#DuO'xQPO'#DvO'}QPO'#DxOOQO'#Dq'#DqO(SQPO'#DqO)pQQO'#C{OOQO'#C{'#C{OOQO'#Dk'#DkO)uQPO'#DkO)zQPO'#DkO*PQPO'#DfOVQPO'#EQO*XQPO'#EQO*^QPO'#EQO*fQPO'#DdOOQO'#De'#DeO*wQPO'#ETQOQPOOO*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:QO+TQPO'#DjO+[QPO,5:[O,oQPO,5:SO,wQPO'#DiO,|QPO,5:PO-_QPO,5:aO.QQPO'#DjO*|QPO'#DpO._QPO,5:^OOQO'#Dj'#DjO*|QPO,5:_OOQO,5:_,5:_O.gQPO,5:`O*^QPO,5:aO*|QPO,5:bO*^QPO,5:dOOQO,59g,59gO*|QPO,5:VO*|QPO,5:VO$lQPO'#DfO.lQPO,5:lO*|QPO,5:lO*|QPO'#DhOOQO'#Dg'#DgOOQO'#ER'#ERO/ZQPO,5:lOVQPO,5:nOVQPO,5:nOVQPO,5:nOVQPO,5:nO/cQPO'#EVOOQO'#EU'#EUO/hQPO,5:oOOQO1G/v1G/vO3XQPO1G/vO3`QPO1G/vO7OQPO1G/vO:nQPO1G/vO:uQPO1G/vO:|QPO1G/vO;TQPO1G/vO>oQPO1G/lOOQO1G/n1G/nO*|QPO,5:TOOQO1G/k1G/kO*|QPO1G/{OOQO1G/x1G/xO?`QPO1G/yO*|QPO1G/zO?gQPO1G/{O?lQPO1G/|O?sQPO1G0OO?xQPO1G/qO@QQPO1G/qO@XQPO1G0WO*^QPO,5:mO*|QPO1G0WO@{QPO1G0YOASQPO1G0YOAZQPO1G0YOOQO1G0Y1G0YO*|QPO,5:qO*wQPO,5:pOVQPO1G0ZOAxQPO1G/oOBYQPO7+%gOOQO7+%e7+%eOBaQPO7+%fO*|QPO7+%gOVQPO7+%hO*|QPO7+%jOOQO7+%]7+%]OOQO7+%r7+%rOOQO1G0X1G0XOBiQPO7+%rOCVQPO1G0]OOQO1G0[1G0[OOQO7+%u7+%uOVQPO<<IROCaQPO<<IQOCfQPO<<IROCmQPO'#DwODRQPO<<ISODZQPO<<IUODbQPO<<I^OEPQPOAN>mO*|QPOAN>lOVQPOAN>mOVQPO,5:cOOQOAN>nAN>nOVQPOAN>pOOQOG24XG24XOEbQPOG24WOEiQPOG24XOEzQPO1G/}OF`QPOG24[OFgQPOG24[OOQOLD)rLD)rOOQOLD)sLD)sOOQOLD)vLD)vO*|QPOLD)vOFxQPO!$'MbOOQO!)9B|!)9B|",
|
||||||
stateData:
|
stateData:
|
||||||
'Fs~O!VOS~OQQORTOTROURO`UObVOcWOiXOjYOlZOnaO{dO|eO}fO!OfO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OeiO~PVOV!cXW!cXX!cXY!cXZ!cX[!cX]!cX^!cX!s!cXa!cX~Og!cXm!ZXp!ZXq!cXr!cXs!cXt!cXu!cXv!cXw!cXx!cXy!cXz!cX!T!cX~P!zOVkOWlOXmOYnOZoO[pO]qO^rOgsOqsOrsOssOtsOusOvsOwsOxsOysOzsO~O!T!WX~P$lOR{OTROURO`UObVOcWOiXOjYOlZOnaO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OQyO~P&QOR!OObVO~Od!QO~O`!RO~O`!SO~O`!TO~OV!eXW!eXX!eXY!eXZ!eX[!eX]!eX^!eXg!eXm!ZXp!ZXq!eXr!eXs!eXt!eXu!eXv!eXw!eXx!eXy!eXz!eX!T!eX!s!eXa!eX~Od!UO~Oe!VO~OR!WO~OmsOpsO~Oe!ZO~OQ!]OR![O~O!P!`O!Q!aO!R!bO!S!cO!T!WX~OQ!dO~OQRO~P&QO!s!^X~P$lO_!gOV!_XW!_XX!_XY!_XZ!_X[!_X]!_X^!_Xg!_Xq!_Xr!_Xs!_Xt!_Xu!_Xv!_Xw!_Xx!_Xy!_Xz!_X!s!_X~O_!pO!s!^X~O!s!qO~O_!rO!P!`O!Q!aO!R!bO!S!cO~Og!sO~P!zOVkOWlOXmOYnOZoO[pO]qO^rO~Oa!^X!s!^Xf!^X~P-fOa!tO!s!qO~Oe!vO~Og#OO!s!}O~Og#TO~Of#VO!s#UO~OVkOX!diY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OW!di~P/ROWlO~P/ROVkOWlOXmO^rOZ!di[!di]!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OY!di~P2xOVkOWlOXmOYnO]qO^rO[!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OZ!di~P6hOZoO~P6hOYnO~P2xOVkOWlOXmOY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~O!P!Yi!Q!Yi!R!Yi!S!Yi!T!Yi_!Yia!Yik!Yih!Yi~P-fO_#YO~P-fOg#[O~Oh#]O~P-fOm#^O~Of#_O!s!qO~O_#_O~P-fOf#`O!s!qO~O!P!`O!R!vi!S!vi!T!vi_!via!vik!vih!vi~O!Q!vi~P?rO!Q!aO~P?rO!P!`O!Q!aO!R!bO!S!vi!T!vi_!via!vik!vih!vi~O_!]i!s!]ia!]if!]i~P-fOh#fO~P-fOf#gO!s!qO~OVkOWlOXmOYnOZoO[pO]qO^rO~PVOf!yi!s!yi~P-fOR#nO~Oh#oO~P-fO!P!`O!Q!aO!R!bO!S!cOa!kXk!kX~Oa#qOk#pO~Oh#rO~P-fO!P!`O!Q!aO!R!bO!S!cO!T!ty_!tya!tyk!tyh!ty~Oa#sO!P!`O!Q!aO!R!bO!S!cO~O_#yO~P-fOa#zO!P!`O!Q!aO!R!bO!S!cO~O!P!`O!Q!aO!R!bO!S!cOa!kik!ki~Oa#{O~P$lOh#|O!P!`O!Q!aO!R!bO!S!cO~Oa$OO~P-fOc!m|nTU!o!p!q!r!nQljij~',
|
'Gb~O!VOS~OQQORTOTROURO`UObVOcWOiXOjYOlZOnaO{dO|eO}fO!OfO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OeiO~PVOV!cXW!cXX!cXY!cXZ!cX[!cX]!cX^!cX!s!cXa!cX~Og!cXm!ZXp!ZXq!cXr!cXs!cXt!cXu!cXv!cXw!cXx!cXy!cXz!cX!T!cX~P!zOVkOWlOXmOYnOZoO[pO]qO^rOgsOqsOrsOssOtsOusOvsOwsOxsOysOzsO~O!T!WX~P$lOR{OTROURO`UObVOcWOiXOjYOlZOnaO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OQyO~P&QOR!OObVO~Od!QO~O`!RO~O`!SO~O`!TO~OV!eXW!eXX!eXY!eXZ!eX[!eX]!eX^!eXg!eXm!ZXp!ZXq!eXr!eXs!eXt!eXu!eXv!eXw!eXx!eXy!eXz!eX!T!eX!s!eXa!eX~Od!UO~Oe!VO~OR!WO~OmsOpsO~Oe!ZO~OQ!]OR![O~O!P!`O!Q!aO!R!bO!S!cO!T!WX~OQ!dO~OQRO~P&QO!s!^X~P$lO_!gOV!_XW!_XX!_XY!_XZ!_X[!_X]!_X^!_Xg!_Xq!_Xr!_Xs!_Xt!_Xu!_Xv!_Xw!_Xx!_Xy!_Xz!_X!s!_X~O_!pO!s!^X~O!s!qO~O_!rO!P!`O!Q!aO!R!bO!S!cO~Og!sO~P!zOVkOWlOXmOYnOZoO[pO]qO^rO~Oa!^X!s!^Xf!^X~P-fOa!tO!s!qO~Oe!vO~O!P!`O!Q!aO!R!bO!S!cO!T!ta_!taa!tak!tah!ta~Og#OO!s!}O~Og#TO~Of#VO!s#UO~OVkOX!diY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OW!di~P/pOWlO~P/pOVkOWlOXmO^rOZ!di[!di]!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OY!di~P3gOVkOWlOXmOYnO]qO^rO[!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OZ!di~P7VOZoO~P7VOYnO~P3gOVkOWlOXmOY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~O!P!Yi!Q!Yi!R!Yi!S!Yi!T!Yi_!Yia!Yik!Yih!Yi~P-fO_#YO~P-fOg#[O~Oh#]O~P-fOm#^O~Of#_O!s!qO~O_#_O~P-fOf#`O!s!qO~O!R!bO!S!cO!P!vi!T!vi_!via!vik!vih!vi~O!Q!aO~P@aO!Q!vi~P@aO!S!cO!P!vi!Q!vi!R!vi!T!vi_!via!vik!vih!vi~O_!]i!s!]ia!]if!]i~P-fOh#fO~P-fOf#gO!s!qO~OVkOWlOXmOYnOZoO[pO]qO^rO~PVOf!yi!s!yi~P-fOR#nO~Oh#oO~P-fO!P!`O!Q!aO!R!bO!S!cOa!kXk!kX~Oa#qOk#pO~Oh#rO~P-fO!P!`O!Q!aO!R!bO!S!cO!T!ty_!tya!tyk!tyh!ty~Oa#sO!P!`O!Q!aO!R!bO!S!cO~O_#yO~P-fOa#zO!P!`O!Q!aO!R!bO!S!cO~O!P!`O!Q!aO!R!bO!S!cOa!kik!ki~Oa#{O~P$lOh#|O!P!`O!Q!aO!R!bO!S!cO~Oa$OO~P-fOc!m|nTU!o!p!q!r!nQljij~',
|
||||||
goto: "-l!zPPPP!{PPPPPPPPPPPPPPPPPPPPPPPPPP#wPPPPPPPPPPPPPPPPPPPPPP$s$y%s&T&q'u(Q(cPPP!{*a!{+_,Z+_+_+_-Y+_PPPPPPP%s-]%s-`-c-f!s`OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|!sbOTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|QjOR#e#VSgO#VQxTQ!YdQ#P!`Q#Q!aQ#R!bQ#S!cQ#i#]Q#l#bQ#m#fQ#u#oQ#v#pR#x#rmhOTd!`!a!b!c#V#]#b#f#o#p#rlcOTd!`!a!b!c#V#]#b#f#o#p#rQ!^fQ!w!RQ!y!TR#a!}!U[Uklmnopqrs{!O!S!V!W!Z![!q!s!v#O#T#[#^#n#|l]OTd!`!a!b!c#V#]#b#f#o#p#rX!]f!R!T!}UvT{![X}U!V!Z!vUwT{![Q|UQ!z!VQ!|!ZR#Z!vSSO#VQtT[zU{!V!Z![!vd!Xd!`!a!b!c#]#b#f#o#pQ!gkQ!hlQ!imQ!jnQ!koQ!lpQ!mqQ!nrQ!osQ!u!OQ!x!SQ!{!WQ#W!qQ#X!sQ#b#OQ#c#TQ#h#[Q#k#^Q#t#nQ#w#rR#}#|!n`OUdklmnopqrs!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|TuT{!s[OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|!r[OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|R!PVR#j#]R!_fRjOR!fiQ!eiR#d#U",
|
goto: "-l!zPPPP!{PPPPPPPPPPPPPPPPPPPPPPPPPP#wPPPPPPPPPPPPPPPPPPPPPP$s$y%s&T&q'u(Q(cPPP!{*a!{+_,Z+_+_+_-Y+_PPPPPPP%s-]%s-`-c-f!s`OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|!sbOTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|QjOR#e#VSgO#VQxTQ!YdQ#P!`Q#Q!aQ#R!bQ#S!cQ#i#]Q#l#bQ#m#fQ#u#oQ#v#pR#x#rmhOTd!`!a!b!c#V#]#b#f#o#p#rlcOTd!`!a!b!c#V#]#b#f#o#p#rQ!^fQ!w!RQ!y!TR#a!}!U[Uklmnopqrs{!O!S!V!W!Z![!q!s!v#O#T#[#^#n#|l]OTd!`!a!b!c#V#]#b#f#o#p#rX!]f!R!T!}UvT{![X}U!V!Z!vUwT{![Q|UQ!z!VQ!|!ZR#Z!vSSO#VQtT[zU{!V!Z![!vd!Xd!`!a!b!c#]#b#f#o#pQ!gkQ!hlQ!imQ!jnQ!koQ!lpQ!mqQ!nrQ!osQ!u!OQ!x!SQ!{!WQ#W!qQ#X!sQ#b#OQ#c#TQ#h#[Q#k#^Q#t#nQ#w#rR#}#|!n`OUdklmnopqrs!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|TuT{!s[OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|!r[OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|R!PVR#j#]R!_fRjOR!fiQ!eiR#d#U",
|
||||||
nodeNames:
|
nodeNames:
|
||||||
'⚠ Expression Local ( Literal Global Radical + - * ∪ \\ ∆ ∩ × ) { } ℬ Filter ComplexIndex [ ] ∈ | PrefixD PrefixI ; PrefixR := Function TextFunction :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = ¬ Predicate ∀ ∃ ⇔ ⇒ ∨ &',
|
'⚠ Expression Local ( Literal Global Radical * + - ∪ \\ ∆ ∩ × ) { } ℬ Filter ComplexIndex [ ] ∈ | PrefixD PrefixI ; PrefixR := Function TextFunction :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = ¬ Predicate ∀ ∃ ⇔ ⇒ ∨ &',
|
||||||
maxTerm: 87,
|
maxTerm: 87,
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 0,
|
repeatNodeCount: 0,
|
||||||
tokenData:
|
tokenData:
|
||||||
"6P~R!jX^%spq%svw&hxy&myz&rz{&w{|&||}'R}!O'W!Q!R']!R[!h!i)d!k!l)z!r!s*P!t!u*g!u!v(|!v!w(|!z!{(|!|!}*w!}#O*|#O#P+R#P#Q+W#R#S+]#T#U+]#U#V+s#V#W-c#W#X/O#X#d+]#d#e1h#e#f+]#f#g2^#g#o+]#o#p3h#p#q3m#q#r3r#y#z%s$f$g%s$r$s3w%o%p3|5i6S+]#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s% l% m4R%%Y%%Z4W%%[%%]4]%&Y%&Z4b%&]%&^4g%&_%&`4l%&`%&a4q%&b%&c4v%&c%&d4{%'S%'T5Q%'T%'U5V%'U%'V5[%(^%(_5a%(b%(c5f%(c%(d5k%)Q%)R5p%)S%)T5u%)U%)V5z&FU&FV%s~%xY!V~X^%spq%s#y#z%s$f$g%s#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s&FU&FV%s~&mO!S~~&rOR~~&wO_~~&|OX~~'ROV~~'WO!s~~']OW~P'bP!`P!Q![']R'lQdQ!`P|}'r!Q!['eQ'uP!R!['xQ'}QdQ|}'r!Q!['x~(WQ!_!`(^%&b%&c(c~(cOm~~(hOp~~(mOk~~(rOw~~(wOz~~(|Ou~~)PP!Q![)S~)XPT~!Q![)S~)aPi~!Q![)S~)gQ!Q![)m#]#^)u~)rPn~!Q![)m~)zOc~~*POj~~*SQ!Q![*Y#f#g*b~*_P|~!Q![*Y~*gO!m~~*lPl~!Q![*o~*tPU~!Q![*o~*|O!b~~+ROe~~+WOZ~~+]Of~~+bRQ~!Q![+k#T#o+]5i6S+]~+pPQ~!Q![+k~+xTQ~!Q![+k#T#c+]#c#d,X#d#o+]5i6S+]~,^TQ~!Q![+k#T#c+]#c#d,m#d#o+]5i6S+]~,rTQ~!Q![+k#T#`+]#`#a-R#a#o+]5i6S+]~-YR!p~Q~!Q![+k#T#o+]5i6S+]~-hSQ~!Q![+k#T#U-t#U#o+]5i6S+]~-yTQ~!Q![+k#T#f+]#f#g.Y#g#o+]5i6S+]~._TQ~!Q![+k#T#W+]#W#X.n#X#o+]5i6S+]~.uR!o~Q~!Q![+k#T#o+]5i6S+]~/TTQ~!Q![+k#T#X+]#X#Y/d#Y#o+]5i6S+]~/iTQ~!Q![+k#T#U+]#U#V/x#V#o+]5i6S+]~/}TQ~!Q![+k#T#c+]#c#d0^#d#o+]5i6S+]~0cTQ~!Q![+k#T#c+]#c#d0r#d#o+]5i6S+]~0wTQ~!Q![+k#T#`+]#`#a1W#a#o+]5i6S+]~1_R!q~Q~!Q![+k#T#o+]5i6S+]~1mTQ~!Q![+k#T#f+]#f#g1|#g#o+]5i6S+]~2TR!n~Q~!Q![+k#T#o+]5i6S+]~2cTQ~!Q![+k#T#X+]#X#Y2r#Y#o+]5i6S+]~2wTQ~!Q![+k#T#W+]#W#X3W#X#o+]5i6S+]~3_R!r~Q~!Q![+k#T#o+]5i6S+]~3mO`~~3rOh~~3wOa~~3|O{~~4RO^~~4WOb~~4]O!Q~~4bO!P~~4gO}~~4lO!O~~4qO!a~~4vO[~~4{Og~~5QOq~~5VO!R~~5[O]~~5aOY~~5fOy~~5kOx~~5pOv~~5uOt~~5zOs~~6POr~",
|
"6P~R!jX^%spq%svw&hxy&myz&rz{&w{|&||}'R}!O'W!Q!R']!R[!h!i)d!k!l)z!r!s*P!t!u*g!u!v(|!v!w(|!z!{(|!|!}*w!}#O*|#O#P+R#P#Q+W#R#S+]#T#U+]#U#V+s#V#W-c#W#X/O#X#d+]#d#e1h#e#f+]#f#g2^#g#o+]#o#p3h#p#q3m#q#r3r#y#z%s$f$g%s$r$s3w%o%p3|5i6S+]#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s% l% m4R%%Y%%Z4W%%[%%]4]%&Y%&Z4b%&]%&^4g%&_%&`4l%&`%&a4q%&b%&c4v%&c%&d4{%'S%'T5Q%'T%'U5V%'U%'V5[%(^%(_5a%(b%(c5f%(c%(d5k%)Q%)R5p%)S%)T5u%)U%)V5z&FU&FV%s~%xY!V~X^%spq%s#y#z%s$f$g%s#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s&FU&FV%s~&mO!S~~&rOR~~&wO_~~&|OV~~'ROW~~'WO!s~~']OX~P'bP!`P!Q![']R'lQdQ!`P|}'r!Q!['eQ'uP!R!['xQ'}QdQ|}'r!Q!['x~(WQ!_!`(^%&b%&c(c~(cOm~~(hOp~~(mOk~~(rOw~~(wOz~~(|Ou~~)PP!Q![)S~)XPT~!Q![)S~)aPi~!Q![)S~)gQ!Q![)m#]#^)u~)rPn~!Q![)m~)zOc~~*POj~~*SQ!Q![*Y#f#g*b~*_P|~!Q![*Y~*gO!m~~*lPl~!Q![*o~*tPU~!Q![*o~*|O!b~~+ROe~~+WOZ~~+]Of~~+bRQ~!Q![+k#T#o+]5i6S+]~+pPQ~!Q![+k~+xTQ~!Q![+k#T#c+]#c#d,X#d#o+]5i6S+]~,^TQ~!Q![+k#T#c+]#c#d,m#d#o+]5i6S+]~,rTQ~!Q![+k#T#`+]#`#a-R#a#o+]5i6S+]~-YR!p~Q~!Q![+k#T#o+]5i6S+]~-hSQ~!Q![+k#T#U-t#U#o+]5i6S+]~-yTQ~!Q![+k#T#f+]#f#g.Y#g#o+]5i6S+]~._TQ~!Q![+k#T#W+]#W#X.n#X#o+]5i6S+]~.uR!o~Q~!Q![+k#T#o+]5i6S+]~/TTQ~!Q![+k#T#X+]#X#Y/d#Y#o+]5i6S+]~/iTQ~!Q![+k#T#U+]#U#V/x#V#o+]5i6S+]~/}TQ~!Q![+k#T#c+]#c#d0^#d#o+]5i6S+]~0cTQ~!Q![+k#T#c+]#c#d0r#d#o+]5i6S+]~0wTQ~!Q![+k#T#`+]#`#a1W#a#o+]5i6S+]~1_R!q~Q~!Q![+k#T#o+]5i6S+]~1mTQ~!Q![+k#T#f+]#f#g1|#g#o+]5i6S+]~2TR!n~Q~!Q![+k#T#o+]5i6S+]~2cTQ~!Q![+k#T#X+]#X#Y2r#Y#o+]5i6S+]~2wTQ~!Q![+k#T#W+]#W#X3W#X#o+]5i6S+]~3_R!r~Q~!Q![+k#T#o+]5i6S+]~3mO`~~3rOh~~3wOa~~3|O{~~4RO^~~4WOb~~4]O!Q~~4bO!P~~4gO}~~4lO!O~~4qO!a~~4vO[~~4{Og~~5QOq~~5VO!R~~5[O]~~5aOY~~5fOy~~5kOx~~5pOv~~5uOt~~5zOs~~6POr~",
|
||||||
tokenizers: [0, 1],
|
tokenizers: [0, 1],
|
||||||
topRules: { Expression: [0, 1] },
|
topRules: { Expression: [0, 1] },
|
||||||
tokenPrec: 1720
|
tokenPrec: 1749
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,16 +7,20 @@
|
||||||
// ------------- Precedence Definitions --------------------
|
// ------------- Precedence Definitions --------------------
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
@precedence {
|
@precedence {
|
||||||
plus @left minus @left,
|
|
||||||
times @left,
|
|
||||||
not @right,
|
|
||||||
log_equiv @left,
|
|
||||||
log_impl @left,
|
|
||||||
log_or @left,
|
|
||||||
log_and @left,
|
log_and @left,
|
||||||
|
log_or @left,
|
||||||
|
log_impl @left,
|
||||||
|
log_equiv @left,
|
||||||
|
|
||||||
|
times @left,
|
||||||
|
plus @left minus @left,
|
||||||
|
|
||||||
set_decart @left set_union @left set_intersect @left set_minus @left set_symminus @left,
|
set_decart @left set_union @left set_intersect @left set_minus @left set_symminus @left,
|
||||||
set_bool @right,
|
set_bool @right,
|
||||||
quant @right
|
|
||||||
|
quant @right,
|
||||||
|
|
||||||
|
not @right
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
@ -197,9 +201,9 @@ identifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
Setexpr_binary {
|
Setexpr_binary {
|
||||||
|
Setexpr !times "*" Setexpr |
|
||||||
Setexpr !plus "+" Setexpr |
|
Setexpr !plus "+" Setexpr |
|
||||||
Setexpr !minus "-" Setexpr |
|
Setexpr !minus "-" Setexpr |
|
||||||
Setexpr !times "*" Setexpr |
|
|
||||||
Setexpr !set_union "∪" Setexpr |
|
Setexpr !set_union "∪" Setexpr |
|
||||||
Setexpr !set_minus "\\" Setexpr |
|
Setexpr !set_minus "\\" Setexpr |
|
||||||
Setexpr !set_symminus "∆" Setexpr |
|
Setexpr !set_symminus "∆" Setexpr |
|
||||||
|
|
|
@ -8,16 +8,20 @@
|
||||||
// ------------- Precedence Definitions --------------------
|
// ------------- Precedence Definitions --------------------
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
@precedence {
|
@precedence {
|
||||||
plus @left minus @left,
|
|
||||||
times @left,
|
|
||||||
not @right,
|
|
||||||
log_equiv @left,
|
|
||||||
log_impl @left,
|
|
||||||
log_or @left,
|
|
||||||
log_and @left,
|
log_and @left,
|
||||||
|
log_or @left,
|
||||||
|
log_impl @left,
|
||||||
|
log_equiv @left,
|
||||||
|
|
||||||
|
times @left,
|
||||||
|
plus @left minus @left,
|
||||||
|
|
||||||
set_decart @left set_union @left set_intersect @left set_minus @left set_symminus @left,
|
set_decart @left set_union @left set_intersect @left set_minus @left set_symminus @left,
|
||||||
set_bool @right,
|
set_bool @right,
|
||||||
quant @right
|
|
||||||
|
quant @right,
|
||||||
|
|
||||||
|
not @right
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
@ -195,9 +199,9 @@ identifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
setexpr_binary {
|
setexpr_binary {
|
||||||
|
setexpr !times "*" setexpr |
|
||||||
setexpr !plus "+" setexpr |
|
setexpr !plus "+" setexpr |
|
||||||
setexpr !minus "-" setexpr |
|
setexpr !minus "-" setexpr |
|
||||||
setexpr !times "*" setexpr |
|
|
||||||
setexpr !set_union "∪" setexpr |
|
setexpr !set_union "∪" setexpr |
|
||||||
setexpr !set_minus "\\" setexpr |
|
setexpr !set_minus "\\" setexpr |
|
||||||
setexpr !set_symminus "∆" setexpr |
|
setexpr !set_symminus "∆" setexpr |
|
||||||
|
|
|
@ -145,6 +145,15 @@ export class RSTextWrapper extends CodeMirrorWrapper {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TokenID.BOOLEAN: {
|
case TokenID.BOOLEAN: {
|
||||||
|
if (!hasSelection && selection.from >= 2) {
|
||||||
|
const enclosingText = this.ref.view.state.sliceDoc(selection.from - 2, selection.from + 1);
|
||||||
|
if (enclosingText === 'ℬ()') {
|
||||||
|
this.ref.view.dispatch({
|
||||||
|
changes: [{ from: selection.from - 2, insert: 'ℬ' }]
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (hasSelection && this.startsWithBoolean(selection)) {
|
if (hasSelection && this.startsWithBoolean(selection)) {
|
||||||
this.envelopeWith('ℬ', '');
|
this.envelopeWith('ℬ', '');
|
||||||
} else {
|
} else {
|
||||||
|
@ -290,7 +299,7 @@ export class RSTextWrapper extends CodeMirrorWrapper {
|
||||||
if (!text.startsWith('ℬ') || !text.endsWith(')')) {
|
if (!text.startsWith('ℬ') || !text.endsWith(')')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const openParenIndex = text.indexOf('(', 1); // start after 'ℬ'
|
const openParenIndex = text.indexOf('(', 1);
|
||||||
if (openParenIndex === -1) {
|
if (openParenIndex === -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
@ -121,10 +121,12 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevDirty = useRef(isDirty);
|
const prevDirty = useRef(isDirty);
|
||||||
|
useEffect(() => {
|
||||||
if (prevDirty.current !== isDirty) {
|
if (prevDirty.current !== isDirty) {
|
||||||
prevDirty.current = isDirty;
|
prevDirty.current = isDirty;
|
||||||
setIsModified(isDirty);
|
setIsModified(isDirty);
|
||||||
}
|
}
|
||||||
|
}, [isDirty, setIsModified]);
|
||||||
|
|
||||||
function onSubmit(data: IUpdateConstituentaDTO) {
|
function onSubmit(data: IUpdateConstituentaDTO) {
|
||||||
return cstUpdate({ itemID: schema.id, data }).then(() => {
|
return cstUpdate({ itemID: schema.id, data }).then(() => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
|
@ -70,10 +70,12 @@ export function FormRSForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevDirty = useRef(isDirty);
|
const prevDirty = useRef(isDirty);
|
||||||
|
useEffect(() => {
|
||||||
if (prevDirty.current !== isDirty) {
|
if (prevDirty.current !== isDirty) {
|
||||||
prevDirty.current = isDirty;
|
prevDirty.current = isDirty;
|
||||||
setIsModified(isDirty);
|
setIsModified(isDirty);
|
||||||
}
|
}
|
||||||
|
}, [isDirty, setIsModified]);
|
||||||
|
|
||||||
function handleSelectVersion(version: CurrentVersion) {
|
function handleSelectVersion(version: CurrentVersion) {
|
||||||
router.push({ path: urls.schema(schema.id, version === 'latest' ? undefined : version) });
|
router.push({ path: urls.schema(schema.id, version === 'latest' ? undefined : version) });
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
import clsx from 'clsx';
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
|
@ -123,7 +124,7 @@ export function EditorRSList() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableHeight = useFitHeight('4rem + 5px');
|
const tableHeight = useFitHeight(isContentEditable ? '4rem + 5px' : '2rem');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div tabIndex={-1} onKeyDown={handleKeyDown} className='relative pt-8'>
|
<div tabIndex={-1} onKeyDown={handleKeyDown} className='relative pt-8'>
|
||||||
|
@ -146,7 +147,7 @@ export function EditorRSList() {
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
className='absolute z-pop top-18 right-4 hidden sm:block'
|
className={clsx('absolute z-pop right-4 hidden sm:block', isContentEditable ? 'top-18' : 'top-8')}
|
||||||
title='Выгрузить в формате CSV'
|
title='Выгрузить в формате CSV'
|
||||||
icon={<IconCSV size='1.25rem' className='icon-green' />}
|
icon={<IconCSV size='1.25rem' className='icon-green' />}
|
||||||
onClick={handleDownloadCSV}
|
onClick={handleDownloadCSV}
|
||||||
|
|
|
@ -5,41 +5,41 @@
|
||||||
/**
|
/**
|
||||||
* Represents single node of a {@link Graph}, as implemented by storing outgoing and incoming connections.
|
* Represents single node of a {@link Graph}, as implemented by storing outgoing and incoming connections.
|
||||||
*/
|
*/
|
||||||
export class GraphNode {
|
export class GraphNode<NodeID> {
|
||||||
/** Unique identifier of the node. */
|
/** Unique identifier of the node. */
|
||||||
id: number;
|
id: NodeID;
|
||||||
/** List of outgoing nodes. */
|
/** List of outgoing nodes. */
|
||||||
outputs: number[];
|
outputs: NodeID[];
|
||||||
/** List of incoming nodes. */
|
/** List of incoming nodes. */
|
||||||
inputs: number[];
|
inputs: NodeID[];
|
||||||
|
|
||||||
constructor(id: number) {
|
constructor(id: NodeID) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.outputs = [];
|
this.outputs = [];
|
||||||
this.inputs = [];
|
this.inputs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(): GraphNode {
|
clone(): GraphNode<NodeID> {
|
||||||
const result = new GraphNode(this.id);
|
const result = new GraphNode(this.id);
|
||||||
result.outputs = [...this.outputs];
|
result.outputs = [...this.outputs];
|
||||||
result.inputs = [...this.inputs];
|
result.inputs = [...this.inputs];
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
addOutput(node: number): void {
|
addOutput(node: NodeID): void {
|
||||||
this.outputs.push(node);
|
this.outputs.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
addInput(node: number): void {
|
addInput(node: NodeID): void {
|
||||||
this.inputs.push(node);
|
this.inputs.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeInput(target: number): number | null {
|
removeInput(target: NodeID): NodeID | null {
|
||||||
const index = this.inputs.findIndex(node => node === target);
|
const index = this.inputs.findIndex(node => node === target);
|
||||||
return index > -1 ? this.inputs.splice(index, 1)[0] : null;
|
return index > -1 ? this.inputs.splice(index, 1)[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeOutput(target: number): number | null {
|
removeOutput(target: NodeID): NodeID | null {
|
||||||
const index = this.outputs.findIndex(node => node === target);
|
const index = this.outputs.findIndex(node => node === target);
|
||||||
return index > -1 ? this.outputs.splice(index, 1)[0] : null;
|
return index > -1 ? this.outputs.splice(index, 1)[0] : null;
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,11 @@ export class GraphNode {
|
||||||
*
|
*
|
||||||
* This class is optimized for TermGraph use case and not supposed to be used as generic graph implementation.
|
* This class is optimized for TermGraph use case and not supposed to be used as generic graph implementation.
|
||||||
*/
|
*/
|
||||||
export class Graph {
|
export class Graph<NodeID = number> {
|
||||||
/** Map of nodes. */
|
/** Map of nodes. */
|
||||||
nodes = new Map<number, GraphNode>();
|
nodes = new Map<NodeID, GraphNode<NodeID>>();
|
||||||
|
|
||||||
constructor(arr?: number[][]) {
|
constructor(arr?: NodeID[][]) {
|
||||||
if (!arr) {
|
if (!arr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -67,17 +67,17 @@ export class Graph {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(): Graph {
|
clone(): Graph<NodeID> {
|
||||||
const result = new Graph();
|
const result = new Graph<NodeID>();
|
||||||
this.nodes.forEach(node => result.nodes.set(node.id, node.clone()));
|
this.nodes.forEach(node => result.nodes.set(node.id, node.clone()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
at(target: number): GraphNode | undefined {
|
at(target: NodeID): GraphNode<NodeID> | undefined {
|
||||||
return this.nodes.get(target);
|
return this.nodes.get(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
addNode(target: number): GraphNode {
|
addNode(target: NodeID): GraphNode<NodeID> {
|
||||||
let node = this.nodes.get(target);
|
let node = this.nodes.get(target);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = new GraphNode(target);
|
node = new GraphNode(target);
|
||||||
|
@ -86,11 +86,11 @@ export class Graph {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNode(target: number): boolean {
|
hasNode(target: NodeID): boolean {
|
||||||
return !!this.nodes.get(target);
|
return !!this.nodes.get(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeNode(target: number): void {
|
removeNode(target: NodeID): void {
|
||||||
this.nodes.forEach(node => {
|
this.nodes.forEach(node => {
|
||||||
node.removeInput(target);
|
node.removeInput(target);
|
||||||
node.removeOutput(target);
|
node.removeOutput(target);
|
||||||
|
@ -98,7 +98,7 @@ export class Graph {
|
||||||
this.nodes.delete(target);
|
this.nodes.delete(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
foldNode(target: number): void {
|
foldNode(target: NodeID): void {
|
||||||
const nodeToRemove = this.nodes.get(target);
|
const nodeToRemove = this.nodes.get(target);
|
||||||
if (!nodeToRemove) {
|
if (!nodeToRemove) {
|
||||||
return;
|
return;
|
||||||
|
@ -111,8 +111,8 @@ export class Graph {
|
||||||
this.removeNode(target);
|
this.removeNode(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeIsolated(): GraphNode[] {
|
removeIsolated(): GraphNode<NodeID>[] {
|
||||||
const result: GraphNode[] = [];
|
const result: GraphNode<NodeID>[] = [];
|
||||||
this.nodes.forEach(node => {
|
this.nodes.forEach(node => {
|
||||||
if (node.outputs.length === 0 && node.inputs.length === 0) {
|
if (node.outputs.length === 0 && node.inputs.length === 0) {
|
||||||
result.push(node);
|
result.push(node);
|
||||||
|
@ -122,7 +122,7 @@ export class Graph {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
addEdge(source: number, destination: number): void {
|
addEdge(source: NodeID, destination: NodeID): void {
|
||||||
if (this.hasEdge(source, destination)) {
|
if (this.hasEdge(source, destination)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ export class Graph {
|
||||||
destinationNode.addInput(sourceNode.id);
|
destinationNode.addInput(sourceNode.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEdge(source: number, destination: number): void {
|
removeEdge(source: NodeID, destination: NodeID): void {
|
||||||
const sourceNode = this.nodes.get(source);
|
const sourceNode = this.nodes.get(source);
|
||||||
const destinationNode = this.nodes.get(destination);
|
const destinationNode = this.nodes.get(destination);
|
||||||
if (sourceNode && destinationNode) {
|
if (sourceNode && destinationNode) {
|
||||||
|
@ -141,7 +141,7 @@ export class Graph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasEdge(source: number, destination: number): boolean {
|
hasEdge(source: NodeID, destination: NodeID): boolean {
|
||||||
const sourceNode = this.nodes.get(source);
|
const sourceNode = this.nodes.get(source);
|
||||||
if (!sourceNode) {
|
if (!sourceNode) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -149,8 +149,8 @@ export class Graph {
|
||||||
return !!sourceNode.outputs.find(id => id === destination);
|
return !!sourceNode.outputs.find(id => id === destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
expandOutputs(origin: number[]): number[] {
|
expandOutputs(origin: NodeID[]): NodeID[] {
|
||||||
const result: number[] = [];
|
const result: NodeID[] = [];
|
||||||
origin.forEach(id => {
|
origin.forEach(id => {
|
||||||
const node = this.nodes.get(id);
|
const node = this.nodes.get(id);
|
||||||
if (node) {
|
if (node) {
|
||||||
|
@ -164,8 +164,8 @@ export class Graph {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
expandInputs(origin: number[]): number[] {
|
expandInputs(origin: NodeID[]): NodeID[] {
|
||||||
const result: number[] = [];
|
const result: NodeID[] = [];
|
||||||
origin.forEach(id => {
|
origin.forEach(id => {
|
||||||
const node = this.nodes.get(id);
|
const node = this.nodes.get(id);
|
||||||
if (node) {
|
if (node) {
|
||||||
|
@ -179,13 +179,13 @@ export class Graph {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
expandAllOutputs(origin: number[]): number[] {
|
expandAllOutputs(origin: NodeID[]): NodeID[] {
|
||||||
const result: number[] = this.expandOutputs(origin);
|
const result: NodeID[] = this.expandOutputs(origin);
|
||||||
if (result.length === 0) {
|
if (result.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const marked = new Map<number, boolean>();
|
const marked = new Map<NodeID, boolean>();
|
||||||
origin.forEach(id => marked.set(id, true));
|
origin.forEach(id => marked.set(id, true));
|
||||||
let position = 0;
|
let position = 0;
|
||||||
while (position < result.length) {
|
while (position < result.length) {
|
||||||
|
@ -203,13 +203,13 @@ export class Graph {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
expandAllInputs(origin: number[]): number[] {
|
expandAllInputs(origin: NodeID[]): NodeID[] {
|
||||||
const result: number[] = this.expandInputs(origin);
|
const result: NodeID[] = this.expandInputs(origin);
|
||||||
if (result.length === 0) {
|
if (result.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const marked = new Map<number, boolean>();
|
const marked = new Map<NodeID, boolean>();
|
||||||
origin.forEach(id => marked.set(id, true));
|
origin.forEach(id => marked.set(id, true));
|
||||||
let position = 0;
|
let position = 0;
|
||||||
while (position < result.length) {
|
while (position < result.length) {
|
||||||
|
@ -227,8 +227,8 @@ export class Graph {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
maximizePart(origin: number[]): number[] {
|
maximizePart(origin: NodeID[]): NodeID[] {
|
||||||
const outputs: number[] = this.expandAllOutputs(origin);
|
const outputs: NodeID[] = this.expandAllOutputs(origin);
|
||||||
const result = [...origin];
|
const result = [...origin];
|
||||||
this.topologicalOrder()
|
this.topologicalOrder()
|
||||||
.filter(id => outputs.includes(id))
|
.filter(id => outputs.includes(id))
|
||||||
|
@ -241,10 +241,10 @@ export class Graph {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
topologicalOrder(): number[] {
|
topologicalOrder(): NodeID[] {
|
||||||
const result: number[] = [];
|
const result: NodeID[] = [];
|
||||||
const marked = new Set<number>();
|
const marked = new Set<NodeID>();
|
||||||
const nodeStack: number[] = [];
|
const nodeStack: NodeID[] = [];
|
||||||
this.nodes.forEach(node => {
|
this.nodes.forEach(node => {
|
||||||
if (marked.has(node.id)) {
|
if (marked.has(node.id)) {
|
||||||
return;
|
return;
|
||||||
|
@ -275,12 +275,12 @@ export class Graph {
|
||||||
|
|
||||||
transitiveReduction() {
|
transitiveReduction() {
|
||||||
const order = this.topologicalOrder();
|
const order = this.topologicalOrder();
|
||||||
const marked = new Map<number, boolean>();
|
const marked = new Map<NodeID, boolean>();
|
||||||
order.forEach(nodeID => {
|
order.forEach(nodeID => {
|
||||||
if (marked.get(nodeID)) {
|
if (marked.get(nodeID)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const stack: { id: number; parents: number[] }[] = [];
|
const stack: { id: NodeID; parents: NodeID[] }[] = [];
|
||||||
stack.push({ id: nodeID, parents: [] });
|
stack.push({ id: nodeID, parents: [] });
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
const item = stack.splice(0, 1)[0];
|
const item = stack.splice(0, 1)[0];
|
||||||
|
@ -299,20 +299,20 @@ export class Graph {
|
||||||
/**
|
/**
|
||||||
* Finds a cycle in the graph.
|
* Finds a cycle in the graph.
|
||||||
*
|
*
|
||||||
* @returns {number[] | null} The cycle if found, otherwise `null`.
|
* @returns {NodeID[] | null} The cycle if found, otherwise `null`.
|
||||||
* Uses non-recursive DFS.
|
* Uses non-recursive DFS.
|
||||||
*/
|
*/
|
||||||
findCycle(): number[] | null {
|
findCycle(): NodeID[] | null {
|
||||||
const visited = new Set<number>();
|
const visited = new Set<NodeID>();
|
||||||
const nodeStack = new Set<number>();
|
const nodeStack = new Set<NodeID>();
|
||||||
const parents = new Map<number, number>();
|
const parents = new Map<NodeID, NodeID>();
|
||||||
|
|
||||||
for (const nodeId of this.nodes.keys()) {
|
for (const nodeId of this.nodes.keys()) {
|
||||||
if (visited.has(nodeId)) {
|
if (visited.has(nodeId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const callStack: { nodeId: number; parentId: number | null }[] = [];
|
const callStack: { nodeId: NodeID; parentId: NodeID | null }[] = [];
|
||||||
callStack.push({ nodeId: nodeId, parentId: null });
|
callStack.push({ nodeId: nodeId, parentId: null });
|
||||||
while (callStack.length > 0) {
|
while (callStack.length > 0) {
|
||||||
const { nodeId, parentId } = callStack[callStack.length - 1];
|
const { nodeId, parentId } = callStack[callStack.length - 1];
|
||||||
|
@ -336,7 +336,7 @@ export class Graph {
|
||||||
if (!nodeStack.has(child)) {
|
if (!nodeStack.has(child)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const cycle: number[] = [];
|
const cycle: NodeID[] = [];
|
||||||
let current = nodeId;
|
let current = nodeId;
|
||||||
cycle.push(child);
|
cycle.push(child);
|
||||||
while (current !== child) {
|
while (current !== child) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user