mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
F: Simplify TGFlow tooltips
This commit is contained in:
parent
9e50312d23
commit
0d899f6258
230
rsconcept/frontend/package-lock.json
generated
230
rsconcept/frontend/package-lock.json
generated
|
@ -9,10 +9,10 @@
|
|||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@dagrejs/dagre": "^1.1.4",
|
||||
"@hookform/resolvers": "^4.1.0",
|
||||
"@hookform/resolvers": "^4.1.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@tanstack/react-query": "^5.66.8",
|
||||
"@tanstack/react-query-devtools": "^5.66.8",
|
||||
"@tanstack/react-query": "^5.66.9",
|
||||
"@tanstack/react-query-devtools": "^5.66.9",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@uiw/codemirror-themes": "^4.23.8",
|
||||
"@uiw/react-codemirror": "^4.23.8",
|
||||
|
@ -43,16 +43,16 @@
|
|||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.2",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/vite": "^4.0.7",
|
||||
"@tailwindcss/vite": "^4.0.8",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-21e868a-20250216",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-compiler": "^19.0.0-beta-21e868a-20250216",
|
||||
|
@ -61,7 +61,7 @@
|
|||
"globals": "^16.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"tailwindcss": "^4.0.7",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-jest": "^29.2.6",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.1.1"
|
||||
|
@ -1473,9 +1473,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz",
|
||||
"integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==",
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
|
||||
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
@ -1486,9 +1486,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
|
||||
"integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
|
||||
"integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -1547,9 +1547,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz",
|
||||
"integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==",
|
||||
"version": "9.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
|
||||
"integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -1567,13 +1567,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.6.tgz",
|
||||
"integrity": "sha512-+0TjwR1eAUdZtvv/ir1mGX+v0tUoR3VEPB8Up0LLJC+whRW0GgBBtpbOkg/a/U4Dxa6l5a3l9AJ1aWIQVyoWJA==",
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
|
||||
"integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.11.0",
|
||||
"@eslint/core": "^0.12.0",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -1678,9 +1678,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@hookform/resolvers": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-4.1.0.tgz",
|
||||
"integrity": "sha512-fX/uHKb+OOCpACLc6enuTQsf0ZpRrKbeBBPETg5PCPLCIYV6osP2Bw6ezuclM61lH+wBF9eXcuC0+BFh9XOEnQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-4.1.1.tgz",
|
||||
"integrity": "sha512-S9YN1RgNWG+klUz5uQaV6rjE4pr6Py2tamj7ekshzLcMyg+/Pal1KZAYgGszV0+doiy41dUiQgXL3uRS9stndQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001698"
|
||||
|
@ -2957,44 +2957,44 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/node": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.7.tgz",
|
||||
"integrity": "sha512-dkFXufkbRB2mu3FPsW5xLAUWJyexpJA+/VtQj18k3SUiJVLdpgzBd1v1gRRcIpEJj7K5KpxBKfOXlZxT3ZZRuA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.8.tgz",
|
||||
"integrity": "sha512-FKArQpbrbwv08TNT0k7ejYXpF+R8knZFAatNc0acOxbgeqLzwb86r+P3LGOjIeI3Idqe9CVkZrh4GlsJLJKkkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"enhanced-resolve": "^5.18.1",
|
||||
"jiti": "^2.4.2",
|
||||
"tailwindcss": "4.0.7"
|
||||
"tailwindcss": "4.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.7.tgz",
|
||||
"integrity": "sha512-yr6w5YMgjy+B+zkJiJtIYGXW+HNYOPfRPtSs+aqLnKwdEzNrGv4ZuJh9hYJ3mcA+HMq/K1rtFV+KsEr65S558g==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.8.tgz",
|
||||
"integrity": "sha512-KfMcuAu/Iw+DcV1e8twrFyr2yN8/ZDC/odIGta4wuuJOGkrkHZbvJvRNIbQNhGh7erZTYV6Ie0IeD6WC9Y8Hcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-android-arm64": "4.0.7",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.0.7",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.0.7",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.0.7",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.7",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.0.7",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.0.7",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.0.7",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.0.7",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.0.7",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.0.7"
|
||||
"@tailwindcss/oxide-android-arm64": "4.0.8",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.0.8",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.0.8",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.0.8",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.8",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.0.8",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.0.8",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.0.8",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.0.8",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.0.8",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.7.tgz",
|
||||
"integrity": "sha512-5iQXXcAeOHBZy8ASfHFm1k0O/9wR2E3tKh6+P+ilZZbQiMgu+qrnfpBWYPc3FPuQdWiWb73069WT5D+CAfx/tg==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.8.tgz",
|
||||
"integrity": "sha512-We7K79+Sm4mwJHk26Yzu/GAj7C7myemm7PeXvpgMxyxO70SSFSL3uCcqFbz9JA5M5UPkrl7N9fkBe/Y0iazqpA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -3009,9 +3009,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.7.tgz",
|
||||
"integrity": "sha512-7yGZtEc5IgVYylqK/2B0yVqoofk4UAbkn1ygNpIJZyrOhbymsfr8uUFCueTu2fUxmAYIfMZ8waWo2dLg/NgLgg==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.8.tgz",
|
||||
"integrity": "sha512-Lv9Isi2EwkCTG1sRHNDi0uRNN1UGFdEThUAGFrydRmQZnraGLMjN8gahzg2FFnOizDl7LB2TykLUuiw833DSNg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -3026,9 +3026,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.7.tgz",
|
||||
"integrity": "sha512-tPQDV20fBjb26yWbPqT1ZSoDChomMCiXTKn4jupMSoMCFyU7+OJvIY1ryjqBuY622dEBJ8LnCDDWsnj1lX9nNQ==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.8.tgz",
|
||||
"integrity": "sha512-fWfywfYIlSWtKoqWTjukTHLWV3ARaBRjXCC2Eo0l6KVpaqGY4c2y8snUjp1xpxUtpqwMvCvFWFaleMoz1Vhzlw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -3043,9 +3043,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.7.tgz",
|
||||
"integrity": "sha512-sZqJpTyTZiknU9LLHuByg5GKTW+u3FqM7q7myequAXxKOpAFiOfXpY710FuMY+gjzSapyRbDXJlsTQtCyiTo5w==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.8.tgz",
|
||||
"integrity": "sha512-SO+dyvjJV9G94bnmq2288Ke0BIdvrbSbvtPLaQdqjqHR83v5L2fWADyFO+1oecHo9Owsk8MxcXh1agGVPIKIqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -3060,9 +3060,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.7.tgz",
|
||||
"integrity": "sha512-PBgvULgeSswjd8cbZ91gdIcIDMdc3TUHV5XemEpxlqt9M8KoydJzkuB/Dt910jYdofOIaTWRL6adG9nJICvU4A==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.8.tgz",
|
||||
"integrity": "sha512-ZSHggWiEblQNV69V0qUK5vuAtHP+I+S2eGrKGJ5lPgwgJeAd6GjLsVBN+Mqn2SPVfYM3BOpS9jX/zVg9RWQVDQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
@ -3077,9 +3077,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.7.tgz",
|
||||
"integrity": "sha512-By/a2yeh+e9b+C67F88ndSwVJl2A3tcUDb29FbedDi+DZ4Mr07Oqw9Y1DrDrtHIDhIZ3bmmiL1dkH2YxrtV+zw==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.8.tgz",
|
||||
"integrity": "sha512-xWpr6M0OZLDNsr7+bQz+3X7zcnDJZJ1N9gtBWCtfhkEtDjjxYEp+Lr5L5nc/yXlL4MyCHnn0uonGVXy3fhxaVA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -3094,9 +3094,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.7.tgz",
|
||||
"integrity": "sha512-WHYs3cpPEJb/ccyT20NOzopYQkl7JKncNBUbb77YFlwlXMVJLLV3nrXQKhr7DmZxz2ZXqjyUwsj2rdzd9stYdw==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.8.tgz",
|
||||
"integrity": "sha512-5tz2IL7LN58ssGEq7h/staD7pu/izF/KeMWdlJ86WDe2Ah46LF3ET6ZGKTr5eZMrnEA0M9cVFuSPprKRHNgjeg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -3111,9 +3111,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.7.tgz",
|
||||
"integrity": "sha512-7bP1UyuX9kFxbOwkeIJhBZNevKYPXB6xZI37v09fqi6rqRJR8elybwjMUHm54GVP+UTtJ14ueB1K54Dy1tIO6w==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.8.tgz",
|
||||
"integrity": "sha512-KSzMkhyrxAQyY2o194NKVKU9j/c+NFSoMvnHWFaNHKi3P1lb+Vq1UC19tLHrmxSkKapcMMu69D7+G1+FVGNDXQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -3128,9 +3128,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.7.tgz",
|
||||
"integrity": "sha512-gBQIV8nL/LuhARNGeroqzXymMzzW5wQzqlteVqOVoqwEfpHOP3GMird5pGFbnpY+NP0fOlsZGrxxOPQ4W/84bQ==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.8.tgz",
|
||||
"integrity": "sha512-yFYKG5UtHTRimjtqxUWXBgI4Tc6NJe3USjRIVdlTczpLRxq/SFwgzGl5JbatCxgSRDPBFwRrNPxq+ukfQFGdrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -3145,9 +3145,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.7.tgz",
|
||||
"integrity": "sha512-aH530NFfx0kpQpvYMfWoeG03zGnRCMVlQG8do/5XeahYydz+6SIBxA1tl/cyITSJyWZHyVt6GVNkXeAD30v0Xg==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.8.tgz",
|
||||
"integrity": "sha512-tndGujmCSba85cRCnQzXgpA2jx5gXimyspsUYae5jlPyLRG0RjXbDshFKOheVXU4TLflo7FSG8EHCBJ0EHTKdQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
@ -3162,9 +3162,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.7.tgz",
|
||||
"integrity": "sha512-8Cva6bbJN7ZJx320k7vxGGdU0ewmpfS5A4PudyzUuofdi8MgeINuiiWiPQ0VZCda/GX88K6qp+6UpDZNVr8HMQ==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.8.tgz",
|
||||
"integrity": "sha512-T77jroAc0p4EHVVgTUiNeFn6Nj3jtD3IeNId2X+0k+N1XxfNipy81BEkYErpKLiOkNhpNFjPee8/ZVas29b2OQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
@ -3179,16 +3179,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/vite": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.7.tgz",
|
||||
"integrity": "sha512-GYx5sxArfIMtdZCsxfya3S/efMmf4RvfqdiLUozkhmSFBNUFnYVodatpoO/en4/BsOIGvq/RB6HwcTLn9prFnQ==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.8.tgz",
|
||||
"integrity": "sha512-+SAq44yLzYlzyrb7QTcFCdU8Xa7FOA0jp+Xby7fPMUie+MY9HhJysM7Vp+vL8qIp8ceQJfLD+FjgJuJ4lL6nyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tailwindcss/node": "4.0.7",
|
||||
"@tailwindcss/oxide": "4.0.7",
|
||||
"@tailwindcss/node": "4.0.8",
|
||||
"@tailwindcss/oxide": "4.0.8",
|
||||
"lightningcss": "^1.29.1",
|
||||
"tailwindcss": "4.0.7"
|
||||
"tailwindcss": "4.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.2.0 || ^6"
|
||||
|
@ -3215,9 +3215,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.66.8",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.66.8.tgz",
|
||||
"integrity": "sha512-LqYHYArmM7ycyT1I/Txc/n6KzI8S/hBFw2SQ9Uj1GpbZ89AvZLEvetquiQEHkZ5rFEm+iVNpZ6zYjTiPmJ9N5Q==",
|
||||
"version": "5.66.9",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.66.9.tgz",
|
||||
"integrity": "sha512-NRI02PHJsP5y2gAuWKP+awamTIBFBSKMnO6UVzi03GTclmHHHInH5UzVgzi5tpu4+FmGfsdT7Umqegobtsp23A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.66.4"
|
||||
|
@ -3231,9 +3231,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query-devtools": {
|
||||
"version": "5.66.8",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.66.8.tgz",
|
||||
"integrity": "sha512-OOdGk4ijs1SEliluWoE5D0ghxwDQz4x2LKzO69K6++VuxtizONXEXtk/oC3QGa9Tru/2CQmsJ5HUk8pmUAi9Qg==",
|
||||
"version": "5.66.9",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.66.9.tgz",
|
||||
"integrity": "sha512-70G6AR35he53SYUcUK6EdqNR18zejCv1rM6900gjZP408EAex56YLwVSeijzk9lWeU2J42G9Fjh0i1WngUTsgw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-devtools": "5.65.0"
|
||||
|
@ -3243,7 +3243,7 @@
|
|||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/react-query": "^5.66.8",
|
||||
"@tanstack/react-query": "^5.66.9",
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
|
@ -3669,9 +3669,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
|
||||
"integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==",
|
||||
"version": "22.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz",
|
||||
"integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -5266,9 +5266,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.102",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz",
|
||||
"integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==",
|
||||
"version": "1.5.103",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.103.tgz",
|
||||
"integrity": "sha512-P6+XzIkfndgsrjROJWfSvVEgNHtPgbhVyTkwLjUM2HU/h7pZRORgaTlHqfAikqxKmdJMLW8fftrdGWbd/Ds0FA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emittery": {
|
||||
|
@ -5546,22 +5546,22 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.20.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz",
|
||||
"integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==",
|
||||
"version": "9.21.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
|
||||
"integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.19.0",
|
||||
"@eslint/core": "^0.11.0",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "9.20.0",
|
||||
"@eslint/plugin-kit": "^0.2.5",
|
||||
"@eslint/config-array": "^0.19.2",
|
||||
"@eslint/core": "^0.12.0",
|
||||
"@eslint/eslintrc": "^3.3.0",
|
||||
"@eslint/js": "9.21.0",
|
||||
"@eslint/plugin-kit": "^0.2.7",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"ajv": "^6.12.4",
|
||||
|
@ -6375,17 +6375,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.0",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
|
@ -10315,9 +10315,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.7.tgz",
|
||||
"integrity": "sha512-yH5bPPyapavo7L+547h3c4jcBXcrKwybQRjwdEIVAd9iXRvy/3T1CC6XSQEgZtRySjKfqvo3Cc0ZF1DTheuIdA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.8.tgz",
|
||||
"integrity": "sha512-Me7N5CKR+D2A1xdWA5t5+kjjT7bwnxZOE6/yDI/ixJdJokszsn2n++mdU5yJwrsTpqFX2B9ZNMBJDwcqk9C9lw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
@ -10404,9 +10404,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ts-jest": {
|
||||
"version": "29.2.5",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz",
|
||||
"integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==",
|
||||
"version": "29.2.6",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz",
|
||||
"integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -10417,7 +10417,7 @@
|
|||
"json5": "^2.2.3",
|
||||
"lodash.memoize": "^4.1.2",
|
||||
"make-error": "^1.3.6",
|
||||
"semver": "^7.6.3",
|
||||
"semver": "^7.7.1",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"bin": {
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@dagrejs/dagre": "^1.1.4",
|
||||
"@hookform/resolvers": "^4.1.0",
|
||||
"@hookform/resolvers": "^4.1.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@tanstack/react-query": "^5.66.8",
|
||||
"@tanstack/react-query-devtools": "^5.66.8",
|
||||
"@tanstack/react-query": "^5.66.9",
|
||||
"@tanstack/react-query-devtools": "^5.66.9",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@uiw/codemirror-themes": "^4.23.8",
|
||||
"@uiw/react-codemirror": "^4.23.8",
|
||||
|
@ -48,16 +48,16 @@
|
|||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.2",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/vite": "^4.0.7",
|
||||
"@tailwindcss/vite": "^4.0.8",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-21e868a-20250216",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-compiler": "^19.0.0-beta-21e868a-20250216",
|
||||
|
@ -66,7 +66,7 @@
|
|||
"globals": "^16.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"tailwindcss": "^4.0.7",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-jest": "^29.2.6",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.1.1"
|
||||
|
|
|
@ -37,7 +37,7 @@ export const GlobalTooltips = () => {
|
|||
<Tooltip
|
||||
clickable
|
||||
id={globalIDs.constituenta_tooltip}
|
||||
layer='z-modalTooltip'
|
||||
layer='z-topmost'
|
||||
className='max-w-[30rem]'
|
||||
hidden={!hoverCst}
|
||||
>
|
||||
|
@ -47,7 +47,7 @@ export const GlobalTooltips = () => {
|
|||
</Tooltip>
|
||||
<Tooltip
|
||||
id={globalIDs.operation_tooltip}
|
||||
layer='z-modalTooltip'
|
||||
layer='z-topmost'
|
||||
className='max-w-[35rem] max-h-[40rem] dense'
|
||||
hidden={!hoverOperation}
|
||||
>
|
||||
|
|
|
@ -119,7 +119,6 @@ export function PickMultiConstituenta({
|
|||
isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
emptySelection={value.length === 0}
|
||||
className='w-fit'
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -21,7 +21,6 @@ interface ToolbarGraphSelectionProps extends Styling {
|
|||
graph: Graph;
|
||||
isCore: (item: number) => boolean;
|
||||
isOwned?: (item: number) => boolean;
|
||||
emptySelection?: boolean;
|
||||
}
|
||||
|
||||
export function ToolbarGraphSelection({
|
||||
|
@ -31,9 +30,10 @@ export function ToolbarGraphSelection({
|
|||
isCore,
|
||||
isOwned,
|
||||
onChange,
|
||||
emptySelection,
|
||||
...restProps
|
||||
}: ToolbarGraphSelectionProps) {
|
||||
const emptySelection = selected.length === 0;
|
||||
|
||||
function handleSelectCore() {
|
||||
const core = [...graph.nodes.keys()].filter(isCore);
|
||||
onChange([...core, ...graph.expandInputs(core)]);
|
||||
|
|
|
@ -4,16 +4,10 @@ interface SelectedCounterProps {
|
|||
totalCount: number;
|
||||
selectedCount: number;
|
||||
position?: string;
|
||||
hideZero?: boolean;
|
||||
}
|
||||
|
||||
export function SelectedCounter({
|
||||
totalCount,
|
||||
selectedCount,
|
||||
hideZero,
|
||||
position = 'top-0 left-0'
|
||||
}: SelectedCounterProps) {
|
||||
if (selectedCount === 0 && hideZero) {
|
||||
export function SelectedCounter({ totalCount, selectedCount, position = 'top-0 left-0' }: SelectedCounterProps) {
|
||||
if (selectedCount === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
type Edge,
|
||||
getNodesBounds,
|
||||
getViewportForBounds,
|
||||
MarkerType,
|
||||
type Node,
|
||||
ReactFlow,
|
||||
|
@ -15,21 +12,16 @@ import {
|
|||
useReactFlow,
|
||||
useStoreApi
|
||||
} from 'reactflow';
|
||||
import clsx from 'clsx';
|
||||
import { toPng } from 'html-to-image';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { useMainHeight } from '@/stores/appLayout';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useTooltipsStore } from '@/stores/tooltips';
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
|
||||
import { CstType } from '../../../backend/types';
|
||||
import { useMutatingRSForm } from '../../../backend/useMutatingRSForm';
|
||||
import { colorBgGraphNode } from '../../../colors';
|
||||
import { InfoConstituenta } from '../../../components/InfoConstituenta';
|
||||
import { ToolbarGraphSelection } from '../../../components/ToolbarGraphSelection';
|
||||
import { type IConstituenta, type IRSForm } from '../../../models/rsform';
|
||||
import { isBasicConcept } from '../../../models/rsformAPI';
|
||||
|
@ -46,12 +38,8 @@ import { ToolbarFocusedCst } from './ToolbarFocusedCst';
|
|||
import { ToolbarTermGraph } from './ToolbarTermGraph';
|
||||
import { ViewHidden } from './ViewHidden';
|
||||
|
||||
const ZOOM_MAX = 3;
|
||||
const ZOOM_MIN = 0.25;
|
||||
|
||||
// ratio to client size used to determine which side of screen popup should be
|
||||
const HOVER_LIMIT_X = 0.4;
|
||||
const HOVER_LIMIT_Y = 0.6;
|
||||
export const ZOOM_MAX = 3;
|
||||
export const ZOOM_MIN = 0.25;
|
||||
|
||||
export function TGFlow() {
|
||||
const mainHeight = useMainHeight();
|
||||
|
@ -65,19 +53,17 @@ export function TGFlow() {
|
|||
selected,
|
||||
setSelected,
|
||||
navigateCst,
|
||||
createCst,
|
||||
toggleSelect,
|
||||
canDeleteSelected,
|
||||
promptDeleteCst
|
||||
} = useRSEdit();
|
||||
|
||||
const showParams = useDialogsStore(state => state.showGraphParams);
|
||||
|
||||
const filter = useTermGraphStore(state => state.filter);
|
||||
const setFilter = useTermGraphStore(state => state.setFilter);
|
||||
const coloring = useTermGraphStore(state => state.coloring);
|
||||
const setColoring = useTermGraphStore(state => state.setColoring);
|
||||
|
||||
const setActiveCst = useTooltipsStore(state => state.setActiveCst);
|
||||
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||
const [edges, setEdges] = useEdgesState([]);
|
||||
|
||||
|
@ -85,14 +71,7 @@ export function TGFlow() {
|
|||
const filteredGraph = produceFilteredGraph(schema, filter, focusCst);
|
||||
const [hidden, setHidden] = useState<number[]>([]);
|
||||
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [hoverID, setHoverID] = useState<number | null>(null);
|
||||
const hoverCst = hoverID && schema.cstByID.get(hoverID);
|
||||
const [hoverCstDebounced] = useDebounce(hoverCst, PARAMETER.graphPopupDelay);
|
||||
const [hoverLeft, setHoverLeft] = useState(true);
|
||||
|
||||
const [needReset, setNeedReset] = useState(true);
|
||||
const [toggleResetView, setToggleResetView] = useState(false);
|
||||
|
||||
function onSelectionChange({ nodes }: { nodes: Node[] }) {
|
||||
const ids = nodes.map(node => Number(node.id));
|
||||
|
@ -115,7 +94,6 @@ export function TGFlow() {
|
|||
}
|
||||
});
|
||||
setHidden(newDismissed);
|
||||
setHoverID(null);
|
||||
}, [schema, filteredGraph]);
|
||||
|
||||
const resetNodes = useCallback(() => {
|
||||
|
@ -177,62 +155,11 @@ export function TGFlow() {
|
|||
resetNodes();
|
||||
}, [needReset, schema, resetNodes, flow.viewportInitialized]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
flow.fitView({ duration: PARAMETER.zoomDuration });
|
||||
}, PARAMETER.minimalTimeout);
|
||||
}, [toggleResetView, flow, focusCst, filter]);
|
||||
|
||||
function handleSetSelected(newSelection: number[]) {
|
||||
setSelected(newSelection);
|
||||
addSelectedNodes(newSelection.map(id => String(id)));
|
||||
}
|
||||
|
||||
function handleCreateCst() {
|
||||
const definition = selected.map(id => schema.cstByID.get(id)!.alias).join(' ');
|
||||
createCst(selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition);
|
||||
}
|
||||
|
||||
function handleDeleteCst() {
|
||||
if (!canDeleteSelected) {
|
||||
return;
|
||||
}
|
||||
promptDeleteCst();
|
||||
}
|
||||
|
||||
function handleSaveImage() {
|
||||
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
||||
if (canvas === null) {
|
||||
toast.error(errorMsg.imageFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
const imageWidth = PARAMETER.ossImageWidth;
|
||||
const imageHeight = PARAMETER.ossImageHeight;
|
||||
const nodesBounds = getNodesBounds(nodes);
|
||||
const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, ZOOM_MIN, ZOOM_MAX);
|
||||
toPng(canvas, {
|
||||
backgroundColor: APP_COLORS.bgDefault,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
style: {
|
||||
width: String(imageWidth),
|
||||
height: String(imageHeight),
|
||||
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom * 2})`
|
||||
}
|
||||
})
|
||||
.then(dataURL => {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('download', `${schema.alias}.png`);
|
||||
a.setAttribute('href', dataURL);
|
||||
a.click();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
toast.error(errorMsg.imageFailed);
|
||||
});
|
||||
}
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (isProcessing) {
|
||||
return;
|
||||
|
@ -240,7 +167,7 @@ export function TGFlow() {
|
|||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setFocusCst(null);
|
||||
handleSetFocus(null);
|
||||
handleSetSelected([]);
|
||||
return;
|
||||
}
|
||||
|
@ -250,31 +177,24 @@ export function TGFlow() {
|
|||
if (event.key === 'Delete') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleDeleteCst();
|
||||
if (canDeleteSelected) {
|
||||
promptDeleteCst();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function handleFoldDerived() {
|
||||
setFilter({
|
||||
...filter,
|
||||
foldDerived: !filter.foldDerived
|
||||
});
|
||||
setTimeout(() => {
|
||||
setToggleResetView(prev => !prev);
|
||||
}, PARAMETER.graphRefreshDelay);
|
||||
}
|
||||
|
||||
function handleSetFocus(cstID: number | null) {
|
||||
if (cstID === null) {
|
||||
setFocusCst(null);
|
||||
} else {
|
||||
const target = schema.cstByID.get(cstID) ?? null;
|
||||
setFocusCst(prev => (prev === target ? null : target));
|
||||
if (target) {
|
||||
}
|
||||
setSelected([]);
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
flow.fitView({ duration: PARAMETER.zoomDuration });
|
||||
}, PARAMETER.minimalTimeout);
|
||||
}
|
||||
|
||||
function handleNodeContextMenu(event: React.MouseEvent, cstID: number) {
|
||||
|
@ -289,32 +209,18 @@ export function TGFlow() {
|
|||
navigateCst(cstID);
|
||||
}
|
||||
|
||||
function handleNodeEnter(event: React.MouseEvent, cstID: number) {
|
||||
setHoverID(cstID);
|
||||
setHoverLeft(
|
||||
event.clientX / window.innerWidth >= HOVER_LIMIT_X || event.clientY / window.innerHeight >= HOVER_LIMIT_Y
|
||||
);
|
||||
function handleNodeEnter(cstID: number) {
|
||||
const cst = schema.cstByID.get(cstID);
|
||||
if (cst) {
|
||||
setActiveCst(cst);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Overlay position='cc-tab-tools' className='flex flex-col items-center rounded-b-2xl cc-blur'>
|
||||
<ToolbarTermGraph
|
||||
noText={filter.noText}
|
||||
foldDerived={filter.foldDerived}
|
||||
showParamsDialog={() => showParams()}
|
||||
onCreate={handleCreateCst}
|
||||
onDelete={handleDeleteCst}
|
||||
onFitView={() => setToggleResetView(prev => !prev)}
|
||||
onSaveImage={handleSaveImage}
|
||||
toggleFoldDerived={handleFoldDerived}
|
||||
toggleNoText={() =>
|
||||
setFilter({
|
||||
...filter,
|
||||
noText: !filter.noText
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ToolbarTermGraph />
|
||||
{focusCst ? <ToolbarFocusedCst focusedCst={focusCst} onResetFocus={() => handleSetFocus(null)} /> : null}
|
||||
{!focusCst ? (
|
||||
<ToolbarGraphSelection
|
||||
graph={schema.graph}
|
||||
|
@ -325,59 +231,17 @@ export function TGFlow() {
|
|||
isOwned={schema.inheritance.length > 0 ? cstID => !schema.cstByID.get(cstID)?.is_inherited : undefined}
|
||||
value={selected}
|
||||
onChange={handleSetSelected}
|
||||
emptySelection={selected.length === 0}
|
||||
/>
|
||||
) : null}
|
||||
{focusCst ? (
|
||||
<ToolbarFocusedCst
|
||||
center={focusCst}
|
||||
reset={() => handleSetFocus(null)}
|
||||
showInputs={filter.focusShowInputs}
|
||||
showOutputs={filter.focusShowOutputs}
|
||||
toggleShowInputs={() =>
|
||||
setFilter({
|
||||
...filter,
|
||||
focusShowInputs: !filter.focusShowInputs
|
||||
})
|
||||
}
|
||||
toggleShowOutputs={() =>
|
||||
setFilter({
|
||||
...filter,
|
||||
focusShowOutputs: !filter.focusShowOutputs
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</Overlay>
|
||||
|
||||
<div className='cc-fade-in' tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||
<SelectedCounter
|
||||
hideZero
|
||||
totalCount={schema.stats?.count_all ?? 0}
|
||||
selectedCount={selected.length}
|
||||
position='top-[4.4rem] sm:top-[4.1rem] left-[0.5rem] sm:left-[0.65rem]'
|
||||
/>
|
||||
|
||||
{hoverCstDebounced ? (
|
||||
<Overlay
|
||||
layer='z-tooltip'
|
||||
position={clsx('top-[3.5rem]', { 'left-[2.6rem]': hoverLeft, 'right-[2.6rem]': !hoverLeft })}
|
||||
className={clsx(
|
||||
'w-[25rem] max-h-[calc(100dvh-15rem)]',
|
||||
'px-3',
|
||||
'cc-scroll-y cc-fade-in',
|
||||
'border shadow-md',
|
||||
'clr-input cc-fade-in',
|
||||
'text-sm'
|
||||
)}
|
||||
style={{
|
||||
opacity: !isDragging && hoverCst && hoverCst === hoverCstDebounced ? 1 : 0
|
||||
}}
|
||||
>
|
||||
<InfoConstituenta className='pt-1 pb-2' data={hoverCstDebounced} />
|
||||
</Overlay>
|
||||
) : null}
|
||||
|
||||
<Overlay position='top-[6.15rem] sm:top-[5.9rem] left-0' className='flex gap-1 pointer-events-none'>
|
||||
<div className='flex flex-col ml-2 w-[13.5rem]'>
|
||||
<GraphSelectors schema={schema} coloring={coloring} onChangeColoring={setColoring} />
|
||||
|
@ -405,10 +269,7 @@ export function TGFlow() {
|
|||
edgeTypes={TGEdgeTypes}
|
||||
maxZoom={ZOOM_MAX}
|
||||
minZoom={ZOOM_MIN}
|
||||
onNodeDragStart={() => setIsDragging(true)}
|
||||
onNodeDragStop={() => setIsDragging(false)}
|
||||
onNodeMouseEnter={(event, node) => handleNodeEnter(event, Number(node.id))}
|
||||
onNodeMouseLeave={() => setHoverID(null)}
|
||||
onNodeMouseEnter={(_, node) => handleNodeEnter(Number(node.id))}
|
||||
onNodeDoubleClick={(event, node) => handleNodeDoubleClick(event, Number(node.id))}
|
||||
onNodeContextMenu={(event, node) => handleNodeContextMenu(event, Number(node.id))}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { useTermGraphStore } from '@/features/rsform/stores/termGraph';
|
||||
|
||||
import { MiniButton } from '@/components/Control';
|
||||
import { IconGraphInputs, IconGraphOutputs, IconReset } from '@/components/Icons';
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
|
@ -8,35 +10,40 @@ import { type IConstituenta } from '../../../models/rsform';
|
|||
import { useRSEdit } from '../RSEditContext';
|
||||
|
||||
interface ToolbarFocusedCstProps {
|
||||
center: IConstituenta;
|
||||
showInputs: boolean;
|
||||
showOutputs: boolean;
|
||||
|
||||
reset: () => void;
|
||||
toggleShowInputs: () => void;
|
||||
toggleShowOutputs: () => void;
|
||||
focusedCst: IConstituenta;
|
||||
onResetFocus: () => void;
|
||||
}
|
||||
|
||||
export function ToolbarFocusedCst({
|
||||
center,
|
||||
reset,
|
||||
showInputs,
|
||||
showOutputs,
|
||||
toggleShowInputs,
|
||||
toggleShowOutputs
|
||||
}: ToolbarFocusedCstProps) {
|
||||
export function ToolbarFocusedCst({ focusedCst, onResetFocus }: ToolbarFocusedCstProps) {
|
||||
const { deselectAll } = useRSEdit();
|
||||
|
||||
const filter = useTermGraphStore(state => state.filter);
|
||||
const setFilter = useTermGraphStore(state => state.setFilter);
|
||||
|
||||
function resetSelection() {
|
||||
reset();
|
||||
onResetFocus();
|
||||
deselectAll();
|
||||
}
|
||||
|
||||
function handleShowInputs() {
|
||||
setFilter({
|
||||
...filter,
|
||||
focusShowInputs: !filter.focusShowInputs
|
||||
});
|
||||
}
|
||||
|
||||
function handleShowOutputs() {
|
||||
setFilter({
|
||||
...filter,
|
||||
focusShowOutputs: !filter.focusShowOutputs
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='items-center cc-icons'>
|
||||
<div className='w-[7.8rem] text-right select-none' style={{ color: APP_COLORS.fgPurple }}>
|
||||
Фокус
|
||||
<b className='px-1'> {center.alias} </b>
|
||||
<b className='px-1'> {focusedCst.alias} </b>
|
||||
</div>
|
||||
<MiniButton
|
||||
titleHtml='Сбросить фокус'
|
||||
|
@ -44,14 +51,14 @@ export function ToolbarFocusedCst({
|
|||
onClick={resetSelection}
|
||||
/>
|
||||
<MiniButton
|
||||
title={showInputs ? 'Скрыть поставщиков' : 'Отобразить поставщиков'}
|
||||
icon={<IconGraphInputs size='1.25rem' className={showInputs ? 'icon-green' : 'icon-primary'} />}
|
||||
onClick={toggleShowInputs}
|
||||
title={filter.focusShowInputs ? 'Скрыть поставщиков' : 'Отобразить поставщиков'}
|
||||
icon={<IconGraphInputs size='1.25rem' className={filter.focusShowInputs ? 'icon-green' : 'icon-primary'} />}
|
||||
onClick={handleShowInputs}
|
||||
/>
|
||||
<MiniButton
|
||||
title={showOutputs ? 'Скрыть потребителей' : 'Отобразить потребителей'}
|
||||
icon={<IconGraphOutputs size='1.25rem' className={showOutputs ? 'icon-green' : 'icon-primary'} />}
|
||||
onClick={toggleShowOutputs}
|
||||
title={filter.focusShowOutputs ? 'Скрыть потребителей' : 'Отобразить потребителей'}
|
||||
icon={<IconGraphOutputs size='1.25rem' className={filter.focusShowOutputs ? 'icon-green' : 'icon-primary'} />}
|
||||
onClick={handleShowOutputs}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { toast } from 'react-toastify';
|
||||
import { getNodesBounds, getViewportForBounds, useNodes, useReactFlow } from 'reactflow';
|
||||
import clsx from 'clsx';
|
||||
import { toPng } from 'html-to-image';
|
||||
|
||||
import { BadgeHelp, HelpTopic } from '@/features/help';
|
||||
import { MiniSelectorOSS } from '@/features/library';
|
||||
import { CstType } from '@/features/rsform/backend/types';
|
||||
import { useTermGraphStore } from '@/features/rsform/stores/termGraph';
|
||||
|
||||
import { MiniButton } from '@/components/Control';
|
||||
import {
|
||||
|
@ -17,39 +22,34 @@ import {
|
|||
IconTypeGraph
|
||||
} from '@/components/Icons';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
|
||||
import { useMutatingRSForm } from '../../../backend/useMutatingRSForm';
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
|
||||
interface ToolbarTermGraphProps {
|
||||
noText: boolean;
|
||||
foldDerived: boolean;
|
||||
import { ZOOM_MAX, ZOOM_MIN } from './TGFlow';
|
||||
|
||||
showParamsDialog: () => void;
|
||||
onCreate: () => void;
|
||||
onDelete: () => void;
|
||||
onFitView: () => void;
|
||||
onSaveImage: () => void;
|
||||
|
||||
toggleFoldDerived: () => void;
|
||||
toggleNoText: () => void;
|
||||
}
|
||||
|
||||
export function ToolbarTermGraph({
|
||||
noText,
|
||||
foldDerived,
|
||||
toggleNoText,
|
||||
toggleFoldDerived,
|
||||
showParamsDialog,
|
||||
onCreate,
|
||||
onDelete,
|
||||
onFitView,
|
||||
onSaveImage
|
||||
}: ToolbarTermGraphProps) {
|
||||
export function ToolbarTermGraph() {
|
||||
const isProcessing = useMutatingRSForm();
|
||||
const darkMode = usePreferencesStore(state => state.darkMode);
|
||||
const {
|
||||
schema, //
|
||||
selected,
|
||||
navigateOss,
|
||||
isContentEditable,
|
||||
canDeleteSelected,
|
||||
createCst,
|
||||
promptDeleteCst
|
||||
} = useRSEdit();
|
||||
const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph);
|
||||
const { schema, navigateOss, isContentEditable, canDeleteSelected } = useRSEdit();
|
||||
const showParams = useDialogsStore(state => state.showGraphParams);
|
||||
const filter = useTermGraphStore(state => state.filter);
|
||||
const setFilter = useTermGraphStore(state => state.setFilter);
|
||||
const nodes = useNodes();
|
||||
const flow = useReactFlow();
|
||||
|
||||
function handleShowTypeGraph() {
|
||||
const typeInfo = schema.items.map(item => ({
|
||||
|
@ -60,6 +60,74 @@ export function ToolbarTermGraph({
|
|||
showTypeGraph({ items: typeInfo });
|
||||
}
|
||||
|
||||
function handleCreateCst() {
|
||||
const definition = selected.map(id => schema.cstByID.get(id)!.alias).join(' ');
|
||||
createCst(selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition);
|
||||
}
|
||||
|
||||
function handleDeleteCst() {
|
||||
if (!canDeleteSelected || isProcessing) {
|
||||
return;
|
||||
}
|
||||
promptDeleteCst();
|
||||
}
|
||||
|
||||
function handleToggleNoText() {
|
||||
setFilter({
|
||||
...filter,
|
||||
noText: !filter.noText
|
||||
});
|
||||
}
|
||||
|
||||
function handleSaveImage() {
|
||||
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
||||
if (canvas === null) {
|
||||
toast.error(errorMsg.imageFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
const imageWidth = PARAMETER.ossImageWidth;
|
||||
const imageHeight = PARAMETER.ossImageHeight;
|
||||
const nodesBounds = getNodesBounds(nodes);
|
||||
const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, ZOOM_MIN, ZOOM_MAX);
|
||||
toPng(canvas, {
|
||||
backgroundColor: darkMode ? APP_COLORS.bgDefaultDark : APP_COLORS.bgDefaultLight,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
style: {
|
||||
width: String(imageWidth),
|
||||
height: String(imageHeight),
|
||||
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom * 2})`
|
||||
}
|
||||
})
|
||||
.then(dataURL => {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('download', `${schema.alias}.png`);
|
||||
a.setAttribute('href', dataURL);
|
||||
a.click();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
toast.error(errorMsg.imageFailed);
|
||||
});
|
||||
}
|
||||
|
||||
function handleFitView() {
|
||||
setTimeout(() => {
|
||||
flow.fitView({ duration: PARAMETER.zoomDuration });
|
||||
}, PARAMETER.minimalTimeout);
|
||||
}
|
||||
|
||||
function handleFoldDerived() {
|
||||
setFilter({
|
||||
...filter,
|
||||
foldDerived: !filter.foldDerived
|
||||
});
|
||||
setTimeout(() => {
|
||||
flow.fitView({ duration: PARAMETER.zoomDuration });
|
||||
}, PARAMETER.graphRefreshDelay);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='cc-icons'>
|
||||
{schema.oss.length > 0 ? (
|
||||
|
@ -71,41 +139,41 @@ export function ToolbarTermGraph({
|
|||
<MiniButton
|
||||
title='Настройки фильтрации узлов и связей'
|
||||
icon={<IconFilter size='1.25rem' className='icon-primary' />}
|
||||
onClick={showParamsDialog}
|
||||
onClick={showParams}
|
||||
/>
|
||||
<MiniButton
|
||||
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
||||
title='Граф целиком'
|
||||
onClick={onFitView}
|
||||
onClick={handleFitView}
|
||||
/>
|
||||
<MiniButton
|
||||
title={!noText ? 'Скрыть текст' : 'Отобразить текст'}
|
||||
title={!filter.noText ? 'Скрыть текст' : 'Отобразить текст'}
|
||||
icon={
|
||||
!noText ? (
|
||||
!filter.noText ? (
|
||||
<IconText size='1.25rem' className='icon-green' />
|
||||
) : (
|
||||
<IconTextOff size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={toggleNoText}
|
||||
onClick={handleToggleNoText}
|
||||
/>
|
||||
<MiniButton
|
||||
title={!foldDerived ? 'Скрыть порожденные' : 'Отобразить порожденные'}
|
||||
title={!filter.foldDerived ? 'Скрыть порожденные' : 'Отобразить порожденные'}
|
||||
icon={
|
||||
!foldDerived ? (
|
||||
!filter.foldDerived ? (
|
||||
<IconClustering size='1.25rem' className='icon-green' />
|
||||
) : (
|
||||
<IconClusteringOff size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={toggleFoldDerived}
|
||||
onClick={handleFoldDerived}
|
||||
/>
|
||||
{isContentEditable ? (
|
||||
<MiniButton
|
||||
title='Новая конституента'
|
||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||
disabled={isProcessing}
|
||||
onClick={onCreate}
|
||||
onClick={handleCreateCst}
|
||||
/>
|
||||
) : null}
|
||||
{isContentEditable ? (
|
||||
|
@ -113,7 +181,7 @@ export function ToolbarTermGraph({
|
|||
title='Удалить выбранные'
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
disabled={!canDeleteSelected || isProcessing}
|
||||
onClick={onDelete}
|
||||
onClick={handleDeleteCst}
|
||||
/>
|
||||
) : null}
|
||||
<MiniButton
|
||||
|
@ -124,7 +192,7 @@ export function ToolbarTermGraph({
|
|||
<MiniButton
|
||||
icon={<IconImage size='1.25rem' className='icon-primary' />}
|
||||
title='Сохранить изображение'
|
||||
onClick={onSaveImage}
|
||||
onClick={handleSaveImage}
|
||||
/>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.UI_GRAPH_TERM}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import { Handle, Position } from 'reactflow';
|
||||
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
const DESCRIPTION_THRESHOLD = 15;
|
||||
const LABEL_THRESHOLD = 3;
|
||||
|
@ -39,6 +40,7 @@ export function TGNode(node: TGNodeInternal) {
|
|||
backgroundColor: !node.selected ? node.data.fill : APP_COLORS.bgActiveSelection,
|
||||
fontSize: node.data.label.length > LABEL_THRESHOLD ? FONT_SIZE_MED : FONT_SIZE_MAX
|
||||
}}
|
||||
data-tooltip-id={globalIDs.constituenta_tooltip}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
|
|
Loading…
Reference in New Issue
Block a user