mirror of
https://github.com/immich-app/immich.git
synced 2024-12-28 06:31:58 +00:00
feat(web): scrollable context menus
This commit is contained in:
parent
971ba63447
commit
e628e6a807
15 changed files with 42 additions and 44 deletions
|
@ -46,7 +46,11 @@ export const contextMenuNavigation: Action<HTMLElement, Options> = (node, option
|
|||
};
|
||||
|
||||
const moveSelection = async (direction: 'up' | 'down', event: KeyboardEvent) => {
|
||||
const { selectionChanged, container, openDropdown } = options;
|
||||
const { selectionChanged, container, openDropdown, isOpen } = options;
|
||||
if (!isOpen) {
|
||||
// reset the scroll position before opening the menu
|
||||
container?.scrollTo({ top: 0 });
|
||||
}
|
||||
if (openDropdown) {
|
||||
openDropdown(event);
|
||||
await tick();
|
||||
|
|
|
@ -109,7 +109,13 @@
|
|||
{/if}
|
||||
</div>
|
||||
{#if isOwned}
|
||||
<ButtonContextMenu icon={mdiDotsVertical} size="20" title={$t('options')}>
|
||||
<ButtonContextMenu
|
||||
icon={mdiDotsVertical}
|
||||
size="20"
|
||||
title={$t('options')}
|
||||
direction="right"
|
||||
align="top-left"
|
||||
>
|
||||
{#if role === AlbumUserRole.Viewer}
|
||||
<MenuOption onClick={() => handleSetReadonly(user, AlbumUserRole.Editor)} text={$t('allow_edits')} />
|
||||
{:else}
|
||||
|
|
|
@ -186,13 +186,7 @@
|
|||
{/if}
|
||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
||||
<div class="mr-4">
|
||||
<ButtonContextMenu
|
||||
icon={mdiDotsVertical}
|
||||
title={$t('comment_options')}
|
||||
align="top-right"
|
||||
direction="left"
|
||||
size="16"
|
||||
>
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('comment_options')} size="16">
|
||||
<MenuOption
|
||||
activeColor="bg-red-200"
|
||||
icon={mdiDeleteOutline}
|
||||
|
@ -239,13 +233,7 @@
|
|||
{/if}
|
||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
||||
<div class="mr-4">
|
||||
<ButtonContextMenu
|
||||
icon={mdiDotsVertical}
|
||||
title={$t('reaction_options')}
|
||||
align="top-right"
|
||||
direction="left"
|
||||
size="16"
|
||||
>
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('reaction_options')} size="16">
|
||||
<MenuOption
|
||||
activeColor="bg-red-200"
|
||||
icon={mdiDeleteOutline}
|
||||
|
|
|
@ -128,7 +128,7 @@
|
|||
{#if isOwner}
|
||||
<DeleteAction {asset} {onAction} />
|
||||
|
||||
<ButtonContextMenu direction="left" align="top-right" color="opaque" title={$t('more')} icon={mdiDotsVertical}>
|
||||
<ButtonContextMenu color="opaque" title={$t('more')} icon={mdiDotsVertical}>
|
||||
{#if showSlideshow}
|
||||
<MenuOption icon={mdiPresentationPlay} text={$t('slideshow')} onClick={onPlaySlideshow} />
|
||||
{/if}
|
||||
|
|
|
@ -67,6 +67,8 @@
|
|||
size="20"
|
||||
icon={mdiDotsVertical}
|
||||
title={$t('show_person_options')}
|
||||
direction="right"
|
||||
align="top-left"
|
||||
>
|
||||
<MenuOption onClick={onHidePerson} icon={mdiEyeOffOutline} text={$t('hide_person')} />
|
||||
<MenuOption onClick={onChangeName} icon={mdiAccountEditOutline} text={$t('change_name')} />
|
||||
|
|
|
@ -237,7 +237,7 @@
|
|||
|
||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={handleUpdate} />
|
||||
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('add')}>
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem />
|
||||
<ChangeDate menuItem />
|
||||
<ChangeLocation menuItem />
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
/**
|
||||
* The alignment of the context menu relative to the button.
|
||||
*/
|
||||
export let align: Align = 'top-left';
|
||||
export let align: Align = 'top-right';
|
||||
/**
|
||||
* The direction in which the context menu should open.
|
||||
*/
|
||||
export let direction: 'left' | 'right' = 'right';
|
||||
export let direction: 'left' | 'right' = 'left';
|
||||
export let color: Color = 'transparent';
|
||||
export let size: string | undefined = undefined;
|
||||
export let padding: Padding | undefined = undefined;
|
||||
|
|
|
@ -18,25 +18,26 @@
|
|||
let left: number;
|
||||
let top: number;
|
||||
|
||||
// We need to bind clientHeight since the bounding box may return a height
|
||||
// of zero when starting the 'slide' animation.
|
||||
let height: number;
|
||||
|
||||
$: {
|
||||
if (menuElement) {
|
||||
const rect = menuElement.getBoundingClientRect();
|
||||
const directionWidth = direction === 'left' ? rect.width : 0;
|
||||
const menuHeight = Math.min(menuElement.clientHeight, height) || 0;
|
||||
const menuHeight = menuElement.clientHeight || 0;
|
||||
|
||||
left = Math.min(window.innerWidth - rect.width, x - directionWidth);
|
||||
const calcLeft = Math.min(window.innerWidth - rect.width, x - directionWidth);
|
||||
left = Math.max(0, calcLeft);
|
||||
top = Math.min(window.innerHeight - menuHeight, y);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:clientHeight={height}
|
||||
class="fixed z-10 min-w-[200px] w-max max-w-[300px] overflow-hidden rounded-lg shadow-lg"
|
||||
class="fixed z-10 overflow-hidden rounded-lg duration-[250ms] ease-in {isVisible
|
||||
? 'shadow-lg transition-shadow'
|
||||
: 'shadow-none transition-none'}"
|
||||
class:shadow-none={!isVisible}
|
||||
class:shadow-lg={isVisible}
|
||||
class:transition-none={!isVisible}
|
||||
style:left="{left}px"
|
||||
style:top="{top}px"
|
||||
transition:slide={{ duration: 250, easing: quintOut }}
|
||||
|
@ -48,9 +49,9 @@
|
|||
aria-label={ariaLabel}
|
||||
aria-labelledby={ariaLabelledBy}
|
||||
bind:this={menuElement}
|
||||
class:max-h-[100vh]={isVisible}
|
||||
class:max-h-0={!isVisible}
|
||||
class="flex flex-col transition-all duration-[250ms] ease-in-out outline-none"
|
||||
class="flex flex-col transition-all duration-[250ms] ease-in-out outline-none immich-scrollbar bg-slate-100 relative min-w-[200px] max-w-[200px] sm:max-w-[256px] rounded-lg {isVisible
|
||||
? 'translate-x-0 max-h-dvh overflow-y-auto'
|
||||
: `${direction === 'left' ? 'translate-x-28' : '-translate-x-28'} max-h-0 overflow-y-hidden`}"
|
||||
role="menu"
|
||||
tabindex="-1"
|
||||
>
|
||||
|
|
|
@ -33,7 +33,9 @@
|
|||
role="menuitem"
|
||||
>
|
||||
{#if icon}
|
||||
<Icon path={icon} ariaHidden={true} size="18" />
|
||||
<div class="flex-none">
|
||||
<Icon path={icon} ariaHidden={true} size="18" />
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
{text}
|
||||
|
|
|
@ -107,6 +107,8 @@
|
|||
size="24"
|
||||
padding="3"
|
||||
hideContent
|
||||
direction="right"
|
||||
align="top-left"
|
||||
>
|
||||
<SharedLinkEdit menuItem {onEdit} />
|
||||
<SharedLinkCopy menuItem {link} />
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<AddToAlbum shared />
|
||||
</ButtonContextMenu>
|
||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('add')}>
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem />
|
||||
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
</ButtonContextMenu>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
{#if $isMultiSelectState}
|
||||
<AssetSelectControlBar assets={$selectedAssets} clearSelect={clearMultiselect}>
|
||||
<CreateSharedLink />
|
||||
<ButtonContextMenu icon={mdiPlus} title={$t('add')}>
|
||||
<ButtonContextMenu icon={mdiPlus} title={$t('add_to')}>
|
||||
<AddToAlbum />
|
||||
<AddToAlbum shared />
|
||||
</ButtonContextMenu>
|
||||
|
|
|
@ -385,7 +385,7 @@
|
|||
<AddToAlbum shared />
|
||||
</ButtonContextMenu>
|
||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('add')}>
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem filename="{person.name || 'immich'}.zip" />
|
||||
<MenuOption
|
||||
icon={mdiAccountMultipleCheckOutline}
|
||||
|
|
|
@ -235,7 +235,7 @@
|
|||
</ButtonContextMenu>
|
||||
<FavoriteAction removeFavorite={isAllFavorite} onFavorite={triggerAssetUpdate} />
|
||||
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('add')}>
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem />
|
||||
<ChangeDate menuItem />
|
||||
<ChangeLocation menuItem />
|
||||
|
|
|
@ -285,14 +285,7 @@
|
|||
</td>
|
||||
|
||||
<td class=" text-ellipsis px-4 text-sm">
|
||||
<ButtonContextMenu
|
||||
align="top-right"
|
||||
direction="left"
|
||||
color="primary"
|
||||
size="16"
|
||||
icon={mdiDotsVertical}
|
||||
title={$t('library_options')}
|
||||
>
|
||||
<ButtonContextMenu color="primary" size="16" icon={mdiDotsVertical} title={$t('library_options')}>
|
||||
<MenuOption onClick={() => onScanClicked(library)} text={$t('scan_library')} />
|
||||
<hr />
|
||||
<MenuOption onClick={() => onRenameClicked(index)} text={$t('rename')} />
|
||||
|
|
Loading…
Reference in a new issue