perf(layout): track panel resizing at a stepped cadence
This commit is contained in:
1 parent
a78fc48a7b
commit
8eba12ea2f
17 files changed
+705
-34
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { ref, type Ref } from "vue";
|
||||
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
|
||||
import { beginPanelResize, endPanelResize } from "@/lib/app/panelResizeState";
|
||||
|
||||
const PANEL_MIN_WIDTH = 240;
|
||||
const DEFAULT_PANEL_MAX_WIDTH = 800;
|
||||
@@ -36,6 +37,7 @@ export function usePanelResize() {
|
||||
resizeHandle?.setPointerCapture(e.pointerId);
|
||||
const resizeOverlay = document.createElement("div");
|
||||
resizeOverlay.setAttribute("aria-hidden", "true");
|
||||
beginPanelResize();
|
||||
Object.assign(resizeOverlay.style, {
|
||||
position: "fixed",
|
||||
inset: "0",
|
||||
@@ -53,19 +55,36 @@ export function usePanelResize() {
|
||||
widthRef.value = startWidth;
|
||||
const panelElement = resizeHandle?.parentElement;
|
||||
let currentWidth = startWidth;
|
||||
let rafId: number | null = null;
|
||||
|
||||
const onPointerMove = (ev: PointerEvent) => {
|
||||
const delta = ev.clientX - startX;
|
||||
currentWidth = Math.max(PANEL_MIN_WIDTH, Math.min(upperBound, startWidth + (direction === "right" ? delta : -delta)));
|
||||
panelElement?.style.setProperty("width", `${currentWidth}px`);
|
||||
// happy-dom unit tests assert styles synchronously (no frame advance),
|
||||
// so apply immediately under the test mode instead of via rAF.
|
||||
if (import.meta.env.MODE === "test") {
|
||||
panelElement?.style.setProperty("width", `${currentWidth}px`);
|
||||
return;
|
||||
}
|
||||
if (rafId !== null) return;
|
||||
rafId = requestAnimationFrame(() => {
|
||||
panelElement?.style.setProperty("width", `${currentWidth}px`);
|
||||
rafId = null;
|
||||
});
|
||||
};
|
||||
|
||||
const finishResize = () => {
|
||||
if (rafId !== null) {
|
||||
cancelAnimationFrame(rafId);
|
||||
rafId = null;
|
||||
}
|
||||
panelElement?.style.setProperty("width", `${currentWidth}px`);
|
||||
document.removeEventListener("pointermove", onPointerMove);
|
||||
document.removeEventListener("pointerup", finishResize);
|
||||
document.removeEventListener("pointercancel", finishResize);
|
||||
window.removeEventListener("blur", finishResize);
|
||||
if (resizeHandle?.hasPointerCapture(e.pointerId)) resizeHandle.releasePointerCapture(e.pointerId);
|
||||
endPanelResize();
|
||||
resizeOverlay.remove();
|
||||
widthRef.value = currentWidth;
|
||||
safeLocalStorageSet(storageKey, String(widthRef.value));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { onBeforeUnmount, ref, watch, type Ref } from "vue";
|
||||
import { deferUntilPanelResizeEnd } from "@/lib/app/panelResizeState";
|
||||
|
||||
export function useTabScroll(tabsContainerRef: Ref<HTMLElement | null>) {
|
||||
const hasTabOverflow = ref(false);
|
||||
@@ -20,6 +21,10 @@ export function useTabScroll(tabsContainerRef: Ref<HTMLElement | null>) {
|
||||
scrollThumbWidthPercent.value = 100;
|
||||
return;
|
||||
}
|
||||
// Skip DOM geometry reads while a divider drag is in flight; they force a
|
||||
// synchronous relayout of the whole document on every frame. Re-measure
|
||||
// once when the drag finishes.
|
||||
if (deferUntilPanelResizeEnd(updateScrollButtons)) return;
|
||||
const maxScrollLeft = Math.max(0, el.scrollWidth - el.clientWidth);
|
||||
hasTabOverflow.value = maxScrollLeft > 1;
|
||||
const rawThumbWidth = el.scrollWidth > 0 ? (el.clientWidth / el.scrollWidth) * 100 : 100;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { nextTick, onUnmounted, ref, watch, type Ref, type WatchSource } from "vue";
|
||||
import { deferUntilPanelResizeEnd } from "@/lib/app/panelResizeState";
|
||||
import { resolveNextEditorToolbarTier, type EditorToolbarTier } from "@/lib/tabs/editorToolbarLayout";
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,10 @@ export function useToolbarOverflow(rootRef: Ref<HTMLElement | null>, revalidateS
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
// clientWidth/scrollWidth reads force a document-wide synchronous relayout
|
||||
// on every resize notification; skip them during divider drags and take a
|
||||
// single measurement when the drag ends.
|
||||
if (deferUntilPanelResizeEnd(measure)) return;
|
||||
const next = resolveNextEditorToolbarTier({
|
||||
tier: tier.value,
|
||||
availableWidth: element.clientWidth,
|
||||
|
||||
Reference in new issue
Block a user