mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
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
This commit is contained in:
parent
3e73cfb71a
commit
68d6d89a3b
@ -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) => {
|
const showAlbumContextMenu = (e: MouseEvent) =>
|
||||||
dispatchShowContextMenu('showalbumcontextmenu', {
|
dispatchShowContextMenu('showalbumcontextmenu', getContextMenuPosition(e));
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
|
imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
|
||||||
|
@ -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 showContextMenu = (event: MouseEvent, user: UserResponseDto) => {
|
||||||
const iconButton = document.getElementById('icon-' + user.id);
|
position = getContextMenuPosition(event);
|
||||||
|
|
||||||
if (iconButton) {
|
|
||||||
position = {
|
|
||||||
x: iconButton.getBoundingClientRect().left,
|
|
||||||
y: iconButton.getBoundingClientRect().bottom,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
||||||
|
@ -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) => {
|
const showOptionsMenu = (event: MouseEvent) => {
|
||||||
contextMenuPosition = { x, y };
|
contextMenuPosition = getContextMenuPosition(event, 'top-right');
|
||||||
isShowAssetOptions = !isShowAssetOptions;
|
isShowAssetOptions = !isShowAssetOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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) => {
|
const showMenu = (event: MouseEvent) => {
|
||||||
contextMenuPosition = { x, y };
|
contextMenuPosition = getContextMenuPosition(event);
|
||||||
showContextMenu = !showContextMenu;
|
showContextMenu = !showContextMenu;
|
||||||
};
|
};
|
||||||
const onMenuExit = () => {
|
const onMenuExit = () => {
|
||||||
|
@ -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 handleShowMenu = (event: MouseEvent) => {
|
||||||
const navigationBarHeight = 75;
|
contextMenuPosition = getContextMenuPosition(event, 'top-left');
|
||||||
contextMenuPosition = { x: x, y: navigationBarHeight };
|
|
||||||
showContextMenu = !showContextMenu;
|
showContextMenu = !showContextMenu;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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) => {
|
const showMenu = (event: MouseEvent, type: LibraryType) => {
|
||||||
contextMenuPosition = { x, y };
|
contextMenuPosition = getContextMenuPosition(event);
|
||||||
showContextMenu = !showContextMenu;
|
showContextMenu = !showContextMenu;
|
||||||
libraryType = type;
|
libraryType = type;
|
||||||
};
|
};
|
||||||
|
18
web/src/lib/utils/context-menu.ts
Normal file
18
web/src/lib/utils/context-menu.ts
Normal file
@ -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 };
|
||||||
|
};
|
@ -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 handleOpenAlbumOptions = (event: MouseEvent) => {
|
||||||
const navigationBarHeight = 75;
|
contextMenuPosition = getContextMenuPosition(event, 'top-left');
|
||||||
contextMenuPosition = { x: x, y: navigationBarHeight };
|
|
||||||
viewMode = viewMode === ViewMode.VIEW ? ViewMode.ALBUM_OPTIONS : ViewMode.VIEW;
|
viewMode = viewMode === ViewMode.VIEW ? ViewMode.ALBUM_OPTIONS : ViewMode.VIEW;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user