mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	fix(web): context menu overlap + outclick types (#2506)
This commit is contained in:
		
							
								
								
									
										5
									
								
								web/src/app.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								web/src/app.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -25,10 +25,9 @@ declare namespace App {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Source: https://stackoverflow.com/questions/63814432/typescript-typing-of-non-standard-window-event-in-svelte
 | 
					// Source: https://stackoverflow.com/questions/63814432/typescript-typing-of-non-standard-window-event-in-svelte
 | 
				
			||||||
// To fix the <svelte:window... in components/asset-viewer/photo-viewer.svelte
 | 
					// To fix the <svelte:window... in components/asset-viewer/photo-viewer.svelte
 | 
				
			||||||
declare namespace svelte.JSX {
 | 
					declare namespace svelteHTML {
 | 
				
			||||||
	// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
						// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
	interface HTMLAttributes<T> {
 | 
						interface HTMLAttributes<T> {
 | 
				
			||||||
		oncopyImage?: () => void;
 | 
							'on:copyImage'?: () => void;
 | 
				
			||||||
		onoutclick?: () => void;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,14 +128,12 @@
 | 
				
			|||||||
					title="More"
 | 
										title="More"
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
					{#if isShowAssetOptions}
 | 
										{#if isShowAssetOptions}
 | 
				
			||||||
						<ContextMenu {...contextMenuPosition}>
 | 
											<ContextMenu {...contextMenuPosition} direction="left">
 | 
				
			||||||
							<div class="flex flex-col rounded-lg text-black bg-immich-bg">
 | 
												<MenuOption on:click={() => onMenuClick('addToAlbum')} text="Add to Album" />
 | 
				
			||||||
								<MenuOption on:click={() => onMenuClick('addToAlbum')} text="Add to Album" />
 | 
												<MenuOption
 | 
				
			||||||
								<MenuOption
 | 
													on:click={() => onMenuClick('addToSharedAlbum')}
 | 
				
			||||||
									on:click={() => onMenuClick('addToSharedAlbum')}
 | 
													text="Add to Shared Album"
 | 
				
			||||||
									text="Add to Shared Album"
 | 
												/>
 | 
				
			||||||
								/>
 | 
					 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
						</ContextMenu>
 | 
											</ContextMenu>
 | 
				
			||||||
					{/if}
 | 
										{/if}
 | 
				
			||||||
				</CircleIconButton>
 | 
									</CircleIconButton>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@
 | 
				
			|||||||
<CircleIconButton {title} logo={icon} on:click={handleShowMenu} />
 | 
					<CircleIconButton {title} logo={icon} on:click={handleShowMenu} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#if showContextMenu}
 | 
					{#if showContextMenu}
 | 
				
			||||||
	<ContextMenu {...contextMenuPosition} on:clickoutside={() => (showContextMenu = false)}>
 | 
						<ContextMenu {...contextMenuPosition} on:outclick={() => (showContextMenu = false)}>
 | 
				
			||||||
		<div class="flex flex-col rounded-lg">
 | 
							<div class="flex flex-col rounded-lg">
 | 
				
			||||||
			<slot />
 | 
								<slot />
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,41 +1,33 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import { clickOutside } from '$lib/utils/click-outside';
 | 
						import { clickOutside } from '$lib/utils/click-outside';
 | 
				
			||||||
	import { createEventDispatcher } from 'svelte';
 | 
					 | 
				
			||||||
	import { quintOut } from 'svelte/easing';
 | 
						import { quintOut } from 'svelte/easing';
 | 
				
			||||||
	import { slide } from 'svelte/transition';
 | 
						import { slide } from 'svelte/transition';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						export let direction: 'left' | 'right' = 'right';
 | 
				
			||||||
	 * x coordiante of the context menu.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	export let x = 0;
 | 
						export let x = 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * x coordiante of the context menu.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	export let y = 0;
 | 
						export let y = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const dispatch = createEventDispatcher();
 | 
						let menuElement: HTMLDivElement;
 | 
				
			||||||
 | 
						let left: number;
 | 
				
			||||||
 | 
						let top: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let menuEl: HTMLElement;
 | 
						$: if (menuElement) {
 | 
				
			||||||
 | 
							const rect = menuElement.getBoundingClientRect();
 | 
				
			||||||
 | 
							const directionWidth = direction === 'left' ? rect.width : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	$: (() => {
 | 
							left = Math.min(window.innerWidth - rect.width, x - directionWidth);
 | 
				
			||||||
		if (!menuEl) return;
 | 
							top = Math.min(window.innerHeight - rect.height, y);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
		const rect = menuEl.getBoundingClientRect();
 | 
					 | 
				
			||||||
		x = Math.min(window.innerWidth - rect.width, x);
 | 
					 | 
				
			||||||
		if (y > window.innerHeight - rect.height) {
 | 
					 | 
				
			||||||
			y -= rect.height;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	})();
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div
 | 
					<div
 | 
				
			||||||
	transition:slide={{ duration: 200, easing: quintOut }}
 | 
						transition:slide={{ duration: 200, easing: quintOut }}
 | 
				
			||||||
	bind:this={menuEl}
 | 
						bind:this={menuElement}
 | 
				
			||||||
	class="absolute w-[200px] z-[99999] rounded-lg overflow-hidden shadow-lg"
 | 
						class="absolute w-[200px] z-[99999] rounded-lg overflow-hidden shadow-lg"
 | 
				
			||||||
	style={`top: ${y}px; left: ${x}px;`}
 | 
						style="left: {left}px; top: {top}px;"
 | 
				
			||||||
 | 
						role="menu"
 | 
				
			||||||
	use:clickOutside
 | 
						use:clickOutside
 | 
				
			||||||
	on:outclick={() => dispatch('clickoutside')}
 | 
						on:outclick
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	<slot />
 | 
						<slot />
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,11 @@
 | 
				
			|||||||
<script>
 | 
					<script>
 | 
				
			||||||
	import { createEventDispatcher } from 'svelte';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	export let isDisabled = false;
 | 
					 | 
				
			||||||
	export let text = '';
 | 
						export let text = '';
 | 
				
			||||||
 | 
					 | 
				
			||||||
	const dispatch = createEventDispatcher();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const handleClick = () => {
 | 
					 | 
				
			||||||
		if (isDisabled) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		dispatch('click');
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<button
 | 
					<button
 | 
				
			||||||
	class:disabled={isDisabled}
 | 
						on:click
 | 
				
			||||||
	on:click={handleClick}
 | 
						class="bg-slate-100 hover:bg-gray-200 dark:text-immich-dark-bg p-4 w-full text-left text-sm font-medium focus:outline-none focus:ring-inset focus:ring-2"
 | 
				
			||||||
	class="bg-slate-100 hover:bg-gray-200 dark:text-immich-dark-bg transition-all p-4 w-full text-left text-sm font-medium"
 | 
						role="menuitem"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{#if text}
 | 
						{#if text}
 | 
				
			||||||
		{text}
 | 
							{text}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,11 @@
 | 
				
			|||||||
export function clickOutside(node: Node) {
 | 
					import type { ActionReturn } from 'svelte/action';
 | 
				
			||||||
	const handleClick = (event: Event) => {
 | 
					
 | 
				
			||||||
 | 
					interface Attributes {
 | 
				
			||||||
 | 
						'on:outclick'?: (e: CustomEvent) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function clickOutside(node: HTMLElement): ActionReturn<void, Attributes> {
 | 
				
			||||||
 | 
						const handleClick = (event: MouseEvent) => {
 | 
				
			||||||
		const targetNode = event.target as Node | null;
 | 
							const targetNode = event.target as Node | null;
 | 
				
			||||||
		if (!node.contains(targetNode)) {
 | 
							if (!node.contains(targetNode)) {
 | 
				
			||||||
			node.dispatchEvent(new CustomEvent('outclick'));
 | 
								node.dispatchEvent(new CustomEvent('outclick'));
 | 
				
			||||||
@@ -7,7 +13,7 @@ export function clickOutside(node: Node) {
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const handleKey = (event: KeyboardEvent) => {
 | 
						const handleKey = (event: KeyboardEvent) => {
 | 
				
			||||||
		if (event.key == 'Escape') {
 | 
							if (event.key === 'Escape') {
 | 
				
			||||||
			node.dispatchEvent(new CustomEvent('outclick'));
 | 
								node.dispatchEvent(new CustomEvent('outclick'));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<!-- Context Menu -->
 | 
					<!-- Context Menu -->
 | 
				
			||||||
{#if $isShowContextMenu}
 | 
					{#if $isShowContextMenu}
 | 
				
			||||||
	<ContextMenu {...$contextMenuPosition} on:clickoutside={closeAlbumContextMenu}>
 | 
						<ContextMenu {...$contextMenuPosition} on:outclick={closeAlbumContextMenu}>
 | 
				
			||||||
		<MenuOption on:click={deleteSelectedContextAlbum}>
 | 
							<MenuOption on:click={deleteSelectedContextAlbum}>
 | 
				
			||||||
			<span class="flex place-items-center place-content-center gap-2">
 | 
								<span class="flex place-items-center place-content-center gap-2">
 | 
				
			||||||
				<DeleteOutline size="18" />
 | 
									<DeleteOutline size="18" />
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user