{
- 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);