From be5d60d98faa1e1d83ef60d8aeff5410e9e19b2f Mon Sep 17 00:00:00 2001 From: bflatastic Date: Fri, 4 Apr 2025 20:56:01 +0700 Subject: [PATCH] camera modal ui --- .../components/reusable/SliderV2/SliderV2.tsx | 184 ++++++++++++++++++ .../app/components/reusable/SliderV2/index.ts | 1 + .../comps/PromptBox/CameraSettingsModal.tsx | 147 +++++++------- 3 files changed, 262 insertions(+), 70 deletions(-) create mode 100644 frontend/editor3d/app/components/reusable/SliderV2/SliderV2.tsx create mode 100644 frontend/editor3d/app/components/reusable/SliderV2/index.ts diff --git a/frontend/editor3d/app/components/reusable/SliderV2/SliderV2.tsx b/frontend/editor3d/app/components/reusable/SliderV2/SliderV2.tsx new file mode 100644 index 0000000000..9ad504c2a5 --- /dev/null +++ b/frontend/editor3d/app/components/reusable/SliderV2/SliderV2.tsx @@ -0,0 +1,184 @@ +import { Transition } from "@headlessui/react"; +import React, { useState, useRef, useEffect, ReactNode } from "react"; +import { twMerge } from "tailwind-merge"; +import { Button } from "../Button"; +import { faMinus, faPlus } from "@fortawesome/pro-solid-svg-icons"; + +interface SliderProps { + min: number; + max: number; + value: number; + onChange: (value: number) => void; + step: number; + className?: string; + showTooltip?: boolean; + tooltipContent?: ReactNode; + suffix?: string; + innerLabel?: string; + showDecrement?: boolean; + showIncrement?: boolean; +} + +export const SliderV2 = ({ + min, + max, + value, + onChange, + step, + className, + showTooltip, + tooltipContent, + suffix, + innerLabel, + showDecrement, + showIncrement, +}: SliderProps) => { + const [localValue, setLocalValue] = useState(value); + const sliderRef = useRef(null); + const [isDragging, setIsDragging] = useState(false); + const [showHandleTooltip, setShowHandleTooltip] = useState(false); + + useEffect(() => { + setLocalValue(value); + }, [value]); + + const setClampedValue = (value: number) => { + const roundedValue = Math.round(value / step) * step; + const clampedValue = Math.min(Math.max(roundedValue, min), max); + setLocalValue(clampedValue); + onChange(clampedValue); + }; + + const updatePosition = (x: number) => { + if (!sliderRef.current) return; + const rect = sliderRef.current.getBoundingClientRect(); + const fraction = (x - rect.left) / rect.width; + const newValue = fraction * (max - min) + min; + setClampedValue(newValue); + }; + + const handleMouseDown = (e: React.MouseEvent) => { + e.preventDefault(); + setIsDragging(true); + setShowHandleTooltip(true); + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + updatePosition(e.clientX); + }; + + const handleMouseMove = (e: MouseEvent) => { + e.preventDefault(); + updatePosition(e.clientX); + }; + + const handleMouseUp = (e: MouseEvent) => { + e.preventDefault(); + setIsDragging(false); + setShowHandleTooltip(false); + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + + const handleMouseEnter = () => { + if (!isDragging) { + setShowHandleTooltip(true); + } + }; + + const handleMouseLeave = () => { + if (!isDragging) { + setShowHandleTooltip(false); + } + }; + + const handleDecrement = () => { + setClampedValue(localValue - step); + }; + + const handleIncrement = () => { + setClampedValue(localValue + step); + }; + + const percentage = ((localValue - min) / (max - min)) * 100; + const displayValue = + localValue.toFixed(step.toString().split(".")[1]?.length || 0) + + (suffix || ""); + + return ( +
+ {showDecrement && ( +
+ ); +}; diff --git a/frontend/editor3d/app/components/reusable/SliderV2/index.ts b/frontend/editor3d/app/components/reusable/SliderV2/index.ts new file mode 100644 index 0000000000..ac81175ade --- /dev/null +++ b/frontend/editor3d/app/components/reusable/SliderV2/index.ts @@ -0,0 +1 @@ +export * from "./SliderV2"; diff --git a/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/CameraSettingsModal.tsx b/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/CameraSettingsModal.tsx index 90dd67f2a4..5baf39fb65 100644 --- a/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/CameraSettingsModal.tsx +++ b/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/CameraSettingsModal.tsx @@ -2,7 +2,8 @@ import { TransitionDialogue } from "~/components/reusable/TransitionDialogue"; import { faPlus, faTrashAlt, faXmark } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { PopoverItem } from "~/components/reusable/Popover"; -import { Input, Label, Tooltip } from "~/components"; +import { Button, Input, Label, Tooltip } from "~/components"; +import { SliderV2 } from "~/components/reusable/SliderV2/SliderV2"; interface ExtendedPopoverItem extends PopoverItem { id: string; @@ -73,77 +74,83 @@ export const CameraSettingsModal = ({
-
-

- {selectedCamera?.label || "Camera"} -

- - - -
-
-
-
- - - selectedCamera && - onCameraNameChange(selectedCamera.id, e.target.value) - } - className="text-sm" - /> -
-
- -
- - selectedCamera && - onCameraFocalLengthChange( - selectedCamera.id, - Number(e.target.value), - ) +
+
+
+

+ {selectedCamera?.label || "Camera"} +

+ - - {selectedCamera?.focalLength || 35}mm - + position="top" + delay={200} + > + +
+
+
+
+ + + selectedCamera && + onCameraNameChange(selectedCamera.id, e.target.value) + } + className="text-sm" + /> +
+
+ +
+ + selectedCamera && + onCameraFocalLengthChange(selectedCamera.id, value) + } + step={1} + suffix="mm" + showDecrement={true} + showIncrement={true} + className="w-full" + /> + + {selectedCamera?.focalLength || 35}mm + +
+
+
+
+
+