diff --git a/package-lock.json b/package-lock.json index bf0f493..071b400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,19 @@ { - "name": "new", + "name": "lego-builder", "version": "0.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "new", + "name": "lego-builder", "version": "0.0.0", "dependencies": { - "@radix-ui/colors": "^0.1.8", + "@radix-ui/colors": "^2.0.1", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-toggle-group": "^1.0.4", "@react-three/drei": "^9.80.2", "@react-three/fiber": "^8.13.6", "framer-motion": "^10.15.1", @@ -18,6 +21,7 @@ "leva": "^0.9.35", "r3f-perf": "^7.1.2", "react": "^18.2.0", + "react-colorful": "^5.6.1", "react-dom": "^18.2.0", "three": "^0.155.0", "use-keyboard-shortcut": "^1.1.6", @@ -1017,9 +1021,17 @@ } }, "node_modules/@radix-ui/colors": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-0.1.8.tgz", - "integrity": "sha512-jwRMXYwC0hUo0mv6wGpuw254Pd9p/R6Td5xsRpOmaWkUHlooNWqVcadgyzlRumMq3xfOTXwJReU0Jv+EIy4Jbw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-2.0.1.tgz", + "integrity": "sha512-EhWky2oSIf5y+BDfrAY+Rykrkh7m2/M/bgCVRr3M+Hnr4JOh+mtFZLHUS1j/WzaPw1uvd0vwhK3cbxsR0QnCgw==" + }, + "node_modules/@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } }, "node_modules/@radix-ui/primitive": { "version": "1.0.1", @@ -1052,6 +1064,62 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz", + "integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", @@ -1122,6 +1190,23 @@ } } }, + "node_modules/@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", @@ -1319,6 +1404,70 @@ } } }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.1.2.tgz", + "integrity": "sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", @@ -1337,6 +1486,60 @@ } } }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", + "integrity": "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz", + "integrity": "sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-toggle": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tooltip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz", @@ -1441,6 +1644,23 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-rect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", @@ -5902,9 +6122,17 @@ } }, "@radix-ui/colors": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-0.1.8.tgz", - "integrity": "sha512-jwRMXYwC0hUo0mv6wGpuw254Pd9p/R6Td5xsRpOmaWkUHlooNWqVcadgyzlRumMq3xfOTXwJReU0Jv+EIy4Jbw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-2.0.1.tgz", + "integrity": "sha512-EhWky2oSIf5y+BDfrAY+Rykrkh7m2/M/bgCVRr3M+Hnr4JOh+mtFZLHUS1j/WzaPw1uvd0vwhK3cbxsR0QnCgw==" + }, + "@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "requires": { + "@babel/runtime": "^7.13.10" + } }, "@radix-ui/primitive": { "version": "1.0.1", @@ -5923,6 +6151,34 @@ "@radix-ui/react-primitive": "1.0.3" } }, + "@radix-ui/react-checkbox": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz", + "integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + } + }, + "@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + } + }, "@radix-ui/react-compose-refs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", @@ -5961,6 +6217,14 @@ "react-remove-scroll": "2.5.5" } }, + "@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, "@radix-ui/react-dismissable-layer": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", @@ -6054,6 +6318,42 @@ "@radix-ui/react-slot": "1.0.2" } }, + "@radix-ui/react-roving-focus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, + "@radix-ui/react-slider": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.1.2.tgz", + "integrity": "sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + } + }, "@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", @@ -6063,6 +6363,32 @@ "@radix-ui/react-compose-refs": "1.0.1" } }, + "@radix-ui/react-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", + "integrity": "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, + "@radix-ui/react-toggle-group": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz", + "integrity": "sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-toggle": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, "@radix-ui/react-tooltip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz", @@ -6117,6 +6443,14 @@ "@babel/runtime": "^7.13.10" } }, + "@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, "@radix-ui/react-use-rect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", diff --git a/package.json b/package.json index 10aefe0..596749b 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "new", + "name": "lego-builder", "private": true, "version": "0.0.0", "type": "module", @@ -10,9 +10,12 @@ "preview": "vite preview" }, "dependencies": { - "@radix-ui/colors": "^0.1.8", + "@radix-ui/colors": "^2.0.1", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-toggle-group": "^1.0.4", "@react-three/drei": "^9.80.2", "@react-three/fiber": "^8.13.6", "framer-motion": "^10.15.1", @@ -20,6 +23,7 @@ "leva": "^0.9.35", "r3f-perf": "^7.1.2", "react": "^18.2.0", + "react-colorful": "^5.6.1", "react-dom": "^18.2.0", "three": "^0.155.0", "use-keyboard-shortcut": "^1.1.6", diff --git a/src/components/3D/BrickCursor.jsx b/src/components/3D/BrickCursor.jsx index 5664293..83f91e5 100644 --- a/src/components/3D/BrickCursor.jsx +++ b/src/components/3D/BrickCursor.jsx @@ -4,7 +4,8 @@ /* eslint-disable react/display-name */ import React, { forwardRef, useMemo } from "react"; import { Vector3 } from "three"; -import { getMeasurementsFromDimensions, base } from "../../utils"; +import { getMeasurementsFromDimensions, base, CREATE_MODE } from "../../utils"; +import { useStore } from "../../store"; export const BrickCursor = forwardRef( ( @@ -16,10 +17,13 @@ export const BrickCursor = forwardRef( dimensions = { x: 1, z: 1 }, rotation = 0, translation = { x: 0, z: 0 }, - visible = true, }, ref ) => { + const mode = useStore((state) => state.mode); + + const visible = mode === CREATE_MODE; + const { height, width, depth } = getMeasurementsFromDimensions(dimensions); const position = useMemo(() => { diff --git a/src/components/3D/ControlsWrapper.jsx b/src/components/3D/ControlsWrapper.jsx index af438fc..8a3371f 100644 --- a/src/components/3D/ControlsWrapper.jsx +++ b/src/components/3D/ControlsWrapper.jsx @@ -8,18 +8,7 @@ import { useControls } from "leva"; import { OrbitControls } from "@react-three/drei"; export const ControlsWrapper = () => { - const { setEvents, camera } = useThree(); - - const [{ MultiSelect }, set] = useControls(() => ({ - MultiSelect: false, - })); - - useEffect(() => { - if (MultiSelect) { - camera.position.set(0, 1000, 0); - camera.lookAt(0, 0, 0); - } - }, [MultiSelect, camera]); + const { setEvents } = useThree(); const startTimeoutID = useRef(); const endTimeoutID = useRef(); @@ -37,12 +26,5 @@ export const ControlsWrapper = () => { endTimeoutID.current = setTimeout(() => setEvents({ enabled: true }), 500); }; - return ( - - ); + return ; }; diff --git a/src/components/3D/LegoRoom.jsx b/src/components/3D/LegoRoom.jsx index 280b9c0..7f9e279 100644 --- a/src/components/3D/LegoRoom.jsx +++ b/src/components/3D/LegoRoom.jsx @@ -8,10 +8,15 @@ import { LinearToneMapping } from "three"; import { ControlsWrapper, Scene } from "."; import { DialogBox } from "../UI"; import { Perf } from "r3f-perf"; +import { ModeToggleBar } from "../UI/ModeToggleBar"; +import { Panel } from "../UI/Panel"; export const LegoRoom = () => { return ( <> + + + { - + {/* */} - ); }; diff --git a/src/components/3D/Scene.jsx b/src/components/3D/Scene.jsx index 37bf8b8..6042b1c 100644 --- a/src/components/3D/Scene.jsx +++ b/src/components/3D/Scene.jsx @@ -18,58 +18,29 @@ import { base, useAnchorShorcuts, minWorkSpaceSize, + EDIT_MODE, } from "../../utils"; -import { button, useControls } from "leva"; import { ChangeColor } from "./ChangeColor"; +import { useStore } from "../../store"; export const Scene = () => { const [bricks, setBricks] = useState([]); - const minWorkSpaceSizeRef = useRef(minWorkSpaceSize); - - const [workspaceSize, setWorkspaceSize] = useState(minWorkSpaceSize); - const bricksBoundBox = useRef([]); const brickCursorRef = useRef(); - const box = useMemo(() => new Box3(), []); + const mode = useStore((state) => state.mode); - const [{ width, depth, rotate, color, anchorX, anchorZ, Edit }, set] = - useControls(() => ({ - width: { - value: 1, - min: 1, - max: 5, - step: 1, - }, - depth: { - value: 1, - min: 1, - max: 5, - step: 1, - }, - rotate: false, - color: "#ff0", - anchorX: { - value: 0, - min: -2, - max: 2, - step: 1, - }, - anchorZ: { - value: 0, - min: -2, - max: 2, - step: 1, - }, - Undo: button((get) => { - undoAddedBrick(); - }), - Edit: false, - })); + const isEditMode = mode === EDIT_MODE; - useAnchorShorcuts(anchorX, anchorZ, set); + const width = useStore((state) => state.width); + const depth = useStore((state) => state.depth); + const anchorX = useStore((state) => state.anchorX); + const anchorZ = useStore((state) => state.anchorZ); + const rotate = useStore((state) => state.rotate); + const color = useStore((state) => state.color); + // useAnchorShorcuts(anchorX, anchorZ, set); const setSelection = useSetSelection(); @@ -84,7 +55,7 @@ export const Scene = () => { const addBrick = (e) => { e.stopPropagation(); - if (Edit) return; + if (isEditMode) return; if (!e.face?.normal || !e.point) return; @@ -99,26 +70,6 @@ export const Scene = () => { brickCursorRef.current ); - const maxMax = Math.max( - Math.abs(boundingBoxOfBrickToBeAdded.max.x * 2), - Math.abs(boundingBoxOfBrickToBeAdded.max.z * 2) - ); - const maxMin = Math.max( - Math.abs(boundingBoxOfBrickToBeAdded.min.x * 2), - Math.abs(boundingBoxOfBrickToBeAdded.min.z * 2) - ); - - const max = Math.max(maxMax, maxMin); - - minWorkSpaceSizeRef.current = Math.max(max, minWorkSpaceSizeRef.current); - - setWorkspaceSize((currentSize) => { - if (currentSize <= max) { - return max; - } - return currentSize; - }); - let canCreate = true; for (let index = 0; index < bricksBoundBox.current.length; index++) { @@ -165,9 +116,8 @@ export const Scene = () => { const setBrickCursorPosition = (e) => { e.stopPropagation(); - if (Edit) return; + if (isEditMode) return; if (!brickCursorRef.current) return; - const { height } = getMeasurementsFromDimensions({ x: width, z: depth, @@ -191,27 +141,10 @@ export const Scene = () => { evenDepth ? base : base / 2 ) ); - - const bb = box.setFromObject(brickCursorRef.current); - const max = bb.max.multiplyScalar(1 / base); - const min = bb.min.multiplyScalar(1 / base); - const brickXEnd = brickCursorRef.current.position.x > 0 ? max.x : min.x; - const brickZEnd = brickCursorRef.current.position.z > 0 ? max.z : min.z; - - setWorkspaceSize((currentSize) => { - const sizeDiff = Math.max( - Math.abs(brickXEnd + anchorX) * base * 2 - currentSize, - Math.abs(brickZEnd + anchorZ) * base * 2 - currentSize - ); - if (sizeDiff > 0 || currentSize > minWorkSpaceSizeRef.current) { - return currentSize + sizeDiff; - } - return currentSize; - }); }; const onClick = (e) => { - if (!Edit) addBrick(e); + if (!isEditMode) addBrick(e); }; const mouseMove = (e) => { @@ -245,7 +178,7 @@ export const Scene = () => { return ( <> - {bricks.map((b, i) => { return ( { { - const { setEvents } = useThree(); - - const [{ events }, set] = useControls(() => ({ - events: true, - })); - - React.useEffect(() => { - setEvents({ enabled: events }); - }, [events, setEvents]); - - return null; -}; +import { useStore } from "../../../store"; +import { EDIT_MODE } from "../../../utils"; const context = React.createContext([]); const dispatchContext = React.createContext(() => {}); @@ -34,13 +21,14 @@ export function Select({ border = "1px solid #55aaff", backgroundColor = "rgba(75, 160, 255, 0.1)", filter: customFilter = (item) => item, - enable, ...props }) { const [downed, down] = React.useState(false); const { setEvents, camera, raycaster, gl, controls, size, get } = useThree(); - useEventControl(); + const mode = useStore((state) => state.mode); + + const enable = mode === EDIT_MODE; const [active, dispatch] = React.useReducer((state, { object, shift }) => { if (object === undefined) return []; diff --git a/src/components/UI/ModeToggleBar/index.jsx b/src/components/UI/ModeToggleBar/index.jsx new file mode 100644 index 0000000..595cc05 --- /dev/null +++ b/src/components/UI/ModeToggleBar/index.jsx @@ -0,0 +1,36 @@ +/* eslint-disable no-unused-vars */ +import React from "react"; +import * as ToggleGroup from "@radix-ui/react-toggle-group"; +import "./styles.css"; +import { CREATE_MODE, EDIT_MODE } from "../../../utils"; +import { useStore } from "../../../store"; + +export const ModeToggleBar = () => { + const setMode = useStore((state) => state.setMode); + + return ( + + setMode(CREATE_MODE)} + > + {CREATE_MODE} + + setMode(EDIT_MODE)} + > + {EDIT_MODE} + + + ); +}; diff --git a/src/components/UI/ModeToggleBar/styles.css b/src/components/UI/ModeToggleBar/styles.css new file mode 100644 index 0000000..4a021fe --- /dev/null +++ b/src/components/UI/ModeToggleBar/styles.css @@ -0,0 +1,47 @@ +@import "@radix-ui/colors/black-alpha.css"; +@import "@radix-ui/colors/mauve.css"; +@import "@radix-ui/colors/violet.css"; + +/* reset */ +button { + all: unset; +} + +.ToggleGroup { + display: inline-flex; + background-color: white; + border-radius: 4px; + box-shadow: 0 2px 10px var(--black-a7); + position: fixed; + left: 50%; + top: 15px; + transform: translateX(-50%); + z-index: 1000; +} + +.ToggleGroupItem { + background-color: white; + color: var(--mauve-11); + height: 35px; + display: flex; + font-size: 15px; + line-height: 1; + align-items: center; + justify-content: center; + margin: 6px; + padding: 0 4px; + border-radius: 4px; +} + +.ToggleGroupItem:hover { + background-color: var(--violet-3); + cursor: pointer; +} +.ToggleGroupItem[data-state="on"] { + background-color: var(--violet-5); + color: var(--violet-11); +} +.ToggleGroupItem:focus { + position: relative; + box-shadow: 0 0 0 2px black; +} diff --git a/src/components/UI/Panel/Checkbox/index.jsx b/src/components/UI/Panel/Checkbox/index.jsx new file mode 100644 index 0000000..e176d69 --- /dev/null +++ b/src/components/UI/Panel/Checkbox/index.jsx @@ -0,0 +1,39 @@ +/* eslint-disable react/prop-types */ +/* eslint-disable no-unused-vars */ +import React from "react"; +import * as CheckboxRadix from "@radix-ui/react-checkbox"; +import { CheckIcon } from "@radix-ui/react-icons"; +import "./styles.css"; +import { useStore } from "../../../../store"; + +const stateMap = { + rotate: "rotate", +}; + +const setterMap = { + rotate: "setRotate", +}; + +export const Checkbox = ({ label = "rotate" }) => { + const setValue = useStore((state) => state[setterMap[label]]); + + return ( +
+ + setValue(bool)} + > + + + + +
+ ); +}; diff --git a/src/components/UI/Panel/Checkbox/styles.css b/src/components/UI/Panel/Checkbox/styles.css new file mode 100644 index 0000000..9624c5e --- /dev/null +++ b/src/components/UI/Panel/Checkbox/styles.css @@ -0,0 +1,28 @@ +@import "@radix-ui/colors/black-alpha.css"; +@import "@radix-ui/colors/violet.css"; + +/* reset */ +button { + all: unset; +} + +.CheckboxRoot { + background-color: var(--black-a7); + width: 20px; + height: 20px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 2px 10px var(--black-a7); +} +.CheckboxRoot:hover { + cursor: pointer; +} +.CheckboxRoot:focus { + box-shadow: 0 0 0 2px black; +} + +.CheckboxIndicator { + color: var(--violet-11); +} diff --git a/src/components/UI/Panel/ColorInput/index.jsx b/src/components/UI/Panel/ColorInput/index.jsx new file mode 100644 index 0000000..9fc91ee --- /dev/null +++ b/src/components/UI/Panel/ColorInput/index.jsx @@ -0,0 +1,16 @@ +/* eslint-disable no-unused-vars */ +import React from "react"; +import { HexColorPicker } from "react-colorful"; +import "./styles.css"; +import { useStore } from "../../../../store"; + +export const ColorInput = () => { + const setColor = useStore((state) => state.setColor); + + return ( +
+ + setColor(color)} /> +
+ ); +}; diff --git a/src/components/UI/Panel/ColorInput/styles.css b/src/components/UI/Panel/ColorInput/styles.css new file mode 100644 index 0000000..8c3509c --- /dev/null +++ b/src/components/UI/Panel/ColorInput/styles.css @@ -0,0 +1,31 @@ +@import "@radix-ui/colors/black-alpha.css"; + +.SliderInputContainer .react-colorful__saturation-pointer { + width: 10px; + height: 10px; +} + +.SliderInputContainer .react-colorful__saturation { + border-bottom: none; + margin: 10px 0; + border-radius: 4px; +} + +.SliderInputContainer .react-colorful__hue-pointer { + width: 10px; + height: 10px; +} + +.SliderInputContainer .react-colorful__hue { + height: 8px; + /* padding: 8px; */ + border-radius: 2px; +} + +.SliderInputContainer .react-colorful { + width: 150px; + height: 150px; + border-radius: 4px; + padding: 8px; + background-color: var(--black-a7); +} diff --git a/src/components/UI/Panel/Left Panel/index.jsx b/src/components/UI/Panel/Left Panel/index.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/UI/Panel/Right Panel/index.jsx b/src/components/UI/Panel/Right Panel/index.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/UI/Panel/Slider/index.jsx b/src/components/UI/Panel/Slider/index.jsx new file mode 100644 index 0000000..93f40b8 --- /dev/null +++ b/src/components/UI/Panel/Slider/index.jsx @@ -0,0 +1,51 @@ +/* eslint-disable react/prop-types */ +/* eslint-disable no-unused-vars */ +import React, { useState } from "react"; +import * as Slider from "@radix-ui/react-slider"; +import "./styles.css"; +import { useStore } from "../../../../store"; + +const stateMap = { + width: "width", + depth: "depth", + "anchor X": "anchorX", + "anchor Z": "anchorZ", +}; + +const setterMap = { + width: "setWidth", + depth: "setDepth", + "anchor X": "setAnchorX", + "anchor Z": "setAnchorZ", +}; + +export const SliderWithLabel = ({ + label = "width", + defaultValue = 1, + ...props +}) => { + const value = useStore((state) => state[stateMap[label]]); + const setValue = useStore((state) => state[setterMap[label]]); + + return ( +
+ + setValue(newVal)} + {...props} + > + + + + + {value} + + +
+ ); +}; diff --git a/src/components/UI/Panel/Slider/styles.css b/src/components/UI/Panel/Slider/styles.css new file mode 100644 index 0000000..5a536cf --- /dev/null +++ b/src/components/UI/Panel/Slider/styles.css @@ -0,0 +1,68 @@ +@import "@radix-ui/colors/black-alpha.css"; +@import "@radix-ui/colors/violet.css"; + +.SliderInputContainer { + display: flex; + align-items: center; + gap: 8px; + margin: 14px 0; + padding: 8px; +} + +.SliderRoot { + position: relative; + display: flex; + align-items: center; + user-select: none; + touch-action: none; + width: 150px; + height: 20px; +} + +.SliderTrack { + background-color: var(--black-a7); + position: relative; + flex-grow: 1; + border-radius: 9999px; + height: 3px; +} + +.SliderRange { + position: absolute; + background-color: var(--black-a12); + border-radius: 9999px; + height: 100%; +} + +.SliderThumb { + display: block; + width: 20px; + height: 20px; + background-color: black; + box-shadow: 0 2px 10px var(--black-a7); + border-radius: 10px; + color: white; + display: flex; + justify-content: center; + align-items: center; + font-size: 10px; +} + +.SliderThumb:hover { + cursor: pointer; +} +.SliderThumb:focus { + outline: none; + box-shadow: 0 0 0 5px var(--black-a8); +} + +.SliderLabel { + font-size: 15px; + font-weight: 500; + line-height: 35px; + color: var(--black-a12); + text-transform: capitalize; + font-weight: 600; + min-width: 75px; + font-size: 14px; +} diff --git a/src/components/UI/Panel/index.jsx b/src/components/UI/Panel/index.jsx new file mode 100644 index 0000000..4baa2ed --- /dev/null +++ b/src/components/UI/Panel/index.jsx @@ -0,0 +1,20 @@ +/* eslint-disable react/prop-types */ +/* eslint-disable no-unused-vars */ +import React from "react"; +import "./styles.css"; +import { SliderWithLabel } from "./Slider"; +import { Checkbox } from "./Checkbox"; +import { ColorInput } from "./ColorInput"; + +export const Panel = () => { + return ( +
+ + + + + + +
+ ); +}; diff --git a/src/components/UI/Panel/styles.css b/src/components/UI/Panel/styles.css new file mode 100644 index 0000000..03b5881 --- /dev/null +++ b/src/components/UI/Panel/styles.css @@ -0,0 +1,10 @@ +.Panel { + position: fixed; + left: 20px; + top: 50%; + transform: translateY(-50%); + z-index: 10000; + background: white; + border-radius: 4px; + padding: 10px; +} diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 2c3fac6..0000000 --- a/src/index.css +++ /dev/null @@ -1,69 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/src/main.jsx b/src/main.jsx index 54b39dd..569fdf2 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,10 +1,9 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.jsx' -import './index.css' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; -ReactDOM.createRoot(document.getElementById('root')).render( +ReactDOM.createRoot(document.getElementById("root")).render( - , -) + +); diff --git a/src/store/index.js b/src/store/index.js index e69de29..b78b587 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -0,0 +1,25 @@ +import { create } from "zustand"; +import { CREATE_MODE, defaultAnchor, defaultWidth } from "../utils"; + +export const useStore = create((set) => ({ + mode: CREATE_MODE, + setMode: (newMode) => set({ mode: newMode }), + + width: defaultWidth, + setWidth: (newWidth) => set({ width: newWidth }), + + depth: defaultWidth, + setDepth: (newDepth) => set({ depth: newDepth }), + + anchorX: defaultAnchor, + setAnchorX: (newAnchorPoint) => set({ anchorX: newAnchorPoint }), + + anchorZ: defaultAnchor, + setAnchorZ: (newAnchorPoint) => set({ anchorZ: newAnchorPoint }), + + rotate: false, + setRotate: (bool) => set({ rotate: bool }), + + color: "#ff0000", + setColor: (newColor) => set({ color: newColor }), +})); diff --git a/src/utils/constants.js b/src/utils/constants.js index a15eea1..864928e 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -1,7 +1,13 @@ export const base = 25; export const knobSize = 7; export const outlineWidth = 1.3; -export const minWorkSpaceSize = 500; +export const minWorkSpaceSize = 1000; + +export const CREATE_MODE = "Create Mode"; +export const EDIT_MODE = "Edit Mode"; + +export const defaultWidth = 1; +export const defaultAnchor = 0; export const bricks = [ { x: 1, z: 1 }, diff --git a/src/utils/helpers.js b/src/utils/helpers.js index 4c2e001..7b717b1 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -9,7 +9,7 @@ export function CSSToHex(cssColor) { export function getMeasurementsFromDimensions({ x, y, z }) { return { width: base * x, - height: base * y || (base * 2) / 1.5, + height: base * y || base, depth: base * z, }; } @@ -34,7 +34,7 @@ export function createGeometry({ for (let j = 0; j < dimensions.z; j++) { const cylinder = new CylinderGeometry(knobDim, knobDim, knobDim, 20); const x = base * i - ((dimensions.x - 1) * base) / 2; - const y = base / 1.5; + const y = base - 12.5; const z = base * j - ((dimensions.z - 1) * base) / 2; cylinder.translate(x, y, z); geometries.push(cylinder);