From 3f310cd689bb6c2e3eddd47daac8418707fd1abb Mon Sep 17 00:00:00 2001 From: d9m0n4 Date: Sun, 23 Feb 2025 22:55:39 +0300 Subject: [PATCH 1/4] feat(46266194): board zoom toolbar --- package-lock.json | 185 +++++++++--------- packages/pkg.module.board/Board.tsx | 5 + .../components/BackgroundLayer.tsx | 4 +- .../pkg.module.board/components/ZoomMenu.tsx | 19 +- packages/pkg.module.board/const.ts | 1 + .../pkg.module.board/hooks/useWheelZoom.ts | 3 +- 6 files changed, 120 insertions(+), 97 deletions(-) diff --git a/package-lock.json b/package-lock.json index 06979964..b0137175 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1938,9 +1938,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==", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -1949,9 +1949,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==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1991,9 +1991,9 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "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==", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -2007,11 +2007,11 @@ } }, "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==", "dependencies": { - "@eslint/core": "^0.11.0", + "@eslint/core": "^0.12.0", "levn": "^0.4.1" }, "engines": { @@ -2164,9 +2164,9 @@ "peer": true }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "engines": { "node": ">=18.18" }, @@ -3049,12 +3049,12 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.0.tgz", - "integrity": "sha512-04VHHV1KIN/c1wLWwzmLI02d/welgscBJ4BuDqrHaxd+ZIdlVXK9UYQsYf3JwSeF52z/4YoSzr8bfdVBSWoMAg==", + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz", + "integrity": "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.0", + "@opentelemetry/instrumentation": "^0.57.1", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -6284,9 +6284,9 @@ } }, "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==", "dependencies": { "undici-types": "~6.20.0" } @@ -10494,9 +10494,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==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -10869,20 +10869,20 @@ } }, "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==", "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", @@ -10998,9 +10998,9 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.8.2.tgz", - "integrity": "sha512-o0nvXxsatYCDTzI1K5b3aYGQ6PjpDGJEVN86zqJw5SEewhmmggfRTotd2dqWr2t2zbeYpIEWGTCkgtUpIEIcaQ==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.8.3.tgz", + "integrity": "sha512-A0bu4Ks2QqDWNpeEgTQMPTngaMhuDu4yv6xpftBMAf+1ziXnpx+eSR1WRfoPTe2BAiAjHFZ7kSNx1fvr5g5pmQ==", "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.3.7", @@ -11008,7 +11008,7 @@ "get-tsconfig": "^4.10.0", "is-bun-module": "^1.0.2", "stable-hash": "^0.0.4", - "tinyglobby": "^0.2.11" + "tinyglobby": "^0.2.12" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -11912,16 +11912,16 @@ } }, "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==", "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", @@ -12316,15 +12316,15 @@ } }, "node_modules/hast-util-from-parse5": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.2.tgz", - "integrity": "sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", - "property-information": "^6.0.0", + "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" @@ -12371,9 +12371,9 @@ } }, "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", - "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.3.tgz", + "integrity": "sha512-pdpkP8YD4v+qMKn2lnKSiJvZvb3FunDmFYQvVOsoO08+eTNWdaWKPMrC5wwNICtU3dQWHhElj5Sf5jPEnv4qJg==", "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", @@ -12385,7 +12385,7 @@ "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", + "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-object": "^1.0.0", "unist-util-position": "^5.0.0", @@ -12414,6 +12414,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-parse5/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -12427,14 +12436,14 @@ } }, "node_modules/hastscript": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.0.tgz", - "integrity": "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", - "property-information": "^6.0.0", + "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" }, "funding": { @@ -13403,14 +13412,14 @@ } }, "node_modules/its-fine": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.2.5.tgz", - "integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", + "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", "dependencies": { - "@types/react-reconciler": "^0.28.0" + "@types/react-reconciler": "^0.28.9" }, "peerDependencies": { - "react": ">=18.0" + "react": "^19.0.0" } }, "node_modules/jackspeak": { @@ -15709,9 +15718,9 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz", - "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", @@ -16226,9 +16235,9 @@ } }, "node_modules/postcss": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", - "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -16430,9 +16439,9 @@ } }, "node_modules/prettier": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", - "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", + "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -16572,9 +16581,9 @@ } }, "node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", + "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -16828,9 +16837,9 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-konva": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/react-konva/-/react-konva-19.0.2.tgz", - "integrity": "sha512-JNCfMO2V7Wm4fh+YwvMkuo3G7/2yq0/JhLbqG2yyuw+vHOn1qj9jm0+Jxq7R83x0M6mi45uK0GKn4lDWMhHReQ==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/react-konva/-/react-konva-19.0.3.tgz", + "integrity": "sha512-MxaGCwKCo9DiGBPfsJKU1JShZ9YRsCjrJNF/KVyyzBB7UjrRtY7s46hH1Obr70y4trYZzFuqmW3ljB/mO0dAfA==", "funding": [ { "type": "patreon", @@ -16846,8 +16855,8 @@ } ], "dependencies": { - "@types/react-reconciler": "^0.28.8", - "its-fine": "^1.2.5", + "@types/react-reconciler": "^0.28.9", + "its-fine": "^2.0.0", "react-reconciler": "0.31.0", "scheduler": "0.25.0" }, @@ -17258,9 +17267,9 @@ } }, "node_modules/require-in-the-middle": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.1.tgz", - "integrity": "sha512-fgZEz/t3FDrU9o7EhI+iNNq1pNNpJImOvX72HUd6RoFiw8MaKd8/gR5tLuc8A0G0e55LMbP6ImjnmXY6zrTmjw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", @@ -18825,9 +18834,9 @@ "dev": true }, "node_modules/tinyglobby": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.11.tgz", - "integrity": "sha512-32TmKeeKUahv0Go8WmQgiEp9Y21NuxjwjqiRC1nrUB51YacfSwuB44xgXD+HdIppmMRgjQNPdrHyA6vIybYZ+g==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", "dependencies": { "fdir": "^6.4.3", "picomatch": "^4.0.2" @@ -20347,9 +20356,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "engines": { "node": ">=10.0.0" }, diff --git a/packages/pkg.module.board/Board.tsx b/packages/pkg.module.board/Board.tsx index 58ba6108..ce6edf00 100644 --- a/packages/pkg.module.board/Board.tsx +++ b/packages/pkg.module.board/Board.tsx @@ -9,10 +9,12 @@ import { ToolType } from './types'; import { useBoardStore, useUIStore } from './store'; import { useWheelZoom } from './hooks'; import { BackgroundLayer } from './components'; +import { ZoomMenu } from './components/ZoomMenu'; export const Board: React.FC = () => { // Выбранный инструмент const [selectedTool, setSelectedTool] = useState('pen'); + const { boardElements } = useBoardStore(); const stageRef = useRef(null); @@ -56,6 +58,7 @@ export const Board: React.FC = () => { return (
+ { className="bg-gray-0" onWheel={handleOnWheel} onDragMove={handleDragMove} + scaleX={scale} + scaleY={scale} draggable > diff --git a/packages/pkg.module.board/components/BackgroundLayer.tsx b/packages/pkg.module.board/components/BackgroundLayer.tsx index afa07487..68cf7d66 100644 --- a/packages/pkg.module.board/components/BackgroundLayer.tsx +++ b/packages/pkg.module.board/components/BackgroundLayer.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useMemo } from 'react'; import { Layer, Shape } from 'react-konva'; import { useUIStore } from '../store'; -import { boardGridStep } from '../const'; +import { boardBackgroundDotSize, boardGridStep } from '../const'; type BackgroundLayerPropsT = { scaleValue: number; @@ -46,7 +46,7 @@ export const BackgroundLayer = ({ scaleValue }: BackgroundLayerPropsT) => { for (let x = startX; x <= endX; x += boardGridStep) { for (let y = startY; y <= endY; y += boardGridStep) { context.beginPath(); - context.arc(x, y, 4 / scaleValue, 0, Math.PI * 2); + context.arc(x, y, boardBackgroundDotSize / scaleValue, 0, Math.PI * 2); context.fill(); } } diff --git a/packages/pkg.module.board/components/ZoomMenu.tsx b/packages/pkg.module.board/components/ZoomMenu.tsx index 3a84da40..bc27f5c8 100644 --- a/packages/pkg.module.board/components/ZoomMenu.tsx +++ b/packages/pkg.module.board/components/ZoomMenu.tsx @@ -4,21 +4,28 @@ import { useUIStore } from '../store'; export const ZoomMenu = () => { // Получаем значения из UI-стора - const { scale, zoomIn, zoomOut } = useUIStore(); + const { scale, zoomIn, zoomOut, setScale } = useUIStore(); return ( -
-
+
+
-
{(scale * 100).toFixed(0)}%
+ diff --git a/packages/pkg.module.board/hooks/index.ts b/packages/pkg.module.board/hooks/index.ts index 7573ae35..f97987a0 100644 --- a/packages/pkg.module.board/hooks/index.ts +++ b/packages/pkg.module.board/hooks/index.ts @@ -1 +1 @@ -export { useWheelZoom } from './useWheelZoom'; +export { useZoom } from './useWheelZoom'; diff --git a/packages/pkg.module.board/hooks/useWheelZoom.ts b/packages/pkg.module.board/hooks/useWheelZoom.ts index ec1ba4b4..4eabbd70 100644 --- a/packages/pkg.module.board/hooks/useWheelZoom.ts +++ b/packages/pkg.module.board/hooks/useWheelZoom.ts @@ -2,23 +2,23 @@ // hooks/useWheelZoom.ts import Konva from 'konva'; import { useCallback } from 'react'; +import { calculateZoom, defaultZoomConfig } from '../utils'; import { useUIStore } from '../store'; +import { roundScale } from '../utils/zoomConfig'; /** * Хук для обработки масштабирования (зума) при помощи колесика мыши/тачпада. - * Принимает ссылку на Stage и возвращает обработчик onWheel. */ -export const useWheelZoom = (stageRef: React.RefObject) => { - const { setScale } = useUIStore(); +export const useZoom = (stageRef: React.RefObject) => { + const { setScale, setStagePosition } = useUIStore(); const handleWheel = useCallback( - (e: any) => { + (e: Konva.KonvaEventObject) => { e.evt.preventDefault(); const stage = stageRef.current; if (!stage) return; - const scaleBy = 1.1; const oldScale = stage.scaleX(); const pointer = stage.getPointerPosition(); if (!pointer) return; @@ -29,10 +29,20 @@ export const useWheelZoom = (stageRef: React.RefObject) => { y: (pointer.y - stage.y()) / oldScale, }; - // Если прокрутка вниз – уменьшаем масштаб, иначе – увеличиваем - let newScale = e.evt.deltaY > 0 ? oldScale / scaleBy : oldScale * scaleBy; + const delta = e.evt.deltaY; + const scaleStep = 0.02; + const speedFactor = Math.abs(delta) > 50 ? 2 : 1; + + let newScale = + delta > 0 ? oldScale - scaleStep * speedFactor : oldScale + scaleStep * speedFactor; + // Ограничиваем масштаб от 50% до 300% - newScale = Math.max(0.5, Math.min(newScale, 3)); + newScale = Math.max( + defaultZoomConfig.minScale, + Math.min(newScale, defaultZoomConfig.maxScale), + ); + + newScale = roundScale(newScale); setScale(newScale); // Обновляем масштаб и позицию Stage, чтобы точка под курсором оставалась на месте @@ -47,5 +57,73 @@ export const useWheelZoom = (stageRef: React.RefObject) => { [stageRef, setScale], ); - return handleWheel; + const handleZoom = useCallback( + (direction: 'in' | 'out') => { + const stage = stageRef.current; + if (!stage) { + return; + } + + const oldScale = stage.scaleX(); + let newScale = oldScale; + + if (direction === 'in') { + newScale = oldScale >= 1 ? oldScale + 0.25 : oldScale * 1.1; + } else { + newScale = oldScale > 1 ? oldScale - 0.25 : oldScale * 0.9; + } + + // Ограничиваем масштаб в рамках minScale и maxScale + newScale = Math.max( + defaultZoomConfig.minScale, + Math.min(newScale, defaultZoomConfig.maxScale), + ); + newScale = roundScale(newScale); + + const result = calculateZoom(stageRef, newScale, null, defaultZoomConfig); + if (!result) { + return; + } + + const { newScale: finalScale, newPos } = result; + + stage.to({ + scaleX: finalScale, + scaleY: finalScale, + x: newPos.x, + y: newPos.y, + duration: defaultZoomConfig.animationDuration / 1000, + easing: Konva.Easings.Linear, + }); + + setScale(finalScale); + setStagePosition({ x: newPos.x, y: newPos.y }); + stage.batchDraw(); + }, + [setScale, stageRef, setStagePosition], + ); + + const handleZoomIn = useCallback(() => handleZoom('in'), [handleZoom]); + const handleZoomOut = useCallback(() => handleZoom('out'), [handleZoom]); + + const handleResetZoom = useCallback(() => { + const stage = stageRef.current; + if (!stage) return; + + setScale(1); + setStagePosition({ x: stage.x(), y: stage.y() }); + + stage.to({ + scaleX: 1, + scaleY: 1, + x: stage.x(), + y: stage.y(), + duration: defaultZoomConfig.animationDuration / 1000, + easing: Konva.Easings.Linear, + }); + + stage.batchDraw(); + }, [stageRef, setScale, setStagePosition]); + + return { handleWheel, handleZoomIn, handleZoomOut, handleResetZoom }; }; diff --git a/packages/pkg.module.board/types.ts b/packages/pkg.module.board/types.ts index bca55eb2..61b9c17a 100644 --- a/packages/pkg.module.board/types.ts +++ b/packages/pkg.module.board/types.ts @@ -27,3 +27,15 @@ export interface BoardElement { // Свойства для изображения src?: string; } + +export interface Point { + x: number; + y: number; +} + +export interface ZoomConfig { + minScale: number; + maxScale: number; + scaleBy: number; + animationDuration: number; +} diff --git a/packages/pkg.module.board/utils/calculateBoardZoom.ts b/packages/pkg.module.board/utils/calculateBoardZoom.ts new file mode 100644 index 00000000..f56e532a --- /dev/null +++ b/packages/pkg.module.board/utils/calculateBoardZoom.ts @@ -0,0 +1,36 @@ +import { Stage } from 'konva/lib/Stage'; +import { Point, ZoomConfig } from '../types'; + +export const calculateZoom = ( + stageRef: React.RefObject, + newScale: number, + pointer: Point | null, + config: ZoomConfig, +): { newScale: number; newPos: Point } | null => { + const stage = stageRef?.current; + if (!stage) return null; + + const { minScale, maxScale } = config; + + if (newScale > maxScale || newScale < minScale) return null; + + const oldScale = stage.scaleX(); + + const center = { x: stage.width() / 2, y: stage.height() / 2 }; + const zoomPointer = pointer || center; + + const mousePointTo = { + x: zoomPointer.x / oldScale - stage.x() / oldScale, + y: zoomPointer.y / oldScale - stage.y() / oldScale, + }; + + const newPos = { + x: zoomPointer.x - mousePointTo.x * newScale, + y: zoomPointer.y - mousePointTo.y * newScale, + }; + + newPos.x = Math.round(newPos.x * 100) / 100; + newPos.y = Math.round(newPos.y * 100) / 100; + + return { newScale, newPos }; +}; diff --git a/packages/pkg.module.board/utils/index.ts b/packages/pkg.module.board/utils/index.ts new file mode 100644 index 00000000..30524cc9 --- /dev/null +++ b/packages/pkg.module.board/utils/index.ts @@ -0,0 +1,2 @@ +export { defaultZoomConfig } from './zoomConfig'; +export { calculateZoom } from './calculateBoardZoom'; diff --git a/packages/pkg.module.board/utils/zoomConfig.ts b/packages/pkg.module.board/utils/zoomConfig.ts new file mode 100644 index 00000000..b0bdfb05 --- /dev/null +++ b/packages/pkg.module.board/utils/zoomConfig.ts @@ -0,0 +1,10 @@ +import { ZoomConfig } from '../types'; + +export const defaultZoomConfig: ZoomConfig = { + minScale: 0.01, + maxScale: 3, + scaleBy: 0.1, + animationDuration: 250, +}; + +export const roundScale = (scale: number) => Math.round(scale * 1000) / 1000; From 1ac736bae522aa431b821d02841f3b82a3364afa Mon Sep 17 00:00:00 2001 From: d9m0n4 Date: Mon, 3 Mar 2025 21:29:54 +0300 Subject: [PATCH 3/4] fix(46266194): fix board bg scale logic --- packages/pkg.module.board/Board.tsx | 15 ++++------ .../components/BackgroundLayer.tsx | 23 ++++++++------ .../pkg.module.board/components/ZoomMenu.tsx | 3 +- packages/pkg.module.board/const.ts | 5 ++-- .../pkg.module.board/hooks/useWheelZoom.ts | 30 ++++++++++--------- packages/pkg.module.board/utils/zoomConfig.ts | 6 +++- 6 files changed, 44 insertions(+), 38 deletions(-) diff --git a/packages/pkg.module.board/Board.tsx b/packages/pkg.module.board/Board.tsx index c9b34f17..ad60c508 100644 --- a/packages/pkg.module.board/Board.tsx +++ b/packages/pkg.module.board/Board.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ // components/Whiteboard.tsx import React, { useState, useRef, useEffect } from 'react'; -import { Layer, Rect, Stage } from 'react-konva'; +import { Stage } from 'react-konva'; import Konva from 'konva'; import CanvasLayer from './CanvasLayer'; import { ToolType } from './types'; @@ -14,12 +14,14 @@ export const Board: React.FC = () => { // Выбранный инструмент const [selectedTool, setSelectedTool] = useState('pen'); - const { boardElements } = useBoardStore(); const stageRef = useRef(null); - // Получаем scale, setScale, zoomIn и zoomOut из UI‑стора + const { boardElements } = useBoardStore(); const { setStagePosition } = useUIStore(); + const boardWidth = window.innerWidth; + const boardHeight = window.innerHeight; + // Пример хоткеев: Escape – переключиться в режим выделения, // Delete – удалить выделенные элементы (реализовать логику выбора) useEffect(() => { @@ -42,10 +44,6 @@ export const Board: React.FC = () => { handleWheel(e); }; - // Получаем размеры доски (для примера используем window.innerWidth и window.innerHeight - 50) - const boardWidth = window.innerWidth; - const boardHeight = window.innerHeight; - return (
@@ -62,9 +60,6 @@ export const Board: React.FC = () => { draggable > - - -
diff --git a/packages/pkg.module.board/components/BackgroundLayer.tsx b/packages/pkg.module.board/components/BackgroundLayer.tsx index 73316937..8e1eb2b1 100644 --- a/packages/pkg.module.board/components/BackgroundLayer.tsx +++ b/packages/pkg.module.board/components/BackgroundLayer.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useMemo } from 'react'; import { Layer, Shape } from 'react-konva'; import { useUIStore } from '../store'; -import { boardBackgroundDotSize, boardGridStep } from '../const'; +import { baseDotSize, baseGridStep, minDotSize } from '../const'; export const BackgroundLayer = () => { const { viewport, setViewport, stagePosition, scale } = useUIStore(); @@ -22,24 +22,29 @@ export const BackgroundLayer = () => { const visibleWidth = viewport.width / scale; const visibleHeight = viewport.height / scale; + const stepMultiplier = 2 ** Math.round(Math.log2(1 / scale)); + + const gridStep = baseGridStep * stepMultiplier; + + const dotSize = Math.max(baseDotSize * stepMultiplier ** 1, minDotSize); + const buffer = Math.max(visibleWidth, visibleHeight) * 2; - const startX = Math.floor((-stagePosition.x / scale - buffer) / boardGridStep) * boardGridStep; + const startX = Math.floor((-stagePosition.x / scale - buffer) / gridStep) * gridStep; const endX = - Math.ceil((-stagePosition.x / scale + visibleWidth + buffer) / boardGridStep) * boardGridStep; - const startY = Math.floor((-stagePosition.y / scale - buffer) / boardGridStep) * boardGridStep; + Math.ceil((-stagePosition.x / scale + visibleWidth + buffer) / gridStep) * gridStep; + const startY = Math.floor((-stagePosition.y / scale - buffer) / gridStep) * gridStep; const endY = - Math.ceil((-stagePosition.y / scale + visibleHeight + buffer) / boardGridStep) * - boardGridStep; + Math.ceil((-stagePosition.y / scale + visibleHeight + buffer) / gridStep) * gridStep; return ( { context.fillStyle = '#e8e8e8'; - for (let x = startX; x <= endX; x += boardGridStep) { - for (let y = startY; y <= endY; y += boardGridStep) { + for (let x = startX; x <= endX; x += gridStep) { + for (let y = startY; y <= endY; y += gridStep) { context.beginPath(); - context.arc(x, y, boardBackgroundDotSize / scale, 0, Math.PI * 2); + context.arc(x, y, dotSize, 0, Math.PI * 2); context.fill(); } } diff --git a/packages/pkg.module.board/components/ZoomMenu.tsx b/packages/pkg.module.board/components/ZoomMenu.tsx index 414a19fc..bae665c5 100644 --- a/packages/pkg.module.board/components/ZoomMenu.tsx +++ b/packages/pkg.module.board/components/ZoomMenu.tsx @@ -9,7 +9,6 @@ type ZoomMenuPropsT = { }; export const ZoomMenu = ({ zoomIn, zoomOut, resetZoom }: ZoomMenuPropsT) => { - // Получаем значения из UI-стора const { scale } = useUIStore(); return ( @@ -28,7 +27,7 @@ export const ZoomMenu = ({ zoomIn, zoomOut, resetZoom }: ZoomMenuPropsT) => { size="s" onClick={() => resetZoom()} > - {(scale * 100).toFixed(0)}% + {scale < 0.01 ? '< 1%' : `${(scale * 100).toFixed(0)}%`}