Merge pull request #204 from storytold/feature/control-panel-redesign

control panel redesign
This commit is contained in:
ArEnSc authored and GitHub committed 2024-04-23 13:27:25 -04:00
commit 0693f2218f
3 files changed
+173 -66

No files matched your search

+85 -23
View File
@@ -1,24 +1,35 @@
import { useRef } from "react";
import { twMerge } from "tailwind-merge";
import { disableHotkeyInput,enableHotkeyInput,DomLevels } from "~/pages/PageEnigma/store";
import {
disableHotkeyInput,
enableHotkeyInput,
DomLevels,
} from "~/pages/PageEnigma/store";
interface InputVectorProps {
x: number;
y: number;
z: number;
onChange: (v: { x: number; y: number; z: number }) => void;
increment?: number;
}
export const InputVector = ({ x, y, z, onChange }: InputVectorProps) => {
export const InputVector = ({
x,
y,
z,
onChange,
increment = 0.1,
}: InputVectorProps) => {
const xRef = useRef<HTMLInputElement>(null);
const yRef = useRef<HTMLInputElement>(null);
const zRef = useRef<HTMLInputElement>(null);
const inputCommonClasses =
"relative w-14 h-7 rounded-r-lg bg-brand-secondary p-3 text-sm text-white transition-all duration-150 ease-in-out outline-none -outline-offset-2";
"relative h-6 rounded-r-lg bg-brand-secondary p-2 text-sm text-white transition-all duration-100 ease-in-out outline-none -outline-offset-2 text-end w-full hover:cursor-e-resize hover:bg-brand-secondary-900";
const wrapperCommonClasses =
"flex items-center before:inline-block before:w-10 before:h-10 before:bg-brand-primary before:text-white before:rounded-l-lg before:h-7 before:w-5 before:text-center before:justify-center before:items-center before:font-semibold before:flex before:align-middle before:leading-8 text-xs";
"relative flex items-center before:inline-block before:h-6 before:bg-brand-primary before:text-white before:rounded-l-lg before:w-1.5 before:text-center before:justify-center before:items-center before:font-semibold before:flex before:align-middle before:leading-8 text-xs";
function handleOnChange() {
const newVector = {
@@ -28,14 +39,67 @@ export const InputVector = ({ x, y, z, onChange }: InputVectorProps) => {
};
onChange(newVector);
}
const blurAllInputs = () => {
xRef.current?.blur();
yRef.current?.blur();
zRef.current?.blur();
};
// For dragging the input value to increment/decrement
const handleMouseDown = (
e: React.MouseEvent,
ref: React.RefObject<HTMLInputElement>,
) => {
e.preventDefault();
blurAllInputs();
let isDragging = false;
let previousMousePosition = e.clientX;
const mouseMoveHandler = (e: MouseEvent) => {
isDragging = true;
const currentMousePosition = e.clientX;
const direction = currentMousePosition > previousMousePosition ? 1 : -1;
previousMousePosition = currentMousePosition;
const currentValue = Number(ref.current?.value) || 0;
const newValue = currentValue + direction * increment;
if (ref.current) {
ref.current.value = newValue.toFixed(1);
ref.current.blur();
}
onChange({
x: Number(xRef.current?.value) || 0,
y: Number(yRef.current?.value) || 0,
z: Number(zRef.current?.value) || 0,
});
};
const mouseUpHandler = () => {
document.removeEventListener("mousemove", mouseMoveHandler);
document.removeEventListener("mouseup", mouseUpHandler);
if (!isDragging) {
ref.current?.focus();
ref.current?.select();
}
};
document.addEventListener("mousemove", mouseMoveHandler);
document.addEventListener("mouseup", mouseUpHandler);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
(e.currentTarget as HTMLInputElement).blur(); // Unfocus the input field on pressing Enter
}
};
return (
<div className="flex w-full justify-between gap-2">
<span
className={twMerge(
wrapperCommonClasses,
"before:bg-axis-x before:content-['X']",
)}
>
<div className="flex w-full flex-col justify-between gap-1.5">
<span className={twMerge(wrapperCommonClasses, "before:bg-axis-x")}>
<div className="absolute left-3.5 z-10 font-semibold">X</div>
<input
className={twMerge(inputCommonClasses, "focus:outline-axis-x")}
type="number"
@@ -44,14 +108,12 @@ export const InputVector = ({ x, y, z, onChange }: InputVectorProps) => {
value={x}
onFocus={() => disableHotkeyInput(DomLevels.INPUT)}
onBlur={() => enableHotkeyInput(DomLevels.INPUT)}
onMouseDown={(e) => handleMouseDown(e, xRef)}
onKeyDown={handleKeyDown}
/>
</span>
<span
className={twMerge(
wrapperCommonClasses,
"before:bg-axis-y before:content-['Y']",
)}
>
<span className={twMerge(wrapperCommonClasses, "before:bg-axis-y")}>
<div className="absolute left-3.5 z-10 font-semibold">Y</div>
<input
className={twMerge(inputCommonClasses, "focus:outline-axis-y")}
type="number"
@@ -60,14 +122,12 @@ export const InputVector = ({ x, y, z, onChange }: InputVectorProps) => {
value={y}
onFocus={() => disableHotkeyInput(DomLevels.INPUT)}
onBlur={() => enableHotkeyInput(DomLevels.INPUT)}
onMouseDown={(e) => handleMouseDown(e, yRef)}
onKeyDown={handleKeyDown}
/>
</span>
<span
className={twMerge(
wrapperCommonClasses,
"before:bg-axis-z before:content-['Z']",
)}
>
<span className={twMerge(wrapperCommonClasses, "before:bg-axis-z")}>
<div className="absolute left-3.5 z-10 font-semibold">Z</div>
<input
className={twMerge(inputCommonClasses, "focus:outline-axis-z")}
type="number"
@@ -76,6 +136,8 @@ export const InputVector = ({ x, y, z, onChange }: InputVectorProps) => {
value={z}
onFocus={() => disableHotkeyInput(DomLevels.INPUT)}
onBlur={() => enableHotkeyInput(DomLevels.INPUT)}
onMouseDown={(e) => handleMouseDown(e, zRef)}
onKeyDown={handleKeyDown}
/>
</span>
</div>
@@ -1,7 +1,8 @@
import { useContext, useEffect } from "react";
import { useContext, useEffect, useState } from "react";
import { Transition } from "@headlessui/react";
import {
faArrowRightArrowLeft,
faChevronDown,
faChevronUp,
faCube,
faTrash,
} from "@fortawesome/pro-solid-svg-icons";
@@ -19,9 +20,30 @@ import Queue from "~/pages/PageEnigma/Queue/Queue";
import { toTimelineActions } from "../../Queue/toTimelineActions";
import { QueueKeyframe } from "~/pages/PageEnigma/models";
// TODO this will be useful later to fix the bug on leading zeros
const formatNumber = (input: string): number => {
// Convert the input string to a number to remove leading zeros
const num = parseFloat(input);
// Convert the number back to a string with at least two decimal places
const str = num.toFixed(2);
return parseFloat(str);
}
// console.log(formatNumber("000123.4567")); // Outputs: "123.46"
// console.log(formatNumber("000123.4")); // Outputs: "123.40"
// console.log(formatNumber("000123")); // Outputs: "123.00"
// console.log(formatNumber("0000.00")); // Outputs: "0.00"
// console.log(formatNumber("0000.000001")); // Outputs: "0.00"
export const ControlPanelSceneObject = () => {
const editorEngine = useContext(EngineContext);
const [appUiState, dispatchAppUiState] = useContext(AppUiContext);
const [isCollapsed, setIsCollapsed] = useState(false);
const toggleCollapse = () => {
setIsCollapsed(!isCollapsed);
};
const position =
appUiState.controlPanel.currentSceneObject?.objectVectors?.position;
@@ -31,6 +53,7 @@ export const ControlPanelSceneObject = () => {
appUiState.controlPanel.currentSceneObject?.objectVectors?.scale;
const currentSceneObject = appUiState.controlPanel.currentSceneObject;
useEffect(() => {
// TODO this causes a subtle bug because it renders way too many times.
if (!appUiState.controlPanel.currentSceneObject) {
@@ -169,63 +192,73 @@ export const ControlPanelSceneObject = () => {
show={appUiState?.controlPanel.isShowing}
className={[
"absolute bottom-0 right-0",
"m-4 flex h-fit w-fit flex-col gap-2",
"m-4 flex h-fit w-56 flex-col gap-2",
"rounded-lg",
"border border-ui-panel-border",
"bg-ui-panel p-4 text-white",
"bg-ui-panel p-3.5 text-white",
].join(" ")}
enter="transition-opacity duration-100"
enter="transition-opacity duration-150"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-100"
leave="transition-opacity duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<div className="flex justify-between">
<div className="mb-1 flex justify-between">
<div className="flex items-center gap-2">
<FontAwesomeIcon icon={faCube} />
<p className="font-semibold">
<p className="max-w-36 truncate font-semibold">
{appUiState.controlPanel.currentSceneObject.object_name}
</p>
</div>
<div className="flex items-center gap-2 text-xs font-medium opacity-60">
<FontAwesomeIcon icon={faArrowRightArrowLeft} />
<p>Swap Object</p>
<FontAwesomeIcon
icon={isCollapsed ? faChevronUp : faChevronDown}
onClick={toggleCollapse}
className="cursor-pointer opacity-75 transition-opacity duration-100 ease-in-out hover:opacity-50"
/>
</div>
<Transition
show={!isCollapsed}
enter="transition-all duration-200 ease-in-out"
enterFrom="opacity-0 max-h-0"
enterTo="opacity-100 max-h-96"
leave="transition-all duration-200 ease-in-out"
leaveFrom="opacity-100 max-h-96"
leaveTo="opacity-0 max-h-0"
className={"flex flex-col gap-2 overflow-y-auto"}>
<div className="flex flex-col gap-1">
<H5>Location</H5>
<InputVector
x={position?.x || 0}
y={position?.y || 0}
z={position?.z || 0}
onChange={handlePositionChange}
/>
</div>
</div>
<span className="h-1" />
<div className="flex flex-col gap-1">
<H5>Position</H5>
<InputVector
x={position?.x || 0}
y={position?.y || 0}
z={position?.z || 0}
onChange={handlePositionChange}
/>
</div>
<div className="flex flex-col gap-1">
<H5>Rotation</H5>
<InputVector
x={rotation?.x || 0}
y={rotation?.y || 0}
z={rotation?.z || 0}
onChange={handleRotationChange}
increment={1}
/>
</div>
<div className="flex flex-col gap-1">
<H5>Rotation</H5>
<InputVector
x={rotation?.x || 0}
y={rotation?.y || 0}
z={rotation?.z || 0}
onChange={handleRotationChange}
/>
</div>
<div className="flex flex-col gap-1">
<H5>Scale</H5>
<InputVector
x={scale?.x || 0}
y={scale?.y || 0}
z={scale?.z || 0}
onChange={handleScaleChange}
/>
</div>
</Transition>
<div className="flex flex-col gap-1">
<H5>Scale</H5>
<InputVector
x={scale?.x || 0}
y={scale?.y || 0}
z={scale?.z || 0}
onChange={handleScaleChange}
/>
</div>
<span className="h-2" />
<div className="flex gap-2">
<div className="mt-2 flex gap-1.5">
<Button
variant="secondary"
className="grow"
+12
View File
@@ -18,6 +18,18 @@
border: 3px solid gray;
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
body {
font-family: "Fira Sans", sans-serif;
color: #fff;