mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
feat(studio): add flat Shadow and Blend rows to the Style group
This commit is contained in:
@@ -214,3 +214,66 @@ describe("FlatStyleSection — Stroke and Radius", () => {
|
|||||||
act(() => root.unmount());
|
act(() => root.unmount());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getFlatSelectRow(host: HTMLElement, label: string) {
|
||||||
|
const rows = Array.from(host.querySelectorAll<HTMLElement>(".group"));
|
||||||
|
const row = rows.find((el) => el.querySelector("span")?.textContent === label);
|
||||||
|
if (!row) throw new Error(`expected a select row for "${label}"`);
|
||||||
|
const select = row.querySelector<HTMLSelectElement>("select");
|
||||||
|
if (!select) throw new Error(`expected a select for "${label}"`);
|
||||||
|
const resetButton = row.querySelector<HTMLButtonElement>('[data-flat-select-reset="true"]');
|
||||||
|
return { row, select, resetButton };
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeFlatSelectRow(host: HTMLElement, label: string, nextValue: string) {
|
||||||
|
const { select } = getFlatSelectRow(host, label);
|
||||||
|
act(() => {
|
||||||
|
select.value = nextValue;
|
||||||
|
select.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("FlatStyleSection — Shadow and Blend", () => {
|
||||||
|
it("renders Shadow with the inferred preset and a reset when set, Blend with a plain select", () => {
|
||||||
|
const { host, root } = renderSection({ "box-shadow": "0 8px 24px rgba(0,0,0,.35)" });
|
||||||
|
expect(host.textContent).toContain("Shadow");
|
||||||
|
expect(host.textContent).toContain("Blend");
|
||||||
|
const selects = host.querySelectorAll("select");
|
||||||
|
expect(selects.length).toBeGreaterThanOrEqual(2);
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("commits a shadow preset change through onSetStyle", () => {
|
||||||
|
const { host, root, onSetStyle } = renderSection({});
|
||||||
|
changeFlatSelectRow(host, "Shadow", "soft");
|
||||||
|
expect(onSetStyle).toHaveBeenCalledWith("box-shadow", expect.any(String));
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("commits a blend mode change through onSetStyle", () => {
|
||||||
|
const { host, root, onSetStyle } = renderSection({});
|
||||||
|
changeFlatSelectRow(host, "Blend", "multiply");
|
||||||
|
expect(onSetStyle).toHaveBeenCalledWith("mix-blend-mode", "multiply");
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resets the shadow preset back to none via the reset button", () => {
|
||||||
|
const { host, root, onSetStyle } = renderSection({
|
||||||
|
"box-shadow": "0 12px 36px rgba(0, 0, 0, 0.28)",
|
||||||
|
});
|
||||||
|
const { resetButton } = getFlatSelectRow(host, "Shadow");
|
||||||
|
if (!resetButton) throw new Error("expected the shadow reset button");
|
||||||
|
act(() => resetButton.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
||||||
|
expect(onSetStyle).toHaveBeenCalledWith("box-shadow", "none");
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resets the blend mode back to normal via the reset button", () => {
|
||||||
|
const { host, root, onSetStyle } = renderSection({ "mix-blend-mode": "multiply" });
|
||||||
|
const { resetButton } = getFlatSelectRow(host, "Blend");
|
||||||
|
if (!resetButton) throw new Error("expected the blend reset button");
|
||||||
|
act(() => resetButton.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
||||||
|
expect(onSetStyle).toHaveBeenCalledWith("mix-blend-mode", "normal");
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -6,16 +6,19 @@ import { Link as LinkIcon } from "../../icons/SystemIcons";
|
|||||||
import { BorderRadiusEditor } from "./BorderRadiusEditor";
|
import { BorderRadiusEditor } from "./BorderRadiusEditor";
|
||||||
import { formatStrokeSummary, parseStrokeSummary } from "./propertyPanelFlatStyleHelpers";
|
import { formatStrokeSummary, parseStrokeSummary } from "./propertyPanelFlatStyleHelpers";
|
||||||
import {
|
import {
|
||||||
|
buildBoxShadowPresetValue,
|
||||||
buildStrokeStyleUpdates,
|
buildStrokeStyleUpdates,
|
||||||
buildStrokeWidthStyleUpdates,
|
buildStrokeWidthStyleUpdates,
|
||||||
extractBackgroundImageUrl,
|
extractBackgroundImageUrl,
|
||||||
formatNumericValue,
|
formatNumericValue,
|
||||||
formatPxMetricValue,
|
formatPxMetricValue,
|
||||||
|
inferBoxShadowPreset,
|
||||||
normalizePanelPxValue,
|
normalizePanelPxValue,
|
||||||
parseNumericValue,
|
parseNumericValue,
|
||||||
parsePxMetricValue,
|
parsePxMetricValue,
|
||||||
|
type BoxShadowPreset,
|
||||||
} from "./propertyPanelHelpers";
|
} from "./propertyPanelHelpers";
|
||||||
import { FlatRow, FlatSegmentedRow } from "./propertyPanelFlatPrimitives";
|
import { FlatRow, FlatSegmentedRow, FlatSelectRow } from "./propertyPanelFlatPrimitives";
|
||||||
import { resolveValueTier } from "./propertyPanelValueTier";
|
import { resolveValueTier } from "./propertyPanelValueTier";
|
||||||
import { ColorField } from "./propertyPanelColor";
|
import { ColorField } from "./propertyPanelColor";
|
||||||
import { GradientField, ImageFillField } from "./propertyPanelFill";
|
import { GradientField, ImageFillField } from "./propertyPanelFill";
|
||||||
@@ -268,6 +271,52 @@ function FlatRadiusRow({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Flat Shadow + Blend rows */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
function FlatShadowBlendRows({
|
||||||
|
styles,
|
||||||
|
disabled,
|
||||||
|
onSetStyle,
|
||||||
|
}: {
|
||||||
|
styles: Record<string, string>;
|
||||||
|
disabled: boolean;
|
||||||
|
onSetStyle: (prop: string, value: string) => void | Promise<void>;
|
||||||
|
}) {
|
||||||
|
const boxShadowPreset = inferBoxShadowPreset(styles["box-shadow"]);
|
||||||
|
const blendValue = styles["mix-blend-mode"] || "normal";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FlatSelectRow
|
||||||
|
label="Shadow"
|
||||||
|
value={boxShadowPreset}
|
||||||
|
options={["none", "soft", "lift", "glow", "custom"]}
|
||||||
|
tier={resolveValueTier(boxShadowPreset === "none" ? undefined : boxShadowPreset, "none")}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(next) => {
|
||||||
|
if (next === "custom") return;
|
||||||
|
void onSetStyle(
|
||||||
|
"box-shadow",
|
||||||
|
buildBoxShadowPresetValue(next as BoxShadowPreset, styles["box-shadow"]),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onReset={() => void onSetStyle("box-shadow", "none")}
|
||||||
|
/>
|
||||||
|
<FlatSelectRow
|
||||||
|
label="Blend"
|
||||||
|
value={blendValue}
|
||||||
|
options={["normal", "multiply", "screen", "overlay", "darken", "lighten"]}
|
||||||
|
tier={resolveValueTier(styles["mix-blend-mode"], "normal")}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(next) => void onSetStyle("mix-blend-mode", next)}
|
||||||
|
onReset={() => void onSetStyle("mix-blend-mode", "normal")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function FlatStyleSection({
|
export function FlatStyleSection({
|
||||||
projectId,
|
projectId,
|
||||||
element,
|
element,
|
||||||
@@ -303,6 +352,11 @@ export function FlatStyleSection({
|
|||||||
disabled={styleEditingDisabled}
|
disabled={styleEditingDisabled}
|
||||||
onSetStyle={onSetStyle}
|
onSetStyle={onSetStyle}
|
||||||
/>
|
/>
|
||||||
|
<FlatShadowBlendRows
|
||||||
|
styles={styles}
|
||||||
|
disabled={styleEditingDisabled}
|
||||||
|
onSetStyle={onSetStyle}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user