Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ export function PropertyPanelFlat({
onUngroup={onUngroup}
showUngroup={Boolean(onUngroup && element.dataAttributes["hf-group"] != null)}
/>
<div data-flat-panel-body="true" className="flex min-h-0 flex-1 flex-col overflow-hidden">
<div data-flat-panel-body="true" className="flex min-h-0 flex-1 flex-col overflow-y-auto">
{beforeOpen.map((g) => (
<FlatGroupHeader
key={g.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,11 @@ export function ColorGradingSection({
assets: string[];
previewIframeRef?: RefObject<HTMLIFrameElement | null>;
onImportAssets?: (files: FileList, dir?: string) => Promise<string[]>;
onSetAttributeLive: (attr: string, value: string | null) => void | Promise<void>;
onSetAttributeLive: (
attr: string,
value: string | null,
onSettled?: (ok: boolean) => void,
) => void | Promise<void>;
onApplyScope?: (
scope: "source-file" | "project",
value: string | null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }));
Expand Down Expand Up @@ -274,6 +278,7 @@ describe("FlatColorGradingSection — Preset + LUT", () => {
act(() => lutToggle?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
const lutSelect = host.querySelector<HTMLSelectElement>('[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 }));
Expand Down Expand Up @@ -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 <label>", () => {
const { host, root } = renderInto(<FlatColorGradingSection {...neutralPropsBase()} />);
const scopeSelect = host.querySelector<HTMLSelectElement>('[aria-label="Copy grade to"]');
expect(scopeSelect).not.toBeNull();
act(() => root.unmount());
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import {
HF_COLOR_GRADING_PRESETS,
isHfColorGradingActive,
Expand Down Expand Up @@ -32,6 +32,18 @@ export function FlatColorGradingAccessory({
}) {
const { grading, compareEnabled, runtimeStatus, commitCompare, resetGrading } = state;
const gradingActive = isHfColorGradingActive(grading);
// Tracks the active hold's cleanup so it can be torn down on unmount too —
// without this, switching selection away mid-hold (unmounting this
// accessory) leaves the pointerup/pointercancel/blur listeners registered
// on `window` forever, each holding a closure over the old commitCompare.
const releaseRef = useRef<(() => void) | null>(null);
useEffect(
() => () => {
releaseRef.current?.();
releaseRef.current = null;
},
[],
);

return (
<span className="flex items-center gap-2.5">
Expand All @@ -50,7 +62,9 @@ export function FlatColorGradingAccessory({
window.removeEventListener("pointerup", release);
window.removeEventListener("pointercancel", release);
window.removeEventListener("blur", release);
releaseRef.current = null;
};
releaseRef.current = release;
window.addEventListener("pointerup", release);
window.addEventListener("pointercancel", release);
window.addEventListener("blur", release);
Expand Down Expand Up @@ -298,6 +312,7 @@ export function FlatColorGradingSection({
<span className="text-[11px] text-panel-text-2">Preset</span>
<FlatSelectRow
label=""
ariaLabel="Preset"
value={grading.preset ?? "neutral"}
options={PRESET_OPTIONS}
tier={resolveValueTier(
Expand Down Expand Up @@ -344,6 +359,7 @@ export function FlatColorGradingSection({
</span>
<select
data-flat-grade-lut-select="true"
aria-label="Custom LUT"
value={lut?.src ?? ""}
onChange={(e) => {
const src = e.target.value;
Expand Down Expand Up @@ -510,6 +526,7 @@ export function FlatColorGradingSection({
<span className="flex items-center gap-1.5 text-[11px] text-panel-text-2">
Copy grade to
<select
aria-label="Copy grade to"
value={applyScope}
onChange={(e) => onSetApplyScope(e.target.value as "source-file" | "project")}
disabled={applyBusy}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ export function LayoutFlexBlock({
<FlatSegmentedRow
label="Direction"
options={[
{ key: "row", node: "→ Row", active: direction === "row" },
{ key: "column", node: "Column", active: direction === "column" },
{ key: "row", node: "→ Row", label: "Row", active: direction === "row" },
{ key: "column", node: "Column", label: "Column", active: direction === "column" },
]}
disabled={disabled}
onChange={(next) => void onSetStyle("flex-direction", next)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
buildInsetClipPathSides,
buildInsetClipPathValue,
formatNumericValue,
formatPxMetricValue,
getClipPathInsetPx,
inferClipPathPreset,
parseInsetClipPathSides,
parsePxMetricValue,
type ClipPathInsetSides,
} from "./propertyPanelHelpers";
import { FlatSlider } from "./propertyPanelFlatPrimitives";
import { MetricField } from "./propertyPanelPrimitives";

/* ------------------------------------------------------------------ */
/* Flat Mask inset — uniform slider + per-side fields */
/* (split out of propertyPanelFlatStyleSections.tsx to stay under the */
/* 600-line file-size gate) */
/* ------------------------------------------------------------------ */

export function FlatMaskInsetRows({
clipPathValue,
radiusValue,
disabled,
onSetStyle,
}: {
clipPathValue: string;
radiusValue: number;
disabled: boolean;
onSetStyle: (prop: string, value: string) => void | Promise<void>;
}) {
const clipPathPreset = inferClipPathPreset(clipPathValue);
const parsedClipInsets = parseInsetClipPathSides(clipPathValue);
const clipInsetValue = getClipPathInsetPx(clipPathValue);
const clipInsetSides = parsedClipInsets ?? {
top: clipInsetValue,
right: clipInsetValue,
bottom: clipInsetValue,
left: clipInsetValue,
radius: radiusValue,
};
const showClipInsetSides = clipPathPreset === "inset" || parsedClipInsets != null;

const commitClipInsetSide = (side: keyof ClipPathInsetSides, nextValue: string) => {
const next = parsePxMetricValue(nextValue);
if (next == null) return;
const sides: ClipPathInsetSides = {
top: clipInsetSides.top,
right: clipInsetSides.right,
bottom: clipInsetSides.bottom,
left: clipInsetSides.left,
};
sides[side] = next;
void onSetStyle("clip-path", buildInsetClipPathSides(sides, clipInsetSides.radius));
};

return (
<>
<FlatSlider
label="Mask inset"
value={clipInsetValue}
min={0}
max={Math.max(120, Math.ceil(clipInsetValue))}
step={1}
tier={clipInsetValue > 0 ? "explicitCustom" : "default"}
displayValue={`${formatNumericValue(clipInsetValue)}px`}
disabled={disabled}
onCommit={(next) =>
void onSetStyle("clip-path", buildInsetClipPathValue(next, radiusValue))
}
/>
{showClipInsetSides && (
<div className="grid grid-cols-4 gap-2">
<MetricField
label="T"
value={formatPxMetricValue(clipInsetSides.top)}
disabled={disabled}
onCommit={(next) => commitClipInsetSide("top", next)}
/>
<MetricField
label="R"
value={formatPxMetricValue(clipInsetSides.right)}
disabled={disabled}
onCommit={(next) => commitClipInsetSide("right", next)}
/>
<MetricField
label="B"
value={formatPxMetricValue(clipInsetSides.bottom)}
disabled={disabled}
onCommit={(next) => commitClipInsetSide("bottom", next)}
/>
<MetricField
label="L"
value={formatPxMetricValue(clipInsetSides.left)}
disabled={disabled}
onCommit={(next) => commitClipInsetSide("left", next)}
/>
</div>
)}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
parseNumericValue,
stripQueryAndHash,
} from "./propertyPanelHelpers";
import { FlatSelectRow, FlatSlider, FlatToggle } from "./propertyPanelFlatPrimitives";
import { FlatSelectRow, FlatSlider } from "./propertyPanelFlatPrimitives";
import { FlatToggle } from "./propertyPanelFlatToggle";

// fallow-ignore-next-line complexity
export function FlatMediaSection({
Expand Down
Loading
Loading