feat: add resizer and add zoom in / out for canvas
This commit is contained in:
parent
8207c37e12
commit
6cf7ef02ac
|
@ -0,0 +1,50 @@
|
|||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
BaseBoard: typeof import('./src/components/equipments/BaseBoard.vue')['default']
|
||||
Canvas: typeof import('./src/components/Canvas.vue')['default']
|
||||
CollapsibleSection: typeof import('./src/components/CollapsibleSection.vue')['default']
|
||||
ComponentSelector: typeof import('./src/components/ComponentSelector.vue')['default']
|
||||
DDR: typeof import('./src/components/equipments/DDR.vue')['default']
|
||||
DDS: typeof import('./src/components/equipments/DDS.vue')['default']
|
||||
DDSPropertyEditor: typeof import('./src/components/equipments/DDSPropertyEditor.vue')['default']
|
||||
DiagramCanvas: typeof import('./src/components/DiagramCanvas.vue')['default']
|
||||
Dialog: typeof import('./src/components/Dialog.vue')['default']
|
||||
ETH: typeof import('./src/components/equipments/ETH.vue')['default']
|
||||
HDMI: typeof import('./src/components/equipments/HDMI.vue')['default']
|
||||
LabCanvas: typeof import('./src/components/LabCanvas.vue')['default']
|
||||
LoginCard: typeof import('./src/components/LoginCard.vue')['default']
|
||||
MarkdownRenderer: typeof import('./src/components/MarkdownRenderer.vue')['default']
|
||||
MechanicalButton: typeof import('./src/components/equipments/MechanicalButton.vue')['default']
|
||||
MotherBoard: typeof import('./src/components/equipments/MotherBoard.vue')['default']
|
||||
MotherBoardCaps: typeof import('./src/components/equipments/MotherBoardCaps.vue')['default']
|
||||
Navbar: typeof import('./src/components/Navbar.vue')['default']
|
||||
PG2L100H_FBG676: typeof import('./src/components/equipments/PG2L100H_FBG676.vue')['default']
|
||||
Pin: typeof import('./src/components/equipments/Pin.vue')['default']
|
||||
PopButton: typeof import('./src/components/PopButton.vue')['default']
|
||||
PropertyEditor: typeof import('./src/components/PropertyEditor.vue')['default']
|
||||
PropertyPanel: typeof import('./src/components/PropertyPanel.vue')['default']
|
||||
RekaSplitterGroup: typeof import('reka-ui')['SplitterGroup']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SD: typeof import('./src/components/equipments/SD.vue')['default']
|
||||
SevenSegmentDisplay: typeof import('./src/components/equipments/SevenSegmentDisplay.vue')['default']
|
||||
SFP: typeof import('./src/components/equipments/SFP.vue')['default']
|
||||
Sidebar: typeof import('./src/components/Sidebar.vue')['default']
|
||||
SMA: typeof import('./src/components/equipments/SMA.vue')['default']
|
||||
SMT_LED: typeof import('./src/components/equipments/SMT_LED.vue')['default']
|
||||
Switch: typeof import('./src/components/equipments/Switch.vue')['default']
|
||||
ThemeControlButton: typeof import('./src/components/ThemeControlButton.vue')['default']
|
||||
ThemeControlToggle: typeof import('./src/components/ThemeControlToggle.vue')['default']
|
||||
TutorialCarousel: typeof import('./src/components/TutorialCarousel.vue')['default']
|
||||
UploadCard: typeof import('./src/components/UploadCard.vue')['default']
|
||||
Wire: typeof import('./src/components/equipments/Wire.vue')['default']
|
||||
}
|
||||
}
|
|
@ -15,10 +15,11 @@
|
|||
"konva": "^9.3.20",
|
||||
"lodash": "^4.17.21",
|
||||
"log-symbols": "^7.0.0",
|
||||
"lucide-vue-next": "^0.525.0",
|
||||
"marked": "^12.0.0",
|
||||
"mathjs": "^14.4.0",
|
||||
"pinia": "^3.0.1",
|
||||
"tinypool": "^1.0.2",
|
||||
"reka-ui": "^2.3.1",
|
||||
"ts-log": "^2.2.7",
|
||||
"ts-results-es": "^5.0.1",
|
||||
"vue": "^3.5.13",
|
||||
|
@ -41,6 +42,7 @@
|
|||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^4.0.12",
|
||||
"typescript": "~5.7.3",
|
||||
"unplugin-vue-components": "^28.8.0",
|
||||
"vite": "^6.1.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.2",
|
||||
"vue-tsc": "^2.2.2"
|
||||
|
@ -962,6 +964,86 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz",
|
||||
"integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz",
|
||||
"integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.2",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@floating-ui/vue": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.7.tgz",
|
||||
"integrity": "sha512-idmAtbAIigGXN2SI5gItiXYBYtNfDTP9yIiObxgu13dgtG7ARCHlNfnR29GxP4LI4o13oiwsJ8wVgghj1lNqcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.2",
|
||||
"@floating-ui/utils": "^0.2.10",
|
||||
"vue-demi": ">=0.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/vue/node_modules/vue-demi": {
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@internationalized/date": {
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz",
|
||||
"integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@internationalized/number": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.3.tgz",
|
||||
"integrity": "sha512-p+Zh1sb6EfrfVaS86jlHGQ9HA66fJhV9x5LiE5vCbZtXEHAuhcmUZUdZ4WrFpUBfNalr2OkAJI5AcKEQF+Lebw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
|
@ -1354,6 +1436,15 @@
|
|||
"url": "https://github.com/sponsors/Fuzzyma"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/node": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz",
|
||||
|
@ -1622,6 +1713,32 @@
|
|||
"tailwindcss": "4.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.13.12",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
|
||||
"integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/vue-virtual": {
|
||||
"version": "3.13.12",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz",
|
||||
"integrity": "sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.13.12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^2.7.0 || ^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node22": {
|
||||
"version": "22.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.1.tgz",
|
||||
|
@ -1652,6 +1769,12 @@
|
|||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
||||
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz",
|
||||
|
@ -1992,6 +2115,55 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "12.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz",
|
||||
"integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.21",
|
||||
"@vueuse/metadata": "12.8.2",
|
||||
"@vueuse/shared": "12.8.2",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "12.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz",
|
||||
"integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "12.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz",
|
||||
"integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/alien-signals": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz",
|
||||
|
@ -2012,6 +2184,45 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
|
||||
|
@ -2066,6 +2277,19 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/birpc": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz",
|
||||
|
@ -2085,6 +2309,19 @@
|
|||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||
|
@ -2155,6 +2392,31 @@
|
|||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/complex.js": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz",
|
||||
|
@ -2168,6 +2430,13 @@
|
|||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/confbox": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
|
||||
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
|
@ -2252,9 +2521,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -2318,6 +2587,12 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
|
||||
|
@ -2461,6 +2736,13 @@
|
|||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/exsolve": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz",
|
||||
"integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
|
@ -2492,6 +2774,19 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
|
@ -2563,6 +2858,19 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
|
@ -2615,6 +2923,19 @@
|
|||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
|
||||
|
@ -2631,6 +2952,29 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-inside-container": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
|
||||
|
@ -2650,6 +2994,16 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-plain-obj": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
|
||||
|
@ -3064,6 +3418,24 @@
|
|||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/local-pkg": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz",
|
||||
"integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mlly": "^1.7.4",
|
||||
"pkg-types": "^2.0.1",
|
||||
"quansync": "^0.2.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
|
@ -3096,6 +3468,15 @@
|
|||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-vue-next": {
|
||||
"version": "0.525.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-0.525.0.tgz",
|
||||
"integrity": "sha512-Xf8+x8B2DrnGDV/rxylS+KBp2FIe6ljwDn2JsGTZZvXIfhmm/q+nv8RuGO1OyoMjOVkkz7CqtUqJfwtFPRbB2w==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
|
@ -3184,6 +3565,38 @@
|
|||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mlly": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
|
||||
"integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
"pathe": "^2.0.1",
|
||||
"pkg-types": "^1.3.0",
|
||||
"ufo": "^1.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mlly/node_modules/confbox": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
|
||||
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mlly/node_modules/pkg-types": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
|
||||
"integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.1.8",
|
||||
"mlly": "^1.7.4",
|
||||
"pathe": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||
|
@ -3233,6 +3646,16 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
|
@ -3323,6 +3746,12 @@
|
|||
"npm": ">=3.10.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ohash": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
|
||||
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/open": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-10.1.1.tgz",
|
||||
|
@ -3438,6 +3867,18 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-types": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.1.tgz",
|
||||
"integrity": "sha512-eY0QFb6eSwc9+0d/5D2lFFUq+A3n3QNGSy/X2Nvp+6MfzGw2u6EbA7S80actgjY1lkvvI0pqB+a4hioMh443Ew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.2.2",
|
||||
"exsolve": "^1.0.7",
|
||||
"pathe": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||
|
@ -3489,6 +3930,23 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/quansync": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz",
|
||||
"integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/read-package-json-fast": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz",
|
||||
|
@ -3503,6 +3961,53 @@
|
|||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/reka-ui": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.3.1.tgz",
|
||||
"integrity": "sha512-2SjGeybd7jvD8EQUkzjgg7GdOQdf4cTwdVMq/lDNTMqneUFNnryGO43dg8WaM/jaG9QpSCZBvstfBFWlDdb2Zg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@floating-ui/vue": "^1.1.6",
|
||||
"@internationalized/date": "^3.5.0",
|
||||
"@internationalized/number": "^3.5.0",
|
||||
"@tanstack/vue-virtual": "^3.12.0",
|
||||
"@vueuse/core": "^12.5.0",
|
||||
"@vueuse/shared": "^12.5.0",
|
||||
"aria-hidden": "^1.2.4",
|
||||
"defu": "^6.1.4",
|
||||
"ohash": "^2.0.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">= 3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||
|
@ -3709,9 +4214,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3725,13 +4230,17 @@
|
|||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tinypool": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
|
||||
"integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
|
@ -3785,6 +4294,13 @@
|
|||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/ufo": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
|
||||
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
|
@ -3815,6 +4331,74 @@
|
|||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz",
|
||||
"integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.1",
|
||||
"picomatch": "^4.0.2",
|
||||
"webpack-virtual-modules": "^0.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-utils": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.2.4.tgz",
|
||||
"integrity": "sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pathe": "^2.0.2",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-vue-components": {
|
||||
"version": "28.8.0",
|
||||
"resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-28.8.0.tgz",
|
||||
"integrity": "sha512-2Q6ZongpoQzuXDK0ZsVzMoshH0MWZQ1pzVL538G7oIDKRTVzHjppBDS8aB99SADGHN3lpGU7frraCG6yWNoL5Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.6.0",
|
||||
"debug": "^4.4.1",
|
||||
"local-pkg": "^1.1.1",
|
||||
"magic-string": "^0.30.17",
|
||||
"mlly": "^1.7.4",
|
||||
"tinyglobby": "^0.2.14",
|
||||
"unplugin": "^2.3.5",
|
||||
"unplugin-utils": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/parser": "^7.15.8",
|
||||
"@nuxt/kit": "^3.2.2 || ^4.0.0",
|
||||
"vue": "2 || 3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/parser": {
|
||||
"optional": true
|
||||
},
|
||||
"@nuxt/kit": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||
|
@ -4103,6 +4687,13 @@
|
|||
"typescript": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
|
||||
|
|
|
@ -21,10 +21,11 @@
|
|||
"konva": "^9.3.20",
|
||||
"lodash": "^4.17.21",
|
||||
"log-symbols": "^7.0.0",
|
||||
"lucide-vue-next": "^0.525.0",
|
||||
"marked": "^12.0.0",
|
||||
"mathjs": "^14.4.0",
|
||||
"pinia": "^3.0.1",
|
||||
"tinypool": "^1.0.2",
|
||||
"reka-ui": "^2.3.1",
|
||||
"ts-log": "^2.2.7",
|
||||
"ts-results-es": "^5.0.1",
|
||||
"vue": "^3.5.13",
|
||||
|
@ -47,6 +48,7 @@
|
|||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^4.0.12",
|
||||
"typescript": "~5.7.3",
|
||||
"unplugin-vue-components": "^28.8.0",
|
||||
"vite": "^6.1.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.2",
|
||||
"vue-tsc": "^2.2.2"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
@plugin "daisyui" {
|
||||
themes: winter --default, night --prefersdark;
|
||||
}
|
||||
|
||||
@custom-variant dark (&:where([data-theme=night], [data-theme=night] *));
|
||||
@custom-variant light (&:where([data-theme=winter], [data-theme=winter] *));
|
||||
|
||||
|
|
@ -1,11 +1,4 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
@plugin "daisyui" {
|
||||
themes: winter --default, night --prefersdark;
|
||||
}
|
||||
|
||||
@custom-variant dark (&:where([data-theme=night], [data-theme=night] *));
|
||||
@custom-variant light (&:where([data-theme=winter], [data-theme=winter] *));
|
||||
@import "base.css";
|
||||
|
||||
/* 禁止所有图像和SVG选择 */
|
||||
img, svg {
|
||||
|
|
|
@ -0,0 +1,499 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-stage
|
||||
class="h-full w-full"
|
||||
ref="stageRef"
|
||||
:config="stageSize"
|
||||
@mousedown="handleMouseDown"
|
||||
@mousemove="handleMouseMove"
|
||||
@mouseup="handleMouseUp"
|
||||
@wheel="handleWheel"
|
||||
@click="handleStageClick"
|
||||
>
|
||||
<v-layer ref="layerRef">
|
||||
<template
|
||||
ref="canvasObjects"
|
||||
v-for="item in objMap.values()"
|
||||
:key="item.id"
|
||||
>
|
||||
<v-group
|
||||
:config="{
|
||||
x: item.x,
|
||||
y: item.y,
|
||||
draggable: true,
|
||||
id: `group-${item.id}`,
|
||||
}"
|
||||
@dragstart="handleDragStart"
|
||||
@dragend="handleDragEnd"
|
||||
@mouseover="handleCanvasObjectMouseOver"
|
||||
@mouseout="handleCanvasObjectMouseOut"
|
||||
>
|
||||
<v-rect
|
||||
v-show="!isUndefined(item.box)"
|
||||
:config="{
|
||||
...item.box,
|
||||
visible:
|
||||
!isUndefined(item.box) &&
|
||||
item.isHoverring &&
|
||||
!isDragging &&
|
||||
selectedIds.length == 0,
|
||||
stroke: 'rgb(125,193,239)',
|
||||
strokeWidth: 2.5,
|
||||
dash: [10, 5],
|
||||
cornerRadius: 10,
|
||||
}"
|
||||
>
|
||||
</v-rect>
|
||||
<v-rect :config="item.config" />
|
||||
</v-group>
|
||||
</template>
|
||||
|
||||
<v-transformer
|
||||
ref="transformerRef"
|
||||
:config="{
|
||||
borderStroke: 'rgb(125,193,239)',
|
||||
borderStrokeWidth: 3,
|
||||
}"
|
||||
/>
|
||||
<v-rect
|
||||
ref="selectRectRef"
|
||||
v-if="selectionRectangle.visible"
|
||||
:config="{
|
||||
x: Math.min(selectionRectangle.x1, selectionRectangle.x2),
|
||||
y: Math.min(selectionRectangle.y1, selectionRectangle.y2),
|
||||
width: Math.abs(selectionRectangle.x2 - selectionRectangle.x1),
|
||||
height: Math.abs(selectionRectangle.y2 - selectionRectangle.y1),
|
||||
fill: '#0069FF88',
|
||||
}"
|
||||
/>
|
||||
</v-layer>
|
||||
</v-stage>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Konva from "konva";
|
||||
import { isNull, isUndefined } from "lodash";
|
||||
import type {
|
||||
VGroup,
|
||||
VLayer,
|
||||
VNode,
|
||||
VStage,
|
||||
VTransformer,
|
||||
} from "@/utils/VueKonvaType";
|
||||
import { ref, reactive, watch, onMounted, useTemplateRef } from "vue";
|
||||
import type { IRect } from "konva/lib/types";
|
||||
import type { Stage } from "konva/lib/Stage";
|
||||
|
||||
const stageSize = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
|
||||
type CanvasObjectBox = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
type CanvasObject = {
|
||||
type: "Rect";
|
||||
config: Konva.RectConfig;
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
isHoverring: boolean;
|
||||
box?: CanvasObjectBox;
|
||||
};
|
||||
|
||||
function calculateRectBounding(
|
||||
width: number,
|
||||
height: number,
|
||||
rotation: number,
|
||||
padding?: number,
|
||||
) {
|
||||
// calculate bounding box for rotated rectangle
|
||||
const radians = (rotation * Math.PI) / 180;
|
||||
const cos = Math.cos(radians);
|
||||
const sin = Math.sin(radians);
|
||||
|
||||
// calculate corners of the rectangle
|
||||
const corners = [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: width, y: 0 },
|
||||
{ x: width, y: height },
|
||||
{ x: 0, y: height },
|
||||
].map((point) => ({
|
||||
x: point.x * cos - point.y * sin,
|
||||
y: point.x * sin + point.y * cos,
|
||||
}));
|
||||
|
||||
// find bounding box dimensions
|
||||
const minX = Math.min(...corners.map((p) => p.x));
|
||||
const maxX = Math.max(...corners.map((p) => p.x));
|
||||
const minY = Math.min(...corners.map((p) => p.y));
|
||||
const maxY = Math.max(...corners.map((p) => p.y));
|
||||
|
||||
if (padding)
|
||||
return {
|
||||
x: minX - padding,
|
||||
y: minY - padding,
|
||||
width: maxX - minX + padding * 2,
|
||||
height: maxY - minY + padding * 2,
|
||||
};
|
||||
else
|
||||
return {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
};
|
||||
}
|
||||
|
||||
const objMap = reactive<Map<string, CanvasObject>>(new Map());
|
||||
onMounted(() => {
|
||||
for (let n = 0; n < 100; n++) {
|
||||
const id = Math.round(Math.random() * 10000).toString();
|
||||
const x = Math.random() * stageSize.width;
|
||||
const y = Math.random() * stageSize.height;
|
||||
const width = 30 + Math.random() * 30;
|
||||
const height = 30 + Math.random() * 30;
|
||||
const rotation = Math.random() * 180;
|
||||
|
||||
objMap.set(id, {
|
||||
type: "Rect",
|
||||
config: {
|
||||
width: width,
|
||||
height: height,
|
||||
rotation: rotation,
|
||||
fill: "grey",
|
||||
id: id,
|
||||
},
|
||||
id: id,
|
||||
x: x,
|
||||
y: y,
|
||||
isHoverring: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const layerRef = useTemplateRef<VLayer>("layer");
|
||||
const canvasObjectsRef = useTemplateRef<HTMLTemplateElement[]>("canvasObjects");
|
||||
const transformerRef = useTemplateRef<VTransformer>("transformer");
|
||||
const selectRectRef = useTemplateRef<VNode>("selectRect");
|
||||
const stageRef = useTemplateRef<VStage>("stage");
|
||||
|
||||
const isDragging = ref(false);
|
||||
const dragItemId = ref<string | null>(null);
|
||||
|
||||
const isSelecting = ref(false);
|
||||
const selectedIds = ref<string[]>([]);
|
||||
const selectionRectangle = reactive({
|
||||
visible: false,
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: 0,
|
||||
y2: 0,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (isNull(transformerRef.value)) return;
|
||||
|
||||
const selectedBox = transformerRef.value.getNode();
|
||||
selectedBox.resizeEnabled(false);
|
||||
selectedBox.rotateEnabled(false);
|
||||
});
|
||||
|
||||
function handleCacheChange(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const shouldCache = isNull(target) ? false : target.checked;
|
||||
|
||||
if (isNull(layerRef.value)) return;
|
||||
|
||||
if (shouldCache) {
|
||||
layerRef.value.getNode().cache();
|
||||
} else {
|
||||
layerRef.value.getNode().clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
// Drag event handlers
|
||||
function handleDragStart(e: Event) {
|
||||
isDragging.value = true;
|
||||
|
||||
// save drag element:
|
||||
const target = e.target as unknown as Konva.Node;
|
||||
dragItemId.value = target.id();
|
||||
|
||||
// move current element to the top:
|
||||
// const item = list.value.find((i) => i.id === dragItemId.value);
|
||||
// if (isUndefined(item)) return;
|
||||
//
|
||||
// const index = list.value.indexOf(item);
|
||||
// list.value.splice(index, 1);
|
||||
// list.value.push(item);
|
||||
}
|
||||
|
||||
function handleDragEnd() {
|
||||
isDragging.value = false;
|
||||
dragItemId.value = null;
|
||||
}
|
||||
|
||||
// Update transformer nodes when selection changes
|
||||
watch(selectedIds, () => {
|
||||
if (isNull(transformerRef.value)) return;
|
||||
|
||||
const nodes = selectedIds.value.map((id) => {
|
||||
if (isNull(layerRef.value)) {
|
||||
console.error("layer is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let node of layerRef.value.getNode().children) {
|
||||
if (node instanceof Konva.Group && node.id() === `group-${id}`)
|
||||
return node;
|
||||
}
|
||||
}) as Konva.Node[];
|
||||
|
||||
if (!isUndefined(nodes)) transformerRef.value.getNode().nodes(nodes);
|
||||
});
|
||||
|
||||
// Mouse event handlers
|
||||
function handleStageClick(e: { target: any; evt: MouseEvent }) {
|
||||
if (isNull(e.target)) return;
|
||||
const target = e.target as unknown as Konva.Shape | Konva.Group;
|
||||
// if we are selecting with rect, do nothing
|
||||
if (selectionRectangle.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if click on empty area - remove all selections
|
||||
if (target === target.getStage()) {
|
||||
selectedIds.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const clickedId = target.attrs.id;
|
||||
|
||||
// do we pressed shift or ctrl?
|
||||
const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
|
||||
const isSelected = selectedIds.value.includes(clickedId);
|
||||
|
||||
if (!metaPressed && !isSelected) {
|
||||
// if no key pressed and the node is not selected
|
||||
// select just one
|
||||
selectedIds.value = [clickedId];
|
||||
} else if (metaPressed && isSelected) {
|
||||
// if we pressed keys and node was selected
|
||||
// we need to remove it from selection:
|
||||
selectedIds.value = selectedIds.value.filter((id) => id !== clickedId);
|
||||
} else if (metaPressed && !isSelected) {
|
||||
// add the node into selection
|
||||
selectedIds.value = [...selectedIds.value, clickedId];
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseDown(e: Event) {
|
||||
if (isNull(e.target)) return;
|
||||
const target = e.target as unknown as Konva.Container;
|
||||
|
||||
// do nothing if we mousedown on any shape
|
||||
if ((e.target as unknown) !== target.getStage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start selection rectangle
|
||||
isSelecting.value = true;
|
||||
const pos = target.getStage()?.getPointerPosition();
|
||||
if (!isNull(pos) && !isUndefined(pos)) {
|
||||
selectionRectangle.visible = true;
|
||||
selectionRectangle.x1 = pos.x;
|
||||
selectionRectangle.y1 = pos.y;
|
||||
selectionRectangle.x2 = pos.x;
|
||||
selectionRectangle.y2 = pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseMove(e: Event) {
|
||||
if (isNull(e.target)) return;
|
||||
const target = e.target as unknown as Konva.Container;
|
||||
|
||||
// do nothing if we didn't start selection
|
||||
if (!isSelecting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = target.getStage()?.getPointerPosition();
|
||||
if (!isNull(pos) && !isUndefined(pos)) {
|
||||
selectionRectangle.x2 = pos.x;
|
||||
selectionRectangle.y2 = pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseUp() {
|
||||
// do nothing if we didn't start selection
|
||||
if (!isSelecting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSelecting.value = false;
|
||||
|
||||
// update visibility in timeout, so we can check it in click event
|
||||
setTimeout(() => {
|
||||
selectionRectangle.visible = false;
|
||||
});
|
||||
|
||||
const selBox = {
|
||||
x: Math.min(selectionRectangle.x1, selectionRectangle.x2),
|
||||
y: Math.min(selectionRectangle.y1, selectionRectangle.y2),
|
||||
width: Math.abs(selectionRectangle.x2 - selectionRectangle.x1),
|
||||
height: Math.abs(selectionRectangle.y2 - selectionRectangle.y1),
|
||||
};
|
||||
|
||||
let currentSelectedIds = [];
|
||||
for (let [key, shape] of objMap) {
|
||||
const shapeConfig = objMap.get(shape.id);
|
||||
if (isUndefined(shapeConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUndefined(shapeConfig.box)) {
|
||||
if (isUndefined(shapeConfig.box)) {
|
||||
if (
|
||||
shapeConfig.config.width &&
|
||||
shapeConfig.config.height &&
|
||||
shapeConfig.config.rotation
|
||||
) {
|
||||
shapeConfig.box = calculateRectBounding(
|
||||
shapeConfig.config.width,
|
||||
shapeConfig.config.height,
|
||||
shapeConfig.config.rotation,
|
||||
5,
|
||||
);
|
||||
} else {
|
||||
console.error("Could not calculate rect bounding");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
Konva.Util.haveIntersection(selBox, {
|
||||
x: shapeConfig.box.x + shapeConfig.x,
|
||||
y: shapeConfig.box.y + shapeConfig.y,
|
||||
width: shapeConfig.box.width,
|
||||
height: shapeConfig.box.height,
|
||||
})
|
||||
)
|
||||
currentSelectedIds.push(shapeConfig.id);
|
||||
}
|
||||
|
||||
selectedIds.value = currentSelectedIds;
|
||||
}
|
||||
function handleWheel(e: { target: any; evt: MouseEvent & { deltaY: number } }) {
|
||||
e.evt.preventDefault();
|
||||
|
||||
const stage = e.target as Stage;
|
||||
if (stage === null || stage === undefined) {
|
||||
console.error("Stage is not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
const oldScale = stage.scaleX();
|
||||
const pointer = stage.getPointerPosition();
|
||||
if (pointer === null || pointer === undefined) {
|
||||
console.error("Pointer position is not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
const mousePointTo = {
|
||||
x: (pointer.x - stage.x()) / oldScale,
|
||||
y: (pointer.y - stage.y()) / oldScale,
|
||||
};
|
||||
|
||||
// how to scale? Zoom in? Or zoom out?
|
||||
let direction = e.evt.deltaY < 0 ? 1 : -1;
|
||||
|
||||
// when we zoom on trackpad, e.evt.ctrlKey is true
|
||||
// in that case lets revert direction
|
||||
if (e.evt.ctrlKey) {
|
||||
direction = -direction;
|
||||
}
|
||||
|
||||
const scaleBy = 1.05;
|
||||
const newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
|
||||
|
||||
stage.scale({ x: newScale, y: newScale });
|
||||
|
||||
const newPos = {
|
||||
x: pointer.x - mousePointTo.x * newScale,
|
||||
y: pointer.y - mousePointTo.y * newScale,
|
||||
};
|
||||
stage.position(newPos);
|
||||
}
|
||||
|
||||
function handleCanvasObjectMouseOver(evt: Event) {
|
||||
if (isNull(evt.target)) return;
|
||||
const target = evt.target;
|
||||
|
||||
let object = null;
|
||||
if (target instanceof Konva.Group) {
|
||||
if (!target.hasChildren()) return;
|
||||
object = target.children[0];
|
||||
} else if (target instanceof Konva.Rect) {
|
||||
object = target;
|
||||
} else {
|
||||
console.trace(`Not Konva class: ${target}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get client rect
|
||||
const objectConfig = objMap.get(object.id());
|
||||
if (isUndefined(objectConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get clientBox for first time
|
||||
if (isUndefined(objectConfig.box)) {
|
||||
if (
|
||||
objectConfig.config.width &&
|
||||
objectConfig.config.height &&
|
||||
objectConfig.config.rotation
|
||||
) {
|
||||
objectConfig.box = calculateRectBounding(
|
||||
objectConfig.config.width,
|
||||
objectConfig.config.height,
|
||||
objectConfig.config.rotation,
|
||||
5,
|
||||
);
|
||||
} else console.error("Could not calculate rect bounding");
|
||||
}
|
||||
|
||||
objectConfig.isHoverring = true;
|
||||
}
|
||||
|
||||
function handleCanvasObjectMouseOut(evt: Event) {
|
||||
if (isNull(evt.target)) return;
|
||||
const target = evt.target;
|
||||
|
||||
let object = null;
|
||||
if (target instanceof Konva.Group) {
|
||||
if (!target.hasChildren()) return;
|
||||
object = target.children[0];
|
||||
} else if (target instanceof Konva.Rect) {
|
||||
object = target;
|
||||
} else {
|
||||
console.trace(`Not Konva class: ${target}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get client rect
|
||||
const objectConfig = objMap.get(object.id());
|
||||
if (isUndefined(objectConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
objectConfig.isHoverring = false;
|
||||
}
|
||||
</script>
|
|
@ -14,7 +14,7 @@ interface VGroup extends VueElement {
|
|||
}
|
||||
|
||||
interface VStage extends VueElement {
|
||||
getStage(): Konva.Stage
|
||||
getNode(): Konva.Stage
|
||||
}
|
||||
|
||||
interface VTransformer extends VueElement {
|
||||
|
|
|
@ -1,435 +1,33 @@
|
|||
<template>
|
||||
<div class="h-screen w-screen">
|
||||
<v-stage class="h-full w-full" ref="stage" :config="stageSize" @mousedown="handleMouseDown"
|
||||
@mousemove="handleMouseMove" @mouseup="handleMouseUp" @click="handleStageClick">
|
||||
<v-layer ref="layer">
|
||||
<template ref="canvasObjects" v-for="item in objMap.values()" :key="item.id">
|
||||
<v-group :config="{
|
||||
x: item.x,
|
||||
y: item.y,
|
||||
draggable: true,
|
||||
id: `group-${item.id}`,
|
||||
}" @dragstart="handleDragStart" @dragend="handleDragEnd" @mouseover="handleCanvasObjectMouseOver"
|
||||
@mouseout="handleCanvasObjectMouseOut">
|
||||
<v-rect v-show="!isUndefined(item.box)" :config="{
|
||||
...item.box,
|
||||
visible: !isUndefined(item.box) && item.isHoverring && !isDragging && selectedIds.length == 0,
|
||||
stroke: 'rgb(125,193,239)',
|
||||
strokeWidth: 2.5,
|
||||
dash: [10, 5],
|
||||
cornerRadius: 10
|
||||
}">
|
||||
</v-rect>
|
||||
<v-rect :config="item.config" />
|
||||
</v-group>
|
||||
</template>
|
||||
|
||||
<v-transformer ref="transformer" :config="{
|
||||
borderStroke: 'rgb(125,193,239)',
|
||||
borderStrokeWidth: 3,
|
||||
}" />
|
||||
<v-rect ref="selectRect" v-if="selectionRectangle.visible" :config="{
|
||||
x: Math.min(selectionRectangle.x1, selectionRectangle.x2),
|
||||
y: Math.min(selectionRectangle.y1, selectionRectangle.y2),
|
||||
width: Math.abs(selectionRectangle.x2 - selectionRectangle.x1),
|
||||
height: Math.abs(selectionRectangle.y2 - selectionRectangle.y1),
|
||||
fill: '#0069FF88',
|
||||
}" />
|
||||
</v-layer>
|
||||
</v-stage>
|
||||
<div class="absolute top-20 left-10">
|
||||
<SplitterGroup id="splitter-group" direction="horizontal">
|
||||
<SplitterPanel
|
||||
id="splitter-group-panel-canvas"
|
||||
:default-size="80"
|
||||
:min-size="30"
|
||||
class="bg-white border rounded-xl flex items-center justify-center"
|
||||
>
|
||||
<LabCanvas></LabCanvas>
|
||||
</SplitterPanel>
|
||||
<SplitterResizeHandle id="splitter-group-resize-handle" class="w-2" />
|
||||
<SplitterPanel
|
||||
id="splitter-group-panel-properties"
|
||||
:min-size="20"
|
||||
class="bg-white border rounded-xl flex items-center justify-center"
|
||||
>
|
||||
Panel A
|
||||
</SplitterPanel>
|
||||
</SplitterGroup>
|
||||
<!-- <div class="absolute top-20 left-10">
|
||||
<input type="checkbox" class="checkbox" @change="handleCacheChange" />
|
||||
cache shapes
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isNull, isUndefined } from "mathjs";
|
||||
import Konva from "konva";
|
||||
import type {
|
||||
VGroup,
|
||||
VLayer,
|
||||
VNode,
|
||||
VStage,
|
||||
VTransformer,
|
||||
} from "@/utils/VueKonvaType";
|
||||
import { ref, reactive, watch, onMounted, useTemplateRef } from "vue";
|
||||
import { list } from "postcss";
|
||||
import type { IRect } from "konva/lib/types";
|
||||
|
||||
const stageSize = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
|
||||
type CanvasObjectBox = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
type CanvasObject = {
|
||||
type: "Rect";
|
||||
config: Konva.RectConfig;
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
isHoverring: boolean;
|
||||
box?: CanvasObjectBox;
|
||||
};
|
||||
|
||||
function calculateRectBounding(
|
||||
width: number,
|
||||
height: number,
|
||||
rotation: number,
|
||||
padding?: number,
|
||||
) {
|
||||
// calculate bounding box for rotated rectangle
|
||||
const radians = (rotation * Math.PI) / 180;
|
||||
const cos = Math.cos(radians);
|
||||
const sin = Math.sin(radians);
|
||||
|
||||
// calculate corners of the rectangle
|
||||
const corners = [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: width, y: 0 },
|
||||
{ x: width, y: height },
|
||||
{ x: 0, y: height },
|
||||
].map((point) => ({
|
||||
x: point.x * cos - point.y * sin,
|
||||
y: point.x * sin + point.y * cos,
|
||||
}));
|
||||
|
||||
// find bounding box dimensions
|
||||
const minX = Math.min(...corners.map((p) => p.x));
|
||||
const maxX = Math.max(...corners.map((p) => p.x));
|
||||
const minY = Math.min(...corners.map((p) => p.y));
|
||||
const maxY = Math.max(...corners.map((p) => p.y));
|
||||
|
||||
if (padding)
|
||||
return {
|
||||
x: minX - padding,
|
||||
y: minY - padding,
|
||||
width: maxX - minX + padding * 2,
|
||||
height: maxY - minY + padding * 2,
|
||||
};
|
||||
else
|
||||
return {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
};
|
||||
}
|
||||
|
||||
const objMap = reactive<Map<string, CanvasObject>>(new Map());
|
||||
onMounted(() => {
|
||||
for (let n = 0; n < 100; n++) {
|
||||
const id = Math.round(Math.random() * 10000).toString();
|
||||
const x = Math.random() * stageSize.width;
|
||||
const y = Math.random() * stageSize.height;
|
||||
const width = 30 + Math.random() * 30;
|
||||
const height = 30 + Math.random() * 30;
|
||||
const rotation = Math.random() * 180;
|
||||
|
||||
objMap.set(id, {
|
||||
type: "Rect",
|
||||
config: {
|
||||
width: width,
|
||||
height: height,
|
||||
rotation: rotation,
|
||||
fill: "grey",
|
||||
id: id,
|
||||
},
|
||||
id: id,
|
||||
x: x,
|
||||
y: y,
|
||||
isHoverring: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const layer = useTemplateRef<VLayer>("layer");
|
||||
const canvasObjects = useTemplateRef<HTMLTemplateElement[]>("canvasObjects")
|
||||
const transformer = useTemplateRef<VTransformer>("transformer");
|
||||
const selectRect = useTemplateRef<VNode>("selectRect");
|
||||
const stage = useTemplateRef<VStage>("stage");
|
||||
|
||||
const isDragging = ref(false);
|
||||
const dragItemId = ref<string | null>(null);
|
||||
|
||||
const isSelecting = ref(false);
|
||||
const selectedIds = ref<string[]>([]);
|
||||
const selectionRectangle = reactive({
|
||||
visible: false,
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: 0,
|
||||
y2: 0,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (isNull(transformer.value)) return;
|
||||
|
||||
const selectedBox = transformer.value.getNode();
|
||||
selectedBox.resizeEnabled(false);
|
||||
selectedBox.rotateEnabled(false);
|
||||
});
|
||||
|
||||
function handleCacheChange(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const shouldCache = isNull(target) ? false : target.checked;
|
||||
|
||||
if (isNull(layer.value)) return;
|
||||
|
||||
if (shouldCache) {
|
||||
layer.value.getNode().cache();
|
||||
} else {
|
||||
layer.value.getNode().clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
// Drag event handlers
|
||||
function handleDragStart(e: Event) {
|
||||
isDragging.value = true;
|
||||
|
||||
// save drag element:
|
||||
const target = e.target as unknown as Konva.Node;
|
||||
dragItemId.value = target.id();
|
||||
|
||||
// move current element to the top:
|
||||
// const item = list.value.find((i) => i.id === dragItemId.value);
|
||||
// if (isUndefined(item)) return;
|
||||
//
|
||||
// const index = list.value.indexOf(item);
|
||||
// list.value.splice(index, 1);
|
||||
// list.value.push(item);
|
||||
}
|
||||
|
||||
function handleDragEnd() {
|
||||
isDragging.value = false;
|
||||
dragItemId.value = null;
|
||||
}
|
||||
|
||||
// Update transformer nodes when selection changes
|
||||
watch(selectedIds, () => {
|
||||
if (isNull(transformer.value)) return;
|
||||
|
||||
const nodes = selectedIds.value
|
||||
.map((id) => {
|
||||
if (isNull(layer.value)) {
|
||||
console.error("layer is null")
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let node of layer.value.getNode().children) {
|
||||
if (
|
||||
node instanceof Konva.Group &&
|
||||
node.id() === `group-${id}`
|
||||
) return node;
|
||||
}
|
||||
}) as Konva.Node[]
|
||||
|
||||
if (!isUndefined(nodes)) transformer.value.getNode().nodes(nodes);
|
||||
});
|
||||
|
||||
// Mouse event handlers
|
||||
function handleStageClick(e: { target: any, evt: MouseEvent }) {
|
||||
if (isNull(e.target)) return;
|
||||
const target = e.target as unknown as Konva.Shape | Konva.Group;
|
||||
// if we are selecting with rect, do nothing
|
||||
if (selectionRectangle.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if click on empty area - remove all selections
|
||||
if (target === target.getStage()) {
|
||||
selectedIds.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const clickedId = target.attrs.id;
|
||||
|
||||
// do we pressed shift or ctrl?
|
||||
const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
|
||||
const isSelected = selectedIds.value.includes(clickedId);
|
||||
|
||||
if (!metaPressed && !isSelected) {
|
||||
// if no key pressed and the node is not selected
|
||||
// select just one
|
||||
selectedIds.value = [clickedId];
|
||||
} else if (metaPressed && isSelected) {
|
||||
// if we pressed keys and node was selected
|
||||
// we need to remove it from selection:
|
||||
selectedIds.value = selectedIds.value.filter(id => id !== clickedId);
|
||||
} else if (metaPressed && !isSelected) {
|
||||
// add the node into selection
|
||||
selectedIds.value = [...selectedIds.value, clickedId];
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseDown(e: Event) {
|
||||
if (isNull(e.target)) return;
|
||||
const target = e.target as unknown as Konva.Container;
|
||||
|
||||
// do nothing if we mousedown on any shape
|
||||
if ((e.target as unknown) !== target.getStage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start selection rectangle
|
||||
isSelecting.value = true;
|
||||
const pos = target.getStage()?.getPointerPosition();
|
||||
if (!isNull(pos) && !isUndefined(pos)) {
|
||||
selectionRectangle.visible = true;
|
||||
selectionRectangle.x1 = pos.x;
|
||||
selectionRectangle.y1 = pos.y;
|
||||
selectionRectangle.x2 = pos.x;
|
||||
selectionRectangle.y2 = pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseMove(e: Event) {
|
||||
if (isNull(e.target)) return;
|
||||
const target = e.target as unknown as Konva.Container;
|
||||
|
||||
// do nothing if we didn't start selection
|
||||
if (!isSelecting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = target.getStage()?.getPointerPosition();
|
||||
if (!isNull(pos) && !isUndefined(pos)) {
|
||||
selectionRectangle.x2 = pos.x;
|
||||
selectionRectangle.y2 = pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseUp() {
|
||||
// do nothing if we didn't start selection
|
||||
if (!isSelecting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSelecting.value = false;
|
||||
|
||||
// update visibility in timeout, so we can check it in click event
|
||||
setTimeout(() => {
|
||||
selectionRectangle.visible = false;
|
||||
});
|
||||
|
||||
const selBox = {
|
||||
x: Math.min(selectionRectangle.x1, selectionRectangle.x2),
|
||||
y: Math.min(selectionRectangle.y1, selectionRectangle.y2),
|
||||
width: Math.abs(selectionRectangle.x2 - selectionRectangle.x1),
|
||||
height: Math.abs(selectionRectangle.y2 - selectionRectangle.y1),
|
||||
};
|
||||
|
||||
let currentSelectedIds = [];
|
||||
for (let [key, shape] of objMap) {
|
||||
const shapeConfig = objMap.get(shape.id);
|
||||
if (isUndefined(shapeConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUndefined(shapeConfig.box)) {
|
||||
if (isUndefined(shapeConfig.box)) {
|
||||
if (
|
||||
shapeConfig.config.width &&
|
||||
shapeConfig.config.height &&
|
||||
shapeConfig.config.rotation
|
||||
) {
|
||||
shapeConfig.box = calculateRectBounding(
|
||||
shapeConfig.config.width,
|
||||
shapeConfig.config.height,
|
||||
shapeConfig.config.rotation,
|
||||
5,
|
||||
);
|
||||
} else {
|
||||
console.error("Could not calculate rect bounding");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Konva.Util.haveIntersection(selBox, {
|
||||
x: shapeConfig.box.x + shapeConfig.x,
|
||||
y: shapeConfig.box.y + shapeConfig.y,
|
||||
width: shapeConfig.box.width,
|
||||
height: shapeConfig.box.height,
|
||||
}))
|
||||
currentSelectedIds.push(shapeConfig.id)
|
||||
}
|
||||
|
||||
selectedIds.value = currentSelectedIds;
|
||||
}
|
||||
|
||||
function handleCanvasObjectMouseOver(evt: Event) {
|
||||
if (isNull(evt.target)) return;
|
||||
const target = evt.target;
|
||||
|
||||
let object = null;
|
||||
if (target instanceof Konva.Group) {
|
||||
if (!target.hasChildren()) return;
|
||||
object = target.children[0];
|
||||
} else if (target instanceof Konva.Rect) {
|
||||
object = target;
|
||||
} else {
|
||||
console.trace(`Not Konva class: ${target}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get client rect
|
||||
const objectConfig = objMap.get(object.id());
|
||||
if (isUndefined(objectConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get clientBox for first time
|
||||
if (isUndefined(objectConfig.box)) {
|
||||
if (
|
||||
objectConfig.config.width &&
|
||||
objectConfig.config.height &&
|
||||
objectConfig.config.rotation
|
||||
) {
|
||||
objectConfig.box = calculateRectBounding(
|
||||
objectConfig.config.width,
|
||||
objectConfig.config.height,
|
||||
objectConfig.config.rotation,
|
||||
5,
|
||||
);
|
||||
} else console.error("Could not calculate rect bounding");
|
||||
}
|
||||
|
||||
objectConfig.isHoverring = true;
|
||||
}
|
||||
|
||||
|
||||
function handleCanvasObjectMouseOut(evt: Event) {
|
||||
if (isNull(evt.target)) return;
|
||||
const target = evt.target;
|
||||
|
||||
let object = null;
|
||||
if (target instanceof Konva.Group) {
|
||||
if (!target.hasChildren()) return;
|
||||
object = target.children[0];
|
||||
} else if (target instanceof Konva.Rect) {
|
||||
object = target;
|
||||
} else {
|
||||
console.trace(`Not Konva class: ${target}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get client rect
|
||||
const objectConfig = objMap.get(object.id());
|
||||
if (isUndefined(objectConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
objectConfig.isHoverring = false;
|
||||
}
|
||||
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from "reka-ui";
|
||||
import LabCanvas from "@/components/LabCanvas.vue";
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -440,6 +38,6 @@ function handleCanvasObjectMouseOut(evt: Event) {
|
|||
@import "../assets/main.css";
|
||||
|
||||
.primary {
|
||||
color: rgb(125, 193, 239)
|
||||
color: rgb(125, 193, 239);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -6,6 +6,8 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
|||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
import tailwindcss from '@tailwindcss/postcss'
|
||||
import autoprefixer from 'autoprefixer'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import RekaResolver from 'reka-ui/resolver'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
|
@ -20,6 +22,18 @@ export default defineConfig({
|
|||
}),
|
||||
vueJsx(),
|
||||
vueDevTools(),
|
||||
Components(
|
||||
{
|
||||
dts: true,
|
||||
resolvers: [
|
||||
RekaResolver()
|
||||
|
||||
// RekaResolver({
|
||||
// prefix: '' // use the prefix option to add Prefix to the imported components
|
||||
// })
|
||||
],
|
||||
}
|
||||
)
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
|
Loading…
Reference in New Issue