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 | ||||
| // 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 | ||||
| 	interface HTMLAttributes<T> { | ||||
| 		oncopyImage?: () => void; | ||||
| 		onoutclick?: () => void; | ||||
| 		'on:copyImage'?: () => void; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -128,14 +128,12 @@ | ||||
| 					title="More" | ||||
| 				> | ||||
| 					{#if isShowAssetOptions} | ||||
| 						<ContextMenu {...contextMenuPosition}> | ||||
| 							<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('addToSharedAlbum')} | ||||
| 									text="Add to Shared Album" | ||||
| 								/> | ||||
| 							</div> | ||||
| 						<ContextMenu {...contextMenuPosition} direction="left"> | ||||
| 							<MenuOption on:click={() => onMenuClick('addToAlbum')} text="Add to Album" /> | ||||
| 							<MenuOption | ||||
| 								on:click={() => onMenuClick('addToSharedAlbum')} | ||||
| 								text="Add to Shared Album" | ||||
| 							/> | ||||
| 						</ContextMenu> | ||||
| 					{/if} | ||||
| 				</CircleIconButton> | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
| <CircleIconButton {title} logo={icon} on:click={handleShowMenu} /> | ||||
|  | ||||
| {#if showContextMenu} | ||||
| 	<ContextMenu {...contextMenuPosition} on:clickoutside={() => (showContextMenu = false)}> | ||||
| 	<ContextMenu {...contextMenuPosition} on:outclick={() => (showContextMenu = false)}> | ||||
| 		<div class="flex flex-col rounded-lg"> | ||||
| 			<slot /> | ||||
| 		</div> | ||||
|   | ||||
| @@ -1,41 +1,33 @@ | ||||
| <script lang="ts"> | ||||
| 	import { clickOutside } from '$lib/utils/click-outside'; | ||||
| 	import { createEventDispatcher } from 'svelte'; | ||||
| 	import { quintOut } from 'svelte/easing'; | ||||
| 	import { slide } from 'svelte/transition'; | ||||
|  | ||||
| 	/** | ||||
| 	 * x coordiante of the context menu. | ||||
| 	 */ | ||||
| 	export let direction: 'left' | 'right' = 'right'; | ||||
| 	export let x = 0; | ||||
|  | ||||
| 	/** | ||||
| 	 * x coordiante of the context menu. | ||||
| 	 */ | ||||
| 	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; | ||||
|  | ||||
| 	$: (() => { | ||||
| 		if (!menuEl) return; | ||||
|  | ||||
| 		const rect = menuEl.getBoundingClientRect(); | ||||
| 		x = Math.min(window.innerWidth - rect.width, x); | ||||
| 		if (y > window.innerHeight - rect.height) { | ||||
| 			y -= rect.height; | ||||
| 		} | ||||
| 	})(); | ||||
| 		left = Math.min(window.innerWidth - rect.width, x - directionWidth); | ||||
| 		top = Math.min(window.innerHeight - rect.height, y); | ||||
| 	} | ||||
| </script> | ||||
|  | ||||
| <div | ||||
| 	transition:slide={{ duration: 200, easing: quintOut }} | ||||
| 	bind:this={menuEl} | ||||
| 	bind:this={menuElement} | ||||
| 	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 | ||||
| 	on:outclick={() => dispatch('clickoutside')} | ||||
| 	on:outclick | ||||
| > | ||||
| 	<slot /> | ||||
| </div> | ||||
|   | ||||
| @@ -1,22 +1,11 @@ | ||||
| <script> | ||||
| 	import { createEventDispatcher } from 'svelte'; | ||||
|  | ||||
| 	export let isDisabled = false; | ||||
| 	export let text = ''; | ||||
|  | ||||
| 	const dispatch = createEventDispatcher(); | ||||
|  | ||||
| 	const handleClick = () => { | ||||
| 		if (isDisabled) return; | ||||
|  | ||||
| 		dispatch('click'); | ||||
| 	}; | ||||
| </script> | ||||
|  | ||||
| <button | ||||
| 	class:disabled={isDisabled} | ||||
| 	on:click={handleClick} | ||||
| 	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" | ||||
| 	on:click | ||||
| 	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" | ||||
| 	role="menuitem" | ||||
| > | ||||
| 	{#if text} | ||||
| 		{text} | ||||
|   | ||||
| @@ -1,5 +1,11 @@ | ||||
| export function clickOutside(node: Node) { | ||||
| 	const handleClick = (event: Event) => { | ||||
| import type { ActionReturn } from 'svelte/action'; | ||||
|  | ||||
| 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; | ||||
| 		if (!node.contains(targetNode)) { | ||||
| 			node.dispatchEvent(new CustomEvent('outclick')); | ||||
| @@ -7,7 +13,7 @@ export function clickOutside(node: Node) { | ||||
| 	}; | ||||
|  | ||||
| 	const handleKey = (event: KeyboardEvent) => { | ||||
| 		if (event.key == 'Escape') { | ||||
| 		if (event.key === 'Escape') { | ||||
| 			node.dispatchEvent(new CustomEvent('outclick')); | ||||
| 		} | ||||
| 	}; | ||||
|   | ||||
| @@ -90,7 +90,7 @@ | ||||
|  | ||||
| <!-- Context Menu --> | ||||
| {#if $isShowContextMenu} | ||||
| 	<ContextMenu {...$contextMenuPosition} on:clickoutside={closeAlbumContextMenu}> | ||||
| 	<ContextMenu {...$contextMenuPosition} on:outclick={closeAlbumContextMenu}> | ||||
| 		<MenuOption on:click={deleteSelectedContextAlbum}> | ||||
| 			<span class="flex place-items-center place-content-center gap-2"> | ||||
| 				<DeleteOutline size="18" /> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user