mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(studio): reveal the shown tab only when its strip narrows
A strip the user scrolled by hand no longer snaps back when another panel changes. The load smoke measures the focused group's strip directly.
This commit is contained in:
@@ -177,10 +177,25 @@ describe("dock tab fill", () => {
|
||||
const strip = stripOf("design");
|
||||
if (!strip) throw new Error("no design strip");
|
||||
await activate("variables");
|
||||
// The four tabs fit, so the strip is unscrolled.
|
||||
// The four tabs fit, so the strip is unscrolled; then it narrows to two and a half tabs.
|
||||
strip.scrollLeft = 0;
|
||||
Object.defineProperty(strip, "clientWidth", { value: 1000, configurable: true });
|
||||
act(() => resizeAll());
|
||||
Object.defineProperty(strip, "clientWidth", { value: 250, configurable: true });
|
||||
act(() => resizeAll());
|
||||
expect(strip.scrollLeft).toBe(3 * TAB_STEP + TAB_WIDTH + 1 - 250);
|
||||
});
|
||||
|
||||
it("leaves a strip the user scrolled alone when something else in the dock changes", async () => {
|
||||
const strip = stripOf("design");
|
||||
if (!strip) throw new Error("no design strip");
|
||||
await activate("variables");
|
||||
Object.defineProperty(strip, "clientWidth", { value: 250, configurable: true });
|
||||
act(() => resizeAll());
|
||||
// Scrolled back by hand to look at the first tabs, then another group takes focus.
|
||||
strip.scrollLeft = 0;
|
||||
await activate("assets");
|
||||
act(() => resizeAll());
|
||||
expect(strip.scrollLeft).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,13 +22,16 @@ function revealedScroll(box: { left: number; width: number }, scrollLeft: number
|
||||
return Math.max(scrollLeft, box.left + box.width + 1 - width);
|
||||
}
|
||||
|
||||
/** Reads where the shown tab sits; the write happens after every strip is read, so layout runs once. */
|
||||
function measureFill(list: HTMLElement): () => void {
|
||||
/**
|
||||
* Reads where the shown tab sits; the write happens after every strip is read, so layout runs once.
|
||||
* Only a strip that got narrower is scrolled, so a strip the user scrolled by hand stays put.
|
||||
*/
|
||||
function measureFill(list: HTMLElement, narrowed: boolean): () => void {
|
||||
const fill = fillOf(list);
|
||||
const tab = list.querySelector<HTMLElement>(":scope > .dv-active-tab");
|
||||
const box = tab ? { width: tab.offsetWidth, left: tab.offsetLeft } : null;
|
||||
const scrollLeft = list.scrollLeft;
|
||||
const target = box ? revealedScroll(box, scrollLeft, list.clientWidth) : scrollLeft;
|
||||
const target = box && narrowed ? revealedScroll(box, scrollLeft, list.clientWidth) : scrollLeft;
|
||||
return () => {
|
||||
fill.hidden = !box;
|
||||
if (!box) return;
|
||||
@@ -70,9 +73,16 @@ export function installTabFill(api: DockviewApi, root: HTMLElement): () => void
|
||||
observed.add(element);
|
||||
}
|
||||
}
|
||||
const widths = new WeakMap<HTMLElement, number>();
|
||||
function narrowed(list: HTMLElement) {
|
||||
const width = list.clientWidth;
|
||||
const before = widths.get(list) ?? width;
|
||||
widths.set(list, width);
|
||||
return width < before;
|
||||
}
|
||||
function placeAll() {
|
||||
const strips = [...lists()];
|
||||
const writes = strips.map(measureFill);
|
||||
const writes = strips.map((list) => measureFill(list, narrowed(list)));
|
||||
for (const write of writes) write();
|
||||
for (const list of strips) markClippedEdges(list);
|
||||
observe(strips.flatMap((list) => [list, ...list.querySelectorAll(":scope > .dv-tab")]));
|
||||
|
||||
@@ -101,9 +101,10 @@ export function studioSmokeApiResponse(method, requestUrl) {
|
||||
// about 10px narrower than macOS does; keep that much room so a strip that fits here fits on a Mac.
|
||||
const MAC_FONT_ALLOWANCE_PX = 10;
|
||||
|
||||
/** Runs in the page: the strip's tabs, plus the allowance, must fit before its action slot. */
|
||||
function clippedStrip(index, allowance) {
|
||||
const strip = document.querySelectorAll(".dv-tabs-container")[index];
|
||||
/** Runs in the page: the active group's tabs, plus the allowance, must fit before its actions. */
|
||||
function clippedStrip(allowance) {
|
||||
const strip = document.querySelector(".dv-groupview.dv-active-group .dv-tabs-container");
|
||||
if (!strip) return "no active dock strip";
|
||||
const actions = strip
|
||||
.closest(".dv-tabs-and-actions-container")
|
||||
?.querySelector(".dv-right-actions-container");
|
||||
@@ -125,13 +126,13 @@ async function dockStripErrors(page) {
|
||||
found.push(`${reservedSlots} inactive dock strips hold width for actions they do not draw`);
|
||||
}
|
||||
const shownTabs = await page.$$(".dv-tabs-container .dv-active-tab");
|
||||
for (const [index, tab] of shownTabs.entries()) {
|
||||
for (const tab of shownTabs) {
|
||||
// An active group draws its strip actions, so this is the least room its tabs get.
|
||||
await tab.click();
|
||||
await page.evaluate(
|
||||
() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))),
|
||||
);
|
||||
const clipped = await page.evaluate(clippedStrip, index, MAC_FONT_ALLOWANCE_PX);
|
||||
const clipped = await page.evaluate(clippedStrip, MAC_FONT_ALLOWANCE_PX);
|
||||
if (clipped) {
|
||||
found.push(
|
||||
`Dock tab strip clips a label at the default layout (${MAC_FONT_ALLOWANCE_PX}px macOS allowance): ${clipped}`,
|
||||
|
||||
Reference in New Issue
Block a user