mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
fix(web): prevent context menu from overflowing viewport (#26041)
* fix(web): prevent context menu from overflowing viewport The context menu used `max-h-dvh` (100% viewport height) as its max height, but did not account for the menu's top position. When the menu opens at y > 0, its bottom extends beyond the viewport. Compute `maxHeight` dynamically based on the menu's top position and apply it as an inline style, so the menu always fits within the viewport and scrolls when content exceeds the available space. * fix: linting * fix: overflow --------- Co-authored-by: Jason Rasmussen <jason@rasm.me>
This commit is contained in:
parent
906c38273f
commit
25be5fc22d
1 changed files with 5 additions and 15 deletions
|
|
@ -47,30 +47,23 @@
|
||||||
|
|
||||||
const left = Math.max(8, Math.min(window.innerWidth - rect.width, x - directionWidth));
|
const left = Math.max(8, Math.min(window.innerWidth - rect.width, x - directionWidth));
|
||||||
const top = Math.max(8, Math.min(window.innerHeight - menuHeight, y));
|
const top = Math.max(8, Math.min(window.innerHeight - menuHeight, y));
|
||||||
|
const maxHeight = window.innerHeight - top - 8;
|
||||||
|
|
||||||
return { left, top };
|
return { left, top, maxHeight };
|
||||||
});
|
});
|
||||||
|
|
||||||
// We need to bind clientHeight since the bounding box may return a height
|
// We need to bind clientHeight since the bounding box may return a height
|
||||||
// of zero when starting the 'slide' animation.
|
// of zero when starting the 'slide' animation.
|
||||||
let height: number = $state(0);
|
let height: number = $state(0);
|
||||||
|
|
||||||
let isTransitioned = $state(false);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:clientHeight={height}
|
bind:clientHeight={height}
|
||||||
class="fixed min-w-50 w-max max-w-75 overflow-hidden rounded-lg shadow-lg z-1"
|
class="fixed min-w-50 w-max max-w-75 overflow-hidden rounded-lg shadow-lg z-1 immich-scrollbar"
|
||||||
style:left="{position.left}px"
|
style:left="{position.left}px"
|
||||||
style:top="{position.top}px"
|
style:top="{position.top}px"
|
||||||
transition:slide={{ duration: 250, easing: quintOut }}
|
transition:slide={{ duration: 250, easing: quintOut }}
|
||||||
use:clickOutside={{ onOutclick: onClose }}
|
use:clickOutside={{ onOutclick: onClose }}
|
||||||
onintroend={() => {
|
|
||||||
isTransitioned = true;
|
|
||||||
}}
|
|
||||||
onoutrostart={() => {
|
|
||||||
isTransitioned = false;
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ul
|
<ul
|
||||||
{id}
|
{id}
|
||||||
|
|
@ -78,11 +71,8 @@
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
aria-labelledby={ariaLabelledBy}
|
aria-labelledby={ariaLabelledBy}
|
||||||
bind:this={menuElement}
|
bind:this={menuElement}
|
||||||
class="{isVisible
|
class="flex flex-col transition-all duration-250 ease-in-out outline-none overflow-auto immich-scrollbar"
|
||||||
? 'max-h-dvh'
|
style:max-height={isVisible ? `${position.maxHeight}px` : '0px'}
|
||||||
: 'max-h-0'} flex flex-col transition-all duration-250 ease-in-out outline-none {isTransitioned
|
|
||||||
? 'overflow-auto'
|
|
||||||
: ''}"
|
|
||||||
role="menu"
|
role="menu"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue