mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
feat(studio): add FlatToggle primitive
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
|||||||
FlatSegmentedRow,
|
FlatSegmentedRow,
|
||||||
FlatSelectRow,
|
FlatSelectRow,
|
||||||
FlatSlider,
|
FlatSlider,
|
||||||
|
FlatToggle,
|
||||||
PinnedZoneDivider,
|
PinnedZoneDivider,
|
||||||
} from "./propertyPanelFlatPrimitives";
|
} from "./propertyPanelFlatPrimitives";
|
||||||
|
|
||||||
@@ -286,3 +287,44 @@ describe("FlatSelectRow", () => {
|
|||||||
act(() => root.unmount());
|
act(() => root.unmount());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("FlatToggle", () => {
|
||||||
|
it("renders the off state with a dim label and dim knob, and fires onChange(true) on click", () => {
|
||||||
|
const onChange = vi.fn();
|
||||||
|
const { host, root } = renderInto(
|
||||||
|
<FlatToggle label="Loop" checked={false} onChange={onChange} />,
|
||||||
|
);
|
||||||
|
const label = host.querySelector('[data-flat-toggle-label="true"]');
|
||||||
|
expect(label?.className).toContain("text-panel-text-3");
|
||||||
|
const pill = host.querySelector<HTMLButtonElement>('[data-flat-toggle="true"]');
|
||||||
|
expect(pill).not.toBeNull();
|
||||||
|
act(() => pill?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
||||||
|
expect(onChange).toHaveBeenCalledWith(true);
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the on state with an emphasized label and mint knob, and fires onChange(false) on click", () => {
|
||||||
|
const onChange = vi.fn();
|
||||||
|
const { host, root } = renderInto(<FlatToggle label="Loop" checked onChange={onChange} />);
|
||||||
|
const label = host.querySelector('[data-flat-toggle-label="true"]');
|
||||||
|
expect(label?.className).toContain("text-panel-text-2");
|
||||||
|
const knob = host.querySelector('[data-flat-toggle-knob="true"]');
|
||||||
|
expect(knob?.className).toContain("bg-panel-accent");
|
||||||
|
const pill = host.querySelector<HTMLButtonElement>('[data-flat-toggle="true"]');
|
||||||
|
act(() => pill?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
||||||
|
expect(onChange).toHaveBeenCalledWith(false);
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not fire onChange when disabled", () => {
|
||||||
|
const onChange = vi.fn();
|
||||||
|
const { host, root } = renderInto(
|
||||||
|
<FlatToggle label="Loop" checked={false} disabled onChange={onChange} />,
|
||||||
|
);
|
||||||
|
const pill = host.querySelector<HTMLButtonElement>('[data-flat-toggle="true"]');
|
||||||
|
expect(pill?.disabled).toBe(true);
|
||||||
|
act(() => pill?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
||||||
|
expect(onChange).not.toHaveBeenCalled();
|
||||||
|
act(() => root.unmount());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -375,3 +375,49 @@ export function FlatSelectRow({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* FlatToggle — 24×14 pill switch */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
export function FlatToggle({
|
||||||
|
label,
|
||||||
|
checked,
|
||||||
|
disabled,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
checked: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
onChange: (next: boolean) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-[30px] items-center justify-between">
|
||||||
|
<span
|
||||||
|
data-flat-toggle-label="true"
|
||||||
|
className={`text-[11px] ${checked ? "text-panel-text-2" : "text-panel-text-3"}`}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-flat-toggle="true"
|
||||||
|
role="switch"
|
||||||
|
aria-checked={checked}
|
||||||
|
aria-label={label}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => onChange(!checked)}
|
||||||
|
className={`relative h-[14px] w-6 flex-shrink-0 rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${
|
||||||
|
checked ? "bg-panel-accent/35" : "bg-panel-hover"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
data-flat-toggle-knob="true"
|
||||||
|
className={`absolute top-0.5 h-2.5 w-2.5 rounded-full transition-all ${
|
||||||
|
checked ? "right-0.5 bg-panel-accent" : "left-0.5 bg-panel-text-4"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user