diff --git a/src/components/lighting/LightingEffectModal.test.tsx b/src/components/lighting/LightingEffectModal.test.tsx index 02fac036..f31effcb 100644 --- a/src/components/lighting/LightingEffectModal.test.tsx +++ b/src/components/lighting/LightingEffectModal.test.tsx @@ -57,7 +57,7 @@ describe("LightingEffectModal preview", () => { expect(subject.style.top).toBe("50%"); expect(subject.style.transform).toContain("perspective(420px)"); expect(subject.style.transform).toContain("rotateX(-8deg)"); - expect(subject.style.transform).toContain("rotateY(58deg)"); + expect(subject.style.transform).toContain("rotateY(-58deg)"); expect(beam.style.transform).toContain("rotate(16.53deg)"); }); @@ -118,6 +118,48 @@ describe("LightingEffectModal preview", () => { expect(screen.getByTestId("lighting-hue-thumb").style.left).toBe("50%"); }); + it("keeps the color picker open after dragging the hue slider", () => { + renderLightingModal({ + color: "#ffffff", + }); + + fireEvent.click(screen.getByLabelText("打光颜色")); + + const hueSlider = screen.getByLabelText("打光颜色色相"); + hueSlider.getBoundingClientRect = () => + ({ + left: 0, + top: 0, + width: 180, + height: 12, + right: 180, + bottom: 12, + x: 0, + y: 0, + toJSON: () => ({}), + }) as DOMRect; + + fireEvent.pointerDown(hueSlider, { clientX: 90, pointerId: 1 }); + fireEvent.pointerUp(hueSlider, { clientX: 90, pointerId: 1 }); + fireEvent.click(hueSlider); + + expect(screen.queryByTestId("lighting-color-picker-popover")).not.toBeNull(); + }); + + it("closes the color picker when clicking outside", () => { + renderLightingModal({ + color: "#ffffff", + }); + + fireEvent.click(screen.getByLabelText("打光颜色")); + + expect(screen.queryByTestId("lighting-color-picker-popover")).not.toBeNull(); + + fireEvent.pointerDown(document.body); + + expect(screen.queryByTestId("lighting-color-picker-popover")).toBeNull(); + }); + it("applies smart preset parameters and light position", () => { const { onApply } = renderLightingModal({ smartMode: true, diff --git a/src/components/lighting/LightingEffectModal.tsx b/src/components/lighting/LightingEffectModal.tsx index f35b2af4..32d90501 100644 --- a/src/components/lighting/LightingEffectModal.tsx +++ b/src/components/lighting/LightingEffectModal.tsx @@ -211,7 +211,7 @@ function getSubjectProjection(viewMode: LightingViewMode) { left: 50, top: 50, rotateX: -8, - rotateY: 58, + rotateY: -58, rotateZ: 0, scale: 0.98, width: 45, @@ -451,6 +451,7 @@ function LightingColorPicker({ const [open, setOpen] = useState(false); const hsv = useMemo(() => hexToHsv(value), [value]); const [hue, setHue] = useState(hsv.h); + const pickerRef = useRef(null); useEffect(() => { if (hsv.s > 0) { @@ -458,6 +459,19 @@ function LightingColorPicker({ } }, [hsv.h, hsv.s]); + useEffect(() => { + if (!open) return; + + const handlePointerDown = (event: PointerEvent) => { + const target = event.target; + if (target instanceof Node && pickerRef.current?.contains(target)) return; + setOpen(false); + }; + + document.addEventListener("pointerdown", handlePointerDown); + return () => document.removeEventListener("pointerdown", handlePointerDown); + }, [open]); + const updateSaturationValue = (event: ReactPointerEvent) => { const rect = event.currentTarget.getBoundingClientRect(); const s = clamp((event.clientX - rect.left) / rect.width, 0, 1); @@ -483,7 +497,7 @@ function LightingColorPicker({ }; return ( -
+
{open && ( -
+
-