Skip to content

Commit 5edb00b

Browse files
authored
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
1 parent ab822af commit 5edb00b

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ jobs:
162162
REF="master"
163163
ENVIRONMENT="graphite-dev (Production)"
164164
else
165-
REF="${{ inputs.checkout_ref || github.head_ref || github.ref_name }}"
165+
REF="$(git rev-parse HEAD)"
166166
ENVIRONMENT="graphite-dev (Preview)"
167167
fi
168168
DEPLOY_ID=$(gh api \

frontend/src/components/floating-menus/MenuList.svelte

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
let virtualScrollingEntriesStart = 0;
4848
let keydownListenerAdded = false;
4949
let destroyed = false;
50+
let maxMenuWidth = 0;
51+
let resizeObserver: ResizeObserver | undefined = undefined;
5052
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- `loadedFonts` reactivity is driven by `loadedFontsGeneration`, not the Set itself
5153
let loadedFonts = new Set<string>();
5254
let loadedFontsGeneration = 0;
@@ -77,6 +79,7 @@
7779
});
7880
onDestroy(() => {
7981
removeEventListener("keydown", keydown);
82+
resizeObserver?.disconnect();
8083
// 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
8184
destroyed = true;
8285
});
@@ -144,6 +147,15 @@
144147
keydownListenerAdded = false;
145148
}
146149
150+
// For virtual scrolling menus, observe width changes so the menu only grows and never shrinks while open
151+
if (open && virtualScrolling) {
152+
startMenuWidthObserver();
153+
} else if (resizeObserver) {
154+
resizeObserver.disconnect();
155+
resizeObserver = undefined;
156+
maxMenuWidth = 0;
157+
}
158+
147159
highlighted = activeEntry;
148160
dispatch("open", open);
149161
@@ -156,14 +168,38 @@
156168
});
157169
}
158170
159-
function watchEntriesHash(_entriesHash: bigint) {
171+
function watchEntriesHash(_: bigint) {
160172
reactiveEntries = entries;
161173
}
162174
163175
function watchRemeasureWidth(_: MenuListEntry[][], __: boolean) {
176+
// Skip re-measurement for virtual scrolling menus since ResizeObserver handles their width
177+
if (virtualScrolling) return;
178+
164179
self?.measureAndEmitNaturalWidth();
165180
}
166181
182+
async function startMenuWidthObserver() {
183+
await tick();
184+
// Guard against the menu having closed during the tick
185+
if (!open) return;
186+
187+
const floatingMenuContentDiv = self?.div()?.querySelector("[data-floating-menu-content]");
188+
if (!(floatingMenuContentDiv instanceof HTMLElement)) return;
189+
190+
maxMenuWidth = 0;
191+
192+
resizeObserver?.disconnect();
193+
resizeObserver = new ResizeObserver(() => {
194+
const width = floatingMenuContentDiv.scrollWidth;
195+
if (width > maxMenuWidth) {
196+
maxMenuWidth = width;
197+
floatingMenuContentDiv.style.minWidth = `${maxMenuWidth}px`;
198+
}
199+
});
200+
resizeObserver.observe(floatingMenuContentDiv);
201+
}
202+
167203
function onScroll(e: Event) {
168204
if (!virtualScrollingEntryHeight) return;
169205
virtualScrollingEntriesStart = e.target instanceof HTMLElement ? e.target.scrollTop : 0;

frontend/src/components/layout/FloatingMenu.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@
492492
<div class="tail" bind:this={tail}></div>
493493
{/if}
494494
{#if displayContainer}
495-
<div class="floating-menu-container" bind:this={floatingMenuContainer}>
495+
<div class="floating-menu-container" bind:this={floatingMenuContainer} on:wheel|stopPropagation>
496496
<LayoutCol class="floating-menu-content" styles={{ "min-width": minWidthStyleValue }} {scrollableY} bind:this={floatingMenuContent} data-floating-menu-content>
497497
<slot />
498498
</LayoutCol>

frontend/src/utility-functions/input.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ export function onWheelScroll(e: WheelEvent, editor: EditorWrapper) {
225225

226226
// Redirect vertical scroll wheel movement into a horizontal scroll on a horizontally scrollable element
227227
// There seems to be no possible way to properly employ the browser's smooth scrolling interpolation
228-
const horizontalScrollableElement = e.target instanceof Element && e.target.closest("[data-scrollable-x]");
228+
const horizontalScrollableElement = e.target instanceof Element && !e.target.closest("[data-scrollable-y]") && e.target.closest("[data-scrollable-x]");
229229
if (horizontalScrollableElement && e.deltaY !== 0) {
230230
horizontalScrollableElement.scrollTo(horizontalScrollableElement.scrollLeft + e.deltaY, 0);
231231
return;

0 commit comments

Comments
 (0)