Skip to content

Commit

Permalink
feat(web): rework context menus: add icons and reorder items (#8090)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ethan13310 committed Mar 21, 2024
1 parent 1abb0bd commit 8ed6ed4
Show file tree
Hide file tree
Showing 23 changed files with 204 additions and 84 deletions.
58 changes: 45 additions & 13 deletions web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
Expand Up @@ -5,27 +5,39 @@
import { getAssetJobName } from '$lib/utils';
import { clickOutside } from '$lib/utils/click-outside';
import { getContextMenuPosition } from '$lib/utils/context-menu';
import { AssetJobName, AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
import { AssetJobName, AssetTypeEnum, type AssetResponseDto, type AlbumResponseDto } from '@immich/sdk';
import {
mdiAccountCircleOutline,
mdiAlertOutline,
mdiArchiveArrowDownOutline,
mdiArchiveArrowUpOutline,
mdiArrowLeft,
mdiCogRefreshOutline,
mdiContentCopy,
mdiDatabaseRefreshOutline,
mdiDeleteOutline,
mdiDotsVertical,
mdiFolderDownloadOutline,
mdiHeart,
mdiHeartOutline,
mdiImageAlbum,
mdiImageMinusOutline,
mdiImageOutline,
mdiImageRefreshOutline,
mdiInformationOutline,
mdiMagnifyMinusOutline,
mdiMagnifyPlusOutline,
mdiMotionPauseOutline,
mdiPlaySpeed,
mdiPresentationPlay,
mdiShareVariantOutline,
} from '@mdi/js';
import { createEventDispatcher } from 'svelte';
import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
import MenuOption from '../shared-components/context-menu/menu-option.svelte';
export let asset: AssetResponseDto;
export let album: AlbumResponseDto | null = null;
export let showCopyButton: boolean;
export let showZoomButton: boolean;
export let showMotionPlayButton: boolean;
Expand All @@ -42,6 +54,7 @@
| 'addToAlbum'
| 'addToSharedAlbum'
| 'asProfileImage'
| 'setAsAlbumCover'
| 'download'
| 'playSlideShow'
| 'runJob'
Expand All @@ -59,6 +72,7 @@
addToAlbum: void;
addToSharedAlbum: void;
asProfileImage: void;
setAsAlbumCover: void;
runJob: AssetJobName;
playSlideShow: void;
unstack: void;
Expand Down Expand Up @@ -173,37 +187,55 @@
{#if isShowAssetOptions}
<ContextMenu {...contextMenuPosition} direction="left">
{#if showSlideshow}
<MenuOption on:click={() => onMenuClick('playSlideShow')} text="Slideshow" />
<MenuOption icon={mdiPresentationPlay} on:click={() => onMenuClick('playSlideShow')} text="Slideshow" />
{/if}
{#if showDownloadButton}
<MenuOption on:click={() => onMenuClick('download')} text="Download" />
<MenuOption icon={mdiFolderDownloadOutline} on:click={() => onMenuClick('download')} text="Download" />
{/if}
<MenuOption on:click={() => onMenuClick('addToAlbum')} text="Add to Album" />
<MenuOption on:click={() => onMenuClick('addToSharedAlbum')} text="Add to Shared Album" />
<MenuOption icon={mdiImageAlbum} on:click={() => onMenuClick('addToAlbum')} text="Add to album" />
<MenuOption
icon={mdiShareVariantOutline}
on:click={() => onMenuClick('addToSharedAlbum')}
text="Add to shared album"
/>

{#if isOwner}
{#if hasStackChildren}
<MenuOption icon={mdiImageMinusOutline} on:click={() => onMenuClick('unstack')} text="Un-stack" />
{/if}
{#if album}
<MenuOption
text="Set as album cover"
icon={mdiImageOutline}
on:click={() => onMenuClick('setAsAlbumCover')}
/>
{/if}
{#if asset.type === AssetTypeEnum.Image}
<MenuOption
icon={mdiAccountCircleOutline}
on:click={() => onMenuClick('asProfileImage')}
text="Set as profile picture"
/>
{/if}
<MenuOption
on:click={() => dispatch('toggleArchive')}
icon={asset.isArchived ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline}
text={asset.isArchived ? 'Unarchive' : 'Archive'}
/>
{#if asset.type === AssetTypeEnum.Image}
<MenuOption on:click={() => onMenuClick('asProfileImage')} text="As profile picture" />
{/if}

{#if hasStackChildren}
<MenuOption on:click={() => onMenuClick('unstack')} text="Un-Stack" />
{/if}

<hr />
<MenuOption
icon={mdiDatabaseRefreshOutline}
on:click={() => onJobClick(AssetJobName.RefreshMetadata)}
text={getAssetJobName(AssetJobName.RefreshMetadata)}
/>
<MenuOption
icon={mdiImageRefreshOutline}
on:click={() => onJobClick(AssetJobName.RegenerateThumbnail)}
text={getAssetJobName(AssetJobName.RegenerateThumbnail)}
/>
{#if asset.type === AssetTypeEnum.Video}
<MenuOption
icon={mdiCogRefreshOutline}
on:click={() => onJobClick(AssetJobName.TranscodeVideo)}
text={getAssetJobName(AssetJobName.TranscodeVideo)}
/>
Expand Down
24 changes: 19 additions & 5 deletions web/src/lib/components/faces-page/people-card.svelte
Expand Up @@ -4,7 +4,13 @@
import { getPeopleThumbnailUrl } from '$lib/utils';
import { getContextMenuPosition } from '$lib/utils/context-menu';
import { type PersonResponseDto } from '@immich/sdk';
import { mdiDotsVertical } from '@mdi/js';
import {
mdiAccountEditOutline,
mdiAccountMultipleCheckOutline,
mdiCalendarEditOutline,
mdiDotsVertical,
mdiEyeOffOutline,
} from '@mdi/js';
import { createEventDispatcher } from 'svelte';
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
import IconButton from '../elements/buttons/icon-button.svelte';
Expand Down Expand Up @@ -83,10 +89,18 @@
{#if showContextMenu}
<Portal target="body">
<ContextMenu {...contextMenuPosition} on:outclick={() => onMenuExit()}>
<MenuOption on:click={() => onMenuClick('hide-person')} text="Hide Person" />
<MenuOption on:click={() => onMenuClick('change-name')} text="Change name" />
<MenuOption on:click={() => onMenuClick('set-birth-date')} text="Set date of birth" />
<MenuOption on:click={() => onMenuClick('merge-people')} text="Merge People" />
<MenuOption on:click={() => onMenuClick('hide-person')} icon={mdiEyeOffOutline} text="Hide person" />
<MenuOption on:click={() => onMenuClick('change-name')} icon={mdiAccountEditOutline} text="Change name" />
<MenuOption
on:click={() => onMenuClick('set-birth-date')}
icon={mdiCalendarEditOutline}
text="Set date of birth"
/>
<MenuOption
on:click={() => onMenuClick('merge-people')}
icon={mdiAccountMultipleCheckOutline}
text="Merge people"
/>
</ContextMenu>
</Portal>
{/if}
Expand Up @@ -11,6 +11,7 @@
import { createAlbum, type AlbumResponseDto } from '@immich/sdk';
import { getMenuContext } from '../asset-select-context-menu.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import { mdiImageAlbum, mdiShareVariantOutline } from '@mdi/js';
export let shared = false;
let showAlbumPicker = false;
Expand Down Expand Up @@ -53,7 +54,11 @@
};
</script>

<MenuOption on:click={() => (showAlbumPicker = true)} text={shared ? 'Add to Shared Album' : 'Add to Album'} />
<MenuOption
on:click={() => (showAlbumPicker = true)}
text={shared ? 'Add to shared album' : 'Add to album'}
icon={shared ? mdiShareVariantOutline : mdiImageAlbum}
/>

{#if showAlbumPicker}
<AlbumSelectionModal
Expand Down
Expand Up @@ -56,7 +56,7 @@
</script>

{#if menuItem}
<MenuOption {text} on:click={handleArchive} />
<MenuOption {text} {icon} on:click={handleArchive} />
{/if}

{#if !menuItem}
Expand Down
Expand Up @@ -4,7 +4,7 @@
NotificationType,
notificationController,
} from '$lib/components/shared-components/notification/notification';
import { getAssetJobMessage, getAssetJobName } from '$lib/utils';
import { getAssetJobIcon, getAssetJobMessage, getAssetJobName } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { AssetJobName, AssetTypeEnum, runAssetJobs } from '@immich/sdk';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
Expand Down Expand Up @@ -33,6 +33,6 @@

{#each jobs as job}
{#if isAllVideos || job !== AssetJobName.TranscodeVideo}
<MenuOption text={getAssetJobName(job)} on:click={() => handleRunJob(job)} />
<MenuOption text={getAssetJobName(job)} icon={getAssetJobIcon(job)} on:click={() => handleRunJob(job)} />
{/if}
{/each}
Expand Up @@ -7,6 +7,7 @@
import { DateTime } from 'luxon';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import { mdiCalendarEditOutline } from '@mdi/js';
export let menuItem = false;
const { clearSelect, getOwnedAssets } = getAssetControlContext();
Expand All @@ -26,7 +27,7 @@
</script>

{#if menuItem}
<MenuOption text="Change date" on:click={() => (isShowChangeDate = true)} />
<MenuOption text="Change date" icon={mdiCalendarEditOutline} on:click={() => (isShowChangeDate = true)} />
{/if}
{#if isShowChangeDate}
<ChangeDate
Expand Down
Expand Up @@ -6,6 +6,7 @@
import { updateAssets } from '@immich/sdk';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import { mdiMapMarkerMultipleOutline } from '@mdi/js';
export let menuItem = false;
const { clearSelect, getOwnedAssets } = getAssetControlContext();
Expand All @@ -26,7 +27,11 @@
</script>

{#if menuItem}
<MenuOption text="Change location" on:click={() => (isShowChangeLocation = true)} />
<MenuOption
text="Change location"
icon={mdiMapMarkerMultipleOutline}
on:click={() => (isShowChangeLocation = true)}
/>
{/if}
{#if isShowChangeLocation}
<ChangeLocation
Expand Down
Expand Up @@ -21,6 +21,8 @@
let isShowConfirmation = false;
let loading = false;
$: label = force ? 'Permanently delete' : 'Delete';
const handleTrash = async () => {
if (force) {
isShowConfirmation = true;
Expand All @@ -46,11 +48,11 @@
</script>

{#if menuItem}
<MenuOption text={force ? 'Permanently Delete' : 'Delete'} on:click={handleTrash} />
<MenuOption text={label} icon={mdiDeleteOutline} on:click={handleTrash} />
{:else if loading}
<CircleIconButton title="Loading" icon={mdiTimerSand} />
{:else}
<CircleIconButton title="Delete" icon={mdiDeleteOutline} on:click={handleTrash} />
<CircleIconButton title={label} icon={mdiDeleteOutline} on:click={handleTrash} />
{/if}

{#if isShowConfirmation}
Expand Down
Expand Up @@ -3,7 +3,7 @@
import { downloadArchive, downloadFile } from '$lib/utils/asset-utils';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import { mdiCloudDownloadOutline } from '@mdi/js';
import { mdiCloudDownloadOutline, mdiFileDownloadOutline, mdiFolderDownloadOutline } from '@mdi/js';
export let filename = 'immich.zip';
export let menuItem = false;
Expand All @@ -21,10 +21,12 @@
clearSelect();
await downloadArchive(filename, { assetIds: assets.map((asset) => asset.id) });
};
$: menuItemIcon = getAssets().size === 1 ? mdiFileDownloadOutline : mdiFolderDownloadOutline;
</script>

{#if menuItem}
<MenuOption text="Download" on:click={handleDownloadFiles} />
<MenuOption text="Download" icon={menuItemIcon} on:click={handleDownloadFiles} />
{:else}
<CircleIconButton title="Download" icon={mdiCloudDownloadOutline} on:click={handleDownloadFiles} />
{/if}
Expand Up @@ -16,7 +16,7 @@
export let menuItem = false;
export let removeFavorite: boolean;
$: text = removeFavorite ? 'Remove from Favorites' : 'Favorite';
$: text = removeFavorite ? 'Remove from favorites' : 'Favorite';
$: icon = removeFavorite ? mdiHeartMinusOutline : mdiHeartOutline;
let loading = false;
Expand Down Expand Up @@ -57,7 +57,7 @@
</script>

{#if menuItem}
<MenuOption {text} on:click={handleFavorite} />
<MenuOption {text} {icon} on:click={handleFavorite} />
{/if}

{#if !menuItem}
Expand Down
Expand Up @@ -6,7 +6,7 @@
notificationController,
} from '$lib/components/shared-components/notification/notification';
import { getAlbumInfo, removeAssetFromAlbum, type AlbumResponseDto } from '@immich/sdk';
import { mdiDeleteOutline } from '@mdi/js';
import { mdiDeleteOutline, mdiImageRemoveOutline } from '@mdi/js';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
Expand Down Expand Up @@ -50,7 +50,7 @@
</script>

{#if menuItem}
<MenuOption text="Remove from album" on:click={() => (isShowConfirmation = true)} />
<MenuOption text="Remove from album" icon={mdiImageRemoveOutline} on:click={() => (isShowConfirmation = true)} />
{:else}
<CircleIconButton title="Remove from album" icon={mdiDeleteOutline} on:click={() => (isShowConfirmation = true)} />
{/if}
Expand Down
Expand Up @@ -8,6 +8,7 @@
import { handleError } from '$lib/utils/handle-error';
import { updateAssets } from '@immich/sdk';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import { mdiImageMultipleOutline } from '@mdi/js';
export let onStack: OnStack | undefined;
Expand Down Expand Up @@ -55,4 +56,4 @@
};
</script>

<MenuOption text="Stack" on:click={handleStack} />
<MenuOption text="Stack" icon={mdiImageMultipleOutline} on:click={handleStack} />
Expand Up @@ -29,9 +29,7 @@
<CircleIconButton {title} {icon} on:click={handleShowMenu} />
{#if showContextMenu}
<ContextMenu {...contextMenuPosition}>
<div class="flex flex-col rounded-lg">
<slot />
</div>
<slot />
</ContextMenu>
{/if}
</div>
Expand Up @@ -23,12 +23,14 @@
<div
transition:slide={{ duration: 200, easing: quintOut }}
bind:this={menuElement}
class="absolute z-10 w-[200px] overflow-hidden rounded-lg shadow-lg"
class="absolute z-10 min-w-[200px] w-max max-w-[300px] overflow-hidden rounded-lg shadow-lg"
style="left: {left}px; top: {top}px;"
role="menu"
use:clickOutside
on:outclick
on:escape
>
<slot />
<div class="flex flex-col rounded-lg">
<slot />
</div>
</div>
@@ -1,6 +1,9 @@
<script>
import Icon from '$lib/components/elements/icon.svelte';
export let text = '';
export let subtitle = '';
export let icon = '';
</script>

<button
Expand All @@ -9,7 +12,14 @@
role="menuitem"
>
{#if text}
{text}
{#if icon}
<p class="flex gap-2">
<Icon path={icon} size="18" />
{text}
</p>
{:else}
{text}
{/if}
{:else}
<slot />
{/if}
Expand Down

0 comments on commit 8ed6ed4

Please sign in to comment.