mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
feat(studio): add flat Layout Z-index row and Flex sub-block
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
588895f317
commit
3b958aeb77
@@ -3,7 +3,11 @@
|
|||||||
import React, { act } from "react";
|
import React, { act } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { LayoutGeometryRows } from "./propertyPanelFlatLayoutSection";
|
import {
|
||||||
|
LayoutFlexBlock,
|
||||||
|
LayoutGeometryRows,
|
||||||
|
LayoutZIndexRow,
|
||||||
|
} from "./propertyPanelFlatLayoutSection";
|
||||||
|
|
||||||
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||||
|
|
||||||
@@ -107,3 +111,55 @@ describe("LayoutGeometryRows", () => {
|
|||||||
act(() => root.unmount());
|
act(() => root.unmount());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("LayoutZIndexRow", () => {
|
||||||
|
it("renders the current z-index at the default tier and commits edits", () => {
|
||||||
|
const onSetStyle = vi.fn();
|
||||||
|
const { host, root } = renderInto(
|
||||||
|
<LayoutZIndexRow styles={{ "z-index": "3" }} onSetStyle={onSetStyle} />,
|
||||||
|
);
|
||||||
|
expect(host.textContent).toContain("Z-index");
|
||||||
|
const input = host.querySelector("input");
|
||||||
|
if (!input) throw new Error("expected an input");
|
||||||
|
expect(input.value).toBe("3");
|
||||||
|
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!;
|
||||||
|
act(() => {
|
||||||
|
setter.call(input, "5");
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
input.dispatchEvent(new Event("focusout", { bubbles: true }));
|
||||||
|
});
|
||||||
|
expect(onSetStyle).toHaveBeenCalledWith("z-index", "5");
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("LayoutFlexBlock", () => {
|
||||||
|
it("renders nothing when the element is not flex", () => {
|
||||||
|
const { host, root } = renderInto(
|
||||||
|
<LayoutFlexBlock styles={{ display: "block" }} onSetStyle={vi.fn()} disabled={false} />,
|
||||||
|
);
|
||||||
|
expect(host.textContent).toBe("");
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders direction/justify/align/gap and commits a direction change", () => {
|
||||||
|
const onSetStyle = vi.fn();
|
||||||
|
const { host, root } = renderInto(
|
||||||
|
<LayoutFlexBlock
|
||||||
|
styles={{ display: "flex", "flex-direction": "row", gap: "8px" }}
|
||||||
|
onSetStyle={onSetStyle}
|
||||||
|
disabled={false}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(host.textContent).toContain("Flex");
|
||||||
|
const columnOption = Array.from(host.querySelectorAll('[data-flat-segment="true"]')).find(
|
||||||
|
(el) => el.textContent === "Column",
|
||||||
|
);
|
||||||
|
if (!columnOption) throw new Error("expected a Column segment option");
|
||||||
|
act(() =>
|
||||||
|
(columnOption as HTMLElement).dispatchEvent(new MouseEvent("click", { bubbles: true })),
|
||||||
|
);
|
||||||
|
expect(onSetStyle).toHaveBeenCalledWith("flex-direction", "column");
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { FlatRow } from "./propertyPanelFlatPrimitives";
|
import { FlatRow, FlatSegmentedRow, FlatSelectRow } from "./propertyPanelFlatPrimitives";
|
||||||
import { KeyframeNavigation } from "./KeyframeNavigation";
|
import { KeyframeNavigation } from "./KeyframeNavigation";
|
||||||
import { formatPxMetricValue } from "./propertyPanelHelpers";
|
import { formatPxMetricValue } from "./propertyPanelHelpers";
|
||||||
import { STUDIO_KEYFRAMES_ENABLED } from "./manualEditingAvailability";
|
import { STUDIO_KEYFRAMES_ENABLED } from "./manualEditingAvailability";
|
||||||
|
import { resolveValueTier } from "./propertyPanelValueTier";
|
||||||
|
|
||||||
type KeyframeEntry = Array<{
|
type KeyframeEntry = Array<{
|
||||||
percentage: number;
|
percentage: number;
|
||||||
@@ -156,3 +157,81 @@ export function LayoutGeometryRows({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function LayoutZIndexRow({
|
||||||
|
styles,
|
||||||
|
onSetStyle,
|
||||||
|
}: {
|
||||||
|
styles: Record<string, string>;
|
||||||
|
onSetStyle: (prop: string, value: string) => void | Promise<void>;
|
||||||
|
}) {
|
||||||
|
const zIndex = String(parseInt(styles["z-index"] || "auto", 10) || 0);
|
||||||
|
return (
|
||||||
|
<FlatRow
|
||||||
|
label="Z-index"
|
||||||
|
value={zIndex}
|
||||||
|
tier="default"
|
||||||
|
onCommit={(next) => void onSetStyle("z-index", next)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LayoutFlexBlock({
|
||||||
|
styles,
|
||||||
|
onSetStyle,
|
||||||
|
disabled,
|
||||||
|
}: {
|
||||||
|
styles: Record<string, string>;
|
||||||
|
onSetStyle: (prop: string, value: string) => void | Promise<void>;
|
||||||
|
disabled: boolean;
|
||||||
|
}) {
|
||||||
|
const isFlex = styles.display === "flex" || styles.display === "inline-flex";
|
||||||
|
if (!isFlex) return null;
|
||||||
|
const direction = styles["flex-direction"] || "row";
|
||||||
|
return (
|
||||||
|
<div className="border-l-2 border-panel-border-input py-0.5 pl-[10px]">
|
||||||
|
<div className="mb-[3px] text-[9px] font-semibold uppercase tracking-[0.12em] text-panel-text-5">
|
||||||
|
Flex
|
||||||
|
</div>
|
||||||
|
<FlatSegmentedRow
|
||||||
|
label="Direction"
|
||||||
|
options={[
|
||||||
|
{ key: "row", node: "→ Row", active: direction === "row" },
|
||||||
|
{ key: "column", node: "Column", active: direction === "column" },
|
||||||
|
]}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(next) => void onSetStyle("flex-direction", next)}
|
||||||
|
/>
|
||||||
|
<FlatSelectRow
|
||||||
|
label="Justify"
|
||||||
|
value={styles["justify-content"] || "flex-start"}
|
||||||
|
tier={resolveValueTier(styles["justify-content"], "flex-start")}
|
||||||
|
disabled={disabled}
|
||||||
|
options={[
|
||||||
|
"flex-start",
|
||||||
|
"center",
|
||||||
|
"space-between",
|
||||||
|
"space-around",
|
||||||
|
"space-evenly",
|
||||||
|
"flex-end",
|
||||||
|
]}
|
||||||
|
onChange={(next) => void onSetStyle("justify-content", next)}
|
||||||
|
/>
|
||||||
|
<FlatSelectRow
|
||||||
|
label="Align"
|
||||||
|
value={styles["align-items"] || "stretch"}
|
||||||
|
tier={resolveValueTier(styles["align-items"], "stretch")}
|
||||||
|
disabled={disabled}
|
||||||
|
options={["stretch", "flex-start", "center", "flex-end", "baseline"]}
|
||||||
|
onChange={(next) => void onSetStyle("align-items", next)}
|
||||||
|
/>
|
||||||
|
<FlatRow
|
||||||
|
label="Gap"
|
||||||
|
value={styles.gap ?? "0px"}
|
||||||
|
tier={resolveValueTier(styles.gap, "0px")}
|
||||||
|
disabled={disabled}
|
||||||
|
onCommit={(next) => void onSetStyle("gap", next.endsWith("px") ? next : `${next}px`)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user