mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +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 { createRoot } from "react-dom/client";
|
||||
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;
|
||||
|
||||
@@ -107,3 +111,55 @@ describe("LayoutGeometryRows", () => {
|
||||
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 { formatPxMetricValue } from "./propertyPanelHelpers";
|
||||
import { STUDIO_KEYFRAMES_ENABLED } from "./manualEditingAvailability";
|
||||
import { resolveValueTier } from "./propertyPanelValueTier";
|
||||
|
||||
type KeyframeEntry = Array<{
|
||||
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