Browse Source

feat(web): better context menu position (#4271)

* feat(web): better context menu position

* fix: album context menu

* fix: add middle variant

* fix: rest of context menus

* fix: linting error
Jason Rasmussen 1 year ago
parent
commit
68d6d89a3b

+ 3 - 6
web/src/lib/components/album-page/album-card.svelte

@@ -6,6 +6,7 @@
   import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
   import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
   import IconButton from '../elements/buttons/icon-button.svelte';
   import IconButton from '../elements/buttons/icon-button.svelte';
   import type { OnClick, OnShowContextMenu } from './album-card';
   import type { OnClick, OnShowContextMenu } from './album-card';
+  import { getContextMenuPosition } from '../../utils/context-menu';
 
 
   export let album: AlbumResponseDto;
   export let album: AlbumResponseDto;
   export let isSharingView = false;
   export let isSharingView = false;
@@ -41,12 +42,8 @@
     }
     }
   };
   };
 
 
-  const showAlbumContextMenu = (e: MouseEvent) => {
-    dispatchShowContextMenu('showalbumcontextmenu', {
-      x: e.clientX,
-      y: e.clientY,
-    });
-  };
+  const showAlbumContextMenu = (e: MouseEvent) =>
+    dispatchShowContextMenu('showalbumcontextmenu', getContextMenuPosition(e));
 
 
   onMount(async () => {
   onMount(async () => {
     imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
     imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;

+ 4 - 11
web/src/lib/components/album-page/share-info-modal.svelte

@@ -10,6 +10,7 @@
   import { notificationController, NotificationType } from '../shared-components/notification/notification';
   import { notificationController, NotificationType } from '../shared-components/notification/notification';
   import { handleError } from '../../utils/handle-error';
   import { handleError } from '../../utils/handle-error';
   import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
   import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
+  import { getContextMenuPosition } from '../../utils/context-menu';
 
 
   export let album: AlbumResponseDto;
   export let album: AlbumResponseDto;
 
 
@@ -34,16 +35,8 @@
     }
     }
   });
   });
 
 
-  const showContextMenu = (user: UserResponseDto) => {
-    const iconButton = document.getElementById('icon-' + user.id);
-
-    if (iconButton) {
-      position = {
-        x: iconButton.getBoundingClientRect().left,
-        y: iconButton.getBoundingClientRect().bottom,
-      };
-    }
-
+  const showContextMenu = (event: MouseEvent, user: UserResponseDto) => {
+    position = getContextMenuPosition(event);
     selectedMenuUser = user;
     selectedMenuUser = user;
     selectedRemoveUser = null;
     selectedRemoveUser = null;
   };
   };
@@ -105,7 +98,7 @@
             {#if isOwned}
             {#if isOwned}
               <div>
               <div>
                 <CircleIconButton
                 <CircleIconButton
-                  on:click={() => showContextMenu(user)}
+                  on:click={(event) => showContextMenu(event, user)}
                   logo={DotsVertical}
                   logo={DotsVertical}
                   backgroundColor="transparent"
                   backgroundColor="transparent"
                   hoverColor="#e2e7e9"
                   hoverColor="#e2e7e9"

+ 3 - 2
web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte

@@ -20,6 +20,7 @@
   import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
   import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
   import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
   import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
   import MenuOption from '../shared-components/context-menu/menu-option.svelte';
   import MenuOption from '../shared-components/context-menu/menu-option.svelte';
+  import { getContextMenuPosition } from '$lib/utils/context-menu';
 
 
   export let asset: AssetResponseDto;
   export let asset: AssetResponseDto;
   export let showCopyButton: boolean;
   export let showCopyButton: boolean;
@@ -52,8 +53,8 @@
   let contextMenuPosition = { x: 0, y: 0 };
   let contextMenuPosition = { x: 0, y: 0 };
   let isShowAssetOptions = false;
   let isShowAssetOptions = false;
 
 
-  const showOptionsMenu = ({ x, y }: MouseEvent) => {
-    contextMenuPosition = { x, y };
+  const showOptionsMenu = (event: MouseEvent) => {
+    contextMenuPosition = getContextMenuPosition(event, 'top-right');
     isShowAssetOptions = !isShowAssetOptions;
     isShowAssetOptions = !isShowAssetOptions;
   };
   };
 
 

+ 3 - 2
web/src/lib/components/faces-page/people-card.svelte

@@ -1,5 +1,6 @@
 <script lang="ts">
 <script lang="ts">
   import { PersonResponseDto, api } from '@api';
   import { PersonResponseDto, api } from '@api';
+  import { getContextMenuPosition } from '$lib/utils/context-menu';
   import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
   import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
   import IconButton from '../elements/buttons/icon-button.svelte';
   import IconButton from '../elements/buttons/icon-button.svelte';
   import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
   import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
@@ -21,8 +22,8 @@
   let showVerticalDots = false;
   let showVerticalDots = false;
   let showContextMenu = false;
   let showContextMenu = false;
   let contextMenuPosition = { x: 0, y: 0 };
   let contextMenuPosition = { x: 0, y: 0 };
-  const showMenu = ({ x, y }: MouseEvent) => {
-    contextMenuPosition = { x, y };
+  const showMenu = (event: MouseEvent) => {
+    contextMenuPosition = getContextMenuPosition(event);
     showContextMenu = !showContextMenu;
     showContextMenu = !showContextMenu;
   };
   };
   const onMenuExit = () => {
   const onMenuExit = () => {

+ 3 - 3
web/src/lib/components/photos-page/asset-select-context-menu.svelte

@@ -10,6 +10,7 @@
   import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
   import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
   import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte';
   import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte';
   import type Icon from 'svelte-material-icons/AbTesting.svelte';
   import type Icon from 'svelte-material-icons/AbTesting.svelte';
+  import { getContextMenuPosition } from '$lib/utils/context-menu';
 
 
   export let icon: typeof Icon;
   export let icon: typeof Icon;
   export let title: string;
   export let title: string;
@@ -17,9 +18,8 @@
   let showContextMenu = false;
   let showContextMenu = false;
   let contextMenuPosition = { x: 0, y: 0 };
   let contextMenuPosition = { x: 0, y: 0 };
 
 
-  const handleShowMenu = ({ x }: MouseEvent) => {
-    const navigationBarHeight = 75;
-    contextMenuPosition = { x: x, y: navigationBarHeight };
+  const handleShowMenu = (event: MouseEvent) => {
+    contextMenuPosition = getContextMenuPosition(event, 'top-left');
     showContextMenu = !showContextMenu;
     showContextMenu = !showContextMenu;
   };
   };
 
 

+ 3 - 2
web/src/lib/components/user-settings-page/library-list.svelte

@@ -18,6 +18,7 @@
   import Portal from '../shared-components/portal/portal.svelte';
   import Portal from '../shared-components/portal/portal.svelte';
   import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
   import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
   import MenuOption from '../shared-components/context-menu/menu-option.svelte';
   import MenuOption from '../shared-components/context-menu/menu-option.svelte';
+  import { getContextMenuPosition } from '$lib/utils/context-menu';
 
 
   let libraries: LibraryResponseDto[] = [];
   let libraries: LibraryResponseDto[] = [];
 
 
@@ -60,8 +61,8 @@
     }
     }
   };
   };
 
 
-  const showMenu = ({ x, y }: MouseEvent, type: LibraryType) => {
-    contextMenuPosition = { x, y };
+  const showMenu = (event: MouseEvent, type: LibraryType) => {
+    contextMenuPosition = getContextMenuPosition(event);
     showContextMenu = !showContextMenu;
     showContextMenu = !showContextMenu;
     libraryType = type;
     libraryType = type;
   };
   };

+ 18 - 0
web/src/lib/utils/context-menu.ts

@@ -0,0 +1,18 @@
+export type Align = 'middle' | 'top-left' | 'top-right';
+
+export const getContextMenuPosition = (event: MouseEvent, align: Align = 'middle') => {
+  const { x, y, currentTarget, target } = event;
+  const box = ((currentTarget || target) as HTMLElement)?.getBoundingClientRect();
+  if (box) {
+    switch (align) {
+      case 'middle':
+        return { x: box.x + box.width / 2, y: box.y + box.height / 2 };
+      case 'top-left':
+        return { x: box.x, y: box.y };
+      case 'top-right':
+        return { x: box.x + box.width, y: box.y };
+    }
+  }
+
+  return { x, y };
+};

+ 3 - 3
web/src/routes/(user)/albums/[albumId]/+page.svelte

@@ -45,6 +45,7 @@
   import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
   import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
   import type { PageData } from './$types';
   import type { PageData } from './$types';
   import { clickOutside } from '$lib/utils/click-outside';
   import { clickOutside } from '$lib/utils/click-outside';
+  import { getContextMenuPosition } from '$lib/utils/context-menu';
 
 
   export let data: PageData;
   export let data: PageData;
 
 
@@ -193,9 +194,8 @@
     timelineInteractionStore.clearMultiselect();
     timelineInteractionStore.clearMultiselect();
   };
   };
 
 
-  const handleOpenAlbumOptions = ({ x }: MouseEvent) => {
-    const navigationBarHeight = 75;
-    contextMenuPosition = { x: x, y: navigationBarHeight };
+  const handleOpenAlbumOptions = (event: MouseEvent) => {
+    contextMenuPosition = getContextMenuPosition(event, 'top-left');
     viewMode = viewMode === ViewMode.VIEW ? ViewMode.ALBUM_OPTIONS : ViewMode.VIEW;
     viewMode = viewMode === ViewMode.VIEW ? ViewMode.ALBUM_OPTIONS : ViewMode.VIEW;
   };
   };