Initial commit
This commit is contained in:
403
package-lock.json
generated
403
package-lock.json
generated
@@ -16,11 +16,15 @@
|
|||||||
"@types/node": "^16.18.126",
|
"@types/node": "^16.18.126",
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.93.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
"node_modules/@adobe/css-tools": {
|
||||||
@@ -3019,6 +3023,316 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@parcel/watcher": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^1.0.3",
|
||||||
|
"is-glob": "^4.0.3",
|
||||||
|
"micromatch": "^4.0.5",
|
||||||
|
"node-addon-api": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@parcel/watcher-android-arm64": "2.5.1",
|
||||||
|
"@parcel/watcher-darwin-arm64": "2.5.1",
|
||||||
|
"@parcel/watcher-darwin-x64": "2.5.1",
|
||||||
|
"@parcel/watcher-freebsd-x64": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm-glibc": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm-musl": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-arm64-musl": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-x64-glibc": "2.5.1",
|
||||||
|
"@parcel/watcher-linux-x64-musl": "2.5.1",
|
||||||
|
"@parcel/watcher-win32-arm64": "2.5.1",
|
||||||
|
"@parcel/watcher-win32-ia32": "2.5.1",
|
||||||
|
"@parcel/watcher-win32-x64": "2.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-android-arm64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-darwin-arm64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-darwin-x64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-freebsd-x64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm-glibc": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm-musl": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm64-glibc": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-arm64-musl": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-x64-glibc": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-linux-x64-musl": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-win32-arm64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-win32-ia32": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@parcel/watcher-win32-x64": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@@ -5678,6 +5992,15 @@
|
|||||||
"wrap-ansi": "^7.0.0"
|
"wrap-ansi": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/co": {
|
"node_modules/co": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||||
@@ -6621,6 +6944,20 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-libc": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"detect-libc": "bin/detect-libc.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-newline": {
|
"node_modules/detect-newline": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
|
||||||
@@ -9211,6 +9548,13 @@
|
|||||||
"url": "https://opencollective.com/immer"
|
"url": "https://opencollective.com/immer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immutable": {
|
||||||
|
"version": "5.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
|
||||||
|
"integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||||
@@ -11614,6 +11958,14 @@
|
|||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-addon-api": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/node-forge": {
|
"node_modules/node-forge": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||||
@@ -14522,6 +14874,27 @@
|
|||||||
"integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==",
|
"integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==",
|
||||||
"license": "CC0-1.0"
|
"license": "CC0-1.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/sass": {
|
||||||
|
"version": "1.93.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz",
|
||||||
|
"integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": "^4.0.0",
|
||||||
|
"immutable": "^5.0.2",
|
||||||
|
"source-map-js": ">=0.6.2 <2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sass": "sass.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@parcel/watcher": "^2.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sass-loader": {
|
"node_modules/sass-loader": {
|
||||||
"version": "12.6.0",
|
"version": "12.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
|
||||||
@@ -14560,6 +14933,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sass/node_modules/chokidar": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readdirp": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sass/node_modules/readdirp": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.18.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sax": {
|
"node_modules/sax": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"@types/node": "^16.18.126",
|
"@types/node": "^16.18.126",
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
@@ -40,5 +41,8 @@
|
|||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.93.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/App.tsx
24
src/App.tsx
@@ -1,26 +1,8 @@
|
|||||||
import React from 'react';
|
import SceneView from "./pages/SceneView";
|
||||||
import logo from './logo.svg';
|
import "./styles/main.scss";
|
||||||
import './App.css';
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return <SceneView />;
|
||||||
<div className="App">
|
|
||||||
<header className="App-header">
|
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
|
||||||
<p>
|
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
className="App-link"
|
|
||||||
href="https://reactjs.org"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Learn React
|
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
487
src/components/outlinePanel/OutlineList .tsx
Normal file
487
src/components/outlinePanel/OutlineList .tsx
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
AddIcon,
|
||||||
|
ChevronIcon,
|
||||||
|
CollapseAllIcon,
|
||||||
|
CubeIcon,
|
||||||
|
EyeIcon,
|
||||||
|
FocusIcon,
|
||||||
|
FolderIcon,
|
||||||
|
KebebIcon,
|
||||||
|
LockIcon,
|
||||||
|
} from "../../icons/ExportIcons";
|
||||||
|
import { OutlinePanelProps } from "./OutlinePanel";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
interface AssetGroupChild {
|
||||||
|
groupUuid?: string;
|
||||||
|
groupName?: string;
|
||||||
|
isExpanded?: boolean;
|
||||||
|
children?: AssetGroupChild[];
|
||||||
|
modelUuid?: string;
|
||||||
|
modelName?: string;
|
||||||
|
isVisible?: boolean;
|
||||||
|
isLocked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TreeNodeProps {
|
||||||
|
item: AssetGroupChild;
|
||||||
|
level?: number;
|
||||||
|
textColor?: string;
|
||||||
|
eyeIconColor?: string;
|
||||||
|
lockIconColor?: string;
|
||||||
|
onDragStart: (item: AssetGroupChild) => void;
|
||||||
|
onDrop: (item: AssetGroupChild, parent: AssetGroupChild | null, e: any) => void;
|
||||||
|
draggingItem: AssetGroupChild | null;
|
||||||
|
selectedObject: string | null;
|
||||||
|
onDragOver: (item: AssetGroupChild, parent: AssetGroupChild | null, e: React.DragEvent) => void;
|
||||||
|
setSelectedObject: React.Dispatch<React.SetStateAction<string | null>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DropAction = "above" | "child" | "below" | "none";
|
||||||
|
|
||||||
|
const DEFAULT_PRAMS = {
|
||||||
|
backgroundColor: "linear-gradient(to bottom, #1e1e2f, #12121a)",
|
||||||
|
panelSide: "left",
|
||||||
|
textColor: "linear-gradient(to bottom, rgba(231, 231, 255, 1), #cacad6ff)",
|
||||||
|
addIconColor: "white",
|
||||||
|
lockIconColor: "white",
|
||||||
|
eyeIconColor: "white",
|
||||||
|
};
|
||||||
|
|
||||||
|
const TreeNode: React.FC<TreeNodeProps> = ({
|
||||||
|
item,
|
||||||
|
level = 0,
|
||||||
|
textColor,
|
||||||
|
eyeIconColor,
|
||||||
|
lockIconColor,
|
||||||
|
onDragStart,
|
||||||
|
onDrop,
|
||||||
|
draggingItem,
|
||||||
|
setSelectedObject,
|
||||||
|
selectedObject,
|
||||||
|
onDragOver,
|
||||||
|
}) => {
|
||||||
|
const isGroupNode =
|
||||||
|
Array.isArray(item.children) && item.children.length > 0 ? item.children : false;
|
||||||
|
const [isExpanded, setIsExpanded] = useState(item.isExpanded || false);
|
||||||
|
const [isVisible, setIsVisible] = useState(item.isVisible ?? true);
|
||||||
|
const [isLocked, setIsLocked] = useState(item.isLocked ?? false);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [name, setName] = useState(isGroupNode ? item.groupName : item.modelName);
|
||||||
|
|
||||||
|
const handleDragStart = (e: React.DragEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onDragStart(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onDragOver(item, draggingItem, e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onDrop(item, draggingItem, e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBeingDragged =
|
||||||
|
draggingItem?.groupUuid === item.groupUuid || draggingItem?.modelUuid === item.modelUuid;
|
||||||
|
|
||||||
|
const handleDoubleClick = () => {
|
||||||
|
setIsEditing(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
};
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
setIsEditing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle selection (used by mouse click, keyboard and touch)
|
||||||
|
const toggleSelect = () => {
|
||||||
|
const currentId = item.modelUuid || item.groupUuid || null;
|
||||||
|
setSelectedObject((prev) => (prev === currentId ? null : currentId));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDivKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
// support Enter and Space for accessibility
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleSelect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTouchStart = () => {
|
||||||
|
toggleSelect();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="tree-node">
|
||||||
|
<div
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={handleDivKeyDown}
|
||||||
|
onTouchStart={handleTouchStart}
|
||||||
|
className={clsx("tree-node-content", {
|
||||||
|
"group-node": isGroupNode,
|
||||||
|
"asset-node": !isGroupNode,
|
||||||
|
selected: selectedObject === item.modelUuid,
|
||||||
|
locked: isLocked,
|
||||||
|
hidden: !isVisible,
|
||||||
|
dragging: isBeingDragged,
|
||||||
|
})}
|
||||||
|
draggable
|
||||||
|
onDragStart={handleDragStart}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
id={item.modelUuid || item.groupUuid}
|
||||||
|
onClick={toggleSelect}
|
||||||
|
>
|
||||||
|
<div className="node-wrapper" style={{ paddingLeft: `${level * 25 + 8}px` }}>
|
||||||
|
{isGroupNode && (
|
||||||
|
<button
|
||||||
|
className="expand-button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation(); // prevent triggering selection
|
||||||
|
setIsExpanded(!isExpanded);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChevronIcon isOpen={isExpanded} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="node-icon">
|
||||||
|
{isGroupNode ? <FolderIcon isOpen={isExpanded} /> : <CubeIcon />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rename-input" onDoubleClick={handleDoubleClick}>
|
||||||
|
{isEditing ? (
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
value={name}
|
||||||
|
className="renaming"
|
||||||
|
style={{ color: textColor }}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span
|
||||||
|
className="rename-input"
|
||||||
|
style={{ color: textColor || DEFAULT_PRAMS.textColor }}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="node-controls">
|
||||||
|
<button
|
||||||
|
className="control-button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsVisible(!isVisible);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EyeIcon
|
||||||
|
isClosed={!isVisible}
|
||||||
|
color={eyeIconColor || DEFAULT_PRAMS.eyeIconColor}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{isGroupNode && item.children?.length ? (
|
||||||
|
<button className="control-button">
|
||||||
|
<FocusIcon />
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
<button
|
||||||
|
className="control-button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsLocked(!isLocked);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LockIcon
|
||||||
|
isLocked={isLocked}
|
||||||
|
color={lockIconColor || DEFAULT_PRAMS.lockIconColor}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{isGroupNode ? (
|
||||||
|
<button className="control-button">
|
||||||
|
<KebebIcon />
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isExpanded && item.children?.length ? (
|
||||||
|
<div className="tree-children">
|
||||||
|
{item.children.map((child) => (
|
||||||
|
<TreeNode
|
||||||
|
key={child.groupUuid || child.modelUuid}
|
||||||
|
item={child}
|
||||||
|
level={level + 1}
|
||||||
|
onDragStart={onDragStart}
|
||||||
|
onDrop={onDrop}
|
||||||
|
draggingItem={draggingItem}
|
||||||
|
selectedObject={selectedObject}
|
||||||
|
setSelectedObject={setSelectedObject}
|
||||||
|
onDragOver={onDragOver}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OutlineList: React.FC<OutlinePanelProps> = ({
|
||||||
|
backgroundColor = "linear-gradient(to bottom, #1e1e2f, #12121a)",
|
||||||
|
panelSide = "left",
|
||||||
|
textColor = "linear-gradient(to bottom, rgba(231, 231, 255, 1), #cacad6ff)",
|
||||||
|
addIconColor,
|
||||||
|
lockIconColor,
|
||||||
|
eyeIconColor,
|
||||||
|
}) => {
|
||||||
|
const [selectedObject, setSelectedObject] = useState<string | null>(null); // store UUID
|
||||||
|
|
||||||
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
const [draggingItem, setDraggingItem] = useState<AssetGroupChild | null>(null);
|
||||||
|
const [groupHierarchy, setGroupHierarchy] = useState<AssetGroupChild[]>([
|
||||||
|
{ modelUuid: "a1", modelName: "Asset 1", isVisible: true, isLocked: false, children: [] },
|
||||||
|
{ modelUuid: "a2", modelName: "Asset 2", isVisible: true, isLocked: false, children: [] },
|
||||||
|
{ modelUuid: "a3", modelName: "Asset 3", isVisible: true, isLocked: false, children: [] },
|
||||||
|
{ modelUuid: "a4", modelName: "Asset 4", isVisible: true, isLocked: false, children: [] },
|
||||||
|
{ modelUuid: "a5", modelName: "Asset 5", isVisible: true, isLocked: false, children: [] },
|
||||||
|
{ modelUuid: "a6", modelName: "Asset 6", isVisible: true, isLocked: false, children: [] },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleDragStart = (item: AssetGroupChild) => {
|
||||||
|
setDraggingItem(item); // Set the dragged item when dragging starts
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (
|
||||||
|
targetItem: AssetGroupChild,
|
||||||
|
draggedItem: AssetGroupChild | null,
|
||||||
|
event: DragEvent | React.DragEvent
|
||||||
|
) => {
|
||||||
|
if (!draggedItem) return;
|
||||||
|
|
||||||
|
const targetId = targetItem.modelUuid;
|
||||||
|
if (!targetId) return;
|
||||||
|
|
||||||
|
const hoveredDiv = document.getElementById(targetId);
|
||||||
|
if (!hoveredDiv) return;
|
||||||
|
|
||||||
|
// Calculate drop position
|
||||||
|
const rect = hoveredDiv.getBoundingClientRect();
|
||||||
|
const parentScrollTop = hoveredDiv.parentElement?.scrollTop || 0;
|
||||||
|
const y = (event as any).clientY - (rect.top + parentScrollTop);
|
||||||
|
|
||||||
|
// Determine drop action
|
||||||
|
const action = getDropAction(y);
|
||||||
|
if (action === "none") {
|
||||||
|
hoveredDiv.style.borderTop = "none";
|
||||||
|
hoveredDiv.style.borderBottom = "none";
|
||||||
|
hoveredDiv.style.outline = "none";
|
||||||
|
hoveredDiv.style.border = "none";
|
||||||
|
setDraggingItem(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hierarchy
|
||||||
|
const updatedHierarchy = [...groupHierarchy];
|
||||||
|
|
||||||
|
if (!removeItemFromHierarchy(draggedItem, updatedHierarchy)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!insertItemByAction(draggedItem, targetId, action, updatedHierarchy)) {
|
||||||
|
updatedHierarchy.push(draggedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit changes
|
||||||
|
setGroupHierarchy(updatedHierarchy);
|
||||||
|
setDraggingItem(null);
|
||||||
|
hoveredDiv.style.borderTop = "none";
|
||||||
|
hoveredDiv.style.borderBottom = "none";
|
||||||
|
hoveredDiv.style.outline = "none";
|
||||||
|
hoveredDiv.style.border = "none";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDropAction = (y: number): DropAction => {
|
||||||
|
if (y >= 0 && y < 7) return "above";
|
||||||
|
if (y >= 7 && y < 19) return "child";
|
||||||
|
if (y >= 19 && y < 32) return "below";
|
||||||
|
return "none";
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeItemFromHierarchy = (
|
||||||
|
item: AssetGroupChild,
|
||||||
|
hierarchy: AssetGroupChild[]
|
||||||
|
): boolean => {
|
||||||
|
for (let i = 0; i < hierarchy.length; i++) {
|
||||||
|
const current = hierarchy[i];
|
||||||
|
if (current.modelUuid === item.modelUuid) {
|
||||||
|
hierarchy.splice(i, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (current.children?.length) {
|
||||||
|
const removed = removeItemFromHierarchy(item, current.children);
|
||||||
|
if (removed) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertItemByAction = (
|
||||||
|
item: AssetGroupChild,
|
||||||
|
targetId: string,
|
||||||
|
action: DropAction,
|
||||||
|
hierarchy: AssetGroupChild[]
|
||||||
|
): boolean => {
|
||||||
|
switch (action) {
|
||||||
|
case "above":
|
||||||
|
return insertAsSibling(item, targetId, hierarchy, 0);
|
||||||
|
case "below":
|
||||||
|
return insertAsSibling(item, targetId, hierarchy, 1);
|
||||||
|
case "child":
|
||||||
|
return addAsChild(targetId, item, hierarchy);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertAsSibling = (
|
||||||
|
item: AssetGroupChild,
|
||||||
|
targetId: string,
|
||||||
|
hierarchy: AssetGroupChild[],
|
||||||
|
offset: number // 0 for above, 1 for below
|
||||||
|
): boolean => {
|
||||||
|
for (let i = 0; i < hierarchy.length; i++) {
|
||||||
|
if (hierarchy[i].modelUuid === targetId) {
|
||||||
|
hierarchy.splice(i + offset, 0, item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hierarchy[i].children?.length) {
|
||||||
|
const inserted = insertAsSibling(item, targetId, hierarchy[i].children!, offset);
|
||||||
|
if (inserted) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAsChild = (
|
||||||
|
parentId: string,
|
||||||
|
childItem: AssetGroupChild,
|
||||||
|
hierarchy: AssetGroupChild[]
|
||||||
|
): boolean => {
|
||||||
|
for (let i = 0; i < hierarchy.length; i++) {
|
||||||
|
if (hierarchy[i].modelUuid === parentId) {
|
||||||
|
if (!hierarchy[i].children) hierarchy[i].children = [];
|
||||||
|
hierarchy[i].children!.push(childItem);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hierarchy[i].children?.length) {
|
||||||
|
const added = addAsChild(parentId, childItem, hierarchy[i].children!);
|
||||||
|
if (added) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (
|
||||||
|
targetItem: AssetGroupChild,
|
||||||
|
draggedItem: AssetGroupChild | null,
|
||||||
|
event: DragEvent | React.DragEvent
|
||||||
|
) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const targetId = targetItem?.modelUuid || targetItem?.groupUuid;
|
||||||
|
if (!targetId) return;
|
||||||
|
|
||||||
|
const hoveredDiv = document.getElementById(targetId);
|
||||||
|
if (!hoveredDiv) return;
|
||||||
|
|
||||||
|
// Remove previous outlines before applying new one
|
||||||
|
hoveredDiv.style.outline = "none";
|
||||||
|
hoveredDiv.style.borderTop = "none";
|
||||||
|
hoveredDiv.style.borderBottom = "none";
|
||||||
|
|
||||||
|
const rect = hoveredDiv.getBoundingClientRect();
|
||||||
|
const y = (event as any).clientY - rect.top;
|
||||||
|
|
||||||
|
// Determine where the user is hovering
|
||||||
|
if (y >= 0 && y < 7) {
|
||||||
|
// Top region
|
||||||
|
console.log("Top: ");
|
||||||
|
|
||||||
|
hoveredDiv.style.borderTop = "2px solid purple";
|
||||||
|
return "above";
|
||||||
|
} else if (y >= 19 && y < 32) {
|
||||||
|
// Bottom region
|
||||||
|
console.log("Bottom: ");
|
||||||
|
hoveredDiv.style.borderBottom = "2px solid purple";
|
||||||
|
return "below";
|
||||||
|
} else {
|
||||||
|
// Middle region (child)
|
||||||
|
console.log("Middle: ");
|
||||||
|
hoveredDiv.style.outline = "2px solid #b188ff";
|
||||||
|
return "child";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="outline-overlay"
|
||||||
|
style={{
|
||||||
|
justifyContent: panelSide === "left" ? "flex-start" : "flex-end",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="outline-card"
|
||||||
|
style={{
|
||||||
|
background: backgroundColor || DEFAULT_PRAMS.backgroundColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="outline-header">
|
||||||
|
<div className="header-title">
|
||||||
|
<p>Outline</p>
|
||||||
|
</div>
|
||||||
|
<div className="outline-toolbar">
|
||||||
|
<button className="toolbar-button">
|
||||||
|
<AddIcon color={addIconColor || DEFAULT_PRAMS.addIconColor} />
|
||||||
|
</button>
|
||||||
|
<button className="toolbar-button">
|
||||||
|
<CollapseAllIcon />
|
||||||
|
</button>
|
||||||
|
<button className="close-button" onClick={() => setIsOpen(!isOpen)}>
|
||||||
|
<ChevronIcon isOpen={isOpen} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<div className="outline-content">
|
||||||
|
{groupHierarchy.map((item) => (
|
||||||
|
<TreeNode
|
||||||
|
key={item.groupUuid || item.modelUuid}
|
||||||
|
item={item}
|
||||||
|
textColor={textColor}
|
||||||
|
eyeIconColor={eyeIconColor}
|
||||||
|
lockIconColor={lockIconColor}
|
||||||
|
onDragStart={handleDragStart}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
draggingItem={draggingItem}
|
||||||
|
selectedObject={selectedObject} // pass UUID
|
||||||
|
setSelectedObject={setSelectedObject} // pass setter
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
17
src/components/outlinePanel/OutlinePanel.tsx
Normal file
17
src/components/outlinePanel/OutlinePanel.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { OutlineList } from "./OutlineList ";
|
||||||
|
|
||||||
|
export interface OutlinePanelProps {
|
||||||
|
textColor?: string;
|
||||||
|
panelSide?: "left" | "right";
|
||||||
|
backgroundColor?: string;
|
||||||
|
addIconColor?: string;
|
||||||
|
eyeIconColor?: string;
|
||||||
|
lockIconColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutlinePanel: React.FC<OutlinePanelProps> = (props) => {
|
||||||
|
return <OutlineList {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OutlinePanel;
|
||||||
1742
src/icons/ExportIcons.tsx
Normal file
1742
src/icons/ExportIcons.tsx
Normal file
File diff suppressed because it is too large
Load Diff
16
src/pages/SceneView.tsx
Normal file
16
src/pages/SceneView.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
import OutlinePanel from "../components/outlinePanel/OutlinePanel";
|
||||||
|
|
||||||
|
const SceneView = () => {
|
||||||
|
return (
|
||||||
|
<OutlinePanel
|
||||||
|
// panelSide="right"
|
||||||
|
// textColor=""
|
||||||
|
// addIconColor=""
|
||||||
|
// eyeIconColor=""
|
||||||
|
// backgroundColor=""
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SceneView;
|
||||||
5
src/styles/abstracts/_functions.scss
Normal file
5
src/styles/abstracts/_functions.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// get rem from pixels
|
||||||
|
|
||||||
|
@function get_rem($px) {
|
||||||
|
@return calc($px / 16 * 1rem);
|
||||||
|
}
|
||||||
33
src/styles/abstracts/_mixins.scss
Normal file
33
src/styles/abstracts/_mixins.scss
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
@mixin flex-center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-space-between {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array of base colors
|
||||||
|
$colors: (
|
||||||
|
#f5550b,
|
||||||
|
#1bac1b,
|
||||||
|
#0099ff,
|
||||||
|
#d4c927,
|
||||||
|
#8400ff,
|
||||||
|
#13e9b3,
|
||||||
|
#df1dcf
|
||||||
|
);
|
||||||
|
|
||||||
|
@mixin gradient-by-child($index) {
|
||||||
|
// Get the color based on the index passed
|
||||||
|
$base-color: nth($colors, $index);
|
||||||
|
// Apply gradient using the same color with different alpha values
|
||||||
|
background: linear-gradient(
|
||||||
|
144.19deg,
|
||||||
|
rgba($base-color, 0.2) 16.62%, // 20% opacity
|
||||||
|
rgba($base-color, 0.08) 85.81% // 80% opacity
|
||||||
|
);
|
||||||
|
}
|
||||||
171
src/styles/abstracts/_variables.scss
Normal file
171
src/styles/abstracts/_variables.scss
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
@use "functions";
|
||||||
|
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Josefin+Sans:ital,wght@0,100..700;1,100..700&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap");
|
||||||
|
|
||||||
|
// new variables
|
||||||
|
|
||||||
|
// text colors
|
||||||
|
// ---------- light mode ----------
|
||||||
|
$text-color: #2b3344;
|
||||||
|
$text-disabled: #b7b7c6;
|
||||||
|
$input-text-color: #595965;
|
||||||
|
$highlight-text-color: #6f42c1;
|
||||||
|
$text-button-color: #f3f3fd;
|
||||||
|
|
||||||
|
// ---------- dark mode ----------
|
||||||
|
$text-color-dark: #f3f3fd;
|
||||||
|
$text-disabled-dark: #6f6f7a;
|
||||||
|
$input-text-color-dark: #b5b5c8;
|
||||||
|
$highlight-text-color-dark: #d2baff;
|
||||||
|
$text-button-color-dark: #f3f3fd;
|
||||||
|
|
||||||
|
// background colors
|
||||||
|
// ---------- light mode ----------
|
||||||
|
$background-color: linear-gradient(-45deg, #fcfdfd71 0%, #fcfdfd79 100%);
|
||||||
|
$background-color-solid-gradient: linear-gradient(-45deg, #fcfdfd 0%, #fcfdfd 100%);
|
||||||
|
$background-color-solid: #fcfdfd;
|
||||||
|
$background-color-secondary: #fcfdfd4d;
|
||||||
|
$background-color-accent: #6f42c1;
|
||||||
|
$background-color-button: #6f42c1;
|
||||||
|
$background-color-drop-down: #6f42c14d;
|
||||||
|
$background-color-input: #ffffff4d;
|
||||||
|
$background-color-input-focus: #f2f2f7;
|
||||||
|
$background-color-drop-down-gradient: linear-gradient(-45deg, #75649366 0%, #40257266 100%);
|
||||||
|
$background-color-selected: #e0dfff;
|
||||||
|
$background-radial-gray-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
|
||||||
|
$background-model: #ffffff80;
|
||||||
|
|
||||||
|
// ---------- dark mode ----------
|
||||||
|
$background-color-dark: linear-gradient(-45deg, #333333b3 0%, #2d2437b3 100%);
|
||||||
|
$background-color-solid-gradient-dark: linear-gradient(-45deg, #333333 0%, #2d2437 100%);
|
||||||
|
$background-color-solid-dark: #19191d;
|
||||||
|
$background-color-secondary-dark: #19191d99;
|
||||||
|
$background-color-accent-dark: #6f42c1;
|
||||||
|
$background-color-button-dark: #6f42c1;
|
||||||
|
$background-color-drop-down-dark: #50505080;
|
||||||
|
$background-color-input-dark: #ffffff33;
|
||||||
|
$background-color-input-focus-dark: #333333;
|
||||||
|
$background-color-drop-down-gradient-dark: linear-gradient(-45deg, #8973b166 0%, #53427366 100%);
|
||||||
|
$background-color-selected-dark: #403e66;
|
||||||
|
$background-radial-gray-gradient-dark: radial-gradient(
|
||||||
|
circle,
|
||||||
|
#31373b 0%,
|
||||||
|
#48494b 46%,
|
||||||
|
#52415c 100%
|
||||||
|
);
|
||||||
|
$background-model-dark: #00000080;
|
||||||
|
|
||||||
|
// border colors
|
||||||
|
// ---------- light mode ----------
|
||||||
|
$border-color: #e0dfff;
|
||||||
|
$input-border-color: #d5dddd80;
|
||||||
|
$border-color-accent: #6f42c1;
|
||||||
|
|
||||||
|
// ---------- dark mode ----------
|
||||||
|
$border-color-dark: #564b69;
|
||||||
|
$input-border-color-dark: #d5dddd80;
|
||||||
|
$border-color-accent-dark: #6f42c1;
|
||||||
|
|
||||||
|
// highlight colors
|
||||||
|
// ---------- light mode ----------
|
||||||
|
$highlight-accent-color: #e0dfff;
|
||||||
|
$highlight-secondary-color: #6f42c1;
|
||||||
|
|
||||||
|
// ---------- dark mode ----------
|
||||||
|
$highlight-accent-color-dark: #403e6a;
|
||||||
|
$highlight-secondary-color-dark: #c4abf1;
|
||||||
|
|
||||||
|
// icon colors
|
||||||
|
// ---------- light mode ----------
|
||||||
|
$icon-default-color: #6f42c1;
|
||||||
|
$icon-default-color-hover: #7f4ddb;
|
||||||
|
$icon-default-color-active: #f2f2f7;
|
||||||
|
|
||||||
|
// ---------- dark mode ----------
|
||||||
|
$icon-default-color-dark: #6f42c1;
|
||||||
|
$icon-default-color-hover-dark: #7f4ddb;
|
||||||
|
$icon-default-color-active-dark: #f2f2f7;
|
||||||
|
|
||||||
|
// colors
|
||||||
|
$color1: #a392cd;
|
||||||
|
$color2: #7b4cd3;
|
||||||
|
$color3: #b186ff;
|
||||||
|
$color4: #8752e8;
|
||||||
|
$color5: #c7a8ff;
|
||||||
|
|
||||||
|
// log indication colors
|
||||||
|
// ------------ text -------------
|
||||||
|
$log-default-text-color: #6f42c1;
|
||||||
|
$log-info-text-color: #1773fd;
|
||||||
|
$log-warn-text-color: #f3a50c;
|
||||||
|
$log-error-text-color: #fc230f;
|
||||||
|
$log-success-text-color: #23a84f;
|
||||||
|
// ----------- dark ---------------
|
||||||
|
$log-default-text-color-dark: #b18ef1;
|
||||||
|
$log-info-text-color-dark: #7eb0fa;
|
||||||
|
$log-warn-text-color-dark: #ffaa00;
|
||||||
|
$log-error-text-color-dark: #ff887d;
|
||||||
|
$log-success-text-color-dark: #43ff81;
|
||||||
|
|
||||||
|
// ------------ background -------------
|
||||||
|
$log-default-backgroung-color: #6e42c133;
|
||||||
|
$log-info-background-color: #1773fd5d;
|
||||||
|
$log-warn-background-color: #f3a50c33;
|
||||||
|
$log-error-background-color: #fc230f33;
|
||||||
|
$log-success-background-color: #0ef75b33;
|
||||||
|
|
||||||
|
// old variables
|
||||||
|
$accent-color: #6f42c1;
|
||||||
|
$accent-color-dark: #c4abf1;
|
||||||
|
|
||||||
|
$background-color-gray: #f3f3f3;
|
||||||
|
$background-color-gray-dark: #232323;
|
||||||
|
|
||||||
|
$shadow-color: #3c3c431a;
|
||||||
|
$shadow-color-dark: #8f8f8f1a;
|
||||||
|
|
||||||
|
$acent-gradient-dark: linear-gradient(90deg, #b392f0 0%, #a676ff 100%);
|
||||||
|
$acent-gradient: linear-gradient(90deg, #6f42c1 0%, #925df3 100%);
|
||||||
|
|
||||||
|
$faint-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
|
||||||
|
$faint-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
|
||||||
|
|
||||||
|
$font-inter: "Inter", sans-serif;
|
||||||
|
$font-josefin-sans: "Josefin Sans", sans-serif;
|
||||||
|
$font-poppins: "Poppins", sans-serif;
|
||||||
|
$font-roboto: "Roboto", sans-serif;
|
||||||
|
|
||||||
|
$tiny: 0.625rem;
|
||||||
|
$small: 0.75rem;
|
||||||
|
$regular: 0.8rem;
|
||||||
|
$large: 1rem;
|
||||||
|
$xlarge: 1.125rem;
|
||||||
|
$xxlarge: 1.5rem;
|
||||||
|
$xxxlarge: 2rem;
|
||||||
|
|
||||||
|
$thin-weight: 300;
|
||||||
|
$regular-weight: 400;
|
||||||
|
$medium-weight: 500;
|
||||||
|
$bold-weight: 600;
|
||||||
|
|
||||||
|
$z-index-drei-html: 1;
|
||||||
|
$z-index-default: 1;
|
||||||
|
$z-index-marketplace: 2;
|
||||||
|
$z-index-tools: 3;
|
||||||
|
$z-index-negative: -1;
|
||||||
|
$z-index-ui-base: 10;
|
||||||
|
$z-index-ui-overlay: 20;
|
||||||
|
$z-index-ui-popup: 30;
|
||||||
|
$z-index-ui-highest: 50;
|
||||||
|
|
||||||
|
$box-shadow-light: 0px 2px 4px $shadow-color;
|
||||||
|
$box-shadow-medium: 0px 4px 8px $shadow-color;
|
||||||
|
$box-shadow-heavy: 0px 8px 16px $shadow-color;
|
||||||
|
|
||||||
|
$border-radius-small: 4px;
|
||||||
|
$border-radius-medium: 6px;
|
||||||
|
$border-radius-large: 12px;
|
||||||
|
$border-radius-circle: 50%;
|
||||||
|
$border-radius-xlarge: 16px;
|
||||||
|
$border-radius-extra-large: 20px;
|
||||||
|
$border-radius-xxx: 30px;
|
||||||
580
src/styles/components/outlinePanel.scss
Normal file
580
src/styles/components/outlinePanel.scss
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
// .outline-overlay {
|
||||||
|
// padding: 0 4px;
|
||||||
|
// font-family: "Segoe UI", sans-serif;
|
||||||
|
// display: flex;
|
||||||
|
// position: absolute;
|
||||||
|
// top: 0;
|
||||||
|
// bottom: 0;
|
||||||
|
// width: 100vw;
|
||||||
|
// pointer-events: none;
|
||||||
|
// .outline-card {
|
||||||
|
// pointer-events: all;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .outline-header {
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// justify-content: space-between;
|
||||||
|
// padding: 10px 16px;
|
||||||
|
// border-bottom: 1px solid #2e2e3e;
|
||||||
|
|
||||||
|
// .header-title {
|
||||||
|
// p {
|
||||||
|
// margin: 0;
|
||||||
|
// font-weight: 600;
|
||||||
|
// font-size: 14px;
|
||||||
|
// color: #ffffff;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .outline-toolbar {
|
||||||
|
// display: flex;
|
||||||
|
// gap: 8px;
|
||||||
|
|
||||||
|
// .toolbar-button {
|
||||||
|
// background: none;
|
||||||
|
// border: none;
|
||||||
|
// color: #aaa;
|
||||||
|
// cursor: pointer;
|
||||||
|
// width: 24px;
|
||||||
|
// height: 24px;
|
||||||
|
// border-radius: 4px;
|
||||||
|
// transition: background 0.3s;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
// color: #a855f7;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// svg {
|
||||||
|
// width: 100%;
|
||||||
|
// height: 100%;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .close-button {
|
||||||
|
// background: none;
|
||||||
|
// border: none;
|
||||||
|
// color: #aaa;
|
||||||
|
// cursor: pointer;
|
||||||
|
// width: 24px;
|
||||||
|
// height: 24px;
|
||||||
|
// border-radius: 4px;
|
||||||
|
// transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
// transform: rotate(-90deg);
|
||||||
|
// color: #a855f7;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// svg {
|
||||||
|
// width: 100%;
|
||||||
|
// height: 100%;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .outline-content {
|
||||||
|
// max-height: 52vh;
|
||||||
|
// overflow-y: auto;
|
||||||
|
// padding: 8px 0;
|
||||||
|
|
||||||
|
// &::-webkit-scrollbar {
|
||||||
|
// width: 6px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// &::-webkit-scrollbar-thumb {
|
||||||
|
// background: #a855f7;
|
||||||
|
// border-radius: 10px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .tree-node {
|
||||||
|
// display: flex;
|
||||||
|
// flex-direction: column;
|
||||||
|
// width: 100%;
|
||||||
|
// outline: 2px solid transparent;
|
||||||
|
// transition: all 0.3s ease;
|
||||||
|
|
||||||
|
// .tree-node-content {
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// width: 100%;
|
||||||
|
// padding: 6px 10px;
|
||||||
|
// border-radius: 8px;
|
||||||
|
// background: transparent;
|
||||||
|
// transition: background 0.2s ease, outline 0.2s ease;
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// position: relative;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// background: rgba(255, 255, 255, 0.05);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// &.selected {
|
||||||
|
// background: var(--background-color-accent, #6f42c1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// &.multi-selected {
|
||||||
|
// background: rgba(167, 139, 250, 0.2);
|
||||||
|
// border-left: 3px solid var(--highlight-secondary-color, #a855f7);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .expand-button {
|
||||||
|
// background: none;
|
||||||
|
// border: none;
|
||||||
|
// color: #aaa;
|
||||||
|
// cursor: pointer;
|
||||||
|
// width: 18px;
|
||||||
|
// height: 18px;
|
||||||
|
// flex-shrink: 0;
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// color: #a855f7;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .node-wrapper {
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// flex: 1;
|
||||||
|
// min-width: 0;
|
||||||
|
// gap: 8px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .node-icon {
|
||||||
|
// color: #a855f7;
|
||||||
|
// flex-shrink: 0;
|
||||||
|
// width: 16px;
|
||||||
|
// height: 16px;
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .rename-input {
|
||||||
|
// flex: 1;
|
||||||
|
// min-width: 0;
|
||||||
|
// overflow: hidden;
|
||||||
|
// white-space: nowrap;
|
||||||
|
// text-overflow: ellipsis;
|
||||||
|
// font-size: 14px;
|
||||||
|
// color: #fff;
|
||||||
|
|
||||||
|
// input.renaming {
|
||||||
|
// width: 100%;
|
||||||
|
// border: none;
|
||||||
|
// outline: none;
|
||||||
|
// border-radius: 6px;
|
||||||
|
// background: rgba(255, 255, 255, 0.1);
|
||||||
|
// color: #fff;
|
||||||
|
// padding: 3px 6px;
|
||||||
|
// font-size: 14px;
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .node-controls {
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// gap: 4px;
|
||||||
|
// flex-shrink: 0;
|
||||||
|
|
||||||
|
// .control-button {
|
||||||
|
// background: none;
|
||||||
|
// border: none;
|
||||||
|
// color: #aaa;
|
||||||
|
// cursor: pointer;
|
||||||
|
// border-radius: 4px;
|
||||||
|
// padding: 4px;
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// justify-content: center;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// background: rgba(255, 255, 255, 0.1);
|
||||||
|
// color: #a855f7;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// svg {
|
||||||
|
// width: 14px;
|
||||||
|
// height: 14px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .tree-children {
|
||||||
|
// padding-left: 16px;
|
||||||
|
// border-left: 1px dashed rgba(255, 255, 255, 0.1);
|
||||||
|
// margin-left: 6px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // .tree-node {
|
||||||
|
// // display: flex;
|
||||||
|
// // flex-direction: column;
|
||||||
|
// // outline: 2px solid transparent;
|
||||||
|
// // transition: background 0.3s;
|
||||||
|
|
||||||
|
// // .tree-node-content {
|
||||||
|
// // display: flex;
|
||||||
|
// // align-items: center;
|
||||||
|
// // padding: 8px 9px;
|
||||||
|
// // gap: 6px;
|
||||||
|
// // transition: background 0.2s ease;
|
||||||
|
// // position: relative;
|
||||||
|
// // &.selected {
|
||||||
|
// // background: #6f42c1;
|
||||||
|
// // border-radius: 45px;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // &:hover {
|
||||||
|
// // outline: 1px solid pink;
|
||||||
|
// // border-radius: 80px;
|
||||||
|
// // }
|
||||||
|
// // .node-wrapper {
|
||||||
|
// // display: flex;
|
||||||
|
// // gap: 40px;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // .expand-button {
|
||||||
|
// // background: none;
|
||||||
|
// // border: none;
|
||||||
|
// // cursor: pointer;
|
||||||
|
// // width: 20px;
|
||||||
|
// // height: 20px;
|
||||||
|
// // padding: 0;
|
||||||
|
// // color: #aaa;
|
||||||
|
|
||||||
|
// // &:hover {
|
||||||
|
// // color: #a855f7;
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // .node-icon {
|
||||||
|
// // color: white;
|
||||||
|
// // width: 18px;
|
||||||
|
// // flex-shrink: 0;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // .rename-input {
|
||||||
|
// // background: transparent;
|
||||||
|
// // border: none;
|
||||||
|
// // color: #fff;
|
||||||
|
// // font-size: 14px;
|
||||||
|
// // flex: 1;
|
||||||
|
// // outline: none;
|
||||||
|
// // padding: 2px 4px;
|
||||||
|
// // &:focus {
|
||||||
|
// // border-bottom: 1px solid #a855f7;
|
||||||
|
// // }
|
||||||
|
// // .renaming {
|
||||||
|
// // outline: none;
|
||||||
|
// // border: none;
|
||||||
|
// // border-radius: 15px;
|
||||||
|
// // height: 20px;
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // .node-controls {
|
||||||
|
// // display: flex;
|
||||||
|
// // gap: 6px;
|
||||||
|
|
||||||
|
// // .control-button {
|
||||||
|
// // background: none;
|
||||||
|
// // border: none;
|
||||||
|
// // color: #888;
|
||||||
|
// // cursor: pointer;
|
||||||
|
// // // padding: 4px;
|
||||||
|
// // border-radius: 4px;
|
||||||
|
|
||||||
|
// // &:hover {
|
||||||
|
// // background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
// // color: #a855f7;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // svg {
|
||||||
|
// // width: 16px;
|
||||||
|
// // height: 16px;
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // .tree-children {
|
||||||
|
// // padding-left: 16px;
|
||||||
|
// // border-left: 1px dashed rgba(255, 255, 255, 0.05);
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// Outline Component Styles
|
||||||
|
// Professional tree/hierarchy view with drag-and-drop support
|
||||||
|
|
||||||
|
.outline-overlay {
|
||||||
|
padding: 0 4px;
|
||||||
|
font-family: "Segoe UI", sans-serif;
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-card {
|
||||||
|
pointer-events: all;
|
||||||
|
width: 280px;
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #2e2e3e;
|
||||||
|
background: linear-gradient(to bottom, #1e1e2f, #12121a);
|
||||||
|
box-shadow: 0 0 8px rgba(168, 85, 247, 0.2);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-bottom: 1px solid #2e2e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.toolbar-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #aaa;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #a855f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #aaa;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
color: #a855f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-content {
|
||||||
|
max-height: 52vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px 0;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: #a855f7;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: background 0.3s;
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
gap: 8px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: 2px 8px;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: #6f42c1;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
outline: 2px solid #b188ff;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.locked {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: scale(0.98);
|
||||||
|
background-color: transparent;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
padding: 0;
|
||||||
|
color: #aaa;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #a855f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-icon {
|
||||||
|
color: #fff;
|
||||||
|
width: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rename-input {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
flex: 1;
|
||||||
|
outline: none;
|
||||||
|
padding: 2px 4px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-bottom: 1px solid #a855f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: white;
|
||||||
|
border: none;
|
||||||
|
color: black;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-bottom: 1px solid white;
|
||||||
|
border-radius: 30px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
.control-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #a855f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-children {
|
||||||
|
padding-left: 16px;
|
||||||
|
border-left: 1px dashed rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.outline-card {
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-overlay {
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rename-input {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/styles/main.scss
Normal file
1
src/styles/main.scss
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@use "components/outlinePanel.scss";
|
||||||
Reference in New Issue
Block a user