mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Merge branch 'main' into synthesis
This commit is contained in:
commit
91865e54f0
31
.vscode/settings.json
vendored
31
.vscode/settings.json
vendored
|
@ -84,6 +84,7 @@
|
|||
"Grammeme",
|
||||
"Grammemes",
|
||||
"GRND",
|
||||
"IDEF",
|
||||
"impr",
|
||||
"inan",
|
||||
"incapsulation",
|
||||
|
@ -150,39 +151,67 @@
|
|||
"viewsets",
|
||||
"wordform",
|
||||
"Wordforms",
|
||||
"Айзенштат",
|
||||
"Акименков",
|
||||
"Астрина",
|
||||
"Ашихмин",
|
||||
"Биективная",
|
||||
"биективной",
|
||||
"Булеан",
|
||||
"Бурбаки",
|
||||
"Бурбакизатор",
|
||||
"Версионирование",
|
||||
"Владельцом",
|
||||
"Демешко",
|
||||
"Десинглетон",
|
||||
"доксинг",
|
||||
"Закс",
|
||||
"интерпретируемости",
|
||||
"интерпретируемость",
|
||||
"Инттеор",
|
||||
"Климишин",
|
||||
"компаратив",
|
||||
"Кононенко",
|
||||
"конституент",
|
||||
"Конституента",
|
||||
"конституентами",
|
||||
"конституенте",
|
||||
"конституенту",
|
||||
"конституенты",
|
||||
"Крайнев",
|
||||
"Кучкаров",
|
||||
"Кучкарова",
|
||||
"неинтерпретируемый",
|
||||
"неитерируемого",
|
||||
"Никанорова",
|
||||
"операционализации",
|
||||
"операционализированных",
|
||||
"Оргтеор",
|
||||
"Пакулина",
|
||||
"пересинтез",
|
||||
"Персиц",
|
||||
"Присакарь",
|
||||
"ПРОКСИМА",
|
||||
"Родоструктурная",
|
||||
"родоструктурного",
|
||||
"Родоструктурное",
|
||||
"родоструктурной",
|
||||
"родоструктурном",
|
||||
"родоструктурных",
|
||||
"Савелов",
|
||||
"Синглетон",
|
||||
"твор",
|
||||
"Терминологизация",
|
||||
"троллинг",
|
||||
"Тулисов",
|
||||
"Цермелло",
|
||||
"ЦИВТ",
|
||||
"Чувашов",
|
||||
"Шульпекин",
|
||||
"Экстеор",
|
||||
"Экстеора",
|
||||
"Экстеоре"
|
||||
"Экстеоре",
|
||||
"Юдкин"
|
||||
],
|
||||
"cSpell.language": "en,ru",
|
||||
"cSpell.ignorePaths": ["node_modules/**", "*.json"]
|
||||
|
|
15
TODO.txt
15
TODO.txt
|
@ -2,25 +2,24 @@
|
|||
For more specific TODOs see comments in code
|
||||
|
||||
[Functionality - PROGRESS]
|
||||
- Landing page
|
||||
- Home page (user specific)
|
||||
|
||||
- Operational synthesis schema as LibraryItem ?
|
||||
|
||||
- Draggable rows in constituents table
|
||||
- Clickable IDs in RSEditor tooltips
|
||||
|
||||
- Library organization, search and exploration. Consider new user experience
|
||||
- Private projects and permissions. Consider cooperative editing
|
||||
- Rework access setup: project-based, user-based, enable sharing. Prevent enumerating access to private schemas by default
|
||||
|
||||
|
||||
[Functionality - PENDING]
|
||||
- Search functionality for manuals
|
||||
- User notifications on edit - consider spam prevention and change aggregation
|
||||
- Static analyzer for RSForm
|
||||
- Content based search in Library
|
||||
- User profile: Settings + settings persistency
|
||||
|
||||
- Landing page
|
||||
- Home page (user specific)
|
||||
|
||||
- Draggable rows in constituents table
|
||||
|
||||
- Export PDF (Items list, Graph)
|
||||
- ARIA (accessibility considerations) - for now machine reading not supported
|
||||
- Internationalization - at least english version. Consider react.intl
|
||||
|
@ -42,6 +41,8 @@ For more specific TODOs see comments in code
|
|||
[Security]
|
||||
- password-reset leaks info of email being used
|
||||
- improve nginx config. Consider DDOS and other types of attacks on infrastructure
|
||||
- recaptcha for create user and rest password
|
||||
https://yandex.cloud/ru/docs/smartcaptcha
|
||||
|
||||
|
||||
[Research]
|
||||
|
|
|
@ -4,11 +4,11 @@ djangorestframework==3.15.1
|
|||
django-cors-headers==4.3.1
|
||||
django-filter==24.2
|
||||
drf-spectacular==0.27.2
|
||||
drf-spectacular-sidecar==2024.5.1
|
||||
drf-spectacular-sidecar==2024.6.1
|
||||
coreapi==2.3.3
|
||||
django-rest-passwordreset==1.4.1
|
||||
cctext==0.1.3
|
||||
pyconcept==0.1.5
|
||||
pyconcept==0.1.6
|
||||
|
||||
psycopg2-binary==2.9.9
|
||||
gunicorn==22.0.0
|
|
@ -16,7 +16,7 @@ Styling conventions
|
|||
- border: borer-2 outline-none shadow-md
|
||||
- colors: clr-controls
|
||||
- text: text-start text-sm font-semibold whitespace-nowrap
|
||||
- behavior modifiers: select-none disabled:cursor-not-allowed
|
||||
- behavior modifiers: select-none disabled:cursor-auto
|
||||
- transitions:
|
||||
</pre>
|
||||
</details>
|
||||
|
|
|
@ -22,11 +22,14 @@
|
|||
<title>Концепт Портал</title>
|
||||
|
||||
<script>
|
||||
if (
|
||||
localStorage.getItem('darkMode') === 'true' ||
|
||||
localStorage.getItem('color-theme') === 'dark' ||
|
||||
(!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
let isDark = false;
|
||||
if ('portal.theme.dark' in localStorage) {
|
||||
isDark = localStorage.getItem('portal.theme.dark') === 'true';
|
||||
} else if (window.matchMedia) {
|
||||
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
localStorage.setItem('portal.theme.dark', isDark ? 'true' : 'false');
|
||||
}
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
|
|
496
rsconcept/frontend/package-lock.json
generated
496
rsconcept/frontend/package-lock.json
generated
|
@ -11,8 +11,8 @@
|
|||
"@lezer/lr": "^1.4.1",
|
||||
"@reactflow/core": "^11.8.3",
|
||||
"@tanstack/react-table": "^8.17.3",
|
||||
"@uiw/codemirror-themes": "^4.22.1",
|
||||
"@uiw/react-codemirror": "^4.22.1",
|
||||
"@uiw/codemirror-themes": "^4.22.2",
|
||||
"@uiw/react-codemirror": "^4.22.2",
|
||||
"axios": "^1.7.2",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^10.18.0",
|
||||
|
@ -23,24 +23,24 @@
|
|||
"react-icons": "^4.12.0",
|
||||
"react-intl": "^6.6.8",
|
||||
"react-loader-spinner": "^5.4.5",
|
||||
"react-pdf": "^7.7.3",
|
||||
"react-pdf": "^9.0.0",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-toastify": "^9.1.3",
|
||||
"react-tooltip": "^5.26.4",
|
||||
"reagraph": "^4.19.0",
|
||||
"react-tooltip": "^5.27.0",
|
||||
"reagraph": "^4.19.1",
|
||||
"use-debounce": "^10.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.14.0",
|
||||
"@types/node": "^20.14.5",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
|
@ -49,8 +49,8 @@
|
|||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"jest": "^29.7.0",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"ts-jest": "^29.1.4",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"ts-jest": "^29.1.5",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^4.5.3"
|
||||
}
|
||||
|
@ -82,12 +82,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz",
|
||||
"integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
|
||||
"integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.24.6",
|
||||
"@babel/highlight": "^7.24.7",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -95,30 +95,30 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz",
|
||||
"integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz",
|
||||
"integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz",
|
||||
"integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz",
|
||||
"integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.24.6",
|
||||
"@babel/generator": "^7.24.6",
|
||||
"@babel/helper-compilation-targets": "^7.24.6",
|
||||
"@babel/helper-module-transforms": "^7.24.6",
|
||||
"@babel/helpers": "^7.24.6",
|
||||
"@babel/parser": "^7.24.6",
|
||||
"@babel/template": "^7.24.6",
|
||||
"@babel/traverse": "^7.24.6",
|
||||
"@babel/types": "^7.24.6",
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
"@babel/generator": "^7.24.7",
|
||||
"@babel/helper-compilation-targets": "^7.24.7",
|
||||
"@babel/helper-module-transforms": "^7.24.7",
|
||||
"@babel/helpers": "^7.24.7",
|
||||
"@babel/parser": "^7.24.7",
|
||||
"@babel/template": "^7.24.7",
|
||||
"@babel/traverse": "^7.24.7",
|
||||
"@babel/types": "^7.24.7",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
|
@ -143,12 +143,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz",
|
||||
"integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz",
|
||||
"integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6",
|
||||
"@babel/types": "^7.24.7",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^2.5.1"
|
||||
|
@ -158,25 +158,25 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-annotate-as-pure": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz",
|
||||
"integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz",
|
||||
"integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz",
|
||||
"integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz",
|
||||
"integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.24.6",
|
||||
"@babel/helper-validator-option": "^7.24.6",
|
||||
"@babel/compat-data": "^7.24.7",
|
||||
"@babel/helper-validator-option": "^7.24.7",
|
||||
"browserslist": "^4.22.2",
|
||||
"lru-cache": "^5.1.1",
|
||||
"semver": "^6.3.1"
|
||||
|
@ -195,62 +195,66 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz",
|
||||
"integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
|
||||
"integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz",
|
||||
"integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
|
||||
"integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.24.6",
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/template": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz",
|
||||
"integrity": "sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
|
||||
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz",
|
||||
"integrity": "sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
|
||||
"integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/traverse": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-transforms": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz",
|
||||
"integrity": "sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz",
|
||||
"integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.24.6",
|
||||
"@babel/helper-module-imports": "^7.24.6",
|
||||
"@babel/helper-simple-access": "^7.24.6",
|
||||
"@babel/helper-split-export-declaration": "^7.24.6",
|
||||
"@babel/helper-validator-identifier": "^7.24.6"
|
||||
"@babel/helper-environment-visitor": "^7.24.7",
|
||||
"@babel/helper-module-imports": "^7.24.7",
|
||||
"@babel/helper-simple-access": "^7.24.7",
|
||||
"@babel/helper-split-export-declaration": "^7.24.7",
|
||||
"@babel/helper-validator-identifier": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -260,85 +264,86 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/helper-plugin-utils": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz",
|
||||
"integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz",
|
||||
"integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-simple-access": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz",
|
||||
"integrity": "sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
|
||||
"integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/traverse": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz",
|
||||
"integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
|
||||
"integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz",
|
||||
"integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz",
|
||||
"integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz",
|
||||
"integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
|
||||
"integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-option": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz",
|
||||
"integrity": "sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz",
|
||||
"integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz",
|
||||
"integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz",
|
||||
"integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.24.6",
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/template": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz",
|
||||
"integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
|
||||
"integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.24.6",
|
||||
"@babel/helper-validator-identifier": "^7.24.7",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.0.0"
|
||||
|
@ -348,9 +353,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz",
|
||||
"integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
|
||||
"integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
|
@ -425,12 +430,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-jsx": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.6.tgz",
|
||||
"integrity": "sha512-lWfvAIFNWMlCsU0DRUun2GpFwZdGTukLaHJqRh1JRb80NdAP5Sb1HDHB5X9P9OtgZHQl089UzQkpYlBq2VTPRw==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz",
|
||||
"integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.24.6"
|
||||
"@babel/helper-plugin-utils": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -534,13 +539,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-typescript": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.6.tgz",
|
||||
"integrity": "sha512-TzCtxGgVTEJWWwcYwQhCIQ6WaKlo80/B+Onsk4RRCcYqpYGFcG9etPW94VToGte5AAcxRrhjPUFvUS3Y2qKi4A==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz",
|
||||
"integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.24.6"
|
||||
"@babel/helper-plugin-utils": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -550,13 +555,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-react-jsx-self": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.6.tgz",
|
||||
"integrity": "sha512-FfZfHXtQ5jYPQsCRyLpOv2GeLIIJhs8aydpNh39vRDjhD411XcfWDni5i7OjP/Rs8GAtTn7sWFFELJSHqkIxYg==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz",
|
||||
"integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.24.6"
|
||||
"@babel/helper-plugin-utils": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -566,13 +571,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-react-jsx-source": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.6.tgz",
|
||||
"integrity": "sha512-BQTBCXmFRreU3oTUXcGKuPOfXAGb1liNY4AvvFKsOBAJ89RKcTsIrSsnMYkj59fNa66OFKnSa4AJZfy5Y4B9WA==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz",
|
||||
"integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.24.6"
|
||||
"@babel/helper-plugin-utils": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -582,9 +587,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz",
|
||||
"integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz",
|
||||
"integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
|
@ -594,33 +599,33 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz",
|
||||
"integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
|
||||
"integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.24.6",
|
||||
"@babel/parser": "^7.24.6",
|
||||
"@babel/types": "^7.24.6"
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
"@babel/parser": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz",
|
||||
"integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz",
|
||||
"integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.24.6",
|
||||
"@babel/generator": "^7.24.6",
|
||||
"@babel/helper-environment-visitor": "^7.24.6",
|
||||
"@babel/helper-function-name": "^7.24.6",
|
||||
"@babel/helper-hoist-variables": "^7.24.6",
|
||||
"@babel/helper-split-export-declaration": "^7.24.6",
|
||||
"@babel/parser": "^7.24.6",
|
||||
"@babel/types": "^7.24.6",
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
"@babel/generator": "^7.24.7",
|
||||
"@babel/helper-environment-visitor": "^7.24.7",
|
||||
"@babel/helper-function-name": "^7.24.7",
|
||||
"@babel/helper-hoist-variables": "^7.24.7",
|
||||
"@babel/helper-split-export-declaration": "^7.24.7",
|
||||
"@babel/parser": "^7.24.7",
|
||||
"@babel/types": "^7.24.7",
|
||||
"debug": "^4.3.1",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
|
@ -629,13 +634,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz",
|
||||
"integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz",
|
||||
"integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.24.6",
|
||||
"@babel/helper-validator-identifier": "^7.24.6",
|
||||
"@babel/helper-string-parser": "^7.24.7",
|
||||
"@babel/helper-validator-identifier": "^7.24.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -668,21 +673,21 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz",
|
||||
"integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==",
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz",
|
||||
"integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@codemirror/view": "^6.27.0",
|
||||
"@lezer/common": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
|
||||
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz",
|
||||
"integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
|
@ -734,9 +739,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
|
||||
"integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
|
||||
"version": "6.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz",
|
||||
"integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.0",
|
||||
|
@ -1311,9 +1316,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint-community/regexpp": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
|
||||
"integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz",
|
||||
"integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -1530,6 +1535,7 @@
|
|||
"version": "0.11.14",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
||||
"deprecated": "Use @eslint/config-array instead",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
@ -1583,6 +1589,7 @@
|
|||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
|
||||
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
||||
"deprecated": "Use @eslint/object-schema instead",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
|
@ -3294,9 +3301,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.0.tgz",
|
||||
"integrity": "sha512-5cHBxFGJx6L4s56Bubp4fglrEpmyJypsqI6RgzMfBHWUJQGWAAi8cWcgetEbZXHYXo9C2Fa4EEds/uSyS4cxmA==",
|
||||
"version": "20.14.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz",
|
||||
"integrity": "sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3394,9 +3401,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/webxr": {
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.16.tgz",
|
||||
"integrity": "sha512-0E0Cl84FECtzrB4qG19TNTqpunw0F1YF0QZZnFMF6pDw1kNKJtrlTKlVB34stGIsHbZsYQ7H0tNjPfZftkHHoA==",
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.17.tgz",
|
||||
"integrity": "sha512-JYcclaQIlisHRXM9dMF7SeVvQ54kcYc7QK1eKCExCTLKWnZDxP4cp/rXH4Uoa1j5+5oQJ0Cc2sZC/PWiiG4q2g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
|
@ -3615,9 +3622,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@uiw/codemirror-extensions-basic-setup": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.22.1.tgz",
|
||||
"integrity": "sha512-Iz8eFaZBNrwjaAADszOxOv2byDMn4rqob/luuSPAzJjTrSn5KawRXcoNLoWGPGNO6Mils6bIly/g2LaU34otNw==",
|
||||
"version": "4.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.22.2.tgz",
|
||||
"integrity": "sha512-zcHGkldLFN3cGoI5XdOGAkeW24yaAgrDEYoyPyWHODmPiNwybQQoZGnH3qUdzZwUaXtAcLWoAeOPzfNRW2yGww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
|
@ -3642,9 +3649,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@uiw/codemirror-themes": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.22.1.tgz",
|
||||
"integrity": "sha512-5TeB8wCc0aNd3YEhzOvgekpAFQfEm4fCTUcGmEIQqaRNgKAM83HYNpE1JF2j7x2oDFugdiO0yJynS6bo1zVOuw==",
|
||||
"version": "4.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.22.2.tgz",
|
||||
"integrity": "sha512-gsLHn6SUuV5iboBvGrM7YimzLFHQmsNlkGIYs3UaVUJTo/A/ZrKoSJNyPziShLRjBXA2UwKdBTIU6VhHyyaChw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
|
@ -3661,16 +3668,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@uiw/react-codemirror": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.22.1.tgz",
|
||||
"integrity": "sha512-yrq9FdGZ6E4Rh+7W0xyirSEeESGyG/k54/DfFqSk40fqel/3x/3fqjIImEZUYPxxgFPmZ3RtP+O0Em46nwRvgg==",
|
||||
"version": "4.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.22.2.tgz",
|
||||
"integrity": "sha512-okCSl+WJG63gRx8Fdz7v0C6RakBQnbb3pHhuzIgDB+fwhipgFodSnu2n9oOsQesJ5YQ7mSOcKMgX0JEsu4nnfQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.6",
|
||||
"@codemirror/commands": "^6.1.0",
|
||||
"@codemirror/state": "^6.1.1",
|
||||
"@codemirror/theme-one-dark": "^6.0.0",
|
||||
"@uiw/codemirror-extensions-basic-setup": "4.22.1",
|
||||
"@uiw/codemirror-extensions-basic-setup": "4.22.2",
|
||||
"codemirror": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
|
@ -3712,9 +3719,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.0.tgz",
|
||||
"integrity": "sha512-KcEbMsn4Dpk+LIbHMj7gDPRKaTMStxxWRkRmxsg/jVdFdJCZWt1SchZcf0M4t8lIKdwwMsEyzhrcOXRrDPtOBw==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz",
|
||||
"integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3745,9 +3752,9 @@
|
|||
"optional": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
|
||||
"integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
@ -4240,9 +4247,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.23.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
|
||||
"integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
|
||||
"version": "4.23.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz",
|
||||
"integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -4259,10 +4266,10 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001587",
|
||||
"electron-to-chromium": "^1.4.668",
|
||||
"caniuse-lite": "^1.0.30001629",
|
||||
"electron-to-chromium": "^1.4.796",
|
||||
"node-releases": "^2.0.14",
|
||||
"update-browserslist-db": "^1.0.13"
|
||||
"update-browserslist-db": "^1.0.16"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
|
@ -4340,18 +4347,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/camera-controls": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.8.4.tgz",
|
||||
"integrity": "sha512-pzVKpeZCRXIx2VOMB+E4OPjOhErHqhxrHYxcRLofOVgBeCeKSb8QAC2toc1onMllrxldRWXR8bl4K50hkrtwsg==",
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.8.5.tgz",
|
||||
"integrity": "sha512-7VTwRk7Nu1nRKsY7bEt9HVBfKt8DETvzyYhLN4OW26OByBayMDB5fUaNcPI+z++vG23RH5yqn6ZRhZcgLQy2rA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.126.1"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001626",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001626.tgz",
|
||||
"integrity": "sha512-JRW7kAH8PFJzoPCJhLSHgDgKg5348hsQ68aqb+slnzuB5QFERv846oA/mRChmlLAOdEDeOkRn3ynb1gSFnjt3w==",
|
||||
"version": "1.0.30001636",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz",
|
||||
"integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -5217,9 +5224,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.788",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.788.tgz",
|
||||
"integrity": "sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==",
|
||||
"version": "1.4.805",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.805.tgz",
|
||||
"integrity": "sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ellipsize": {
|
||||
|
@ -5891,9 +5898,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
|
||||
"integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
@ -6331,9 +6338,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/graphology-metrics": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/graphology-metrics/-/graphology-metrics-2.2.0.tgz",
|
||||
"integrity": "sha512-eZZFRLGGyyI+iD+XwQvc+lLM3EKCoqUvVjvF/14Htgy4grB2m95OytToYq3saWuHfuf22VVnj9GBHv/pTzKuTw==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/graphology-metrics/-/graphology-metrics-2.3.0.tgz",
|
||||
"integrity": "sha512-ayDWSnSlq2wIL1QIrRQ3cIUsS7V/kRdBzciLPkGsI8Vl/MDQkxiU9WDt9cUAMzAE6Q1n1pWT5SXPkKeZdKCjzA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graphology-shortest-path": "^2.0.0",
|
||||
|
@ -6780,9 +6787,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
|
||||
"integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz",
|
||||
"integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
|
@ -8474,9 +8481,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "1.21.0",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
|
||||
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
|
||||
"version": "1.21.6",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
|
||||
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
@ -8785,9 +8792,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/meshline": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/meshline/-/meshline-3.2.1.tgz",
|
||||
"integrity": "sha512-/Zrhq1sbQCtqSsrud0hN/U8wOdKKOcxCWefEowZRHsosIcy1p87+2PlWSNO4s9zOoT/zjrQR8YikXYao8XCqVQ==",
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz",
|
||||
"integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.137"
|
||||
|
@ -8958,9 +8965,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz",
|
||||
"integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==",
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
|
||||
"integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
|
@ -9308,27 +9315,27 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path2d-polyfill": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz",
|
||||
"integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==",
|
||||
"node_modules/path2d": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.0.tgz",
|
||||
"integrity": "sha512-KdPAykQX6kmLSOO6Jpu2KNcCED7CKjmaBNGGNuctOsG0hgYO1OdYQaan6cYXJiG0WmXOwZZPILPBimu5QAIw3A==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pdfjs-dist": {
|
||||
"version": "3.11.174",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz",
|
||||
"integrity": "sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==",
|
||||
"version": "4.3.136",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.3.136.tgz",
|
||||
"integrity": "sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"canvas": "^2.11.2",
|
||||
"path2d-polyfill": "^2.0.1"
|
||||
"path2d": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
|
@ -9542,9 +9549,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/lilconfig": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
|
||||
"integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
|
||||
"integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -9555,9 +9562,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/yaml": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz",
|
||||
"integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==",
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz",
|
||||
"integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
|
@ -9853,18 +9860,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-pdf": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.7.3.tgz",
|
||||
"integrity": "sha512-a2VfDl8hiGjugpqezBTUzJHYLNB7IS7a2t7GD52xMI9xHg8LdVaTMsnM9ZlNmKadnStT/tvX5IfV0yLn+JvYmw==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-9.0.0.tgz",
|
||||
"integrity": "sha512-J+pza8R2p9oNEOJOHIQJI4o5rFK7ji7bBl2IvsHvz1OOyphvuzVDo5tOJwWAFAbxYauCH3Kt8jOvcMJUOpxYZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"dequal": "^2.0.3",
|
||||
"make-cancellable-promise": "^1.3.1",
|
||||
"make-event-props": "^1.6.0",
|
||||
"merge-refs": "^1.2.1",
|
||||
"pdfjs-dist": "3.11.174",
|
||||
"prop-types": "^15.6.2",
|
||||
"merge-refs": "^1.3.0",
|
||||
"pdfjs-dist": "4.3.136",
|
||||
"tiny-invariant": "^1.0.0",
|
||||
"warning": "^4.0.0"
|
||||
},
|
||||
|
@ -9872,9 +9878,9 @@
|
|||
"url": "https://github.com/wojtekmaj/react-pdf?sponsor=1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
|
@ -10006,9 +10012,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-tooltip": {
|
||||
"version": "5.26.4",
|
||||
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.26.4.tgz",
|
||||
"integrity": "sha512-5WyDrsfw1+6qNVSr3IjqElqJ+cCwE8+44b+HpJ8qRLv7v0a3mcKf8wvv+NfgALFS6QpksGFqTLV2JQ60c+okZQ==",
|
||||
"version": "5.27.0",
|
||||
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.27.0.tgz",
|
||||
"integrity": "sha512-JXROcdfCEbCqkAkh8LyTSP3guQ0dG53iY2E2o4fw3D8clKzziMpE6QG6CclDaHELEKTzpMSeAOsdtg0ahoQosw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.1",
|
||||
|
@ -10097,9 +10103,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/reagraph": {
|
||||
"version": "4.19.0",
|
||||
"resolved": "https://registry.npmjs.org/reagraph/-/reagraph-4.19.0.tgz",
|
||||
"integrity": "sha512-hQZjA8coxFS6UjDhlP5Ho/woc36sM1dqsliwvTtThN/1ZoGbAuxt5QntVka4jesh0HhNB3ZafYgCIqXoo2sq8Q==",
|
||||
"version": "4.19.1",
|
||||
"resolved": "https://registry.npmjs.org/reagraph/-/reagraph-4.19.1.tgz",
|
||||
"integrity": "sha512-BMSfZ2CoLSWsxe+vowHbCZSqeJJlH2UcbQA6hM1JHI0PmZ/3UHIXCQUHXrsROtYIIH5AnFEj9pUAZh2FGnfGhw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-spring/three": "9.6.1",
|
||||
|
@ -10806,9 +10812,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
|
||||
"integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz",
|
||||
"integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -11086,9 +11092,9 @@
|
|||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/ts-jest": {
|
||||
"version": "29.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.4.tgz",
|
||||
"integrity": "sha512-YiHwDhSvCiItoAgsKtoLFCuakDzDsJ1DLDnSouTaTmdOcOwIkSzbLXduaQ6M5DRVhuZC/NYaaZ/mtHbWMv/S6Q==",
|
||||
"version": "29.1.5",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz",
|
||||
"integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -11134,9 +11140,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tunnel-rat": {
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
"dependencies": {
|
||||
"@lezer/lr": "^1.4.1",
|
||||
"@tanstack/react-table": "^8.17.3",
|
||||
"@uiw/codemirror-themes": "^4.22.1",
|
||||
"@uiw/react-codemirror": "^4.22.1",
|
||||
"@uiw/codemirror-themes": "^4.22.2",
|
||||
"@uiw/react-codemirror": "^4.22.2",
|
||||
"axios": "^1.7.2",
|
||||
"@reactflow/core": "^11.8.3",
|
||||
"clsx": "^2.1.1",
|
||||
|
@ -27,24 +27,24 @@
|
|||
"react-icons": "^4.12.0",
|
||||
"react-intl": "^6.6.8",
|
||||
"react-loader-spinner": "^5.4.5",
|
||||
"react-pdf": "^7.7.3",
|
||||
"react-pdf": "^9.0.0",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-toastify": "^9.1.3",
|
||||
"react-tooltip": "^5.26.4",
|
||||
"reagraph": "^4.19.0",
|
||||
"react-tooltip": "^5.27.0",
|
||||
"reagraph": "^4.19.1",
|
||||
"use-debounce": "^10.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.14.0",
|
||||
"@types/node": "^20.14.5",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
|
@ -53,8 +53,8 @@
|
|||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"jest": "^29.7.0",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"ts-jest": "^29.1.4",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"ts-jest": "^29.1.5",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^4.5.3"
|
||||
},
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /library
|
|
@ -15,7 +15,7 @@ function Footer() {
|
|||
className={clsx(
|
||||
'z-navigation',
|
||||
'mx-auto',
|
||||
'sm:px-4 sm:py-2 flex flex-col items-center gap-1',
|
||||
'px-3 py-2 flex flex-col items-center gap-1',
|
||||
'text-xs sm:text-sm select-none whitespace-nowrap'
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -11,7 +11,7 @@ import { UsersState } from '@/context/UsersContext';
|
|||
|
||||
import ErrorFallback from './ErrorFallback';
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.js', import.meta.url).toString();
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
|
||||
|
||||
const resetState = () => {
|
||||
console.log('Resetting state after error fallback');
|
||||
|
|
|
@ -47,7 +47,7 @@ function Navigation() {
|
|||
<div tabIndex={-1} className='flex items-center mr-auto cursor-pointer' onClick={navigateHome}>
|
||||
<Logo />
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<div className='flex gap-1 py-[0.3rem]'>
|
||||
<NavigationButton
|
||||
text='Новая схема'
|
||||
title='Создать новую схему'
|
||||
|
|
|
@ -23,6 +23,8 @@ function NavigationButton({ icon, title, titleHtml, hideTitle, onClick, text }:
|
|||
'mr-1 h-full', // prettier: split lines
|
||||
'flex items-center gap-1',
|
||||
'clr-btn-nav',
|
||||
'rounded-xl',
|
||||
'transition duration-500',
|
||||
'font-controls whitespace-nowrap',
|
||||
{
|
||||
'px-2': text,
|
||||
|
|
|
@ -14,8 +14,9 @@ function ToggleNavigationButton() {
|
|||
tabIndex={-1}
|
||||
className={clsx(
|
||||
'absolute top-0 right-0 z-navigation flex items-center justify-center',
|
||||
'clr-btn-nav',
|
||||
'select-none disabled:cursor-not-allowed'
|
||||
'clr-hover',
|
||||
'select-none',
|
||||
'min-h-[2rem]'
|
||||
)}
|
||||
onClick={toggleNoNavigation}
|
||||
initial={false}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library';
|
||||
import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
|
||||
import { ExpressionStatus } from '@/models/rsform';
|
||||
|
||||
import {
|
||||
IconAlias,
|
||||
|
@ -21,6 +22,10 @@ import {
|
|||
IconRSForm,
|
||||
IconSettings,
|
||||
IconShow,
|
||||
IconStatusError,
|
||||
IconStatusIncalculable,
|
||||
IconStatusOK,
|
||||
IconStatusUnknown,
|
||||
IconTemplates,
|
||||
IconTerm,
|
||||
IconText,
|
||||
|
@ -80,34 +85,51 @@ export function LocationIcon({ value, size = '1.25rem', className }: DomIconProp
|
|||
}
|
||||
}
|
||||
|
||||
export function DependencyIcon(mode: DependencyMode, size: string, color?: string) {
|
||||
switch (mode) {
|
||||
export function DependencyIcon({ value, size = '1.25rem', className }: DomIconProps<DependencyMode>) {
|
||||
switch (value) {
|
||||
case DependencyMode.ALL:
|
||||
return <IconSettings size={size} className={color} />;
|
||||
return <IconSettings size={size} className={className} />;
|
||||
case DependencyMode.EXPRESSION:
|
||||
return <IconText size={size} className={color} />;
|
||||
return <IconText size={size} className={className ?? 'clr-text-primary'} />;
|
||||
case DependencyMode.OUTPUTS:
|
||||
return <IconGraphOutputs size={size} className={color} />;
|
||||
return <IconGraphOutputs size={size} className={className ?? 'clr-text-primary'} />;
|
||||
case DependencyMode.INPUTS:
|
||||
return <IconGraphInputs size={size} className={color} />;
|
||||
return <IconGraphInputs size={size} className={className ?? 'clr-text-primary'} />;
|
||||
case DependencyMode.EXPAND_OUTPUTS:
|
||||
return <IconGraphExpand size={size} className={color} />;
|
||||
return <IconGraphExpand size={size} className={className ?? 'clr-text-primary'} />;
|
||||
case DependencyMode.EXPAND_INPUTS:
|
||||
return <IconGraphCollapse size={size} className={color} />;
|
||||
return <IconGraphCollapse size={size} className={className ?? 'clr-text-primary'} />;
|
||||
}
|
||||
}
|
||||
|
||||
export function MatchModeIcon(mode: CstMatchMode, size: string, color?: string) {
|
||||
switch (mode) {
|
||||
export function MatchModeIcon({ value, size = '1.25rem', className }: DomIconProps<CstMatchMode>) {
|
||||
switch (value) {
|
||||
case CstMatchMode.ALL:
|
||||
return <IconFilter size={size} className={color} />;
|
||||
return <IconFilter size={size} className={className} />;
|
||||
case CstMatchMode.TEXT:
|
||||
return <IconText size={size} className={color} />;
|
||||
return <IconText size={size} className={className ?? 'clr-text-primary'} />;
|
||||
case CstMatchMode.EXPR:
|
||||
return <IconFormula size={size} className={color} />;
|
||||
return <IconFormula size={size} className={className ?? 'clr-text-primary'} />;
|
||||
case CstMatchMode.TERM:
|
||||
return <IconTerm size={size} className={color} />;
|
||||
return <IconTerm size={size} className={className ?? 'clr-text-primary'} />;
|
||||
case CstMatchMode.NAME:
|
||||
return <IconAlias size={size} className={color} />;
|
||||
return <IconAlias size={size} className={className ?? 'clr-text-primary'} />;
|
||||
}
|
||||
}
|
||||
|
||||
export function StatusIcon({ value, size = '1.25rem', className }: DomIconProps<ExpressionStatus>) {
|
||||
switch (value) {
|
||||
case ExpressionStatus.VERIFIED:
|
||||
case ExpressionStatus.PROPERTY:
|
||||
return <IconStatusOK size={size} className={className} />;
|
||||
|
||||
case ExpressionStatus.UNKNOWN:
|
||||
return <IconStatusUnknown size={size} className={className} />;
|
||||
case ExpressionStatus.INCALCULABLE:
|
||||
return <IconStatusIncalculable size={size} className={className} />;
|
||||
|
||||
case ExpressionStatus.INCORRECT:
|
||||
case ExpressionStatus.UNDEFINED:
|
||||
return <IconStatusError size={size} className={className} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ export { BiCog as IconSettings } from 'react-icons/bi';
|
|||
export { TbEye as IconShow } from 'react-icons/tb';
|
||||
export { TbEyeX as IconHide } from 'react-icons/tb';
|
||||
export { BiShareAlt as IconShare } from 'react-icons/bi';
|
||||
export { BiFilterAlt as IconFilter } from 'react-icons/bi';
|
||||
export { LuFilter as IconFilter } from 'react-icons/lu';
|
||||
export { LuFilterX as IconFilterReset } from 'react-icons/lu';
|
||||
export {BiDownArrowCircle as IconOpenList } from 'react-icons/bi';
|
||||
export { LuAlertTriangle as IconAlert } from 'react-icons/lu';
|
||||
|
||||
|
@ -30,7 +31,11 @@ export { RiMenuFoldFill as IconMenuFold } from 'react-icons/ri';
|
|||
export { RiMenuUnfoldFill as IconMenuUnfold } from 'react-icons/ri';
|
||||
export { LuMoon as IconDarkTheme } from 'react-icons/lu';
|
||||
export { LuSun as IconLightTheme } from 'react-icons/lu';
|
||||
export { FaRegFolder as IconFolder } from 'react-icons/fa';
|
||||
export { LuFolderTree as IconFolderTree } from 'react-icons/lu';
|
||||
export { LuFolder as IconFolder } from 'react-icons/lu';
|
||||
export { LuFolderOpen as IconFolderOpened } from 'react-icons/lu';
|
||||
export { LuFolderClosed as IconFolderClosed } from 'react-icons/lu';
|
||||
export { LuFolderDot as IconFolderEmpty } from 'react-icons/lu';
|
||||
export { LuLightbulb as IconHelp } from 'react-icons/lu';
|
||||
export { LuLightbulbOff as IconHelpOff } from 'react-icons/lu';
|
||||
export { RiPushpinFill as IconPin } from 'react-icons/ri';
|
||||
|
|
|
@ -10,12 +10,13 @@ import { forwardRef, useCallback, useMemo, useRef } from 'react';
|
|||
|
||||
import Label from '@/components/ui/Label';
|
||||
import { useConceptOptions } from '@/context/OptionsContext';
|
||||
import { useRSForm } from '@/context/RSFormContext';
|
||||
import { getFontClassName } from '@/models/miscellaneousAPI';
|
||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||
import { generateAlias, getCstTypePrefix, guessCstType } from '@/models/rsformAPI';
|
||||
import { extractGlobals } from '@/models/rslangAPI';
|
||||
|
||||
import { ccBracketMatching } from './bracketMatching';
|
||||
import { rsNavigation } from './clickNavigation';
|
||||
import { RSLanguage } from './rslang';
|
||||
import { getSymbolSubstitute, RSTextWrapper } from './textEditing';
|
||||
import { rsHoverTooltip } from './tooltip';
|
||||
|
@ -39,6 +40,8 @@ interface RSInputProps
|
|||
noTooltip?: boolean;
|
||||
onChange?: (newValue: string) => void;
|
||||
onAnalyze?: () => void;
|
||||
schema?: IRSForm;
|
||||
onOpenEdit?: (cstID: ConstituentaID) => void;
|
||||
}
|
||||
|
||||
const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||
|
@ -49,6 +52,9 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
disabled,
|
||||
noTooltip,
|
||||
|
||||
schema,
|
||||
onOpenEdit,
|
||||
|
||||
className,
|
||||
style,
|
||||
|
||||
|
@ -59,7 +65,6 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
ref
|
||||
) => {
|
||||
const { darkMode, colors, mathFont } = useConceptOptions();
|
||||
const { schema } = useRSForm();
|
||||
|
||||
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
||||
const thisRef = useMemo(() => (!ref || typeof ref === 'function' ? internalRef : ref), [internalRef, ref]);
|
||||
|
@ -77,7 +82,7 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
caret: colors.fgDefault
|
||||
},
|
||||
styles: [
|
||||
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // GlobalID
|
||||
{ tag: tags.name, color: colors.fgPurple, cursor: schema ? 'default' : cursor }, // GlobalID
|
||||
{ tag: tags.variableName, color: colors.fgGreen }, // LocalID
|
||||
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical
|
||||
{ tag: tags.keyword, color: colors.fgBlue }, // keywords
|
||||
|
@ -87,7 +92,7 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
{ tag: tags.brace, color: colors.fgPurple, fontWeight: '600' } // braces (curly brackets)
|
||||
]
|
||||
}),
|
||||
[disabled, colors, darkMode]
|
||||
[disabled, colors, darkMode, schema, cursor]
|
||||
);
|
||||
|
||||
const editorExtensions = useMemo(
|
||||
|
@ -95,9 +100,10 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
EditorView.lineWrapping,
|
||||
RSLanguage,
|
||||
ccBracketMatching(darkMode),
|
||||
...(noTooltip || !schema ? [] : [rsHoverTooltip(schema)])
|
||||
...(!schema || !onOpenEdit ? [] : [rsNavigation(schema, onOpenEdit)]),
|
||||
...(noTooltip || !schema ? [] : [rsHoverTooltip(schema, onOpenEdit !== undefined)])
|
||||
],
|
||||
[darkMode, schema, noTooltip]
|
||||
[darkMode, schema, noTooltip, onOpenEdit]
|
||||
);
|
||||
|
||||
const handleInput = useCallback(
|
||||
|
|
38
rsconcept/frontend/src/components/RSInput/clickNavigation.ts
Normal file
38
rsconcept/frontend/src/components/RSInput/clickNavigation.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { Extension } from '@codemirror/state';
|
||||
import { EditorView } from '@uiw/react-codemirror';
|
||||
|
||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||
import { findAliasAt } from '@/utils/codemirror';
|
||||
|
||||
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => {
|
||||
return EditorView.domEventHandlers({
|
||||
click: (event: MouseEvent, view: EditorView) => {
|
||||
if (!event.ctrlKey && !event.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = view.posAtCoords({ x: event.clientX, y: event.clientY });
|
||||
if (!pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { alias } = findAliasAt(pos, view.state);
|
||||
if (!alias) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cst = schema.cstByAlias.get(alias);
|
||||
if (!cst) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onOpenEdit(cst.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export function rsNavigation(schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void): Extension {
|
||||
return [navigationProducer(schema, onOpenEdit)];
|
||||
}
|
|
@ -1,31 +1,11 @@
|
|||
import { syntaxTree } from '@codemirror/language';
|
||||
import { Extension } from '@codemirror/state';
|
||||
import { hoverTooltip } from '@codemirror/view';
|
||||
import { EditorState } from '@uiw/react-codemirror';
|
||||
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { findEnvelopingNodes } from '@/utils/codemirror';
|
||||
import { findAliasAt } from '@/utils/codemirror';
|
||||
import { domTooltipConstituenta } from '@/utils/codemirror';
|
||||
|
||||
import { GlobalTokens } from './rslang';
|
||||
|
||||
function findAliasAt(pos: number, state: EditorState) {
|
||||
const { from: lineStart, to: lineEnd, text } = state.doc.lineAt(pos);
|
||||
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(state), GlobalTokens);
|
||||
let alias = '';
|
||||
let start = 0;
|
||||
let end = 0;
|
||||
nodes.forEach(node => {
|
||||
if (node.to <= lineEnd && node.from >= lineStart) {
|
||||
alias = text.slice(node.from - lineStart, node.to - lineStart);
|
||||
start = node.from;
|
||||
end = node.to;
|
||||
}
|
||||
});
|
||||
return { alias, start, end };
|
||||
}
|
||||
|
||||
const globalsHoverTooltip = (schema: IRSForm) => {
|
||||
const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||
return hoverTooltip((view, pos) => {
|
||||
const { alias, start, end } = findAliasAt(pos, view.state);
|
||||
if (!alias) {
|
||||
|
@ -36,11 +16,11 @@ const globalsHoverTooltip = (schema: IRSForm) => {
|
|||
pos: start,
|
||||
end: end,
|
||||
above: false,
|
||||
create: () => domTooltipConstituenta(cst)
|
||||
create: () => domTooltipConstituenta(cst, canClick)
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export function rsHoverTooltip(schema: IRSForm): Extension {
|
||||
return [globalsHoverTooltip(schema)];
|
||||
export function rsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension {
|
||||
return [tooltipProducer(schema, canClick)];
|
||||
}
|
||||
|
|
|
@ -13,10 +13,11 @@ import Label from '@/components/ui/Label';
|
|||
import { useConceptOptions } from '@/context/OptionsContext';
|
||||
import DlgEditReference from '@/dialogs/DlgEditReference';
|
||||
import { ReferenceType } from '@/models/language';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||
import { CodeMirrorWrapper } from '@/utils/codemirror';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { refsNavigation } from './clickNavigation';
|
||||
import { NaturalLanguage, ReferenceTokens } from './parse';
|
||||
import { RefEntity } from './parse/parser.terms';
|
||||
import { refsHoverTooltip } from './tooltip';
|
||||
|
@ -65,6 +66,7 @@ interface RefsInputInputProps
|
|||
label?: string;
|
||||
onChange?: (newValue: string) => void;
|
||||
schema?: IRSForm;
|
||||
onOpenEdit?: (cstID: ConstituentaID) => void;
|
||||
disabled?: boolean;
|
||||
|
||||
initialValue?: string;
|
||||
|
@ -73,7 +75,23 @@ interface RefsInputInputProps
|
|||
}
|
||||
|
||||
const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||
({ id, label, disabled, schema, initialValue, value, resolved, onFocus, onBlur, onChange, ...restProps }, ref) => {
|
||||
(
|
||||
{
|
||||
id, // prettier: split-lines
|
||||
label,
|
||||
disabled,
|
||||
schema,
|
||||
onOpenEdit,
|
||||
initialValue,
|
||||
value,
|
||||
resolved,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onChange,
|
||||
...restProps
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const { darkMode, colors } = useConceptOptions();
|
||||
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
@ -114,9 +132,10 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
|||
EditorView.lineWrapping,
|
||||
EditorView.contentAttributes.of({ spellcheck: 'true' }),
|
||||
NaturalLanguage,
|
||||
...(schema ? [refsHoverTooltip(schema, colors)] : [])
|
||||
...(!schema || !onOpenEdit ? [] : [refsNavigation(schema, onOpenEdit)]),
|
||||
...(schema ? [refsHoverTooltip(schema, colors, onOpenEdit !== undefined)] : [])
|
||||
],
|
||||
[schema, colors]
|
||||
[schema, colors, onOpenEdit]
|
||||
);
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import { Extension } from '@codemirror/state';
|
||||
import { EditorView } from '@uiw/react-codemirror';
|
||||
|
||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||
import { findReferenceAt } from '@/utils/codemirror';
|
||||
|
||||
const navigationProducer = (schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void) => {
|
||||
return EditorView.domEventHandlers({
|
||||
click: (event: MouseEvent, view: EditorView) => {
|
||||
if (!event.ctrlKey && !event.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = view.posAtCoords({ x: event.clientX, y: event.clientY });
|
||||
if (!pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parse = findReferenceAt(pos, view.state);
|
||||
if (!parse || !('entity' in parse.ref)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cst = schema.cstByAlias.get(parse.ref.entity);
|
||||
if (!cst) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onOpenEdit(cst.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export function refsNavigation(schema: IRSForm, onOpenEdit: (cstID: ConstituentaID) => void): Extension {
|
||||
return [navigationProducer(schema, onOpenEdit)];
|
||||
}
|
|
@ -2,65 +2,58 @@ import { syntaxTree } from '@codemirror/language';
|
|||
import { Extension } from '@codemirror/state';
|
||||
import { hoverTooltip } from '@codemirror/view';
|
||||
|
||||
import { parseEntityReference, parseSyntacticReference } from '@/models/languageAPI';
|
||||
import { IEntityReference, ISyntacticReference } from '@/models/language';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { IColorTheme } from '@/styling/color';
|
||||
import {
|
||||
domTooltipEntityReference,
|
||||
domTooltipSyntacticReference,
|
||||
findContainedNodes,
|
||||
findEnvelopingNodes
|
||||
findReferenceAt
|
||||
} from '@/utils/codemirror';
|
||||
|
||||
import { ReferenceTokens } from './parse';
|
||||
import { RefEntity, RefSyntactic } from './parse/parser.terms';
|
||||
import { RefEntity } from './parse/parser.terms';
|
||||
|
||||
export const globalsHoverTooltip = (schema: IRSForm, colors: IColorTheme) => {
|
||||
export const tooltipProducer = (schema: IRSForm, colors: IColorTheme, canClick?: boolean) => {
|
||||
return hoverTooltip((view, pos) => {
|
||||
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(view.state), ReferenceTokens);
|
||||
if (nodes.length !== 1) {
|
||||
const parse = findReferenceAt(pos, view.state);
|
||||
if (!parse) {
|
||||
return null;
|
||||
}
|
||||
const start = nodes[0].from;
|
||||
const end = nodes[0].to;
|
||||
const text = view.state.doc.sliceString(start, end);
|
||||
if (nodes[0].type.id === RefEntity) {
|
||||
const ref = parseEntityReference(text);
|
||||
const cst = schema.cstByAlias.get(ref.entity);
|
||||
|
||||
if ('entity' in parse.ref) {
|
||||
const cst = schema.cstByAlias.get(parse.ref.entity);
|
||||
return {
|
||||
pos: start,
|
||||
end: end,
|
||||
pos: parse.start,
|
||||
end: parse.end,
|
||||
above: false,
|
||||
create: () => domTooltipEntityReference(ref, cst, colors)
|
||||
create: () => domTooltipEntityReference(parse.ref as IEntityReference, cst, colors, canClick)
|
||||
};
|
||||
} else if (nodes[0].type.id === RefSyntactic) {
|
||||
const ref = parseSyntacticReference(text);
|
||||
} else {
|
||||
let masterText: string | undefined = undefined;
|
||||
if (ref.offset > 0) {
|
||||
const entities = findContainedNodes(end, view.state.doc.length, syntaxTree(view.state), [RefEntity]);
|
||||
if (ref.offset <= entities.length) {
|
||||
const master = entities[ref.offset - 1];
|
||||
if (parse.ref.offset > 0) {
|
||||
const entities = findContainedNodes(parse.end, view.state.doc.length, syntaxTree(view.state), [RefEntity]);
|
||||
if (parse.ref.offset <= entities.length) {
|
||||
const master = entities[parse.ref.offset - 1];
|
||||
masterText = view.state.doc.sliceString(master.from, master.to);
|
||||
}
|
||||
} else {
|
||||
const entities = findContainedNodes(0, start, syntaxTree(view.state), [RefEntity]);
|
||||
if (-ref.offset <= entities.length) {
|
||||
const master = entities[-ref.offset - 1];
|
||||
const entities = findContainedNodes(0, parse.start, syntaxTree(view.state), [RefEntity]);
|
||||
if (-parse.ref.offset <= entities.length) {
|
||||
const master = entities[-parse.ref.offset - 1];
|
||||
masterText = view.state.doc.sliceString(master.from, master.to);
|
||||
}
|
||||
}
|
||||
return {
|
||||
pos: start,
|
||||
end: end,
|
||||
pos: parse.start,
|
||||
end: parse.end,
|
||||
above: false,
|
||||
create: () => domTooltipSyntacticReference(ref, masterText)
|
||||
create: () => domTooltipSyntacticReference(parse.ref as ISyntacticReference, masterText, canClick)
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export function refsHoverTooltip(schema: IRSForm, colors: IColorTheme): Extension {
|
||||
return [globalsHoverTooltip(schema, colors)];
|
||||
export function refsHoverTooltip(schema: IRSForm, colors: IColorTheme, canClick?: boolean): Extension {
|
||||
return [tooltipProducer(schema, colors, canClick)];
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ function GraphSelectionToolbar({
|
|||
disabled={emptySelection}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml='<b>Максимизация</b> - дополнение выделения конституентами, зависимыми только от выделенных'
|
||||
titleHtml='<b>Максимизация</b> <br/>дополнение выделения конституентами, <br/>зависимыми только от выделенных'
|
||||
icon={<IconGraphMaximize size='1.25rem' className='icon-primary' />}
|
||||
onClick={() => setSelected(prev => graph.maximizePart(prev))}
|
||||
disabled={emptySelection}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { prefixes } from '@/utils/constants';
|
|||
import { describeConstituenta } from '@/utils/labels';
|
||||
|
||||
import BadgeConstituenta from '../info/BadgeConstituenta';
|
||||
import FlexColumn from '../ui/FlexColumn';
|
||||
import NoData from '../ui/NoData';
|
||||
|
||||
interface PickConstituentaProps {
|
||||
id?: string;
|
||||
|
@ -106,10 +106,10 @@ function PickConstituenta({
|
|||
columns={columns}
|
||||
conditionalRowStyles={conditionalRowStyles}
|
||||
noDataComponent={
|
||||
<FlexColumn className='p-3 items-center min-h-[6rem]'>
|
||||
<NoData className='min-h-[6rem]'>
|
||||
<p>Список конституент пуст</p>
|
||||
<p>Измените параметры фильтра</p>
|
||||
</FlexColumn>
|
||||
</NoData>
|
||||
}
|
||||
onRowClicked={onSelectValue}
|
||||
/>
|
||||
|
|
|
@ -10,7 +10,7 @@ import { isBasicConcept } from '@/models/rsformAPI';
|
|||
import { describeConstituenta } from '@/utils/labels';
|
||||
|
||||
import BadgeConstituenta from '../info/BadgeConstituenta';
|
||||
import FlexColumn from '../ui/FlexColumn';
|
||||
import NoData from '../ui/NoData';
|
||||
import GraphSelectionToolbar from './GraphSelectionToolbar';
|
||||
|
||||
interface PickMultiConstituentaProps {
|
||||
|
@ -103,9 +103,9 @@ function PickMultiConstituenta({ id, schema, prefixID, rows, selected, setSelect
|
|||
rowSelection={rowSelection}
|
||||
onRowSelectionChange={handleRowSelection}
|
||||
noDataComponent={
|
||||
<FlexColumn className='items-center p-3'>
|
||||
<NoData>
|
||||
<p>Список пуст</p>
|
||||
</FlexColumn>
|
||||
</NoData>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@ import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/u
|
|||
import SearchBar from '@/components/ui/SearchBar';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { useConceptOptions } from '@/context/OptionsContext';
|
||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||
|
||||
import FlexColumn from '../ui/FlexColumn';
|
||||
|
@ -32,7 +32,8 @@ function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue }:
|
|||
|
||||
useLayoutEffect(() => {
|
||||
setFilter({
|
||||
query: filterText
|
||||
query: filterText,
|
||||
type: LibraryItemType.RSFORM
|
||||
});
|
||||
}, [filterText]);
|
||||
|
||||
|
@ -103,7 +104,7 @@ function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue }:
|
|||
columns={columns}
|
||||
conditionalRowStyles={conditionalRowStyles}
|
||||
noDataComponent={
|
||||
<FlexColumn className='p-3 items-center min-h-[6rem]'>
|
||||
<FlexColumn className='dense p-3 items-center min-h-[6rem]'>
|
||||
<p>Список схем пуст</p>
|
||||
<p>Измените параметры фильтра</p>
|
||||
</FlexColumn>
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
IconRemove,
|
||||
IconReplace
|
||||
} from '../Icons';
|
||||
import NoData from '../ui/NoData';
|
||||
|
||||
interface PickSubstitutionsProps {
|
||||
prefixID: string;
|
||||
|
@ -259,10 +260,10 @@ function PickSubstitutions({
|
|||
columns={columns}
|
||||
headPosition='0'
|
||||
noDataComponent={
|
||||
<span className='p-2 text-center min-h-[2rem]'>
|
||||
<NoData className='min-h-[2rem]'>
|
||||
<p>Список пуст</p>
|
||||
<p>Добавьте отождествление</p>
|
||||
</span>
|
||||
</NoData>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -37,7 +37,7 @@ function SelectAccessPolicy({ value, disabled, stretchLeft, onChange }: SelectAc
|
|||
<MiniButton
|
||||
title={`Доступ: ${labelAccessPolicy(value)}`}
|
||||
hideTitle={menu.isOpen}
|
||||
className='h-full disabled:cursor-auto'
|
||||
className='h-full'
|
||||
icon={<PolicyIcon value={value} size='1.25rem' />}
|
||||
onClick={menu.toggle}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -36,10 +36,10 @@ function SelectGraphFilter({ value, dense, onChange }: SelectGraphFilterProps) {
|
|||
<SelectorButton
|
||||
transparent
|
||||
tabIndex={-1}
|
||||
title='Настройка фильтрации по графу термов'
|
||||
titleHtml='Настройка фильтрации <br/>по графу термов'
|
||||
hideTitle={menu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={DependencyIcon(value, '1rem', value !== DependencyMode.ALL ? 'icon-primary' : '')}
|
||||
icon={<DependencyIcon value={value} size='1rem' />}
|
||||
text={dense || size.isSmall ? undefined : labelCstSource(value)}
|
||||
onClick={menu.toggle}
|
||||
/>
|
||||
|
@ -55,7 +55,7 @@ function SelectGraphFilter({ value, dense, onChange }: SelectGraphFilterProps) {
|
|||
onClick={() => handleChange(source)}
|
||||
>
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
{DependencyIcon(source, '1rem')}
|
||||
{<DependencyIcon value={source} size='1rem' />}
|
||||
{!dense ? (
|
||||
<span>
|
||||
<b>{labelCstSource(source)}:</b> {describeCstSource(source)}
|
||||
|
|
|
@ -38,7 +38,7 @@ function SelectItemType({ value, disabled, stretchLeft, onChange }: SelectItemTy
|
|||
transparent
|
||||
title={describeLibraryItemType(value)}
|
||||
hideTitle={menu.isOpen}
|
||||
className='h-full py-1 px-2 disabled:cursor-auto rounded-lg'
|
||||
className='h-full px-2 py-1 rounded-lg'
|
||||
icon={<ItemTypeIcon value={value} size='1.25rem' />}
|
||||
text={labelLibraryItemType(value)}
|
||||
onClick={menu.toggle}
|
||||
|
|
42
rsconcept/frontend/src/components/select/SelectLocation.tsx
Normal file
42
rsconcept/frontend/src/components/select/SelectLocation.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import useDropdown from '@/hooks/useDropdown';
|
||||
import { FolderTree } from '@/models/FolderTree';
|
||||
|
||||
import { IconFolderTree } from '../Icons';
|
||||
import MiniButton from '../ui/MiniButton';
|
||||
|
||||
interface SelectLocationProps {
|
||||
value: string;
|
||||
onChange: (newValue: string) => void;
|
||||
|
||||
folderTree: FolderTree;
|
||||
}
|
||||
|
||||
function SelectLocation({ value, onChange, folderTree }: SelectLocationProps) {
|
||||
const menu = useDropdown();
|
||||
|
||||
const handleChange = useCallback(
|
||||
(newValue: string) => {
|
||||
console.log(folderTree.roots.size);
|
||||
console.log(value);
|
||||
menu.hide();
|
||||
onChange(newValue);
|
||||
},
|
||||
[menu, onChange, value, folderTree]
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={menu.ref} className='h-full text-right'>
|
||||
<MiniButton
|
||||
title='Проводник...'
|
||||
icon={<IconFolderTree size='1.25rem' className='icon-green' />}
|
||||
onClick={() => handleChange('/U/test')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SelectLocation;
|
|
@ -14,7 +14,7 @@ import DropdownButton from '../ui/DropdownButton';
|
|||
|
||||
interface SelectLocationHeadProps {
|
||||
value: LocationHead;
|
||||
onChange: (value: LocationHead) => void;
|
||||
onChange: (newValue: LocationHead) => void;
|
||||
excluded?: LocationHead[];
|
||||
}
|
||||
|
||||
|
|
|
@ -35,10 +35,10 @@ function SelectMatchMode({ value, dense, onChange }: SelectMatchModeProps) {
|
|||
<div ref={menu.ref}>
|
||||
<SelectorButton
|
||||
transparent
|
||||
title='Настройка фильтрации по проверяемым атрибутам'
|
||||
titleHtml='Настройка фильтрации <br/>по проверяемым атрибутам'
|
||||
hideTitle={menu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={MatchModeIcon(value, '1rem', value !== CstMatchMode.ALL ? 'icon-primary' : '')}
|
||||
icon={<MatchModeIcon value={value} size='1rem' />}
|
||||
text={dense || size.isSmall ? undefined : labelCstMatchMode(value)}
|
||||
onClick={menu.toggle}
|
||||
/>
|
||||
|
@ -54,7 +54,7 @@ function SelectMatchMode({ value, dense, onChange }: SelectMatchModeProps) {
|
|||
onClick={() => handleChange(matchMode)}
|
||||
>
|
||||
<div className='inline-flex items-center gap-1'>
|
||||
{MatchModeIcon(matchMode, '1rem')}
|
||||
{<MatchModeIcon value={matchMode} size='1rem' />}
|
||||
{!dense ? (
|
||||
<span>
|
||||
<b>{labelCstMatchMode(matchMode)}:</b> {describeCstMatchMode(matchMode)}
|
||||
|
|
|
@ -33,7 +33,7 @@ function Button({
|
|||
disabled={disabled ?? loading}
|
||||
className={clsx(
|
||||
'inline-flex gap-2 items-center justify-center',
|
||||
'select-none disabled:cursor-not-allowed',
|
||||
'select-none disabled:cursor-auto',
|
||||
{
|
||||
'border rounded': !noBorder,
|
||||
'px-1': dense,
|
||||
|
|
|
@ -27,7 +27,7 @@ function Checkbox({
|
|||
}: CheckboxProps) {
|
||||
const cursor = useMemo(() => {
|
||||
if (disabled) {
|
||||
return 'cursor-not-allowed';
|
||||
return 'cursor-auto';
|
||||
} else if (setValue) {
|
||||
return 'cursor-pointer';
|
||||
} else {
|
||||
|
|
|
@ -25,7 +25,7 @@ function CheckboxTristate({
|
|||
}: CheckboxTristateProps) {
|
||||
const cursor = useMemo(() => {
|
||||
if (disabled) {
|
||||
return 'cursor-not-allowed';
|
||||
return 'cursor-auto';
|
||||
} else if (setValue) {
|
||||
return 'cursor-pointer';
|
||||
} else {
|
||||
|
|
|
@ -35,7 +35,7 @@ function DropdownButton({
|
|||
'disabled:clr-text-controls',
|
||||
{
|
||||
'clr-hover': onClick,
|
||||
'cursor-pointer disabled:cursor-not-allowed': onClick,
|
||||
'cursor-pointer disabled:cursor-auto': onClick,
|
||||
'cursor-default': !onClick
|
||||
},
|
||||
className
|
||||
|
|
|
@ -2,9 +2,7 @@ import clsx from 'clsx';
|
|||
|
||||
import { CProps } from '../props';
|
||||
|
||||
export interface FlexColumnProps extends CProps.Div {}
|
||||
|
||||
function FlexColumn({ className, children, ...restProps }: FlexColumnProps) {
|
||||
function FlexColumn({ className, children, ...restProps }: CProps.Div) {
|
||||
return (
|
||||
<div className={clsx('cc-column', className)} {...restProps}>
|
||||
{children}
|
||||
|
|
|
@ -28,7 +28,7 @@ function MiniButton({
|
|||
className={clsx(
|
||||
'rounded-lg',
|
||||
'clr-btn-clear',
|
||||
'cursor-pointer disabled:cursor-not-allowed',
|
||||
'cursor-pointer disabled:cursor-auto',
|
||||
{
|
||||
'px-1 py-1': !noPadding,
|
||||
'outline-none': noHover,
|
||||
|
|
13
rsconcept/frontend/src/components/ui/NoData.tsx
Normal file
13
rsconcept/frontend/src/components/ui/NoData.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
|
||||
function NoData({ className, children, ...restProps }: CProps.Div) {
|
||||
return (
|
||||
<div className={clsx('p-3 flex flex-col items-center text-center select-none w-full', className)} {...restProps}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default NoData;
|
|
@ -11,21 +11,23 @@ import Overlay from '../Overlay';
|
|||
import PageControls from './PageControls';
|
||||
|
||||
const MAXIMUM_WIDTH = 1000;
|
||||
const MINIMUM_WIDTH = 320;
|
||||
const MINIMUM_WIDTH = 300;
|
||||
|
||||
interface PDFViewerProps {
|
||||
file?: string | ArrayBuffer | Blob;
|
||||
offsetXpx?: number;
|
||||
minWidth?: number;
|
||||
}
|
||||
|
||||
function PDFViewer({ file }: PDFViewerProps) {
|
||||
function PDFViewer({ file, offsetXpx, minWidth = MINIMUM_WIDTH }: PDFViewerProps) {
|
||||
const windowSize = useWindowSize();
|
||||
|
||||
const [pageCount, setPageCount] = useState(0);
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
|
||||
const pageWidth = useMemo(() => {
|
||||
return Math.max(MINIMUM_WIDTH, Math.min((windowSize?.width ?? 0) - 10, MAXIMUM_WIDTH));
|
||||
}, [windowSize]);
|
||||
return Math.max(minWidth, Math.min((windowSize?.width ?? 0) - (offsetXpx ?? 0) - 10, MAXIMUM_WIDTH));
|
||||
}, [windowSize, offsetXpx, minWidth]);
|
||||
|
||||
function onDocumentLoadSuccess({ numPages }: PDFDocumentProxy) {
|
||||
setPageCount(numPages);
|
||||
|
@ -42,14 +44,14 @@ function PDFViewer({ file }: PDFViewerProps) {
|
|||
<PageControls pageCount={pageCount} pageNumber={pageNumber} setPageNumber={setPageNumber} />
|
||||
</Overlay>
|
||||
<Page
|
||||
className='pointer-events-none select-none sm:translate-x-0'
|
||||
className='overflow-hidden pointer-events-none select-none'
|
||||
renderTextLayer={false}
|
||||
renderAnnotationLayer={false}
|
||||
pageNumber={pageNumber}
|
||||
width={pageWidth}
|
||||
canvasBackground={graphLightT.canvas.background}
|
||||
/>
|
||||
<Overlay position='bottom-6 left-1/2 -translate-x-1/2' className='flex select-none'>
|
||||
<Overlay position='bottom-3 left-1/2 -translate-x-1/2' className='flex select-none'>
|
||||
<PageControls pageCount={pageCount} pageNumber={pageNumber} setPageNumber={setPageNumber} />
|
||||
</Overlay>
|
||||
</Document>
|
||||
|
|
|
@ -8,7 +8,7 @@ interface PageControlsProps {
|
|||
|
||||
function PageControls({ pageNumber, pageCount, setPageNumber }: PageControlsProps) {
|
||||
return (
|
||||
<>
|
||||
<div className='flex items-center'>
|
||||
<button
|
||||
type='button'
|
||||
className='clr-hover clr-text-controls'
|
||||
|
@ -25,9 +25,9 @@ function PageControls({ pageNumber, pageCount, setPageNumber }: PageControlsProp
|
|||
>
|
||||
<IconPageLeft size='1.5rem' />
|
||||
</button>
|
||||
<p className='px-3 text-black text-nowrap'>
|
||||
<div className='px-3 text-nowrap'>
|
||||
Страница {pageNumber} из {pageCount}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type='button'
|
||||
className='clr-hover clr-text-controls'
|
||||
|
@ -44,7 +44,7 @@ function PageControls({ pageNumber, pageCount, setPageNumber }: PageControlsProp
|
|||
>
|
||||
<IconPageLast size='1.5rem' />
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ function SelectTree<ItemType>({
|
|||
value === item && 'clr-selected'
|
||||
)}
|
||||
data-tooltip-id={globals.tooltip}
|
||||
data-tooltip-content={getDescription(item)}
|
||||
data-tooltip-html={getDescription(item)}
|
||||
onClick={event => handleSetValue(event, item)}
|
||||
initial={{ ...animateSideAppear.initial }}
|
||||
animate={{ ...animateSideAppear.animate }}
|
||||
|
|
|
@ -31,7 +31,7 @@ function SelectorButton({
|
|||
'px-1 flex flex-start items-center gap-1',
|
||||
'text-sm font-controls select-none',
|
||||
'text-btn clr-text-controls',
|
||||
'disabled:cursor-not-allowed cursor-pointer',
|
||||
'disabled:cursor-auto cursor-pointer',
|
||||
{
|
||||
'clr-hover': transparent,
|
||||
'border': !transparent
|
||||
|
|
|
@ -17,7 +17,7 @@ function SubmitButton({ text = 'ОК', icon, disabled, loading, className, ...re
|
|||
'border',
|
||||
'font-medium',
|
||||
'clr-btn-primary',
|
||||
'select-none disabled:cursor-not-allowed',
|
||||
'select-none disabled:cursor-auto',
|
||||
loading && 'cursor-progress',
|
||||
className
|
||||
)}
|
||||
|
|
|
@ -18,9 +18,9 @@ function TextURL({ text, href, title, color = 'clr-text-url', onClick }: TextURL
|
|||
);
|
||||
} else if (onClick) {
|
||||
return (
|
||||
<span tabIndex={-1} className={design} onClick={onClick}>
|
||||
<button type='button' tabIndex={-1} className={design} onClick={onClick}>
|
||||
{text}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
|
|
|
@ -33,7 +33,8 @@ function Tooltip({
|
|||
delayHide={100}
|
||||
opacity={0.97}
|
||||
className={clsx(
|
||||
'overflow-auto sm:overflow-hidden overscroll-contain',
|
||||
'max-h-[calc(100svh-6rem)]',
|
||||
'overflow-y-auto overflow-x-hidden sm:overflow-hidden overscroll-contain',
|
||||
'border shadow-md',
|
||||
'text-balance',
|
||||
layer,
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
'use client';
|
||||
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
|
||||
import Loader from '../ui/Loader';
|
||||
import TextURL from '../ui/TextURL';
|
||||
import AnimateFade from './AnimateFade';
|
||||
|
||||
interface RequireAuthProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function RequireAuth({ children }: RequireAuthProps) {
|
||||
const { user } = useAuth();
|
||||
if (user) {
|
||||
return children;
|
||||
} else {
|
||||
return (
|
||||
<div className='flex flex-col items-center gap-1 mt-2'>
|
||||
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
||||
<TextURL text='Войти в Портал' href='/login' />
|
||||
<TextURL text='Зарегистрироваться' href='/signup' />
|
||||
<TextURL text='Начальная страница' href='/' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const { user, loading } = useAuth();
|
||||
|
||||
return (
|
||||
<AnimatePresence mode='wait'>
|
||||
{loading ? <Loader key='auth-loader' /> : null}
|
||||
{!loading && user ? <AnimateFade key='auth-data'>{children}</AnimateFade> : null}
|
||||
{!loading && !user ? (
|
||||
<AnimateFade key='auth-no-user' className='flex flex-col items-center gap-1 mt-2'>
|
||||
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
||||
<TextURL text='Войти в Портал' href='/login' />
|
||||
<TextURL text='Зарегистрироваться' href='/signup' />
|
||||
<TextURL text='Начальная страница' href='/' />
|
||||
</AnimateFade>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
||||
export default RequireAuth;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
interface IAccessModeContext {
|
||||
accessLevel: UserLevel;
|
||||
|
@ -13,7 +14,7 @@ const AccessContext = createContext<IAccessModeContext | null>(null);
|
|||
export const useAccessMode = () => {
|
||||
const context = useContext(AccessContext);
|
||||
if (!context) {
|
||||
throw new Error('useAccessMode has to be used within <AccessModeState.Provider>');
|
||||
throw new Error(contextOutsideScope('useAccessMode', 'AccessModeState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
IUserSignupData,
|
||||
IUserUpdatePassword
|
||||
} from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
import { useUsers } from './UsersContext';
|
||||
|
||||
|
@ -46,7 +47,7 @@ const AuthContext = createContext<IAuthContext | null>(null);
|
|||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth has to be used within <AuthState.Provider>');
|
||||
throw new Error(contextOutsideScope('useAuth', 'AuthState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
DataCallback,
|
||||
|
@ -14,12 +14,14 @@ import {
|
|||
postRSFormFromFile
|
||||
} from '@/app/backendAPI';
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||
import { FolderTree } from '@/models/FolderTree';
|
||||
import { ILibraryItem, LibraryItemID, LocationHead } from '@/models/library';
|
||||
import { ILibraryCreateData } from '@/models/library';
|
||||
import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI';
|
||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||
import { IRSForm, IRSFormCloneData, IRSFormData } from '@/models/rsform';
|
||||
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
import { useAuth } from './AuthContext';
|
||||
import { useConceptOptions } from './OptionsContext';
|
||||
|
@ -27,10 +29,17 @@ import { useConceptOptions } from './OptionsContext';
|
|||
interface ILibraryContext {
|
||||
items: ILibraryItem[];
|
||||
templates: ILibraryItem[];
|
||||
folders: FolderTree;
|
||||
|
||||
loading: boolean;
|
||||
loadingError: ErrorData;
|
||||
setLoadingError: (error: ErrorData) => void;
|
||||
|
||||
processing: boolean;
|
||||
error: ErrorData;
|
||||
setError: (error: ErrorData) => void;
|
||||
processingError: ErrorData;
|
||||
setProcessingError: (error: ErrorData) => void;
|
||||
|
||||
reloadItems: (callback?: () => void) => void;
|
||||
|
||||
applyFilter: (params: ILibraryFilter) => ILibraryItem[];
|
||||
retrieveTemplate: (templateID: LibraryItemID, callback: (schema: IRSForm) => void) => void;
|
||||
|
@ -46,7 +55,7 @@ const LibraryContext = createContext<ILibraryContext | null>(null);
|
|||
export const useLibrary = (): ILibraryContext => {
|
||||
const context = useContext(LibraryContext);
|
||||
if (context === null) {
|
||||
throw new Error('useLibrary has to be used within <LibraryState.Provider>');
|
||||
throw new Error(contextOutsideScope('useLibrary', 'LibraryState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
@ -63,15 +72,32 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
const [templates, setTemplates] = useState<ILibraryItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const [error, setError] = useState<ErrorData>(undefined);
|
||||
const [loadingError, setLoadingError] = useState<ErrorData>(undefined);
|
||||
const [processingError, setProcessingError] = useState<ErrorData>(undefined);
|
||||
const [cachedTemplates, setCachedTemplates] = useState<IRSForm[]>([]);
|
||||
|
||||
const folders = useMemo(() => {
|
||||
const result = new FolderTree();
|
||||
result.addPath(LocationHead.USER, 0);
|
||||
result.addPath(LocationHead.COMMON, 0);
|
||||
result.addPath(LocationHead.LIBRARY, 0);
|
||||
result.addPath(LocationHead.PROJECTS, 0);
|
||||
items.forEach(item => result.addPath(item.location));
|
||||
return result;
|
||||
}, [items]);
|
||||
|
||||
const applyFilter = useCallback(
|
||||
(filter: ILibraryFilter) => {
|
||||
let result = items;
|
||||
if (filter.head) {
|
||||
if (!filter.folderMode && filter.head) {
|
||||
result = result.filter(item => item.location.startsWith(filter.head!));
|
||||
}
|
||||
if (filter.folderMode && filter.folder) {
|
||||
result = result.filter(item => item.location == filter.folder);
|
||||
}
|
||||
if (filter.type) {
|
||||
result = result.filter(item => item.item_type === filter.type);
|
||||
}
|
||||
if (filter.isVisible !== undefined) {
|
||||
result = result.filter(item => filter.isVisible === item.visible);
|
||||
}
|
||||
|
@ -84,12 +110,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
if (filter.isEditor !== undefined) {
|
||||
result = result.filter(item => filter.isEditor == user?.editor.includes(item.id));
|
||||
}
|
||||
if (!filter.folderMode && filter.path) {
|
||||
result = result.filter(item => matchLibraryItemLocation(item, filter.path!));
|
||||
}
|
||||
if (filter.query) {
|
||||
result = result.filter(item => matchLibraryItem(item, filter.query!));
|
||||
}
|
||||
if (filter.path) {
|
||||
result = result.filter(item => matchLibraryItemLocation(item, filter.path!));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[items, user]
|
||||
|
@ -102,11 +128,11 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
callback(cached);
|
||||
return;
|
||||
}
|
||||
setError(undefined);
|
||||
setProcessingError(undefined);
|
||||
getRSFormDetails(String(templateID), '', {
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setError,
|
||||
onError: setProcessingError,
|
||||
onSuccess: data => {
|
||||
const schema = new RSFormLoader(data).produceRSForm();
|
||||
setCachedTemplates(prev => [...prev, schema]);
|
||||
|
@ -120,12 +146,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
const reloadItems = useCallback(
|
||||
(callback?: () => void) => {
|
||||
setItems([]);
|
||||
setError(undefined);
|
||||
setLoadingError(undefined);
|
||||
if (user?.is_staff && adminMode) {
|
||||
getAdminLibrary({
|
||||
setLoading: setLoading,
|
||||
showError: true,
|
||||
onError: setError,
|
||||
onError: setLoadingError,
|
||||
onSuccess: newData => {
|
||||
setItems(newData);
|
||||
if (callback) callback();
|
||||
|
@ -135,7 +161,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
getLibrary({
|
||||
setLoading: setLoading,
|
||||
showError: true,
|
||||
onError: setError,
|
||||
onError: setLoadingError,
|
||||
onSuccess: newData => {
|
||||
setItems(newData);
|
||||
if (callback) callback();
|
||||
|
@ -149,6 +175,8 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
const reloadTemplates = useCallback(() => {
|
||||
setTemplates([]);
|
||||
getTemplates({
|
||||
setLoading: setLoading,
|
||||
onError: setLoadingError,
|
||||
showError: true,
|
||||
onSuccess: newData => setTemplates(newData)
|
||||
});
|
||||
|
@ -189,13 +217,13 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
}
|
||||
if (callback) callback(newSchema);
|
||||
});
|
||||
setError(undefined);
|
||||
setProcessingError(undefined);
|
||||
if (data.file) {
|
||||
postRSFormFromFile({
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setError,
|
||||
onError: setProcessingError,
|
||||
onSuccess: onSuccess
|
||||
});
|
||||
} else {
|
||||
|
@ -203,7 +231,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setError,
|
||||
onError: setProcessingError,
|
||||
onSuccess: onSuccess
|
||||
});
|
||||
}
|
||||
|
@ -213,11 +241,11 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
|
||||
const destroyItem = useCallback(
|
||||
(target: LibraryItemID, callback?: () => void) => {
|
||||
setError(undefined);
|
||||
setProcessingError(undefined);
|
||||
deleteLibraryItem(String(target), {
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setError,
|
||||
onError: setProcessingError,
|
||||
onSuccess: () =>
|
||||
reloadItems(() => {
|
||||
if (user && user.subscriptions.includes(target)) {
|
||||
|
@ -230,7 +258,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
})
|
||||
});
|
||||
},
|
||||
[setError, reloadItems, user]
|
||||
[reloadItems, user]
|
||||
);
|
||||
|
||||
const cloneItem = useCallback(
|
||||
|
@ -238,12 +266,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
if (!user) {
|
||||
return;
|
||||
}
|
||||
setError(undefined);
|
||||
setProcessingError(undefined);
|
||||
postCloneLibraryItem(String(target), {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setError,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newSchema =>
|
||||
reloadItems(() => {
|
||||
if (user && !user.subscriptions.includes(newSchema.id)) {
|
||||
|
@ -253,18 +281,26 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
})
|
||||
});
|
||||
},
|
||||
[reloadItems, setError, user]
|
||||
[reloadItems, user]
|
||||
);
|
||||
|
||||
return (
|
||||
<LibraryContext.Provider
|
||||
value={{
|
||||
items,
|
||||
folders,
|
||||
templates,
|
||||
|
||||
loading,
|
||||
loadingError,
|
||||
setLoadingError,
|
||||
|
||||
processing,
|
||||
error,
|
||||
setError,
|
||||
processingError,
|
||||
setProcessingError,
|
||||
|
||||
reloadItems,
|
||||
|
||||
applyFilter,
|
||||
createItem,
|
||||
cloneItem,
|
||||
|
|
|
@ -4,6 +4,7 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea
|
|||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
interface INavigationContext {
|
||||
push: (path: string, newTab?: boolean) => void;
|
||||
|
@ -21,7 +22,7 @@ const NavigationContext = createContext<INavigationContext | null>(null);
|
|||
export const useConceptNavigation = () => {
|
||||
const context = useContext(NavigationContext);
|
||||
if (!context) {
|
||||
throw new Error('useConceptNavigation has to be used within <NavigationState.Provider>');
|
||||
throw new Error(contextOutsideScope('useConceptNavigation', 'NavigationState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ import { FontStyle } from '@/models/miscellaneous';
|
|||
import { animationDuration } from '@/styling/animations';
|
||||
import { darkT, IColorTheme, lightT } from '@/styling/color';
|
||||
import { globals, storage } from '@/utils/constants';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
interface IOptionsContext {
|
||||
viewportHeight: string;
|
||||
|
@ -45,7 +46,7 @@ const OptionsContext = createContext<IOptionsContext | null>(null);
|
|||
export const useConceptOptions = () => {
|
||||
const context = useContext(OptionsContext);
|
||||
if (!context) {
|
||||
throw new Error('useConceptTheme has to be used within <ThemeState.Provider>');
|
||||
throw new Error(contextOutsideScope('useConceptTheme', 'ThemeState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ import { AccessPolicy, ILibraryItem } from '@/models/library';
|
|||
import { ILibraryUpdateData } from '@/models/library';
|
||||
import { IOperationSchema } from '@/models/oss';
|
||||
import { UserID } from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
import { useAuth } from './AuthContext';
|
||||
import { useLibrary } from './LibraryContext';
|
||||
|
@ -48,7 +49,7 @@ const OssContext = createContext<IOssContext | null>(null);
|
|||
export const useOSS = () => {
|
||||
const context = useContext(OssContext);
|
||||
if (context === null) {
|
||||
throw new Error('useOSS has to be used within <OssState.Provider>');
|
||||
throw new Error(contextOutsideScope('useOSS', 'OssState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -48,6 +48,7 @@ import {
|
|||
ITargetCst
|
||||
} from '@/models/rsform';
|
||||
import { UserID } from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
import { useAuth } from './AuthContext';
|
||||
import { useLibrary } from './LibraryContext';
|
||||
|
@ -99,7 +100,7 @@ const RSFormContext = createContext<IRSFormContext | null>(null);
|
|||
export const useRSForm = () => {
|
||||
const context = useContext(RSFormContext);
|
||||
if (context === null) {
|
||||
throw new Error('useRSForm has to be used within <RSFormState.Provider>');
|
||||
throw new Error(contextOutsideScope('useRSForm', 'RSFormState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
@ -295,8 +296,7 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
onError: setProcessingError,
|
||||
onSuccess: () => {
|
||||
schema.location = newLocation;
|
||||
library.localUpdateItem(schema);
|
||||
if (callback) callback();
|
||||
library.reloadItems(callback);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -336,7 +336,7 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
setSchema(Object.assign(schema, newData));
|
||||
setSchema(newData);
|
||||
library.localUpdateTimestamp(newData.id);
|
||||
if (callback) callback();
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
setSchema(Object.assign(schema, newData));
|
||||
setSchema(newData);
|
||||
library.localUpdateTimestamp(newData.id);
|
||||
if (callback) callback();
|
||||
}
|
||||
|
@ -572,14 +572,14 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: () => {
|
||||
setSchema(schema);
|
||||
library.localUpdateItem(schema!);
|
||||
onSuccess: newData => {
|
||||
setSchema(newData);
|
||||
library.localUpdateItem(newData);
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
[schema, setSchema, library]
|
||||
[setSchema, library]
|
||||
);
|
||||
|
||||
const inlineSynthesis = useCallback(
|
||||
|
|
|
@ -5,6 +5,7 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea
|
|||
import { DataCallback, getProfile, patchProfile } from '@/app/backendAPI';
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import { IUserProfile, IUserUpdateData } from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
import { useUsers } from './UsersContext';
|
||||
|
||||
|
@ -23,7 +24,7 @@ const ProfileContext = createContext<IUserProfileContext | null>(null);
|
|||
export const useUserProfile = () => {
|
||||
const context = useContext(ProfileContext);
|
||||
if (!context) {
|
||||
throw new Error('useUserProfile has to be used within <UserProfileState.Provider>');
|
||||
throw new Error(contextOutsideScope('useUserProfile', 'UserProfileState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea
|
|||
|
||||
import { getActiveUsers } from '@/app/backendAPI';
|
||||
import { IUserInfo } from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
interface IUsersContext {
|
||||
users: IUserInfo[];
|
||||
|
@ -15,7 +16,7 @@ const UsersContext = createContext<IUsersContext | null>(null);
|
|||
export const useUsers = (): IUsersContext => {
|
||||
const context = useContext(UsersContext);
|
||||
if (context === null) {
|
||||
throw new Error('useUsers has to be used within <UsersState.Provider>');
|
||||
throw new Error(contextOutsideScope('useUsers', 'UsersState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -20,6 +20,7 @@ import { useConceptNavigation } from '@/context/NavigationContext';
|
|||
import { AccessPolicy, ILibraryItem, LocationHead } from '@/models/library';
|
||||
import { cloneTitle, combineLocation, validateLocation } from '@/models/libraryAPI';
|
||||
import { ConstituentaID, IRSFormCloneData } from '@/models/rsform';
|
||||
import { information } from '@/utils/labels';
|
||||
|
||||
interface DlgCloneLibraryItemProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
base: ILibraryItem;
|
||||
|
@ -62,7 +63,7 @@ function DlgCloneLibraryItem({ hideWindow, base, initialLocation, selected, tota
|
|||
data.items = selected;
|
||||
}
|
||||
cloneItem(base.id, data, newSchema => {
|
||||
toast.success(`Копия создана: ${newSchema.alias}`);
|
||||
toast.success(information.cloneComplete(newSchema.alias));
|
||||
router.push(urls.schema(newSchema.id));
|
||||
});
|
||||
}
|
||||
|
@ -100,7 +101,6 @@ function DlgCloneLibraryItem({ hideWindow, base, initialLocation, selected, tota
|
|||
/>
|
||||
|
||||
<MiniButton
|
||||
className='disabled:cursor-auto'
|
||||
title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
|
||||
icon={<VisibilityIcon value={visible} />}
|
||||
onClick={() => setVisible(prev => !prev)}
|
||||
|
|
|
@ -9,6 +9,7 @@ import RSInput from '@/components/RSInput';
|
|||
import PickConstituenta from '@/components/select/PickConstituenta';
|
||||
import DataTable, { IConditionalStyle } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import NoData from '@/components/ui/NoData';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { useConceptOptions } from '@/context/OptionsContext';
|
||||
import { IConstituenta, IRSForm } from '@/models/rsform';
|
||||
|
@ -160,7 +161,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
|||
data={state.arguments}
|
||||
columns={columns}
|
||||
conditionalRowStyles={conditionalRowStyles}
|
||||
noDataComponent={<p className={clsx('min-h-[3.6rem]', 'p-2', 'text-center')}>Аргументы отсутствуют</p>}
|
||||
noDataComponent={<NoData className='min-h-[3.6rem]'>Аргументы отсутствуют</NoData>}
|
||||
onRowClicked={handleSelectArgument}
|
||||
/>
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import { HelpTopic } from '@/models/miscellaneous';
|
|||
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
||||
import { generateAlias, validateNewAlias } from '@/models/rsformAPI';
|
||||
import { inferTemplatedType, substituteTemplateArgs } from '@/models/rslangAPI';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import FormCreateCst from '../DlgCreateCst/FormCreateCst';
|
||||
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
|
||||
|
@ -144,7 +145,11 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
|||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Overlay position='top-0 right-[6rem]'>
|
||||
<BadgeHelp topic={HelpTopic.RSL_TEMPLATES} className='max-w-[40rem]' offset={12} />
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.RSL_TEMPLATES}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
offset={12}
|
||||
/>
|
||||
</Overlay>
|
||||
<Tabs
|
||||
selectedTabClassName='clr-selected'
|
||||
|
@ -155,7 +160,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
|||
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none')}>
|
||||
<TabLabel label='Шаблон' title='Выбор шаблона выражения' className='w-[8rem]' />
|
||||
<TabLabel label='Аргументы' title='Подстановка аргументов шаблона' className='w-[8rem]' />
|
||||
<TabLabel label='Конституента' title='Редактирование атрибутов конституенты' className='w-[8rem]' />
|
||||
<TabLabel label='Конституента' title='Редактирование конституенты' className='w-[8rem]' />
|
||||
</TabList>
|
||||
|
||||
{templatePanel}
|
||||
|
|
|
@ -24,7 +24,7 @@ interface TemplateTabProps {
|
|||
|
||||
function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||
const { templates, retrieveTemplate } = useLibrary();
|
||||
const [category, setCategory] = useState<IRSForm | undefined>(undefined);
|
||||
const [templateSchema, setTemplateSchema] = useState<IRSForm | undefined>(undefined);
|
||||
|
||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
||||
|
||||
|
@ -48,16 +48,16 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
|||
);
|
||||
|
||||
const categorySelector = useMemo((): { value: number; label: string }[] => {
|
||||
if (!category) {
|
||||
if (!templateSchema) {
|
||||
return [];
|
||||
}
|
||||
return category.items
|
||||
return templateSchema.items
|
||||
.filter(cst => cst.cst_type === CATEGORY_CST_TYPE)
|
||||
.map(cst => ({
|
||||
value: cst.id,
|
||||
label: cst.term_raw
|
||||
}));
|
||||
}, [category]);
|
||||
}, [templateSchema]);
|
||||
|
||||
useEffect(() => {
|
||||
if (templates.length > 0 && !state.templateID) {
|
||||
|
@ -67,22 +67,22 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
|||
|
||||
useEffect(() => {
|
||||
if (!state.templateID) {
|
||||
setCategory(undefined);
|
||||
setTemplateSchema(undefined);
|
||||
} else {
|
||||
retrieveTemplate(state.templateID, setCategory);
|
||||
retrieveTemplate(state.templateID, setTemplateSchema);
|
||||
}
|
||||
}, [state.templateID, retrieveTemplate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!category) {
|
||||
if (!templateSchema) {
|
||||
return;
|
||||
}
|
||||
let data = category.items;
|
||||
let data = templateSchema.items;
|
||||
if (state.filterCategory) {
|
||||
data = applyFilterCategory(state.filterCategory, category);
|
||||
data = applyFilterCategory(state.filterCategory, templateSchema);
|
||||
}
|
||||
setFilteredData(data);
|
||||
}, [state.filterCategory, category]);
|
||||
}, [state.filterCategory, templateSchema]);
|
||||
|
||||
return (
|
||||
<AnimateFade>
|
||||
|
@ -93,14 +93,16 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
|||
className='flex-grow border-none'
|
||||
options={categorySelector}
|
||||
value={
|
||||
state.filterCategory && category
|
||||
state.filterCategory && templateSchema
|
||||
? {
|
||||
value: state.filterCategory.id,
|
||||
label: state.filterCategory.term_raw
|
||||
}
|
||||
: null
|
||||
}
|
||||
onChange={data => partialUpdate({ filterCategory: data ? category?.cstByID.get(data?.value) : undefined })}
|
||||
onChange={data =>
|
||||
partialUpdate({ filterCategory: data ? templateSchema?.cstByID.get(data?.value) : undefined })
|
||||
}
|
||||
isClearable
|
||||
/>
|
||||
<SelectSingle
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||
|
||||
|
@ -12,6 +13,7 @@ import AnimateFade from '@/components/wrap/AnimateFade';
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
||||
import { generateAlias, isBaseSet, isBasicConcept, isFunctional, validateNewAlias } from '@/models/rsformAPI';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { labelCstType } from '@/utils/labels';
|
||||
import { SelectorCstType } from '@/utils/selectors';
|
||||
|
||||
|
@ -52,7 +54,11 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
|||
value={{ value: state.cst_type, label: labelCstType(state.cst_type) }}
|
||||
onChange={data => partialUpdate({ cst_type: data?.value ?? CstType.BASE })}
|
||||
/>
|
||||
<BadgeHelp topic={HelpTopic.CC_CONSTITUENTA} offset={16} className='max-w-[40rem] max-h-[calc(100svh-2rem)]' />
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.CC_CONSTITUENTA}
|
||||
offset={16}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
/>
|
||||
<TextInput
|
||||
id='dlg_cst_alias'
|
||||
dense
|
||||
|
@ -89,6 +95,7 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
|||
}
|
||||
value={state.definition_formal}
|
||||
onChange={value => partialUpdate({ definition_formal: value })}
|
||||
schema={schema}
|
||||
/>
|
||||
</AnimateFade>
|
||||
<AnimateFade key='dlg_cst_definition' hideContent={!state.definition_raw && isElementary}>
|
||||
|
|
|
@ -48,7 +48,12 @@ function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstPr
|
|||
schema={schema}
|
||||
prefix={prefixes.cst_dependant_list}
|
||||
/>
|
||||
<Checkbox label='Удалить зависимые конституенты' value={expandOut} setValue={value => setExpandOut(value)} />
|
||||
<Checkbox
|
||||
label='Удалить зависимые конституенты'
|
||||
className='my-2'
|
||||
value={expandOut}
|
||||
setValue={value => setExpandOut(value)}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import TabLabel from '@/components/ui/TabLabel';
|
|||
import { ReferenceType } from '@/models/language';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { labelReferenceType } from '@/utils/labels';
|
||||
|
||||
import EntityTab from './EntityTab';
|
||||
|
@ -69,10 +70,14 @@ function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditRefere
|
|||
hideWindow={hideWindow}
|
||||
canSubmit={isValid}
|
||||
onSubmit={handleSubmit}
|
||||
className='w-[40rem] px-6 min-h-[34rem]'
|
||||
className='w-[40rem] px-6 min-h-[35rem]'
|
||||
>
|
||||
<Overlay position='top-0 right-[4rem]'>
|
||||
<BadgeHelp topic={HelpTopic.TERM_CONTROL} className='max-w-[35rem]' offset={14} />
|
||||
<Overlay position='top-0 right-0'>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.TERM_CONTROL}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
offset={14}
|
||||
/>
|
||||
</Overlay>
|
||||
|
||||
<Tabs
|
||||
|
@ -82,15 +87,10 @@ function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditRefere
|
|||
onSelect={setActiveTab}
|
||||
>
|
||||
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none')}>
|
||||
<TabLabel
|
||||
title='Отсылка на термин в заданной словоформе'
|
||||
label={labelReferenceType(ReferenceType.ENTITY)}
|
||||
className='w-[12rem]'
|
||||
/>
|
||||
<TabLabel title='Отсылка на термин в заданной словоформе' label={labelReferenceType(ReferenceType.ENTITY)} />
|
||||
<TabLabel
|
||||
title='Установление синтаксической связи с отсылкой на термин'
|
||||
label={labelReferenceType(ReferenceType.SYNTACTIC)}
|
||||
className='w-[12rem]'
|
||||
/>
|
||||
</TabList>
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ function SelectWordForm({ selected, setSelected }: SelectWordFormProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className='text-sm'>
|
||||
<div className='text-xs sm:text-sm'>
|
||||
{DefaultWordForms.slice(0, 12).map((data, index) => (
|
||||
<WordformButton
|
||||
key={`${prefixes.wordform_list}${index}`}
|
||||
|
|
|
@ -17,7 +17,7 @@ function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...re
|
|||
tabIndex={-1}
|
||||
onClick={() => onSelectGrams(grams)}
|
||||
className={clsx(
|
||||
'min-w-[6.15rem]',
|
||||
'min-w-[4.15rem] sm:min-w-[6.15rem]',
|
||||
'p-1',
|
||||
'border rounded-none',
|
||||
'cursor-pointer',
|
||||
|
|
|
@ -16,6 +16,8 @@ import { Grammeme, ITextRequest, IWordForm, IWordFormPlain } from '@/models/lang
|
|||
import { parseGrammemes, wordFormEquals } from '@/models/languageAPI';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { IConstituenta, TermForm } from '@/models/rsform';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prompts } from '@/utils/labels';
|
||||
import { IGrammemeOption, SelectorGrammemes, SelectorGrammemesList } from '@/utils/selectors';
|
||||
|
||||
import WordFormsTable from './WordFormsTable';
|
||||
|
@ -92,7 +94,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
|||
|
||||
function handleGenerateLexeme() {
|
||||
if (forms.length > 0) {
|
||||
if (!window.confirm('Данное действие приведет к перезаписи словоформ при совпадении граммем. Продолжить?')) {
|
||||
if (!window.confirm(prompts.generateWordforms)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +132,11 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
|||
className='flex flex-col w-[40rem] px-6'
|
||||
>
|
||||
<Overlay position='top-[-0.2rem] left-[8rem]'>
|
||||
<BadgeHelp topic={HelpTopic.TERM_CONTROL} className='max-w-[38rem]' offset={3} />
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.TERM_CONTROL}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
offset={3}
|
||||
/>
|
||||
</Overlay>
|
||||
|
||||
<TextArea
|
||||
|
@ -178,33 +184,36 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
|||
/>
|
||||
</div>
|
||||
|
||||
<Overlay position='top-2 left-0'>
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Внести словоформу'
|
||||
icon={<IconAccept size='1.5rem' className='icon-green' />}
|
||||
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
|
||||
onClick={handleAddForm}
|
||||
/>
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Генерировать стандартные словоформы'
|
||||
icon={<IconMoveDown size='1.5rem' className='icon-primary' />}
|
||||
disabled={textProcessor.loading || !inputText}
|
||||
onClick={handleGenerateLexeme}
|
||||
/>
|
||||
</Overlay>
|
||||
|
||||
<div className={clsx('mt-3 mb-2', 'flex self-center items-center', 'text-sm text-center font-semibold')}>
|
||||
<span>Заданные вручную словоформы [{forms.length}]</span>
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Сбросить все словоформы'
|
||||
className='py-0'
|
||||
icon={<IconRemove size='1.5rem' className='icon-red' />}
|
||||
disabled={textProcessor.loading || forms.length === 0}
|
||||
onClick={handleResetAll}
|
||||
/>
|
||||
<div className='flex justify-between'>
|
||||
<div className='cc-icons'>
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Внести словоформу'
|
||||
icon={<IconAccept size='1.5rem' className='icon-green' />}
|
||||
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
|
||||
onClick={handleAddForm}
|
||||
/>
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Генерировать стандартные словоформы'
|
||||
icon={<IconMoveDown size='1.5rem' className='icon-primary' />}
|
||||
disabled={textProcessor.loading || !inputText}
|
||||
onClick={handleGenerateLexeme}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={clsx('mt-3 mb-2', 'w-full flex justify-center items-center', 'text-sm text-center font-semibold')}
|
||||
>
|
||||
<div>Заданные вручную словоформы [{forms.length}]</div>
|
||||
<MiniButton
|
||||
noHover
|
||||
title='Сбросить все словоформы'
|
||||
className='py-0'
|
||||
icon={<IconRemove size='1.5rem' className='icon-red' />}
|
||||
disabled={textProcessor.loading || forms.length === 0}
|
||||
onClick={handleResetAll}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WordFormsTable forms={forms} setForms={setForms} onFormSelect={handleSelectForm} />
|
||||
|
|
|
@ -7,6 +7,7 @@ import { IconRemove } from '@/components/Icons';
|
|||
import BadgeWordForm from '@/components/info/BadgeWordForm';
|
||||
import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import NoData from '@/components/ui/NoData';
|
||||
import { IWordForm } from '@/models/language';
|
||||
|
||||
interface WordFormsTableProps {
|
||||
|
@ -78,10 +79,10 @@ function WordFormsTable({ forms, setForms, onFormSelect }: WordFormsTableProps)
|
|||
columns={columns}
|
||||
headPosition='0'
|
||||
noDataComponent={
|
||||
<span className='p-2 text-center min-h-[2rem]'>
|
||||
<NoData className='min-h-[2rem]'>
|
||||
<p>Список пуст</p>
|
||||
<p>Добавьте словоформу</p>
|
||||
</span>
|
||||
</NoData>
|
||||
}
|
||||
onRowClicked={onFormSelect}
|
||||
/>
|
||||
|
|
|
@ -27,7 +27,7 @@ function DlgGraphParams({ hideWindow, initial, onConfirm }: DlgGraphParamsProps)
|
|||
header='Настройки графа термов'
|
||||
onSubmit={handleSubmit}
|
||||
submitText='Применить'
|
||||
className='flex gap-6 px-6 py-2 w-[35rem]'
|
||||
className='flex gap-6 justify-between px-6 pb-3 w-[30rem]'
|
||||
>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<h1 className='mb-2'>Преобразования</h1>
|
||||
|
@ -45,13 +45,13 @@ function DlgGraphParams({ hideWindow, initial, onConfirm }: DlgGraphParamsProps)
|
|||
/>
|
||||
<Checkbox
|
||||
label='Скрыть шаблоны'
|
||||
title='Терм-функции и предикат-функции с параметризованными аргументами'
|
||||
titleHtml='Терм-функции и предикат-функции <br/>с параметризованными аргументами'
|
||||
value={params.noTemplates}
|
||||
setValue={value => updateParams({ noTemplates: value })}
|
||||
/>
|
||||
<Checkbox
|
||||
label='Транзитивная редукция'
|
||||
title='Удалить связи, образующие транзитивные пути в графе'
|
||||
titleHtml='Удалить связи, образующие <br/>транзитивные пути в графе'
|
||||
value={params.noTransitive}
|
||||
setValue={value => updateParams({ noTransitive: value })}
|
||||
/>
|
||||
|
|
|
@ -12,6 +12,7 @@ import usePartialUpdate from '@/hooks/usePartialUpdate';
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { CstType, ICstRenameData } from '@/models/rsform';
|
||||
import { generateAlias, validateNewAlias } from '@/models/rsformAPI';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { labelCstType } from '@/utils/labels';
|
||||
import { SelectorCstType } from '@/utils/selectors';
|
||||
|
||||
|
@ -47,7 +48,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
|||
hideWindow={hideWindow}
|
||||
canSubmit={validated}
|
||||
onSubmit={handleSubmit}
|
||||
className={clsx('w-[30rem]', 'py-6 px-6 flex gap-3 justify-center items-center')}
|
||||
className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex justify-center items-center')}
|
||||
>
|
||||
<SelectSingle
|
||||
id='dlg_cst_type'
|
||||
|
@ -60,15 +61,20 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
|||
}}
|
||||
onChange={data => updateData({ cst_type: data?.value ?? CstType.BASE })}
|
||||
/>
|
||||
<BadgeHelp topic={HelpTopic.CC_CONSTITUENTA} offset={16} className='max-w-[40rem] max-h-[calc(100svh-2rem)]' />
|
||||
|
||||
<TextInput
|
||||
id='dlg_cst_alias'
|
||||
dense
|
||||
label='Имя'
|
||||
className='w-[7rem]'
|
||||
className='w-[7rem] ml-3'
|
||||
value={cstData.alias}
|
||||
onChange={event => updateData({ alias: event.target.value })}
|
||||
/>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.CC_CONSTITUENTA}
|
||||
offset={16}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { HelpTopic } from '@/models/miscellaneous';
|
|||
import { SyntaxTree } from '@/models/rslang';
|
||||
import { graphDarkT, graphLightT } from '@/styling/color';
|
||||
import { colorBgSyntaxTree } from '@/styling/color';
|
||||
import { resources } from '@/utils/constants';
|
||||
import { PARAMETER, resources } from '@/utils/constants';
|
||||
import { labelSyntaxTree } from '@/utils/labels';
|
||||
|
||||
interface DlgShowASTProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
|
@ -55,7 +55,7 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
|||
return (
|
||||
<Modal readonly hideWindow={hideWindow} className='px-6'>
|
||||
<Overlay position='left-[-1rem] top-[0.25rem]'>
|
||||
<BadgeHelp topic={HelpTopic.UI_FORMULA_TREE} className='max-w-[32rem]' />
|
||||
<BadgeHelp topic={HelpTopic.UI_FORMULA_TREE} className={PARAMETER.TOOLTIP_WIDTH} />
|
||||
</Overlay>
|
||||
<div className='my-2 text-lg text-center'>
|
||||
{!hoverNode ? expression : null}
|
||||
|
|
47
rsconcept/frontend/src/models/FolderTree.test.ts
Normal file
47
rsconcept/frontend/src/models/FolderTree.test.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { FolderTree } from './FolderTree';
|
||||
|
||||
// TODO: test FolderNode and FolderTree exhaustively
|
||||
|
||||
describe('Testing Tree construction', () => {
|
||||
test('empty Tree should be empty', () => {
|
||||
const tree = new FolderTree();
|
||||
expect(tree.roots.size).toBe(0);
|
||||
});
|
||||
|
||||
test('constructing from paths', () => {
|
||||
const tree = new FolderTree(['/S', '/S/project1/123', '/U']);
|
||||
expect(tree.roots.size).toBe(2);
|
||||
expect(tree.roots.get('S')?.children.size).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Testing Tree editing', () => {
|
||||
test('add invalid path', () => {
|
||||
const tree = new FolderTree();
|
||||
expect(() => tree.addPath('invalid')).toThrow(Error);
|
||||
});
|
||||
|
||||
test('add valid path', () => {
|
||||
const tree = new FolderTree();
|
||||
const node = tree.addPath('/S/test');
|
||||
expect(node.getPath()).toBe('/S/test');
|
||||
expect(node.filesInside).toBe(1);
|
||||
expect(node.filesTotal).toBe(1);
|
||||
|
||||
expect(node.parent?.getPath()).toBe('/S');
|
||||
expect(node.parent?.filesInside).toBe(0);
|
||||
expect(node.parent?.filesTotal).toBe(1);
|
||||
});
|
||||
|
||||
test('incrementing counter', () => {
|
||||
const tree = new FolderTree();
|
||||
const node1 = tree.addPath('/S/test', 0);
|
||||
expect(node1.filesInside).toBe(0);
|
||||
expect(node1.filesTotal).toBe(0);
|
||||
|
||||
const node2 = tree.addPath('/S/test', 2);
|
||||
expect(node1).toBe(node2);
|
||||
expect(node2.filesInside).toBe(2);
|
||||
expect(node2.filesTotal).toBe(2);
|
||||
});
|
||||
});
|
159
rsconcept/frontend/src/models/FolderTree.ts
Normal file
159
rsconcept/frontend/src/models/FolderTree.ts
Normal file
|
@ -0,0 +1,159 @@
|
|||
/**
|
||||
* Module: Folder tree data structure. Does not support deletions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents single node of a {@link FolderTree}.
|
||||
*/
|
||||
export class FolderNode {
|
||||
rank: number = 0;
|
||||
text: string;
|
||||
children: Map<string, FolderNode>;
|
||||
parent: FolderNode | undefined;
|
||||
|
||||
filesInside: number = 0;
|
||||
filesTotal: number = 0;
|
||||
|
||||
constructor(text: string, parent?: FolderNode) {
|
||||
this.text = text;
|
||||
this.parent = parent;
|
||||
this.children = new Map();
|
||||
if (parent) {
|
||||
this.rank = parent.rank + 1;
|
||||
}
|
||||
}
|
||||
|
||||
addChild(text: string): FolderNode {
|
||||
const node = new FolderNode(text, this);
|
||||
this.children.set(text, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
hasPredecessor(target: FolderNode): boolean {
|
||||
if (this.parent === target) {
|
||||
return true;
|
||||
} else if (!this.parent) {
|
||||
return false;
|
||||
}
|
||||
let node = this.parent;
|
||||
while (node.parent) {
|
||||
if (node.parent === target) {
|
||||
return true;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
incrementFiles(count: number = 1): void {
|
||||
this.filesInside = this.filesInside + count;
|
||||
this.incrementTotal(count);
|
||||
}
|
||||
|
||||
incrementTotal(count: number = 1): void {
|
||||
this.filesTotal = this.filesTotal + count;
|
||||
if (this.parent) {
|
||||
this.parent.incrementTotal(count);
|
||||
}
|
||||
}
|
||||
|
||||
getPath(): string {
|
||||
const suffix = this.text ? `/${this.text}` : '';
|
||||
if (!this.parent) {
|
||||
return suffix;
|
||||
} else {
|
||||
return this.parent.getPath() + suffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a FolderTree.
|
||||
*
|
||||
*/
|
||||
export class FolderTree {
|
||||
roots: Map<string, FolderNode> = new Map();
|
||||
|
||||
constructor(arr?: string[]) {
|
||||
arr?.forEach(path => this.addPath(path));
|
||||
}
|
||||
|
||||
at(path: string): FolderNode | undefined {
|
||||
let parse = ChopPathHead(path);
|
||||
if (!this.roots.has(parse.head)) {
|
||||
return undefined;
|
||||
}
|
||||
let node = this.roots.get(parse.head)!;
|
||||
while (parse.tail !== '') {
|
||||
parse = ChopPathHead(parse.tail);
|
||||
if (!node.children.has(parse.head)) {
|
||||
return undefined;
|
||||
}
|
||||
node = node.children.get(parse.head)!;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
getTree(): FolderNode[] {
|
||||
const result: FolderNode[] = [];
|
||||
this.roots.forEach(root => this.visitNode(root, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
private visitNode(target: FolderNode, result: FolderNode[]) {
|
||||
result.push(target);
|
||||
[...target.children.keys()]
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.forEach(key => this.visitNode(target.children.get(key)!, result));
|
||||
}
|
||||
|
||||
addPath(path: string, filesCount: number = 1): FolderNode {
|
||||
let parse = ChopPathHead(path);
|
||||
if (!parse.head) {
|
||||
throw Error(`Invalid path ${path}`);
|
||||
}
|
||||
let node = this.roots.has(parse.head) ? this.roots.get(parse.head)! : this.addNode(parse.head);
|
||||
while (parse.tail !== '') {
|
||||
parse = ChopPathHead(parse.tail);
|
||||
if (node.children.has(parse.head)) {
|
||||
node = node.children.get(parse.head)!;
|
||||
} else {
|
||||
node = this.addNode(parse.head, node);
|
||||
}
|
||||
}
|
||||
node.incrementFiles(filesCount);
|
||||
return node;
|
||||
}
|
||||
|
||||
private addNode(text: string, parent?: FolderNode): FolderNode {
|
||||
if (parent === undefined) {
|
||||
const newNode = new FolderNode(text);
|
||||
this.roots.set(text, newNode);
|
||||
return newNode;
|
||||
} else {
|
||||
return parent.addChild(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========= Internals =======
|
||||
function ChopPathHead(path: string) {
|
||||
if (!path || path.at(0) !== '/') {
|
||||
return {
|
||||
head: '',
|
||||
tail: ''
|
||||
};
|
||||
}
|
||||
const slash = path.indexOf('/', 1);
|
||||
if (slash === -1) {
|
||||
return {
|
||||
head: path.substring(1),
|
||||
tail: ''
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
head: path.substring(1, slash),
|
||||
tail: path.substring(slash)
|
||||
};
|
||||
}
|
||||
}
|
|
@ -26,9 +26,9 @@ export enum AccessPolicy {
|
|||
*/
|
||||
export enum LocationHead {
|
||||
USER = '/U',
|
||||
LIBRARY = '/L',
|
||||
COMMON = '/S',
|
||||
PROJECTS = '/P'
|
||||
PROJECTS = '/P',
|
||||
LIBRARY = '/L'
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Module: Miscellaneous frontend model types. Future targets for refactoring aimed at extracting modules.
|
||||
*/
|
||||
|
||||
import { LocationHead } from './library';
|
||||
import { LibraryItemType, LocationHead } from './library';
|
||||
|
||||
/**
|
||||
* Represents graph dependency mode.
|
||||
|
@ -37,11 +37,6 @@ export type FontStyle = 'controls' | 'main' | 'math' | 'math2';
|
|||
export enum HelpTopic {
|
||||
MAIN = 'main',
|
||||
|
||||
DOCS = 'documentation',
|
||||
RULES = 'rules',
|
||||
PRIVACY = 'privacy',
|
||||
API = 'api',
|
||||
|
||||
INTERFACE = 'user-interface',
|
||||
UI_LIBRARY = 'ui-library',
|
||||
UI_RS_MENU = 'ui-rsform-menu',
|
||||
|
@ -52,12 +47,14 @@ export enum HelpTopic {
|
|||
UI_FORMULA_TREE = 'ui-formula-tree',
|
||||
UI_CST_STATUS = 'ui-rsform-cst-status',
|
||||
UI_CST_CLASS = 'ui-rsform-cst-class',
|
||||
UI_OSS_GRAPH = 'ui-oss-graph',
|
||||
|
||||
CONCEPTUAL = 'concept',
|
||||
CC_SYSTEM = 'rslang-rsform',
|
||||
CC_CONSTITUENTA = 'rslang-cst',
|
||||
CC_RELATIONS = 'rslang-relations',
|
||||
CC_SYNTHESIS = 'rslang-synthesis',
|
||||
CC_SYSTEM = 'concept-rsform',
|
||||
CC_CONSTITUENTA = 'concept-constituenta',
|
||||
CC_RELATIONS = 'concept-relations',
|
||||
CC_SYNTHESIS = 'concept-synthesis',
|
||||
CC_OSS = 'concept-operations-schema',
|
||||
|
||||
RSLANG = 'rslang',
|
||||
RSL_TYPES = 'rslang-types',
|
||||
|
@ -69,6 +66,13 @@ export enum HelpTopic {
|
|||
TERM_CONTROL = 'terminology-control',
|
||||
ACCESS = 'access',
|
||||
VERSIONS = 'versions',
|
||||
|
||||
INFO = 'documentation',
|
||||
INFO_RULES = 'rules',
|
||||
INFO_CONTRIB = 'contributors',
|
||||
INFO_PRIVACY = 'privacy',
|
||||
INFO_API = 'api',
|
||||
|
||||
EXTEOR = 'exteor'
|
||||
}
|
||||
|
||||
|
@ -78,11 +82,6 @@ export enum HelpTopic {
|
|||
export const topicParent: Map<HelpTopic, HelpTopic> = new Map([
|
||||
[HelpTopic.MAIN, HelpTopic.MAIN],
|
||||
|
||||
[HelpTopic.DOCS, HelpTopic.DOCS],
|
||||
[HelpTopic.RULES, HelpTopic.DOCS],
|
||||
[HelpTopic.API, HelpTopic.DOCS],
|
||||
[HelpTopic.PRIVACY, HelpTopic.DOCS],
|
||||
|
||||
[HelpTopic.INTERFACE, HelpTopic.INTERFACE],
|
||||
[HelpTopic.UI_LIBRARY, HelpTopic.INTERFACE],
|
||||
[HelpTopic.UI_RS_MENU, HelpTopic.INTERFACE],
|
||||
|
@ -93,12 +92,14 @@ export const topicParent: Map<HelpTopic, HelpTopic> = new Map([
|
|||
[HelpTopic.UI_FORMULA_TREE, HelpTopic.INTERFACE],
|
||||
[HelpTopic.UI_CST_STATUS, HelpTopic.INTERFACE],
|
||||
[HelpTopic.UI_CST_CLASS, HelpTopic.INTERFACE],
|
||||
[HelpTopic.UI_OSS_GRAPH, HelpTopic.INTERFACE],
|
||||
|
||||
[HelpTopic.CONCEPTUAL, HelpTopic.CONCEPTUAL],
|
||||
[HelpTopic.CC_SYSTEM, HelpTopic.CONCEPTUAL],
|
||||
[HelpTopic.CC_CONSTITUENTA, HelpTopic.CONCEPTUAL],
|
||||
[HelpTopic.CC_RELATIONS, HelpTopic.CONCEPTUAL],
|
||||
[HelpTopic.CC_SYNTHESIS, HelpTopic.CONCEPTUAL],
|
||||
[HelpTopic.CC_OSS, HelpTopic.CONCEPTUAL],
|
||||
|
||||
[HelpTopic.RSLANG, HelpTopic.RSLANG],
|
||||
[HelpTopic.RSL_TYPES, HelpTopic.RSLANG],
|
||||
|
@ -110,13 +111,20 @@ export const topicParent: Map<HelpTopic, HelpTopic> = new Map([
|
|||
[HelpTopic.TERM_CONTROL, HelpTopic.TERM_CONTROL],
|
||||
[HelpTopic.ACCESS, HelpTopic.ACCESS],
|
||||
[HelpTopic.VERSIONS, HelpTopic.VERSIONS],
|
||||
|
||||
[HelpTopic.INFO, HelpTopic.INFO],
|
||||
[HelpTopic.INFO_RULES, HelpTopic.INFO],
|
||||
[HelpTopic.INFO_CONTRIB, HelpTopic.INFO],
|
||||
[HelpTopic.INFO_PRIVACY, HelpTopic.INFO],
|
||||
[HelpTopic.INFO_API, HelpTopic.INFO],
|
||||
|
||||
[HelpTopic.EXTEOR, HelpTopic.EXTEOR]
|
||||
]);
|
||||
|
||||
/**
|
||||
* Topics that can be folded.
|
||||
*/
|
||||
export const foldableTopics = [HelpTopic.INTERFACE, HelpTopic.RSLANG, HelpTopic.CONCEPTUAL, HelpTopic.DOCS];
|
||||
export const foldableTopics = [HelpTopic.INTERFACE, HelpTopic.RSLANG, HelpTopic.CONCEPTUAL, HelpTopic.INFO];
|
||||
|
||||
/**
|
||||
* Represents {@link IConstituenta} matching mode.
|
||||
|
@ -133,10 +141,15 @@ export enum CstMatchMode {
|
|||
* Represents Library filter parameters.
|
||||
*/
|
||||
export interface ILibraryFilter {
|
||||
type?: LibraryItemType;
|
||||
query?: string;
|
||||
|
||||
path?: string;
|
||||
head?: LocationHead;
|
||||
|
||||
folderMode?: boolean;
|
||||
folder?: string;
|
||||
|
||||
isVisible?: boolean;
|
||||
isOwned?: boolean;
|
||||
isSubscribed?: boolean;
|
||||
|
|
|
@ -65,6 +65,3 @@ export interface IRunSynthesisResponse {
|
|||
export interface IOperationSchema extends IOperationSchemaData {
|
||||
subscribers: UserID[];
|
||||
editors: UserID[];
|
||||
|
||||
//producedData: number[]; // TODO: modify this to store calculated state on load
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
|
@ -10,6 +10,7 @@ import { IconDownload } from '@/components/Icons';
|
|||
import InfoError from '@/components/info/InfoError';
|
||||
import SelectAccessPolicy from '@/components/select/SelectAccessPolicy';
|
||||
import SelectItemType from '@/components/select/SelectItemType';
|
||||
import SelectLocation from '@/components/select/SelectLocation';
|
||||
import SelectLocationHead from '@/components/select/SelectLocationHead';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Label from '@/components/ui/Label';
|
||||
|
@ -25,11 +26,12 @@ import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library';
|
|||
import { ILibraryCreateData } from '@/models/library';
|
||||
import { combineLocation, validateLocation } from '@/models/libraryAPI';
|
||||
import { EXTEOR_TRS_FILE, limits, patterns } from '@/utils/constants';
|
||||
import { information } from '@/utils/labels';
|
||||
|
||||
function FormCreateItem() {
|
||||
const router = useConceptNavigation();
|
||||
const { user } = useAuth();
|
||||
const { createItem, error, setError, processing } = useLibrary();
|
||||
const { createItem, processingError, setProcessingError, processing, folders } = useLibrary();
|
||||
|
||||
const [itemType, setItemType] = useState(LibraryItemType.RSFORM);
|
||||
const [title, setTitle] = useState('');
|
||||
|
@ -49,8 +51,8 @@ function FormCreateItem() {
|
|||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setError(undefined);
|
||||
}, [title, alias, setError]);
|
||||
setProcessingError(undefined);
|
||||
}, [title, alias, setProcessingError]);
|
||||
|
||||
function handleCancel() {
|
||||
if (router.canBack()) {
|
||||
|
@ -78,7 +80,7 @@ function FormCreateItem() {
|
|||
fileName: file?.name
|
||||
};
|
||||
createItem(data, newItem => {
|
||||
toast.success('Схема успешно создана');
|
||||
toast.success(information.newLibraryItem);
|
||||
if (itemType == LibraryItemType.RSFORM) {
|
||||
router.push(urls.schema(newItem.id));
|
||||
} else {
|
||||
|
@ -97,6 +99,11 @@ function FormCreateItem() {
|
|||
}
|
||||
}
|
||||
|
||||
const handleSelectLocation = useCallback((newValue: string) => {
|
||||
setHead(newValue.substring(0, 2) as LocationHead);
|
||||
setBody(newValue.length > 3 ? newValue.substring(3) : '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form className={clsx('cc-column', 'min-w-[30rem] max-w-[30rem] mx-auto', 'px-6 py-3')} onSubmit={handleSubmit}>
|
||||
<h1>
|
||||
|
@ -153,7 +160,6 @@ function FormCreateItem() {
|
|||
<div className='ml-auto cc-icons'>
|
||||
<SelectAccessPolicy value={policy} onChange={setPolicy} />
|
||||
<MiniButton
|
||||
className='disabled:cursor-auto'
|
||||
title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
|
||||
icon={<VisibilityIcon value={visible} />}
|
||||
onClick={() => setVisible(prev => !prev)}
|
||||
|
@ -179,6 +185,11 @@ function FormCreateItem() {
|
|||
excluded={!user?.is_staff ? [LocationHead.LIBRARY] : []}
|
||||
/>
|
||||
</div>
|
||||
{user?.is_staff ? (
|
||||
<div className='self-start mt-[-0.25rem] ml-[-1.5rem]'>
|
||||
<SelectLocation folderTree={folders} value={location} onChange={handleSelectLocation} />
|
||||
</div>
|
||||
) : null}
|
||||
<TextArea
|
||||
id='dlg_cst_body'
|
||||
label='Путь'
|
||||
|
@ -193,7 +204,7 @@ function FormCreateItem() {
|
|||
<SubmitButton text='Создать схему' loading={processing} className='min-w-[10rem]' disabled={!isValid} />
|
||||
<Button text='Отмена' className='min-w-[10rem]' onClick={() => handleCancel()} />
|
||||
</div>
|
||||
{error ? <InfoError error={error} /> : null}
|
||||
{processingError ? <InfoError error={processingError} /> : null}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,27 +1,30 @@
|
|||
import { useLayoutEffect } from 'react';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
function HomePage() {
|
||||
const router = useConceptNavigation();
|
||||
const { user } = useAuth();
|
||||
const { user, loading } = useAuth();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!user) {
|
||||
setTimeout(() => {
|
||||
router.push(urls.manuals);
|
||||
}, PARAMETER.refreshTimeout);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
router.push(urls.library);
|
||||
}, PARAMETER.refreshTimeout);
|
||||
if (!loading) {
|
||||
if (!user) {
|
||||
setTimeout(() => {
|
||||
router.push(urls.manuals);
|
||||
}, PARAMETER.refreshTimeout);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
router.push(urls.library);
|
||||
}, PARAMETER.refreshTimeout);
|
||||
}
|
||||
}
|
||||
}, [router, user]);
|
||||
}, [router, user, loading]);
|
||||
|
||||
return <div />;
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
export default HomePage;
|
||||
|
|
156
rsconcept/frontend/src/pages/LibraryPage/LibraryFolders.tsx
Normal file
156
rsconcept/frontend/src/pages/LibraryPage/LibraryFolders.tsx
Normal file
|
@ -0,0 +1,156 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened, IconFolderTree } from '@/components/Icons';
|
||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||
import { CProps } from '@/components/props';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { FolderNode, FolderTree } from '@/models/FolderTree';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { animateSideView } from '@/styling/animations';
|
||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||
import { information, labelFolderNode } from '@/utils/labels';
|
||||
|
||||
interface LibraryTableProps {
|
||||
folders: FolderTree;
|
||||
currentFolder: string;
|
||||
setFolder: React.Dispatch<React.SetStateAction<string>>;
|
||||
toggleFolderMode: () => void;
|
||||
}
|
||||
|
||||
function LibraryFolders({ folders, currentFolder, setFolder, toggleFolderMode }: LibraryTableProps) {
|
||||
const activeNode = useMemo(() => folders.at(currentFolder), [folders, currentFolder]);
|
||||
|
||||
const items = useMemo(() => folders.getTree(), [folders]);
|
||||
const [folded, setFolded] = useState<FolderNode[]>(items);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setFolded(items.filter(item => item !== activeNode && (!activeNode || !activeNode.hasPredecessor(item))));
|
||||
}, [items, activeNode]);
|
||||
|
||||
const onFoldItem = useCallback(
|
||||
(target: FolderNode, showChildren: boolean) => {
|
||||
setFolded(prev =>
|
||||
items.filter(item => {
|
||||
if (item === target) {
|
||||
return !showChildren;
|
||||
}
|
||||
if (!showChildren && item.hasPredecessor(target)) {
|
||||
return true;
|
||||
} else {
|
||||
return prev.includes(item);
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
[items]
|
||||
);
|
||||
|
||||
const handleClickFolder = useCallback(
|
||||
(event: CProps.EventMouse, target: FolderNode) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
navigator.clipboard
|
||||
.writeText(target.getPath())
|
||||
.then(() => toast.success(information.pathReady))
|
||||
.catch(console.error);
|
||||
} else {
|
||||
setFolder(target.getPath());
|
||||
}
|
||||
},
|
||||
[setFolder]
|
||||
);
|
||||
|
||||
const handleClickFold = useCallback(
|
||||
(event: CProps.EventMouse, target: FolderNode, showChildren: boolean) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onFoldItem(target, showChildren);
|
||||
},
|
||||
[onFoldItem]
|
||||
);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className='flex flex-col select-none text:xs sm:text-sm'
|
||||
initial={{ ...animateSideView.initial }}
|
||||
animate={{ ...animateSideView.animate }}
|
||||
exit={{ ...animateSideView.exit }}
|
||||
>
|
||||
<div className='h-[2.08rem] flex justify-between items-center pr-1'>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.UI_LIBRARY}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'text-sm')}
|
||||
offset={5}
|
||||
place='right-start'
|
||||
/>
|
||||
<MiniButton
|
||||
icon={<IconFolderTree size='1.25rem' className='icon-green' />}
|
||||
title='Переключение в режим Поиск'
|
||||
onClick={toggleFolderMode}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
'max-w-[10rem] sm:max-w-[15rem] min-w-[10rem] sm:min-w-[15rem]',
|
||||
'flex flex-col',
|
||||
'cc-scroll-y'
|
||||
)}
|
||||
>
|
||||
{items.map((item, index) =>
|
||||
!item.parent || !folded.includes(item.parent) ? (
|
||||
<div
|
||||
tabIndex={-1}
|
||||
key={`${prefixes.folders_list}${index}`}
|
||||
className={clsx(
|
||||
'min-h-[2.0825rem] sm:min-h-[2.3125rem]',
|
||||
'pr-3 flex items-center gap-2',
|
||||
'cc-scroll-row',
|
||||
'clr-hover',
|
||||
'cursor-pointer',
|
||||
activeNode === item && 'clr-selected'
|
||||
)}
|
||||
style={{ paddingLeft: `${(item.rank > 5 ? 5 : item.rank) * 0.5 + 0.5}rem` }}
|
||||
onClick={event => handleClickFolder(event, item)}
|
||||
>
|
||||
{item.children.size > 0 ? (
|
||||
<MiniButton
|
||||
noPadding
|
||||
noHover
|
||||
icon={
|
||||
folded.includes(item) ? (
|
||||
item.filesInside ? (
|
||||
<IconFolderClosed size='1rem' className='icon-primary' />
|
||||
) : (
|
||||
<IconFolderEmpty size='1rem' className='icon-primary' />
|
||||
)
|
||||
) : (
|
||||
<IconFolderOpened size='1rem' className='icon-green' />
|
||||
)
|
||||
}
|
||||
onClick={event => handleClickFold(event, item, folded.includes(item))}
|
||||
/>
|
||||
) : (
|
||||
<div>
|
||||
{item.filesInside ? (
|
||||
<IconFolder size='1rem' className='clr-text-default' />
|
||||
) : (
|
||||
<IconFolderEmpty size='1rem' className='clr-text-controls' />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className='self-center'>{labelFolderNode(item)}</div>
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LibraryFolders;
|
|
@ -1,8 +1,10 @@
|
|||
'use client';
|
||||
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
|
||||
import DataLoader from '@/components/wrap/DataLoader';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||
import { ILibraryItem, LocationHead } from '@/models/library';
|
||||
|
@ -10,17 +12,21 @@ import { ILibraryFilter } from '@/models/miscellaneous';
|
|||
import { storage } from '@/utils/constants';
|
||||
import { toggleTristateFlag } from '@/utils/utils';
|
||||
|
||||
import LibraryFolders from './LibraryFolders';
|
||||
import LibraryTable from './LibraryTable';
|
||||
import SearchPanel from './SearchPanel';
|
||||
|
||||
function LibraryPage() {
|
||||
const library = useLibrary();
|
||||
const { user } = useAuth();
|
||||
const [items, setItems] = useState<ILibraryItem[]>([]);
|
||||
|
||||
const [query, setQuery] = useState('');
|
||||
const [path, setPath] = useState('');
|
||||
|
||||
const [head, setHead] = useLocalStorage<LocationHead | undefined>(storage.librarySearchHead, undefined);
|
||||
const [folderMode, setFolderMode] = useLocalStorage<boolean>(storage.librarySearchFolderMode, true);
|
||||
const [folder, setFolder] = useLocalStorage<string>(storage.librarySearchFolder, '');
|
||||
const [isVisible, setIsVisible] = useLocalStorage<boolean | undefined>(storage.librarySearchVisible, true);
|
||||
const [isSubscribed, setIsSubscribed] = useLocalStorage<boolean | undefined>(
|
||||
storage.librarySearchSubscribed,
|
||||
|
@ -34,12 +40,27 @@ function LibraryPage() {
|
|||
head: head,
|
||||
path: path,
|
||||
query: query,
|
||||
isEditor: isEditor,
|
||||
isOwned: isOwned,
|
||||
isSubscribed: isSubscribed,
|
||||
isVisible: isVisible
|
||||
isEditor: user ? isEditor : undefined,
|
||||
isOwned: user ? isOwned : undefined,
|
||||
isSubscribed: user ? isSubscribed : undefined,
|
||||
isVisible: user ? isVisible : true,
|
||||
folderMode: folderMode,
|
||||
folder: folder
|
||||
}),
|
||||
[head, path, query, isEditor, isOwned, isSubscribed, isVisible]
|
||||
[head, path, query, isEditor, isOwned, isSubscribed, isVisible, user, folderMode, folder]
|
||||
);
|
||||
|
||||
const hasCustomFilter = useMemo(
|
||||
() =>
|
||||
!!filter.path ||
|
||||
!!filter.query ||
|
||||
filter.head !== undefined ||
|
||||
filter.isEditor !== undefined ||
|
||||
filter.isOwned !== undefined ||
|
||||
filter.isSubscribed !== undefined ||
|
||||
filter.isVisible !== true ||
|
||||
!!filter.folder,
|
||||
[filter]
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
@ -50,6 +71,7 @@ function LibraryPage() {
|
|||
const toggleOwned = useCallback(() => setIsOwned(prev => toggleTristateFlag(prev)), [setIsOwned]);
|
||||
const toggleSubscribed = useCallback(() => setIsSubscribed(prev => toggleTristateFlag(prev)), [setIsSubscribed]);
|
||||
const toggleEditor = useCallback(() => setIsEditor(prev => toggleTristateFlag(prev)), [setIsEditor]);
|
||||
const toggleFolderMode = useCallback(() => setFolderMode(prev => !prev), [setFolderMode]);
|
||||
|
||||
const resetFilter = useCallback(() => {
|
||||
setQuery('');
|
||||
|
@ -59,34 +81,38 @@ function LibraryPage() {
|
|||
setIsSubscribed(undefined);
|
||||
setIsOwned(undefined);
|
||||
setIsEditor(undefined);
|
||||
}, [setHead, setIsVisible, setIsSubscribed, setIsOwned, setIsEditor]);
|
||||
setFolder('');
|
||||
}, [setHead, setIsVisible, setIsSubscribed, setIsOwned, setIsEditor, setFolder]);
|
||||
|
||||
const view = useMemo(
|
||||
() => (
|
||||
<LibraryTable
|
||||
resetQuery={resetFilter} // prettier: split lines
|
||||
items={items}
|
||||
folderMode={folderMode}
|
||||
toggleFolderMode={toggleFolderMode}
|
||||
/>
|
||||
),
|
||||
[resetFilter, items]
|
||||
[resetFilter, items, folderMode, toggleFolderMode]
|
||||
);
|
||||
|
||||
return (
|
||||
<DataLoader
|
||||
id='library-page' // prettier: split lines
|
||||
isLoading={library.loading}
|
||||
error={library.error}
|
||||
error={library.loadingError}
|
||||
hasNoData={library.items.length === 0}
|
||||
>
|
||||
<SearchPanel
|
||||
total={library.items.length ?? 0}
|
||||
filtered={items.length}
|
||||
hasCustomFilter={hasCustomFilter}
|
||||
query={query}
|
||||
setQuery={setQuery}
|
||||
path={path}
|
||||
setPath={setPath}
|
||||
head={head}
|
||||
setHead={setHead}
|
||||
total={library.items.length ?? 0}
|
||||
filtered={items.length}
|
||||
isVisible={isVisible}
|
||||
isOwned={isOwned}
|
||||
toggleOwned={toggleOwned}
|
||||
|
@ -95,8 +121,24 @@ function LibraryPage() {
|
|||
toggleSubscribed={toggleSubscribed}
|
||||
isEditor={isEditor}
|
||||
toggleEditor={toggleEditor}
|
||||
resetFilter={resetFilter}
|
||||
folderMode={folderMode}
|
||||
toggleFolderMode={toggleFolderMode}
|
||||
/>
|
||||
{view}
|
||||
|
||||
<div className='flex'>
|
||||
<AnimatePresence initial={false}>
|
||||
{folderMode ? (
|
||||
<LibraryFolders
|
||||
currentFolder={folder} // prettier: split-lines
|
||||
setFolder={setFolder}
|
||||
folders={library.folders}
|
||||
toggleFolderMode={toggleFolderMode}
|
||||
/>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
{view}
|
||||
</div>
|
||||
</DataLoader>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
'use client';
|
||||
|
||||
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
import { IconFolder } from '@/components/Icons';
|
||||
import { IconFolderTree } from '@/components/Icons';
|
||||
import BadgeLocation from '@/components/info/BadgeLocation';
|
||||
import { CProps } from '@/components/props';
|
||||
import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '@/components/ui/DataTable';
|
||||
import FlexColumn from '@/components/ui/FlexColumn';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useConceptOptions } from '@/context/OptionsContext';
|
||||
|
@ -21,11 +23,13 @@ import { storage } from '@/utils/constants';
|
|||
interface LibraryTableProps {
|
||||
items: ILibraryItem[];
|
||||
resetQuery: () => void;
|
||||
folderMode: boolean;
|
||||
toggleFolderMode: () => void;
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<ILibraryItem>();
|
||||
|
||||
function LibraryTable({ items, resetQuery }: LibraryTableProps) {
|
||||
function LibraryTable({ items, resetQuery, folderMode, toggleFolderMode }: LibraryTableProps) {
|
||||
const router = useConceptNavigation();
|
||||
const intl = useIntl();
|
||||
const { getUserLabel } = useUsers();
|
||||
|
@ -50,22 +54,40 @@ function LibraryTable({ items, resetQuery }: LibraryTableProps) {
|
|||
});
|
||||
}, [windowSize]);
|
||||
|
||||
const handleToggleFolder = useCallback(
|
||||
(event: CProps.EventMouse) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleFolderMode();
|
||||
},
|
||||
[toggleFolderMode]
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
columnHelper.accessor('location', {
|
||||
id: 'location',
|
||||
header: () => (
|
||||
<div className='pl-2 max-h-[1rem] translate-y-[-0.125rem]'>
|
||||
<IconFolder size='1.25rem' className='clr-text-controls' />
|
||||
</div>
|
||||
),
|
||||
size: 50,
|
||||
minSize: 50,
|
||||
maxSize: 50,
|
||||
enableSorting: true,
|
||||
cell: props => <BadgeLocation location={props.getValue()} />,
|
||||
sortingFn: 'text'
|
||||
}),
|
||||
...(folderMode
|
||||
? []
|
||||
: [
|
||||
columnHelper.accessor('location', {
|
||||
id: 'location',
|
||||
header: () => (
|
||||
<MiniButton
|
||||
noPadding
|
||||
noHover
|
||||
className='pl-2 max-h-[1rem] translate-y-[-0.125rem]'
|
||||
onClick={handleToggleFolder}
|
||||
titleHtml='Переключение в режим Проводник'
|
||||
icon={<IconFolderTree size='1.25rem' className='clr-text-controls' />}
|
||||
/>
|
||||
),
|
||||
size: 50,
|
||||
minSize: 50,
|
||||
maxSize: 50,
|
||||
enableSorting: true,
|
||||
cell: props => <BadgeLocation location={props.getValue()} />,
|
||||
sortingFn: 'text'
|
||||
})
|
||||
]),
|
||||
columnHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
header: 'Шифр',
|
||||
|
@ -116,7 +138,7 @@ function LibraryTable({ items, resetQuery }: LibraryTableProps) {
|
|||
sortDescFirst: true
|
||||
})
|
||||
],
|
||||
[intl, getUserLabel, windowSize]
|
||||
[intl, getUserLabel, windowSize, handleToggleFolder, folderMode]
|
||||
);
|
||||
|
||||
const tableHeight = useMemo(() => calculateHeight('2.2rem'), [calculateHeight]);
|
||||
|
@ -139,10 +161,10 @@ function LibraryTable({ items, resetQuery }: LibraryTableProps) {
|
|||
columns={columns}
|
||||
data={items}
|
||||
headPosition='0'
|
||||
className='text-xs sm:text-sm cc-scroll-y'
|
||||
className={clsx('text-xs sm:text-sm cc-scroll-y', { 'border-l border-b': folderMode })}
|
||||
style={{ maxHeight: tableHeight }}
|
||||
noDataComponent={
|
||||
<FlexColumn className='p-3 items-center min-h-[6rem]'>
|
||||
<FlexColumn className='dense p-3 items-center min-h-[6rem]'>
|
||||
<p>Список схем пуст</p>
|
||||
<p className='flex gap-6'>
|
||||
<TextURL text='Создать схему' href='/library/create' />
|
||||
|
|
|
@ -4,16 +4,16 @@ import clsx from 'clsx';
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { LocationIcon, SubscribeIcon, VisibilityIcon } from '@/components/DomainIcons';
|
||||
import { IconEditor, IconFolder, IconOwner } from '@/components/Icons';
|
||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||
import { IconEditor, IconFilterReset, IconFolder, IconFolderTree, IconOwner } from '@/components/Icons';
|
||||
import { CProps } from '@/components/props';
|
||||
import Dropdown from '@/components/ui/Dropdown';
|
||||
import DropdownButton from '@/components/ui/DropdownButton';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import SearchBar from '@/components/ui/SearchBar';
|
||||
import SelectorButton from '@/components/ui/SelectorButton';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import useDropdown from '@/hooks/useDropdown';
|
||||
import { LocationHead } from '@/models/library';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
import { describeLocationHead, labelLocationHead } from '@/utils/labels';
|
||||
import { tripleToggleColor } from '@/utils/utils';
|
||||
|
@ -21,6 +21,7 @@ import { tripleToggleColor } from '@/utils/utils';
|
|||
interface SearchPanelProps {
|
||||
total: number;
|
||||
filtered: number;
|
||||
hasCustomFilter: boolean;
|
||||
|
||||
query: string;
|
||||
setQuery: React.Dispatch<React.SetStateAction<string>>;
|
||||
|
@ -29,6 +30,9 @@ interface SearchPanelProps {
|
|||
head: LocationHead | undefined;
|
||||
setHead: React.Dispatch<React.SetStateAction<LocationHead | undefined>>;
|
||||
|
||||
folderMode: boolean;
|
||||
toggleFolderMode: () => void;
|
||||
|
||||
isVisible: boolean | undefined;
|
||||
toggleVisible: () => void;
|
||||
isOwned: boolean | undefined;
|
||||
|
@ -37,11 +41,14 @@ interface SearchPanelProps {
|
|||
toggleSubscribed: () => void;
|
||||
isEditor: boolean | undefined;
|
||||
toggleEditor: () => void;
|
||||
resetFilter: () => void;
|
||||
}
|
||||
|
||||
function SearchPanel({
|
||||
total,
|
||||
filtered,
|
||||
hasCustomFilter,
|
||||
|
||||
query,
|
||||
setQuery,
|
||||
path,
|
||||
|
@ -49,6 +56,9 @@ function SearchPanel({
|
|||
head,
|
||||
setHead,
|
||||
|
||||
folderMode,
|
||||
toggleFolderMode,
|
||||
|
||||
isVisible,
|
||||
toggleVisible,
|
||||
isOwned,
|
||||
|
@ -56,8 +66,10 @@ function SearchPanel({
|
|||
isSubscribed,
|
||||
toggleSubscribed,
|
||||
isEditor,
|
||||
toggleEditor
|
||||
toggleEditor,
|
||||
resetFilter
|
||||
}: SearchPanelProps) {
|
||||
const { user } = useAuth();
|
||||
const headMenu = useDropdown();
|
||||
|
||||
const handleChange = useCallback(
|
||||
|
@ -68,6 +80,22 @@ function SearchPanel({
|
|||
[headMenu, setHead]
|
||||
);
|
||||
|
||||
const handleToggleFolder = useCallback(() => {
|
||||
headMenu.hide();
|
||||
toggleFolderMode();
|
||||
}, [headMenu, toggleFolderMode]);
|
||||
|
||||
const handleFolderClick = useCallback(
|
||||
(event: CProps.EventMouse) => {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
toggleFolderMode();
|
||||
} else {
|
||||
headMenu.toggle();
|
||||
}
|
||||
},
|
||||
[headMenu, toggleFolderMode]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
|
@ -79,33 +107,43 @@ function SearchPanel({
|
|||
'clr-input'
|
||||
)}
|
||||
>
|
||||
<div className={clsx('px-3 self-center', 'min-w-[5.5rem]', 'select-none', 'whitespace-nowrap')}>
|
||||
<div className={clsx('px-3 pt-1 self-center', 'min-w-[5.5rem]', 'select-none', 'whitespace-nowrap')}>
|
||||
{filtered} из {total}
|
||||
</div>
|
||||
|
||||
<div className='cc-icons'>
|
||||
<MiniButton
|
||||
title='Видимость'
|
||||
icon={<VisibilityIcon value={true} className={tripleToggleColor(isVisible)} />}
|
||||
onClick={toggleVisible}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Я - Подписчик'
|
||||
icon={<SubscribeIcon value={true} className={tripleToggleColor(isSubscribed)} />}
|
||||
onClick={toggleSubscribed}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Я - Владелец'
|
||||
icon={<IconOwner size='1.25rem' className={tripleToggleColor(isOwned)} />}
|
||||
onClick={toggleOwned}
|
||||
/>
|
||||
{user ? (
|
||||
<div className='cc-icons'>
|
||||
<MiniButton
|
||||
title='Видимость'
|
||||
icon={<VisibilityIcon value={true} className={tripleToggleColor(isVisible)} />}
|
||||
onClick={toggleVisible}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Я - Подписчик'
|
||||
icon={<SubscribeIcon value={true} className={tripleToggleColor(isSubscribed)} />}
|
||||
onClick={toggleSubscribed}
|
||||
/>
|
||||
|
||||
<MiniButton
|
||||
title='Я - Редактор'
|
||||
icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />}
|
||||
onClick={toggleEditor}
|
||||
/>
|
||||
</div>
|
||||
<MiniButton
|
||||
title='Я - Владелец'
|
||||
icon={<IconOwner size='1.25rem' className={tripleToggleColor(isOwned)} />}
|
||||
onClick={toggleOwned}
|
||||
/>
|
||||
|
||||
<MiniButton
|
||||
title='Я - Редактор'
|
||||
icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />}
|
||||
onClick={toggleEditor}
|
||||
/>
|
||||
|
||||
<MiniButton
|
||||
title='Сбросить фильтры'
|
||||
icon={<IconFilterReset size='1.25rem' className='icon-primary' />}
|
||||
onClick={resetFilter}
|
||||
disabled={!hasCustomFilter}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className='flex items-center h-full mx-auto'>
|
||||
<SearchBar
|
||||
|
@ -116,61 +154,67 @@ function SearchPanel({
|
|||
value={query}
|
||||
onChange={setQuery}
|
||||
/>
|
||||
{!folderMode ? (
|
||||
<div ref={headMenu.ref} className='flex items-center h-full py-1 select-none'>
|
||||
<SelectorButton
|
||||
transparent
|
||||
className='h-full rounded-lg'
|
||||
titleHtml={(head ? describeLocationHead(head) : 'Выберите каталог') + '<br/>Ctrl + клик - Проводник'}
|
||||
hideTitle={headMenu.isOpen}
|
||||
icon={
|
||||
head ? (
|
||||
<LocationIcon value={head} size='1.25rem' />
|
||||
) : (
|
||||
<IconFolder size='1.25rem' className='clr-text-controls' />
|
||||
)
|
||||
}
|
||||
onClick={handleFolderClick}
|
||||
text={head ?? '//'}
|
||||
/>
|
||||
|
||||
<div ref={headMenu.ref} className='flex items-center h-full py-1 select-none'>
|
||||
<SelectorButton
|
||||
transparent
|
||||
className='h-full rounded-lg'
|
||||
title={head ? describeLocationHead(head) : 'Выберите каталог'}
|
||||
hideTitle={headMenu.isOpen}
|
||||
icon={
|
||||
head ? (
|
||||
<LocationIcon value={head} size='1.25rem' />
|
||||
) : (
|
||||
<IconFolder size='1.25rem' className='clr-text-controls' />
|
||||
)
|
||||
}
|
||||
onClick={headMenu.toggle}
|
||||
text={head ?? '//'}
|
||||
<Dropdown isOpen={headMenu.isOpen} stretchLeft className='z-modalTooltip'>
|
||||
<DropdownButton className='w-[10rem]' title='Переключение в режим Проводник' onClick={handleToggleFolder}>
|
||||
<div className='inline-flex items-center gap-3'>
|
||||
<IconFolderTree size='1rem' className='clr-text-controls' />
|
||||
<span>проводник...</span>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
<DropdownButton className='w-[10rem]' onClick={() => handleChange(undefined)}>
|
||||
<div className='inline-flex items-center gap-3'>
|
||||
<IconFolder size='1rem' className='clr-text-controls' />
|
||||
<span>отображать все</span>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
{Object.values(LocationHead).map((head, index) => {
|
||||
return (
|
||||
<DropdownButton
|
||||
className='w-[10rem]'
|
||||
key={`${prefixes.location_head_list}${index}`}
|
||||
onClick={() => handleChange(head)}
|
||||
title={describeLocationHead(head)}
|
||||
>
|
||||
<div className='inline-flex items-center gap-3'>
|
||||
<LocationIcon value={head} size='1rem' />
|
||||
{labelLocationHead(head)}
|
||||
</div>
|
||||
</DropdownButton>
|
||||
);
|
||||
})}
|
||||
</Dropdown>
|
||||
</div>
|
||||
) : null}
|
||||
{!folderMode ? (
|
||||
<SearchBar
|
||||
id='path_search'
|
||||
placeholder='Путь'
|
||||
noIcon
|
||||
noBorder
|
||||
className='min-w-[4.5rem] sm:min-w-[5rem]'
|
||||
value={path}
|
||||
onChange={setPath}
|
||||
/>
|
||||
|
||||
<Dropdown isOpen={headMenu.isOpen} stretchLeft className='z-modalTooltip'>
|
||||
<DropdownButton className='w-[10rem]' onClick={() => handleChange(undefined)}>
|
||||
<div className='inline-flex items-center gap-3'>
|
||||
<IconFolder size='1rem' className='clr-text-controls' />
|
||||
<span>отображать все</span>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
{Object.values(LocationHead).map((head, index) => {
|
||||
return (
|
||||
<DropdownButton
|
||||
className='w-[10rem]'
|
||||
key={`${prefixes.location_head_list}${index}`}
|
||||
onClick={() => handleChange(head)}
|
||||
title={describeLocationHead(head)}
|
||||
>
|
||||
<div className='inline-flex items-center gap-3'>
|
||||
<LocationIcon value={head} size='1rem' />
|
||||
{labelLocationHead(head)}
|
||||
</div>
|
||||
</DropdownButton>
|
||||
);
|
||||
})}
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
<SearchBar
|
||||
id='path_search'
|
||||
placeholder='Путь'
|
||||
noIcon
|
||||
noBorder
|
||||
className='min-w-[5rem]'
|
||||
value={path}
|
||||
onChange={setPath}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<BadgeHelp topic={HelpTopic.UI_LIBRARY} className='max-w-[28rem] text-sm' offset={5} place='right-start' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
||||
import { describeHelpTopic, labelHelpTopic, removeTags } from '@/utils/labels';
|
||||
|
||||
import LinkTopic from '../../components/ui/LinkTopic';
|
||||
|
||||
|
@ -10,7 +10,7 @@ interface TopicItemProps {
|
|||
function TopicItem({ topic }: TopicItemProps) {
|
||||
return (
|
||||
<li>
|
||||
<LinkTopic text={labelHelpTopic(topic)} topic={topic} /> – {describeHelpTopic(topic)}
|
||||
<LinkTopic text={labelHelpTopic(topic)} topic={topic} /> – {removeTags(describeHelpTopic(topic))}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import HelpAccess from './items/HelpAccess';
|
||||
import HelpAPI from './items/HelpAPI';
|
||||
import HelpConcept from './items/HelpConcept';
|
||||
import HelpConceptOSS from './items/HelpConceptOSS';
|
||||
import HelpConceptRelations from './items/HelpConceptRelations';
|
||||
import HelpConceptSynthesis from './items/HelpConceptSynthesis';
|
||||
import HelpConceptSystem from './items/HelpConceptSystem';
|
||||
import HelpContributors from './items/HelpContributors';
|
||||
import HelpCstAttributes from './items/HelpCstAttributes';
|
||||
import HelpCstClass from './items/HelpCstClass';
|
||||
import HelpCstEditor from './items/HelpCstEditor';
|
||||
import HelpCstStatus from './items/HelpCstStatus';
|
||||
import HelpDocs from './items/HelpDocs';
|
||||
import HelpExteor from './items/HelpExteor';
|
||||
import HelpFormulaTree from './items/HelpFormulaTree';
|
||||
import HelpInfo from './items/HelpInfo';
|
||||
import HelpInterface from './items/HelpInterface';
|
||||
import HelpLibrary from './items/HelpLibrary';
|
||||
import HelpOssGraph from './items/HelpOssGraph';
|
||||
import HelpPortal from './items/HelpPortal';
|
||||
import HelpPrivacy from './items/HelpPrivacy';
|
||||
import HelpRSFormCard from './items/HelpRSFormCard';
|
||||
|
@ -31,17 +35,21 @@ import HelpTermGraph from './items/HelpTermGraph';
|
|||
import HelpTerminologyControl from './items/HelpTerminologyControl';
|
||||
import HelpVersions from './items/HelpVersions';
|
||||
|
||||
// PDF Viewer setup
|
||||
const OFFSET_X_SMALL = 32;
|
||||
const OFFSET_X_LARGE = 280;
|
||||
|
||||
const MIN_SIZE_SMALL = 300;
|
||||
const MIN_SIZE_LARGE = 600;
|
||||
|
||||
interface TopicPageProps {
|
||||
topic: HelpTopic;
|
||||
}
|
||||
|
||||
function TopicPage({ topic }: TopicPageProps) {
|
||||
if (topic === HelpTopic.MAIN) return <HelpPortal />;
|
||||
const size = useWindowSize();
|
||||
|
||||
if (topic === HelpTopic.DOCS) return <HelpDocs />;
|
||||
if (topic === HelpTopic.RULES) return <HelpRules />;
|
||||
if (topic === HelpTopic.PRIVACY) return <HelpPrivacy />;
|
||||
if (topic === HelpTopic.API) return <HelpAPI />;
|
||||
if (topic === HelpTopic.MAIN) return <HelpPortal />;
|
||||
|
||||
if (topic === HelpTopic.INTERFACE) return <HelpInterface />;
|
||||
if (topic === HelpTopic.UI_LIBRARY) return <HelpLibrary />;
|
||||
|
@ -53,12 +61,14 @@ function TopicPage({ topic }: TopicPageProps) {
|
|||
if (topic === HelpTopic.UI_FORMULA_TREE) return <HelpFormulaTree />;
|
||||
if (topic === HelpTopic.UI_CST_STATUS) return <HelpCstStatus />;
|
||||
if (topic === HelpTopic.UI_CST_CLASS) return <HelpCstClass />;
|
||||
if (topic === HelpTopic.UI_OSS_GRAPH) return <HelpOssGraph />;
|
||||
|
||||
if (topic === HelpTopic.CONCEPTUAL) return <HelpConcept />;
|
||||
if (topic === HelpTopic.CC_SYSTEM) return <HelpConceptSystem />;
|
||||
if (topic === HelpTopic.CC_CONSTITUENTA) return <HelpCstAttributes />;
|
||||
if (topic === HelpTopic.CC_RELATIONS) return <HelpConceptRelations />;
|
||||
if (topic === HelpTopic.CC_SYNTHESIS) return <HelpConceptSynthesis />;
|
||||
if (topic === HelpTopic.CC_OSS) return <HelpConceptOSS />;
|
||||
|
||||
if (topic === HelpTopic.RSLANG) return <HelpRSLang />;
|
||||
if (topic === HelpTopic.RSL_TYPES) return <HelpRSLangTypes />;
|
||||
|
@ -70,6 +80,19 @@ function TopicPage({ topic }: TopicPageProps) {
|
|||
if (topic === HelpTopic.TERM_CONTROL) return <HelpTerminologyControl />;
|
||||
if (topic === HelpTopic.ACCESS) return <HelpAccess />;
|
||||
if (topic === HelpTopic.VERSIONS) return <HelpVersions />;
|
||||
|
||||
if (topic === HelpTopic.INFO) return <HelpInfo />;
|
||||
if (topic === HelpTopic.INFO_RULES) return <HelpRules />;
|
||||
if (topic === HelpTopic.INFO_CONTRIB) return <HelpContributors />;
|
||||
if (topic === HelpTopic.INFO_PRIVACY)
|
||||
return (
|
||||
<HelpPrivacy
|
||||
offsetXpx={size.isSmall ? OFFSET_X_SMALL : OFFSET_X_LARGE}
|
||||
minWidth={size.isSmall ? MIN_SIZE_SMALL : MIN_SIZE_LARGE}
|
||||
/>
|
||||
);
|
||||
if (topic === HelpTopic.INFO_API) return <HelpAPI />;
|
||||
|
||||
if (topic === HelpTopic.EXTEOR) return <HelpExteor />;
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ interface ViewTopicProps {
|
|||
|
||||
function ViewTopic({ topic }: ViewTopicProps) {
|
||||
return (
|
||||
<AnimateFade key={topic} className='py-2 pl-6 pr-3 mx-auto'>
|
||||
<AnimateFade key={topic} className='px-3 py-2 mx-auto'>
|
||||
<TopicPage topic={topic} />
|
||||
</AnimateFade>
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ function HelpAPI() {
|
|||
С описанием интерфейса можно ознакомиться <TextURL text='по ссылке' href={external_urls.restAPI} />.
|
||||
</p>
|
||||
<p>
|
||||
<TextURL text='Принять участие в разработке' href={external_urls.git_repo} />
|
||||
<TextURL text='Принять участие в разработке' href={external_urls.git_portal} />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
import { IconHide, IconImmutable, IconPrivate, IconProtected, IconPublic } from '@/components/Icons';
|
||||
|
||||
function HelpAccess() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Организация доступов</h1>
|
||||
<p>TBD.</p>
|
||||
<p>Редактирование контента осуществляется Редакторами и Владельцом.</p>
|
||||
<p>Редактирование прав и доступов осуществляется Владельцом.</p>
|
||||
<p>
|
||||
Доступ к контенту на Портале может быть ограничен владельцем каждой схемы в рамках <b>политики доступа</b>.
|
||||
</p>
|
||||
<li>
|
||||
<IconPublic className='inline-icon icon-green' /> публичная политика не ограничивает чтение схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconProtected className='inline-icon icon-blue' /> защитная политика запрещает доступ для всех кроме редакторов
|
||||
и владельца схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconPrivate className='inline-icon icon-red' /> личная политика оставляет доступ к схеме только владельцу
|
||||
</li>
|
||||
<li>
|
||||
<IconHide className='inline-icon' /> режим скрытия схемы из списка в Библиотеке не ограничивает доступ к схеме
|
||||
по прямой ссылке
|
||||
</li>
|
||||
<li>
|
||||
<IconImmutable className='inline-icon' /> режим защиты от редактирования предохраняет от случайных изменений
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
function HelpConceptOSS() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Операционная схема синтеза</h1>
|
||||
<p>
|
||||
Работа со сложными предметными областями требует многократного{' '}
|
||||
<LinkTopic text='синтеза' topic={HelpTopic.CC_SYNTHESIS} /> для построения целевых понятий. Последовательность
|
||||
синтезов концептуальных схем задается с помощью <b>Операционной схемы синтеза (ОСС)</b> в форме Графа синтеза.
|
||||
</p>
|
||||
<p>
|
||||
Отдельные операции в рамках ОСС задаются <b>таблицами отождествлений</b> понятий из синтезируемых схем. Таким
|
||||
образом <LinkTopic text='конституенты' topic={HelpTopic.CC_CONSTITUENTA} /> в каждой КС разделяются на
|
||||
наследованные, отождествленные и дописанные.
|
||||
</p>
|
||||
<p>
|
||||
Портал поддерживает <b>сквозные изменения</b> в рамках ОСС. Изменения, внесенные в исходные концептуальные схемы
|
||||
автоматически проносятся через граф синтеза (путем обновления наследованных конституент). Формальные определения
|
||||
наследованных конституент можно редактировать только путем изменения исходных конституент.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpConceptOSS;
|
|
@ -0,0 +1,285 @@
|
|||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { external_urls } from '@/utils/constants';
|
||||
|
||||
function HelpInfo() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Благодарности разработчикам и исследователям</h1>
|
||||
<p>
|
||||
История инструментов работы с концептуальными схемами начинается с 1970-х годов и продолжается в настоящее
|
||||
время. Здесь представлена скромная попытка перечислить вклад различных людей в развитие инструментов и
|
||||
математического аппарата, лежащего в основе экспликации концептуальных схем.
|
||||
</p>
|
||||
<p>
|
||||
В списке указан год окончания работ над соответствующим результатом или год публикации соответствующей статьи.
|
||||
Курсивом выделены комментарии к значимости указанного результата.
|
||||
</p>
|
||||
<p>Любые добавления и поправки приветствуются.</p>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<li>1973 Никаноров С.П., Персиц Д.Б. Формальное проектирование целостных СОУ.</li>
|
||||
<li>
|
||||
1975–1981 Никаноров С.П., Персиц Д.Б., Айзенштат А.В., Закс Б.А. Экспериментальная система пакетов прикладных
|
||||
программ автоматизированного проектирования систем организационного управления (АСП СОУ).
|
||||
</li>
|
||||
<li>1976 Поспелов Д.А., Чернышев С.Б. Метод построения формально-логической модели большой размерности.</li>
|
||||
<li>
|
||||
1977 Персиц Д.Б., Савелов Е.В., Тищенко А.В. Теоретические основы АСП СОУ,{' '}
|
||||
<i>
|
||||
сформировавшие базу для развития формализации предметных областей с помощью экспликации концептуальных схем.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
1980 Никаноров С.П., Персиц Д.Б., Егоров Б.Б., Никитина Н.К., Ашихмин В.С., Астрина И.В., Тищенко А.В. Блок
|
||||
документирования в АСП СОУ.
|
||||
</li>
|
||||
<li>
|
||||
1986 Никаноров С.П., Кучкаров З.А., Никитина Н.К., Крюков И.А., Комаров В.Г. Система автоматизированного
|
||||
проектирования концептуального уровня баз данных (МАКС),{' '}
|
||||
<i>
|
||||
заложивший основу для хранения концептуальных схем в базах данных и ставший первым редактором
|
||||
родоструктурной экспликации в группе Никитиной.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
1987 Иванов А.Ю., Кучкаров З.А. Разработка концептуальных и математических средств описания процессов принятия
|
||||
решений.
|
||||
</li>
|
||||
<li>
|
||||
1989 Кучкаров З.А., Остапов А.В. Методические вопросы концептуализации предметных областей,{' '}
|
||||
<i>
|
||||
как пример одной из работ Остапова, значительно расширившего технику экспликации и практику применения
|
||||
"бескванторных" выражений.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
1990 Никитина Н.К., Постников В.В. Синтаксический анализатор текста рода структуры для МАКС,{' '}
|
||||
<i>являющийся первой попыткой реализовать автоматизированную проверку синтаксиса родов структур.</i>
|
||||
</li>
|
||||
<li>
|
||||
1993 Костюк А.В., Никитина Н.К., Юдкин Ю.Ю. Программа визуализации М-графов, представляющих родовую структуру.
|
||||
</li>
|
||||
<li>1993 Никитина Н.К., Чувашов Е.В. Система проектирования баз данных по их концептуальной модели.</li>
|
||||
<li>
|
||||
1993 Никаноров С.П., Кучкаров З.А., Остапов А.В., Шульпекин А.Н., Коваль А.Г., Костюк А.В. Программа
|
||||
операционализации текстов концептуальных моделей, эксплицированных в аппарате родов структур Экстеор 1,{' '}
|
||||
<i>ставший первым редактором родоструктурной экспликации в группе Кучкарова.</i>
|
||||
</li>
|
||||
<li>
|
||||
1993 Кучкаров З.А., Лавров В., Крайнев А., Шульпекин А.Н., Симонов М. Автоматический генератор
|
||||
PROLOG-программ, формирующих предметные интерпретации родоструктурных экспликаций Инттеор.
|
||||
</li>
|
||||
<li>
|
||||
1994 Кучкаров З.А., Ким В.Л. Разработка родоструктурных конструктов для библиотеки моделей и исследование
|
||||
возможностей их развития.
|
||||
</li>
|
||||
<li>
|
||||
1996 Коваль А.Г., Кучкаров З.А., Костюк А.В., Кононенко А.А., Син Ю.Е., Маклаков Ю.И. Программа
|
||||
родоструктурного синтеза операционализированных терминальных концептуальных моделей Экстеор 2,{' '}
|
||||
<i>ставшая первой версией реализации родоструктурного аппарата на C++ под Windows.</i>
|
||||
</li>
|
||||
<li>
|
||||
1996 Никаноров С.П., Никитина Н.К., Климишин В.В. Автоматизированная система "Библиотека концептуальных схем",{' '}
|
||||
<i>впервые определившая паспорт концептуальной схемы.</i>
|
||||
</li>
|
||||
<li>
|
||||
1997 Никитина Н.К., Юрьев О.И. Система поддержки процессов концептуального анализа и проектирования ПРОКСИМА
|
||||
1.
|
||||
</li>
|
||||
<li>
|
||||
1998 Никитина Н.К., Гараева Ю.Р. Синтаксический анализатор выражений на языке родоструктурной экспликации для
|
||||
ПРОКСИМА 1.
|
||||
</li>
|
||||
<li>
|
||||
1998 Син Ю.Е. Разработка и исследование класса теоретико-модельных операций для технологической линии
|
||||
концептуального проектирования.
|
||||
</li>
|
||||
<li>
|
||||
1999 Кучкаров З.А., Кононенко А.А. Программа преобразования родоструктурного синтеза операционализированных
|
||||
терминальных концептуальных моделей Экстеор 3,{' '}
|
||||
<i>впервые включившая операционную схему синтеза (дерево синтеза).</i>
|
||||
</li>
|
||||
<li>
|
||||
1999 Никитина Н.К., Ландин Н.А. Разработка автоматизированной подсистемы, реализующей операции отслоения и
|
||||
рассечения над концептуальными схемами.
|
||||
</li>
|
||||
<li>
|
||||
1999 Юрьев О.И., Зверев В.Ю. Разработка и создание экспериментальной версии автоматизированной системы
|
||||
"Библиотека проектов систем организационного управления".
|
||||
</li>
|
||||
<li>
|
||||
2000 Кучкаров З.А., Кононенко А.А., Син Ю.Е. Программа генерации концептуально определенной предметно
|
||||
интерпретированной сети организационных процедур Оргтеор,{' '}
|
||||
<i>
|
||||
позволившая строить процессные схемы по графу термов концептуальной схемы и впервые включившая модуль
|
||||
терминологических преобразований текстовых описаний.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2000 Кононенко А.А., Майоров В.А. Программа автоматизированной генерации структуры данных и их визуализации по
|
||||
концептуальной модели БДтеор,{' '}
|
||||
<i>
|
||||
определившая проблемы интерфейса наполнения концептуальной модели в сложных ступенях и предложившая
|
||||
библиотеку Kernel для удержания интерпретации с помощью М-графа.
|
||||
</i>
|
||||
</li>
|
||||
<li>2000 Тищенко А.В. Шкалы множеств и родов структур.</li>
|
||||
<li>
|
||||
2000 Тищенко А.В., Акименков А.М., Ключников А.В. Система операций над концептуальными схемами,
|
||||
представленными в родоструктурной форме.
|
||||
</li>
|
||||
<li>2000 Ключников А.В. Эквивалентность теорий родов структур.</li>
|
||||
<li>
|
||||
2001 Кучкаров З.А., Никитин А.В. Исследование и построение типологии изменений теоретико-множественных
|
||||
интерпретаций класса декартового произведения.
|
||||
</li>
|
||||
<li>
|
||||
2001 Кононенко А.А., Майоров В.А. Программа преобразования сети процедур из формата Оргтеор в формат BPWin
|
||||
(IDEF0).
|
||||
</li>
|
||||
<li>
|
||||
2001 Майоров В.А. Программа построения формального выражения путем выбора альтернатив Grammar,{' '}
|
||||
<i>предложившая альтернативной подход к построению корректных формальных выражений.</i>
|
||||
</li>
|
||||
<li>
|
||||
2004 Гараева Ю.Р., Пономарев И.Н. Семантико-синтаксический анализатор текстов родов структур Бурбакизатор,{' '}
|
||||
<i>
|
||||
впервые включивший полный грамматический анализ родов структур и проверку биективной переносимости
|
||||
выражения.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2003 Юдкин Ю.Ю., Кудюкин Д.А. Разработка и испытание компьютерной программы, формирующей
|
||||
теоретико-множественную интерпретацию терма частной родоструктурной теории.
|
||||
</li>
|
||||
<li>
|
||||
2004 Кононенко А.А. Генерация кода на языке программирования C++ по тексту концептуальной схемы,
|
||||
эксплицированной в родах структур.
|
||||
</li>
|
||||
<li>2006 Кучкаров З.А., Никаноров С.П. Библиотека моделей.</li>
|
||||
<li>2006 Кучкаров З.А., Лавров В.А. Полные системы простых теоретико-множественных операций.</li>
|
||||
<li>
|
||||
2006 Солнцев С.В., Присакарь С.В. Введение количественных отношений в методологию концептуального анализа и
|
||||
проектирования, в том числе в язык родоструктурной экспликации.
|
||||
</li>
|
||||
<li>
|
||||
2007 Пономарев И.Н. Учебное пособие: Введение в математическую логику и роды структур,{' '}
|
||||
<i>
|
||||
являющееся наиболее полным описанием теории родов структур, используемой в родоструктурных экспликациях.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2008 Пономарев И.Н. Об эквивалентной представимости рода структуры с помощью заданной типовой характеристики.
|
||||
</li>
|
||||
<li>
|
||||
2008 Кононенко А.А., Кучкаров З.А., Никаноров С.П., Никитина Н.К. Технология концептуального проектирования,
|
||||
— <i>монография, собравшая исторический обзор и перспективы развития технологического направления.</i>
|
||||
</li>
|
||||
<li>
|
||||
2010 Кононенко А.А., Грязнов А.Д. Исследование и построение транслятора концептуальной схемы в концептуальную
|
||||
модель.
|
||||
</li>
|
||||
<li>2010 Никаноров С.П. Введение в аппарат ступеней.</li>
|
||||
<li>
|
||||
2012 Кононенко А.А., Елисов Д.Н. Использование механизма XSD-схем для хранения и операционализации
|
||||
концептуальных схем и концептуальных моделей с помощью XML.
|
||||
</li>
|
||||
<li>
|
||||
2013 Кононенко А.А., Борисов И.Р. Исследование, разработка и экспериментальная программная реализация операций
|
||||
над концептуальными моделями,{' '}
|
||||
<i>
|
||||
впервые реализовавшая модуль прямого вычисления интерпретации формального выражения, встроенный в Экстеор
|
||||
3.5.
|
||||
</i>
|
||||
</li>
|
||||
<li>2013 Пономарев И.Н., Липатов А.А. Операции над родами структур и пример автоматизации их выполнения.</li>
|
||||
<li>
|
||||
2014 Борисов И.Р., Баширов Р.М. Исследования и программная реализации оптимальной структуры данных для
|
||||
вычисления интерпретации концептуальных схем.
|
||||
</li>
|
||||
<li>
|
||||
2014 Борисов И.Р. Программный комплекс Экстеор 4,{' '}
|
||||
<i>
|
||||
включавший доработанный модуль операционного синтеза и синтаксический анализатор на базе грамматики,
|
||||
предложенной Пономаревым И.Н.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2014 Борисов И.Р. Концептуальные конструкции в задаче синтеза систем на примере Экологического кодекса,
|
||||
—{' '}
|
||||
<i>
|
||||
статья, вводящая понятие концептуальных конструкций как промежуточных форм для операционализации
|
||||
концептуальных схем.
|
||||
</i>
|
||||
</li>
|
||||
<li>2015 Иванов А.Ю. Аппарат ступеней С.П. Никанорова и возможное развитие идей по его использованию.</li>
|
||||
<li>
|
||||
2016 Борисов И.Р., Баширов Р.М. Исследование области компьютерной лингвистики и разработка модулей
|
||||
терминологического контроля в Экстеор 4 и Microsoft Office Word,{' '}
|
||||
<i>
|
||||
являющееся основой библиотеки <TextURL text='cctext' href={external_urls.git_cctext} />.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2016 Борисов И.Р. Программный комплекс Экстеор 4.5,{' '}
|
||||
<i>
|
||||
включавший текстовый модуль и полностью переработанное ядро, выделенное в отдельную библиотеку (впоследствии
|
||||
— <TextURL text='ConceptCore' href={external_urls.git_core} />
|
||||
).
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2017 Иванов А.Ю. Введение в технологию концептуализации предметных областей социологии: основы полагания ядра
|
||||
теории (на примере родственных отношений).
|
||||
</li>
|
||||
<li>
|
||||
2017 Борисов И.Р., Мурадов А.К. Организация операций над системами понятий посредством графических
|
||||
интерфейсов, <i>заложивший основу для технологии Концепт.Блоки и блока графического синтеза.</i>
|
||||
</li>
|
||||
<li>
|
||||
2018 Борисов И.Р., Князев А.В. Изучение методов концептуальной расчистки, разметки текстов и разработка
|
||||
программных средств их автоматизации,{' '}
|
||||
<i> — диплом, сформировавший основу для технологий Концепт.Разметка и Концепт.Майнинг.</i>
|
||||
</li>
|
||||
<li>
|
||||
2018 Никитин А.В., Болотин П.В. Исследование типологии изменения теоретико-множественной интерпретации класса
|
||||
множества подмножеств.
|
||||
</li>
|
||||
<li>
|
||||
2019 Борисов И.Р., Широкова Л.Р. Исследование возможностей применения методов машинного обучения для решения
|
||||
задач расчистки текстов. Разработка прототипа программного модуля, —{' '}
|
||||
<i>первая попытка внедрения технологий ИИ в текстовый модуль.</i>
|
||||
</li>
|
||||
<li>
|
||||
2020 Борисов И.Р., Пакулина Т.А. Исследование применения методов машинного обучения для выделения именованных
|
||||
сущностей в текстах интервью. Экспериментальная разработка программного модуля расчистки текстов,{' '}
|
||||
<i>ставшего расширением технологии Концепт.Расчистка.</i>
|
||||
</li>
|
||||
<li>
|
||||
2020 Программный комплекс Экстеор 4.7, включающий значительное расширение выразительных средств языка родов
|
||||
структур (рекурсивные и императивные выражения, фильтры, ASCII синтаксис).
|
||||
</li>
|
||||
<li>
|
||||
2021 Борисов И.Р., Демешко А.Б. Исследование и разработка программного модуля формирования текстов функций на
|
||||
основе концепта функциональная структура,{' '}
|
||||
<i>дополнившего текстовый модуль возможностью работы с глагольными формами.</i>
|
||||
</li>
|
||||
<li>
|
||||
2023 Борисов И.Р., Тулисов А.В. Разработка инструмента экспликации концептуальных схем в родоструктурной форме
|
||||
через веб-интерфейс, — <i>разработка прототипа интерфейса КонцептПортал.</i>
|
||||
</li>
|
||||
<li>
|
||||
2024 Борисов И.Р. Программный комплекс <LinkTopic text='Экстеор 4.9' topic={HelpTopic.EXTEOR} />,
|
||||
поддерживающий работу со схемами, выгруженными из КонцептПортал.{' '}
|
||||
<i>
|
||||
Функционал ConceptCore (С++) стал доступен в Python через обертку{' '}
|
||||
<TextURL text='pyconcept' href={external_urls.git_core} />.
|
||||
</i>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpInfo;
|
|
@ -1,16 +0,0 @@
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import Subtopics from '../Subtopics';
|
||||
|
||||
function HelpDocs() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Документы</h1>
|
||||
<p>TBD.</p>
|
||||
|
||||
<Subtopics headTopic={HelpTopic.DOCS} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpDocs;
|
|
@ -1,19 +1,26 @@
|
|||
import TextURL from '@/components/ui/TextURL';
|
||||
import { external_urls } from '@/utils/constants';
|
||||
import { external_urls, PARAMETER } from '@/utils/constants';
|
||||
|
||||
function HelpExteor() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Экстеор</h1>
|
||||
<p>Экстеор 4.9 — редактор текстов систем понятий эксплицированных в родах структур</p>
|
||||
<p>Экстеор 4.9 — редактор текстов систем понятий эксплицированных в родах структур.</p>
|
||||
<p>
|
||||
Портал превосходит Экстеор в части редактирования экспликаций, но вычисление интерпретации доступно только в
|
||||
Экстеоре. Также следует использовать Экстеор для выгрузки экспликаций в Word для последующей печати
|
||||
Экстеор. Также следует использовать Экстеор для выгрузки экспликаций в Word для последующей печати.
|
||||
</p>
|
||||
<p>Экстеор доступен на операционной системы Windows 10+</p>
|
||||
|
||||
<p>
|
||||
Скачать установщик: <TextURL href={external_urls.exteor64} text='64bit' /> |{' '}
|
||||
<TextURL href={external_urls.exteor32} text='32bit' />
|
||||
<TextURL href={external_urls.exteor32} text='32bit' /> (Windows 10 и выше)
|
||||
</p>
|
||||
<p>
|
||||
Текущая версия: <b>{PARAMETER.exteorVersion}</b>
|
||||
</p>
|
||||
<p>
|
||||
Экстеор не поддерживает автоматическое обновление. Если в выгруженной схеме присутствуют неожиданные диагностики
|
||||
или ошибки, то попробуйте скачать новую версию по ссылкам выше.
|
||||
</p>
|
||||
|
||||
<h2>Основные функции</h2>
|
||||
|
|
|
@ -5,10 +5,8 @@ function HelpFormulaTree() {
|
|||
return (
|
||||
<div>
|
||||
<h1>Дерево разбора выражения</h1>
|
||||
<p>
|
||||
Дерево разбора получено путем семантических преобразований дерева синтаксического разбора. Оно отражает
|
||||
структуру грамматически корректного выражения языка родов структур.
|
||||
</p>
|
||||
<p>Дерево получено путем семантических преобразований дерева синтаксического разбора.</p>
|
||||
<p>Оно отражает структуру грамматически корректного выражения языка родов структур.</p>
|
||||
<li>Порядок узлов в рамках одного уровня может отличаться от их порядка в выражении</li>
|
||||
<li>При наведении курсора на узел в тексте выделяется соответствующий ему фрагмент</li>
|
||||
<li>Текст в узле дерева соответствует элементу языка</li>
|
||||
|
|
19
rsconcept/frontend/src/pages/ManualsPage/items/HelpInfo.tsx
Normal file
19
rsconcept/frontend/src/pages/ManualsPage/items/HelpInfo.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import Subtopics from '../Subtopics';
|
||||
|
||||
function HelpInfo() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Справочная информация и документы</h1>
|
||||
<p>
|
||||
Раздел содержит различные документы, задающие правовой статус Портала,
|
||||
<br />а также документацию для разработчиков.
|
||||
</p>
|
||||
|
||||
<Subtopics headTopic={HelpTopic.INFO} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpInfo;
|
|
@ -1,4 +1,15 @@
|
|||
import { IconFolder, IconSearch, IconShow, IconSortAsc, IconSortDesc } from '@/components/Icons';
|
||||
import {
|
||||
IconFilterReset,
|
||||
IconFolder,
|
||||
IconFolderClosed,
|
||||
IconFolderEmpty,
|
||||
IconFolderOpened,
|
||||
IconFolderTree,
|
||||
IconSearch,
|
||||
IconShow,
|
||||
IconSortAsc,
|
||||
IconSortDesc
|
||||
} from '@/components/Icons';
|
||||
|
||||
function HelpLibrary() {
|
||||
return (
|
||||
|
@ -6,8 +17,9 @@ function HelpLibrary() {
|
|||
<h1>Библиотека схем</h1>
|
||||
<p>В библиотеке собраны концептуальные схемы, эксплицированные в родоструктурном аппарате</p>
|
||||
|
||||
<h2>Интерфейс</h2>
|
||||
<li>клик по строке - переход к редактированию схемы</li>
|
||||
<li>Ctrl + клик по строке откроет схему в новой вкладке</li>
|
||||
<li>Фильтры атрибутов три позиции: да/нет/не применять</li>
|
||||
<li>
|
||||
<IconShow size='1rem' className='inline-icon' /> фильтры атрибутов применяются по клику
|
||||
</li>
|
||||
|
@ -21,6 +33,32 @@ function HelpLibrary() {
|
|||
<li>
|
||||
<IconFolder size='1rem' className='inline-icon' /> фильтр по расположению
|
||||
</li>
|
||||
<li>
|
||||
<IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Поиск
|
||||
</li>
|
||||
|
||||
<h2>Режим: Проводник</h2>
|
||||
<li>клик по папке отображает справа файлы в ней</li>
|
||||
<li>Ctrl + клик по папке копирует путь в буфер обмена</li>
|
||||
<li>клик по иконке сворачивает/разворачивает вложенные</li>
|
||||
<li>
|
||||
<IconFolderEmpty size='1rem' className='inline-icon clr-text-default' /> папка без файлов
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderEmpty size='1rem' className='inline-icon' /> папка с вложенными без файлов
|
||||
</li>
|
||||
<li>
|
||||
<IconFolder size='1rem' className='inline-icon' /> папка без вложенных
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderClosed size='1rem' className='inline-icon' /> папка с вложенными и файлами
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderOpened size='1rem' className='inline-icon icon-green' /> развернутая папка
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
function HelpOssGraph() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Граф синтеза</h1>
|
||||
<p>TBD.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpOssGraph;
|
|
@ -39,7 +39,7 @@ function HelpPortal() {
|
|||
|
||||
<h2>Разделы Справки</h2>
|
||||
{[
|
||||
HelpTopic.DOCS,
|
||||
HelpTopic.INFO,
|
||||
HelpTopic.INTERFACE,
|
||||
HelpTopic.CONCEPTUAL,
|
||||
HelpTopic.RSLANG,
|
||||
|
@ -55,11 +55,11 @@ function HelpPortal() {
|
|||
<h2>Лицензирование и раскрытие информации</h2>
|
||||
<li>Пользователи Портала сохраняют авторские права на создаваемый ими контент</li>
|
||||
<li>
|
||||
Политика обработки данных доступна по <LinkTopic text='ссылке' topic={HelpTopic.PRIVACY} />
|
||||
Политика обработки данных доступна по <LinkTopic text='ссылке' topic={HelpTopic.INFO_PRIVACY} />
|
||||
</li>
|
||||
<li>
|
||||
Портал является проектом с открытым исходным кодом, доступным на{' '}
|
||||
<TextURL text='Github' href={external_urls.git_repo} />
|
||||
<TextURL text='Github' href={external_urls.git_portal} />
|
||||
</li>
|
||||
<li>
|
||||
Данный сайт использует доменное имя и серверные мощности{' '}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import PDFViewer from '@/components/ui/PDFViewer';
|
||||
import { resources } from '@/utils/constants';
|
||||
|
||||
function HelpPrivacy() {
|
||||
interface HelpPrivacyProps {
|
||||
offsetXpx: number;
|
||||
minWidth: number;
|
||||
}
|
||||
|
||||
function HelpPrivacy({ offsetXpx, minWidth }: HelpPrivacyProps) {
|
||||
return (
|
||||
<div>
|
||||
<PDFViewer file={resources.privacy_policy} />
|
||||
<PDFViewer file={resources.privacy_policy} offsetXpx={offsetXpx} minWidth={minWidth} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,54 @@
|
|||
import { urls } from '@/app/urls';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import { external_urls } from '@/utils/constants';
|
||||
|
||||
function HelpRules() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Правила Портала</h1>
|
||||
<p>TBD.</p>
|
||||
<h1>Правила поведения участников Портала</h1>
|
||||
|
||||
<p>
|
||||
Мы стремимся предоставить возможность участия в работе Портала как можно большему количеству людей. Мы считаем,
|
||||
что сообщества участников у нас должны быть как можно более разнообразными и открытыми для новых участников. Мы
|
||||
хотим, чтобы эти сообщества представляли собой позитивное, безопасное и здоровое окружение для всех, кто
|
||||
присоединяется или хочет присоединиться к ним. Мы стремимся сохранить такое окружение, в том числе путем
|
||||
принятия этих Правил поведения. Кроме того, мы хотим защитить наши проекты от тех, кто портит или искажает их
|
||||
содержание, поэтому к участникам, нарушающим Правила будут применяться санкции, определяемые Администрацией
|
||||
Портала.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Поведение в рамках Портала основано на уважении, вежливости, коллегиальности, солидарности и социальной
|
||||
ответственности. Это относится ко всем читателям, участникам и администраторам в их взаимодействии без
|
||||
исключений, основанных на возрасте, умственных или физических возможностях, внешнем виде, национальном,
|
||||
религиозном, этническом и культурном происхождении, касте, социальном классе, уровне владения языком,
|
||||
сексуальной ориентации, гендерной идентичности, поле или сфере работы. Мы также не будем делать исключения на
|
||||
основании статуса, навыков или достижений.
|
||||
</p>
|
||||
|
||||
<h2>Ожидаемое поведение</h2>
|
||||
<li>взаимное уважением, поддержка в отношениях с участниками Портала.</li>
|
||||
<li>
|
||||
пожелания по доработке, найденные ошибки и иные предложения следует направлять по адресу email:{' '}
|
||||
<TextURL href={external_urls.mail_portal} text='portal@acconcept.ru' />.
|
||||
</li>
|
||||
|
||||
<h2>Неприемлемое поведение</h2>
|
||||
<li>оскорбления, угрозы, сексуальное домогательство, троллинг и преследование других участников.</li>
|
||||
<li>
|
||||
раскрытие персональных данных (доксинг) участников Портала. Не распространяется на персональные данные,
|
||||
раскрытые участниками для отображения на Портале. Эти данные можно изменить в{' '}
|
||||
<TextURL text='профиле' href={urls.profile} />.
|
||||
</li>
|
||||
<li>
|
||||
злоупотребление властью, привилегиями или влиянием, включая использование статусов и доступов, предоставленных
|
||||
Порталом в личных целях, не связанных с разработкой контента, развитием и продвижением Портала.
|
||||
</li>
|
||||
<li>
|
||||
вандализм, намеренное добавление неуместного контента, или препятствование, затруднение или другого рода
|
||||
осложнение создания (и/или поддержания) контента, созданного другими участниками.
|
||||
</li>
|
||||
<li>нарушение работоспособности Портала, в том числе путем использования уязвимостей и ошибок в коде.</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ function HelpTermGraph() {
|
|||
const { colors } = useConceptOptions();
|
||||
return (
|
||||
<div className='flex flex-col'>
|
||||
<div className='flex'>
|
||||
<div className='dense w-[14rem]'>
|
||||
<div className='flex flex-col sm:flex-row'>
|
||||
<div className='w-full sm:w-[14rem]'>
|
||||
<h1>Настройка графа</h1>
|
||||
<li>Цвет – покраска узлов</li>
|
||||
<li>Граф – расположение</li>
|
||||
|
@ -43,9 +43,9 @@ function HelpTermGraph() {
|
|||
</li>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3 mt-3' />
|
||||
<Divider vertical margins='mx-3 mt-3' className='hidden sm:block' />
|
||||
|
||||
<div className='dense w-[21rem]'>
|
||||
<div className='w-full sm:w-[21rem]'>
|
||||
<h1>Изменение узлов</h1>
|
||||
<li>Клик на конституенту – выделение</li>
|
||||
<li>
|
||||
|
@ -66,10 +66,10 @@ function HelpTermGraph() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Divider margins='my-3' />
|
||||
<Divider margins='my-3' className='hidden sm:block' />
|
||||
|
||||
<div className='flex mb-3'>
|
||||
<div className='dense w-[14rem]'>
|
||||
<div className='flex mb-3 flex-col-reverse sm:flex-row'>
|
||||
<div className='w-full sm:w-[14rem]'>
|
||||
<h1>Общие</h1>
|
||||
<li>
|
||||
<IconFilter className='inline-icon' /> Открыть настройки
|
||||
|
@ -82,7 +82,7 @@ function HelpTermGraph() {
|
|||
</li>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3' />
|
||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||
|
||||
<div className='dense w-[21rem]'>
|
||||
<h1>Выделение</h1>
|
||||
|
|
|
@ -12,6 +12,7 @@ import { useOSS } from '@/context/OssContext';
|
|||
import { ILibraryUpdateData, LibraryItemType } from '@/models/library';
|
||||
import AccessToolbar from '@/pages/RSFormPage/EditorRSFormCard/AccessToolbar';
|
||||
import { limits, patterns } from '@/utils/constants';
|
||||
import { information } from '@/utils/labels';
|
||||
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
|
||||
|
@ -81,7 +82,7 @@ function FormOSS({ id, isModified, setIsModified }: FormOSSProps) {
|
|||
visible: visible,
|
||||
read_only: readOnly
|
||||
};
|
||||
update(data, () => toast.success('Изменения сохранены'));
|
||||
update(data, () => toast.success(information.changesSaved));
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -10,6 +10,7 @@ import Overlay from '@/components/ui/Overlay';
|
|||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/labels';
|
||||
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
|
@ -57,7 +58,7 @@ function RSFormToolbar({ modified, anonymous, subscribed, onSubmit, onDestroy }:
|
|||
onClick={onDestroy}
|
||||
/>
|
||||
) : null}
|
||||
<BadgeHelp topic={HelpTopic.UI_RS_CARD} offset={4} className='max-w-[30rem]' />
|
||||
<BadgeHelp topic={HelpTopic.UI_RS_CARD} offset={4} className={PARAMETER.TOOLTIP_WIDTH} />
|
||||
</Overlay>
|
||||
);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user