Fix virtual scrolling MenuList font family dropdowns shrinking when wider content is unmounted (#3987)
* Fix virtual scrolling MenuList dropdowns shrinking when wider content goes away * Code review fixes * Fix small CI workflow bug * Stop scrolling in dropdowns from horizontally scrolling the control bar * Use more robust way of getting commit hash in CI workflow
This commit is contained in:
parent
ab822afae4
commit
5edb00bd9a
|
|
@ -162,7 +162,7 @@ jobs:
|
||||||
REF="master"
|
REF="master"
|
||||||
ENVIRONMENT="graphite-dev (Production)"
|
ENVIRONMENT="graphite-dev (Production)"
|
||||||
else
|
else
|
||||||
REF="${{ inputs.checkout_ref || github.head_ref || github.ref_name }}"
|
REF="$(git rev-parse HEAD)"
|
||||||
ENVIRONMENT="graphite-dev (Preview)"
|
ENVIRONMENT="graphite-dev (Preview)"
|
||||||
fi
|
fi
|
||||||
DEPLOY_ID=$(gh api \
|
DEPLOY_ID=$(gh api \
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@
|
||||||
let virtualScrollingEntriesStart = 0;
|
let virtualScrollingEntriesStart = 0;
|
||||||
let keydownListenerAdded = false;
|
let keydownListenerAdded = false;
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
|
let maxMenuWidth = 0;
|
||||||
|
let resizeObserver: ResizeObserver | undefined = undefined;
|
||||||
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- `loadedFonts` reactivity is driven by `loadedFontsGeneration`, not the Set itself
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- `loadedFonts` reactivity is driven by `loadedFontsGeneration`, not the Set itself
|
||||||
let loadedFonts = new Set<string>();
|
let loadedFonts = new Set<string>();
|
||||||
let loadedFontsGeneration = 0;
|
let loadedFontsGeneration = 0;
|
||||||
|
|
@ -77,6 +79,7 @@
|
||||||
});
|
});
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
removeEventListener("keydown", keydown);
|
removeEventListener("keydown", keydown);
|
||||||
|
resizeObserver?.disconnect();
|
||||||
// Set the destroyed status in the closure kept by the awaited `tick()` in `onMount` in case that delayed run occurs after the component is destroyed
|
// Set the destroyed status in the closure kept by the awaited `tick()` in `onMount` in case that delayed run occurs after the component is destroyed
|
||||||
destroyed = true;
|
destroyed = true;
|
||||||
});
|
});
|
||||||
|
|
@ -144,6 +147,15 @@
|
||||||
keydownListenerAdded = false;
|
keydownListenerAdded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For virtual scrolling menus, observe width changes so the menu only grows and never shrinks while open
|
||||||
|
if (open && virtualScrolling) {
|
||||||
|
startMenuWidthObserver();
|
||||||
|
} else if (resizeObserver) {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
resizeObserver = undefined;
|
||||||
|
maxMenuWidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
highlighted = activeEntry;
|
highlighted = activeEntry;
|
||||||
dispatch("open", open);
|
dispatch("open", open);
|
||||||
|
|
||||||
|
|
@ -156,14 +168,38 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function watchEntriesHash(_entriesHash: bigint) {
|
function watchEntriesHash(_: bigint) {
|
||||||
reactiveEntries = entries;
|
reactiveEntries = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
function watchRemeasureWidth(_: MenuListEntry[][], __: boolean) {
|
function watchRemeasureWidth(_: MenuListEntry[][], __: boolean) {
|
||||||
|
// Skip re-measurement for virtual scrolling menus since ResizeObserver handles their width
|
||||||
|
if (virtualScrolling) return;
|
||||||
|
|
||||||
self?.measureAndEmitNaturalWidth();
|
self?.measureAndEmitNaturalWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function startMenuWidthObserver() {
|
||||||
|
await tick();
|
||||||
|
// Guard against the menu having closed during the tick
|
||||||
|
if (!open) return;
|
||||||
|
|
||||||
|
const floatingMenuContentDiv = self?.div()?.querySelector("[data-floating-menu-content]");
|
||||||
|
if (!(floatingMenuContentDiv instanceof HTMLElement)) return;
|
||||||
|
|
||||||
|
maxMenuWidth = 0;
|
||||||
|
|
||||||
|
resizeObserver?.disconnect();
|
||||||
|
resizeObserver = new ResizeObserver(() => {
|
||||||
|
const width = floatingMenuContentDiv.scrollWidth;
|
||||||
|
if (width > maxMenuWidth) {
|
||||||
|
maxMenuWidth = width;
|
||||||
|
floatingMenuContentDiv.style.minWidth = `${maxMenuWidth}px`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resizeObserver.observe(floatingMenuContentDiv);
|
||||||
|
}
|
||||||
|
|
||||||
function onScroll(e: Event) {
|
function onScroll(e: Event) {
|
||||||
if (!virtualScrollingEntryHeight) return;
|
if (!virtualScrollingEntryHeight) return;
|
||||||
virtualScrollingEntriesStart = e.target instanceof HTMLElement ? e.target.scrollTop : 0;
|
virtualScrollingEntriesStart = e.target instanceof HTMLElement ? e.target.scrollTop : 0;
|
||||||
|
|
|
||||||
|
|
@ -492,7 +492,7 @@
|
||||||
<div class="tail" bind:this={tail}></div>
|
<div class="tail" bind:this={tail}></div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if displayContainer}
|
{#if displayContainer}
|
||||||
<div class="floating-menu-container" bind:this={floatingMenuContainer}>
|
<div class="floating-menu-container" bind:this={floatingMenuContainer} on:wheel|stopPropagation>
|
||||||
<LayoutCol class="floating-menu-content" styles={{ "min-width": minWidthStyleValue }} {scrollableY} bind:this={floatingMenuContent} data-floating-menu-content>
|
<LayoutCol class="floating-menu-content" styles={{ "min-width": minWidthStyleValue }} {scrollableY} bind:this={floatingMenuContent} data-floating-menu-content>
|
||||||
<slot />
|
<slot />
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ export function onWheelScroll(e: WheelEvent, editor: EditorWrapper) {
|
||||||
|
|
||||||
// Redirect vertical scroll wheel movement into a horizontal scroll on a horizontally scrollable element
|
// Redirect vertical scroll wheel movement into a horizontal scroll on a horizontally scrollable element
|
||||||
// There seems to be no possible way to properly employ the browser's smooth scrolling interpolation
|
// There seems to be no possible way to properly employ the browser's smooth scrolling interpolation
|
||||||
const horizontalScrollableElement = e.target instanceof Element && e.target.closest("[data-scrollable-x]");
|
const horizontalScrollableElement = e.target instanceof Element && !e.target.closest("[data-scrollable-y]") && e.target.closest("[data-scrollable-x]");
|
||||||
if (horizontalScrollableElement && e.deltaY !== 0) {
|
if (horizontalScrollableElement && e.deltaY !== 0) {
|
||||||
horizontalScrollableElement.scrollTo(horizontalScrollableElement.scrollLeft + e.deltaY, 0);
|
horizontalScrollableElement.scrollTo(horizontalScrollableElement.scrollLeft + e.deltaY, 0);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue