diff --git a/packages/studio/src/components/StudioRightPanel.tsx b/packages/studio/src/components/StudioRightPanel.tsx index 6be062963c..ed17525c14 100644 --- a/packages/studio/src/components/StudioRightPanel.tsx +++ b/packages/studio/src/components/StudioRightPanel.tsx @@ -1,12 +1,4 @@ -import { - useCallback, - useEffect, - useMemo, - useRef, - useState, - type MutableRefObject, - type PointerEvent as ReactPointerEvent, -} from "react"; +import { useCallback, useEffect, useMemo, useRef, type MutableRefObject } from "react"; import { PropertyPanel } from "./editor/PropertyPanel"; import { LayersPanel } from "./editor/LayersPanel"; import { CaptionPropertyPanel } from "../captions/components/CaptionPropertyPanel"; @@ -39,9 +31,7 @@ import { } from "./studioColorGradingScope"; import type { BackgroundRemovalProgress } from "./editor/propertyPanelTypes"; import { timelineKeysForSelections, type ToggleHiddenHandler } from "../utils/studioHelpers"; - -const MIN_INSPECTOR_SPLIT_PERCENT = 20; -const MAX_INSPECTOR_SPLIT_PERCENT = 75; +import { useInspectorSplitResize } from "../hooks/useInspectorSplitResize"; export interface StudioRightPanelProps { designPanelActive: boolean; @@ -115,6 +105,7 @@ export function StudioRightPanel({ handleDomAttributeCommit, handleDomAttributeLiveCommit, handleDomHtmlAttributeCommit, + handleDomAttributesCommit, handleDomPathOffsetCommit, handleDomBoxSizeCommit, handleDomRotationCommit, @@ -184,13 +175,13 @@ export function StudioRightPanel({ coalesceKey: activeCompPath ? `slideshow-notes:${activeCompPath}` : "slideshow-notes", }); - const [layersPanePercent, setLayersPanePercent] = useState(40); - const splitContainerRef = useRef(null); - const splitDragRef = useRef<{ - startY: number; - startPercent: number; - height: number; - } | null>(null); + const { + layersPanePercent, + splitContainerRef, + handleInspectorSplitResizeStart, + handleInspectorSplitResizeMove, + handleInspectorSplitResizeEnd, + } = useInspectorSplitResize(); const backgroundRemovalAbortRef = useRef(null); useEffect( @@ -231,35 +222,6 @@ export function StudioRightPanel({ toggleRightInspectorPane(pane); }; - const handleInspectorSplitResizeStart = useCallback( - (event: ReactPointerEvent) => { - event.preventDefault(); - event.currentTarget.setPointerCapture(event.pointerId); - const height = splitContainerRef.current?.getBoundingClientRect().height ?? 0; - splitDragRef.current = { - startY: event.clientY, - startPercent: layersPanePercent, - height, - }; - }, - [layersPanePercent], - ); - - const handleInspectorSplitResizeMove = useCallback((event: ReactPointerEvent) => { - const drag = splitDragRef.current; - if (!drag || drag.height <= 0) return; - const deltaPercent = ((event.clientY - drag.startY) / drag.height) * 100; - const next = Math.min( - MAX_INSPECTOR_SPLIT_PERCENT, - Math.max(MIN_INSPECTOR_SPLIT_PERCENT, drag.startPercent + deltaPercent), - ); - setLayersPanePercent(next); - }, []); - - const handleInspectorSplitResizeEnd = useCallback(() => { - splitDragRef.current = null; - }, []); - const handleApplyColorGradingScope = useCallback( async (scope: ColorGradingScope, value: string | null) => applyColorGradingScopeUpdate({ @@ -375,6 +337,7 @@ export function StudioRightPanel({ onUngroup={handleUngroupSelection} onSetStyle={handleDomStyleCommit} onSetAttribute={handleDomAttributeCommit} + onSetAttributes={handleDomAttributesCommit} onSetAttributeLive={handleDomAttributeLiveCommit} onApplyColorGradingScope={handleApplyColorGradingScope} onSetHtmlAttribute={handleDomHtmlAttributeCommit} diff --git a/packages/studio/src/components/editor/PropertyPanelFlat.tsx b/packages/studio/src/components/editor/PropertyPanelFlat.tsx index 90d7ed8698..ac90cfba79 100644 --- a/packages/studio/src/components/editor/PropertyPanelFlat.tsx +++ b/packages/studio/src/components/editor/PropertyPanelFlat.tsx @@ -77,6 +77,7 @@ export function PropertyPanelFlat({ onUngroup, onSetStyle, onSetAttribute, + onSetAttributes, onSetAttributeLive, onApplyColorGradingScope, onSetHtmlAttribute, @@ -145,6 +146,7 @@ export function PropertyPanelFlat({ | "onUngroup" | "onSetStyle" | "onSetAttribute" + | "onSetAttributes" | "onSetAttributeLive" | "onApplyColorGradingScope" | "onSetHtmlAttribute" @@ -440,6 +442,7 @@ export function PropertyPanelFlat({ multipleTimelines={gsapMultipleTimelines} unsupportedTimelinePattern={gsapUnsupportedTimelinePattern} onSetAttribute={onSetAttribute} + onSetAttributes={onSetAttributes} {...(gsapEffectHandlers ?? EMPTY_GSAP_EFFECT_HANDLERS)} /> ), @@ -516,7 +519,7 @@ export function PropertyPanelFlat({ onUngroup={onUngroup} showUngroup={Boolean(onUngroup && element.dataAttributes["hf-group"] != null)} /> -
+
{beforeOpen.map((g) => ( ; onImportAssets?: (files: FileList, dir?: string) => Promise; - onSetAttributeLive: (attr: string, value: string | null) => void | Promise; + onSetAttributeLive: ( + attr: string, + value: string | null, + onSettled?: (ok: boolean) => void, + ) => void | Promise; onApplyScope?: ( scope: "source-file" | "project", value: string | null, diff --git a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx index bf128ca0a2..db615065e9 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx @@ -238,6 +238,10 @@ describe("FlatColorGradingSection — Preset + LUT", () => { ); if (!presetSelect) throw new Error("expected a preset select"); expect(presetSelect.value).toBe("neutral"); + // The visible "Preset" label is a sibling span outside FlatSelectRow + // (label="" there, to avoid rendering it twice) — the select still + // needs its own accessible name via the dedicated ariaLabel prop. + expect(presetSelect.getAttribute("aria-label")).toBe("Preset"); act(() => { presetSelect.value = "fresh-pop"; presetSelect.dispatchEvent(new Event("change", { bubbles: true })); @@ -274,6 +278,7 @@ describe("FlatColorGradingSection — Preset + LUT", () => { act(() => lutToggle?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); const lutSelect = host.querySelector('[data-flat-grade-lut-select="true"]'); if (!lutSelect) throw new Error("expected a LUT catalog select"); + expect(lutSelect.getAttribute("aria-label")).toBe("Custom LUT"); act(() => { lutSelect.value = "assets/luts/cool.cube"; lutSelect.dispatchEvent(new Event("change", { bubbles: true })); @@ -607,4 +612,11 @@ describe("FlatColorGradingSection — HDR banner and Apply scope", () => { expect(onApplyToScope).toHaveBeenCalledTimes(1); act(() => root.unmount()); }); + + it("gives the Copy-grade-to scope select an accessible name — the visible text sits in a sibling span, not a