mirror of
https://github.com/immich-app/immich.git
synced 2025-04-16 12:18:51 +02:00
chore(web): context menu improvements (#10475)
- ability to add custom hover colors - migrate activity menu to ButtonContextMenu component - onClick callbacks rather than events for menu options - remove slots - configurable menu option colors - improve menu option layout
This commit is contained in:
parent
5cde52eec9
commit
0fda67543d
@ -424,13 +424,13 @@
|
|||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiRenameOutline}
|
icon={mdiRenameOutline}
|
||||||
text={$t('edit_album')}
|
text={$t('edit_album')}
|
||||||
on:click={() => contextMenuTargetAlbum && handleEdit(contextMenuTargetAlbum)}
|
onClick={() => contextMenuTargetAlbum && handleEdit(contextMenuTargetAlbum)}
|
||||||
/>
|
/>
|
||||||
<MenuOption icon={mdiShareVariantOutline} text={$t('share')} on:click={() => openShareModal()} />
|
<MenuOption icon={mdiShareVariantOutline} text={$t('share')} onClick={() => openShareModal()} />
|
||||||
{/if}
|
{/if}
|
||||||
<MenuOption icon={mdiFolderDownloadOutline} text={$t('download')} on:click={() => handleDownloadAlbum()} />
|
<MenuOption icon={mdiFolderDownloadOutline} text={$t('download')} onClick={() => handleDownloadAlbum()} />
|
||||||
{#if showFullContextMenu}
|
{#if showFullContextMenu}
|
||||||
<MenuOption icon={mdiDeleteOutline} text={$t('delete')} on:click={() => setAlbumToDelete()} />
|
<MenuOption icon={mdiDeleteOutline} text={$t('delete')} onClick={() => setAlbumToDelete()} />
|
||||||
{/if}
|
{/if}
|
||||||
</RightClickContextMenu>
|
</RightClickContextMenu>
|
||||||
|
|
||||||
|
@ -109,14 +109,14 @@
|
|||||||
{#if isOwned}
|
{#if isOwned}
|
||||||
<ButtonContextMenu icon={mdiDotsVertical} size="20" title={$t('options')}>
|
<ButtonContextMenu icon={mdiDotsVertical} size="20" title={$t('options')}>
|
||||||
{#if role === AlbumUserRole.Viewer}
|
{#if role === AlbumUserRole.Viewer}
|
||||||
<MenuOption on:click={() => handleSetReadonly(user, AlbumUserRole.Editor)} text={$t('allow_edits')} />
|
<MenuOption onClick={() => handleSetReadonly(user, AlbumUserRole.Editor)} text={$t('allow_edits')} />
|
||||||
{:else}
|
{:else}
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => handleSetReadonly(user, AlbumUserRole.Viewer)}
|
onClick={() => handleSetReadonly(user, AlbumUserRole.Viewer)}
|
||||||
text={$t('disallow_edits')}
|
text={$t('disallow_edits')}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<MenuOption on:click={() => handleMenuRemove(user)} text={$t('remove')} />
|
<MenuOption onClick={() => handleMenuRemove(user)} text={$t('remove')} />
|
||||||
</ButtonContextMenu>
|
</ButtonContextMenu>
|
||||||
{:else if user.id == currentUser?.id}
|
{:else if user.id == currentUser?.id}
|
||||||
<button
|
<button
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||||
import { getAssetType } from '$lib/utils/asset-utils';
|
import { getAssetType } from '$lib/utils/asset-utils';
|
||||||
import { autoGrowHeight } from '$lib/actions/autogrow';
|
import { autoGrowHeight } from '$lib/actions/autogrow';
|
||||||
import { clickOutside } from '$lib/actions/click-outside';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { isTenMinutesApart } from '$lib/utils/timesince';
|
import { isTenMinutesApart } from '$lib/utils/timesince';
|
||||||
import {
|
import {
|
||||||
@ -16,7 +15,7 @@
|
|||||||
type AssetTypeEnum,
|
type AssetTypeEnum,
|
||||||
type UserResponseDto,
|
type UserResponseDto,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { mdiClose, mdiDotsVertical, mdiHeart, mdiSend } from '@mdi/js';
|
import { mdiClose, mdiDotsVertical, mdiHeart, mdiSend, mdiDeleteOutline } from '@mdi/js';
|
||||||
import * as luxon from 'luxon';
|
import * as luxon from 'luxon';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||||
@ -26,6 +25,8 @@
|
|||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { shortcut } from '$lib/actions/shortcut';
|
import { shortcut } from '$lib/actions/shortcut';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||||
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
|
|
||||||
const units: Intl.RelativeTimeFormatUnit[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'];
|
const units: Intl.RelativeTimeFormatUnit[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'];
|
||||||
|
|
||||||
@ -71,7 +72,6 @@
|
|||||||
close: void;
|
close: void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
$: showDeleteReaction = Array.from({ length: reactions.length }).fill(false);
|
|
||||||
$: {
|
$: {
|
||||||
if (innerHeight && activityHeight) {
|
if (innerHeight && activityHeight) {
|
||||||
divHeight = innerHeight - activityHeight;
|
divHeight = innerHeight - activityHeight;
|
||||||
@ -109,7 +109,6 @@
|
|||||||
try {
|
try {
|
||||||
await deleteActivity({ id: reaction.id });
|
await deleteActivity({ id: reaction.id });
|
||||||
reactions.splice(index, 1);
|
reactions.splice(index, 1);
|
||||||
showDeleteReaction.splice(index, 1);
|
|
||||||
reactions = reactions;
|
reactions = reactions;
|
||||||
if (isLiked && reaction.type === 'like' && reaction.id == isLiked.id) {
|
if (isLiked && reaction.type === 'like' && reaction.id == isLiked.id) {
|
||||||
dispatch('deleteLike');
|
dispatch('deleteLike');
|
||||||
@ -147,10 +146,6 @@
|
|||||||
}
|
}
|
||||||
isSendingMessage = false;
|
isSendingMessage = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const showOptionsMenu = (index: number) => {
|
|
||||||
showDeleteReaction[index] = !showDeleteReaction[index];
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="overflow-y-hidden relative h-full" bind:offsetHeight={innerHeight}>
|
<div class="overflow-y-hidden relative h-full" bind:offsetHeight={innerHeight}>
|
||||||
@ -188,27 +183,23 @@
|
|||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
||||||
<div class="flex items-start w-fit pt-[5px]">
|
<div class="mr-4">
|
||||||
<CircleIconButton
|
<ButtonContextMenu
|
||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
title={$t('comment_options')}
|
title={$t('comment_options')}
|
||||||
|
align="top-right"
|
||||||
|
direction="left"
|
||||||
size="16"
|
size="16"
|
||||||
on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}
|
>
|
||||||
/>
|
<MenuOption
|
||||||
|
activeColor="bg-red-200"
|
||||||
|
icon={mdiDeleteOutline}
|
||||||
|
text={$t('remove')}
|
||||||
|
onClick={() => handleDeleteReaction(reaction, index)}
|
||||||
|
/>
|
||||||
|
</ButtonContextMenu>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div>
|
|
||||||
{#if showDeleteReaction[index]}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="absolute right-6 rounded-xl items-center bg-gray-300 dark:bg-slate-100 py-3 px-6 text-left text-sm font-medium text-immich-fg hover:bg-red-300 focus:outline-none focus:ring-2 focus:ring-inset dark:text-immich-dark-bg dark:hover:bg-red-100 transition-colors"
|
|
||||||
use:clickOutside={{ onOutclick: () => (showDeleteReaction[index] = false) }}
|
|
||||||
on:click={() => handleDeleteReaction(reaction, index)}
|
|
||||||
>
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if (index != reactions.length - 1 && !shouldGroup(reactions[index].createdAt, reactions[index + 1].createdAt)) || index === reactions.length - 1}
|
{#if (index != reactions.length - 1 && !shouldGroup(reactions[index].createdAt, reactions[index + 1].createdAt)) || index === reactions.length - 1}
|
||||||
@ -240,27 +231,23 @@
|
|||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
{#if reaction.user.id === user.id || albumOwnerId === user.id}
|
||||||
<div class="flex items-start w-fit">
|
<div class="mr-4">
|
||||||
<CircleIconButton
|
<ButtonContextMenu
|
||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
title={$t('reaction_options')}
|
title={$t('reaction_options')}
|
||||||
|
align="top-right"
|
||||||
|
direction="left"
|
||||||
size="16"
|
size="16"
|
||||||
on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}
|
>
|
||||||
/>
|
<MenuOption
|
||||||
|
activeColor="bg-red-200"
|
||||||
|
icon={mdiDeleteOutline}
|
||||||
|
text={$t('remove')}
|
||||||
|
onClick={() => handleDeleteReaction(reaction, index)}
|
||||||
|
/>
|
||||||
|
</ButtonContextMenu>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div>
|
|
||||||
{#if showDeleteReaction[index]}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="absolute right-6 rounded-xl items-center bg-gray-300 dark:bg-slate-100 py-3 px-6 text-left text-sm font-medium text-immich-fg hover:bg-red-300 focus:outline-none focus:ring-2 focus:ring-inset dark:text-immich-dark-bg dark:hover:bg-red-100 transition-colors"
|
|
||||||
use:clickOutside={{ onOutclick: () => (showDeleteReaction[index] = false) }}
|
|
||||||
on:click={() => handleDeleteReaction(reaction, index)}
|
|
||||||
>
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{#if (index != reactions.length - 1 && isTenMinutesApart(reactions[index].createdAt, reactions[index + 1].createdAt)) || index === reactions.length - 1}
|
{#if (index != reactions.length - 1 && isTenMinutesApart(reactions[index].createdAt, reactions[index + 1].createdAt)) || index === reactions.length - 1}
|
||||||
<div
|
<div
|
||||||
|
@ -177,65 +177,65 @@
|
|||||||
/>
|
/>
|
||||||
<ButtonContextMenu direction="left" align="top-right" color="opaque" title={$t('more')} icon={mdiDotsVertical}>
|
<ButtonContextMenu direction="left" align="top-right" color="opaque" title={$t('more')} icon={mdiDotsVertical}>
|
||||||
{#if showSlideshow}
|
{#if showSlideshow}
|
||||||
<MenuOption icon={mdiPresentationPlay} on:click={() => onMenuClick('playSlideShow')} text={$t('slideshow')} />
|
<MenuOption icon={mdiPresentationPlay} onClick={() => onMenuClick('playSlideShow')} text={$t('slideshow')} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if showDownloadButton}
|
{#if showDownloadButton}
|
||||||
<MenuOption icon={mdiFolderDownloadOutline} on:click={() => onMenuClick('download')} text={$t('download')} />
|
<MenuOption icon={mdiFolderDownloadOutline} onClick={() => onMenuClick('download')} text={$t('download')} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if asset.isTrashed}
|
{#if asset.isTrashed}
|
||||||
<MenuOption icon={mdiHistory} on:click={() => onMenuClick('restoreAsset')} text={$t('restore')} />
|
<MenuOption icon={mdiHistory} onClick={() => onMenuClick('restoreAsset')} text={$t('restore')} />
|
||||||
{:else}
|
{:else}
|
||||||
<MenuOption icon={mdiImageAlbum} on:click={() => onMenuClick('addToAlbum')} text={$t('add_to_album')} />
|
<MenuOption icon={mdiImageAlbum} onClick={() => onMenuClick('addToAlbum')} text={$t('add_to_album')} />
|
||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiShareVariantOutline}
|
icon={mdiShareVariantOutline}
|
||||||
on:click={() => onMenuClick('addToSharedAlbum')}
|
onClick={() => onMenuClick('addToSharedAlbum')}
|
||||||
text={$t('add_to_shared_album')}
|
text={$t('add_to_shared_album')}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if isOwner}
|
{#if isOwner}
|
||||||
{#if hasStackChildren}
|
{#if hasStackChildren}
|
||||||
<MenuOption icon={mdiImageMinusOutline} on:click={() => onMenuClick('unstack')} text={$t('unstack')} />
|
<MenuOption icon={mdiImageMinusOutline} onClick={() => onMenuClick('unstack')} text={$t('unstack')} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if album}
|
{#if album}
|
||||||
<MenuOption
|
<MenuOption
|
||||||
text={$t('set_as_album_cover')}
|
text={$t('set_as_album_cover')}
|
||||||
icon={mdiImageOutline}
|
icon={mdiImageOutline}
|
||||||
on:click={() => onMenuClick('setAsAlbumCover')}
|
onClick={() => onMenuClick('setAsAlbumCover')}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if asset.type === AssetTypeEnum.Image}
|
{#if asset.type === AssetTypeEnum.Image}
|
||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiAccountCircleOutline}
|
icon={mdiAccountCircleOutline}
|
||||||
on:click={() => onMenuClick('asProfileImage')}
|
onClick={() => onMenuClick('asProfileImage')}
|
||||||
text={$t('set_as_profile_picture')}
|
text={$t('set_as_profile_picture')}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => onMenuClick('toggleArchive')}
|
onClick={() => onMenuClick('toggleArchive')}
|
||||||
icon={asset.isArchived ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline}
|
icon={asset.isArchived ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline}
|
||||||
text={asset.isArchived ? $t('unarchive') : $t('to_archive')}
|
text={asset.isArchived ? $t('unarchive') : $t('to_archive')}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiUpload}
|
icon={mdiUpload}
|
||||||
on:click={() => openFileUploadDialog({ multiple: false, assetId: asset.id })}
|
onClick={() => openFileUploadDialog({ multiple: false, assetId: asset.id })}
|
||||||
text={$t('replace_with_upload')}
|
text={$t('replace_with_upload')}
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiDatabaseRefreshOutline}
|
icon={mdiDatabaseRefreshOutline}
|
||||||
on:click={() => onJobClick(AssetJobName.RefreshMetadata)}
|
onClick={() => onJobClick(AssetJobName.RefreshMetadata)}
|
||||||
text={getAssetJobName(AssetJobName.RefreshMetadata)}
|
text={getAssetJobName(AssetJobName.RefreshMetadata)}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiImageRefreshOutline}
|
icon={mdiImageRefreshOutline}
|
||||||
on:click={() => onJobClick(AssetJobName.RegenerateThumbnail)}
|
onClick={() => onJobClick(AssetJobName.RegenerateThumbnail)}
|
||||||
text={getAssetJobName(AssetJobName.RegenerateThumbnail)}
|
text={getAssetJobName(AssetJobName.RegenerateThumbnail)}
|
||||||
/>
|
/>
|
||||||
{#if asset.type === AssetTypeEnum.Video}
|
{#if asset.type === AssetTypeEnum.Video}
|
||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiCogRefreshOutline}
|
icon={mdiCogRefreshOutline}
|
||||||
on:click={() => onJobClick(AssetJobName.TranscodeVideo)}
|
onClick={() => onJobClick(AssetJobName.TranscodeVideo)}
|
||||||
text={getAssetJobName(AssetJobName.TranscodeVideo)}
|
text={getAssetJobName(AssetJobName.TranscodeVideo)}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -75,15 +75,15 @@
|
|||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
title={$t('show_person_options')}
|
title={$t('show_person_options')}
|
||||||
>
|
>
|
||||||
<MenuOption on:click={() => onMenuClick('hide-person')} icon={mdiEyeOffOutline} text={$t('hide_person')} />
|
<MenuOption onClick={() => onMenuClick('hide-person')} icon={mdiEyeOffOutline} text={$t('hide_person')} />
|
||||||
<MenuOption on:click={() => onMenuClick('change-name')} icon={mdiAccountEditOutline} text={$t('change_name')} />
|
<MenuOption onClick={() => onMenuClick('change-name')} icon={mdiAccountEditOutline} text={$t('change_name')} />
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => onMenuClick('set-birth-date')}
|
onClick={() => onMenuClick('set-birth-date')}
|
||||||
icon={mdiCalendarEditOutline}
|
icon={mdiCalendarEditOutline}
|
||||||
text={$t('set_date_of_birth')}
|
text={$t('set_date_of_birth')}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => onMenuClick('merge-people')}
|
onClick={() => onMenuClick('merge-people')}
|
||||||
icon={mdiAccountMultipleCheckOutline}
|
icon={mdiAccountMultipleCheckOutline}
|
||||||
text={$t('merge_people')}
|
text={$t('merge_people')}
|
||||||
/>
|
/>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => (showAlbumPicker = true)}
|
onClick={() => (showAlbumPicker = true)}
|
||||||
text={shared ? $t('add_to_shared_album') : $t('add_to_album')}
|
text={shared ? $t('add_to_shared_album') : $t('add_to_album')}
|
||||||
icon={shared ? mdiShareVariantOutline : mdiImageAlbum}
|
icon={shared ? mdiShareVariantOutline : mdiImageAlbum}
|
||||||
/>
|
/>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
<MenuOption {text} {icon} on:click={handleArchive} />
|
<MenuOption {text} {icon} onClick={handleArchive} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !menuItem}
|
{#if !menuItem}
|
||||||
|
@ -34,6 +34,6 @@
|
|||||||
|
|
||||||
{#each jobs as job}
|
{#each jobs as job}
|
||||||
{#if isAllVideos || job !== AssetJobName.TranscodeVideo}
|
{#if isAllVideos || job !== AssetJobName.TranscodeVideo}
|
||||||
<MenuOption text={getAssetJobName(job)} icon={getAssetJobIcon(job)} on:click={() => handleRunJob(job)} />
|
<MenuOption text={getAssetJobName(job)} icon={getAssetJobIcon(job)} onClick={() => handleRunJob(job)} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
<MenuOption text={$t('change_date')} icon={mdiCalendarEditOutline} on:click={() => (isShowChangeDate = true)} />
|
<MenuOption text={$t('change_date')} icon={mdiCalendarEditOutline} onClick={() => (isShowChangeDate = true)} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if isShowChangeDate}
|
{#if isShowChangeDate}
|
||||||
<ChangeDate
|
<ChangeDate
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<MenuOption
|
<MenuOption
|
||||||
text={$t('change_location')}
|
text={$t('change_location')}
|
||||||
icon={mdiMapMarkerMultipleOutline}
|
icon={mdiMapMarkerMultipleOutline}
|
||||||
on:click={() => (isShowChangeLocation = true)}
|
onClick={() => (isShowChangeLocation = true)}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if isShowChangeLocation}
|
{#if isShowChangeLocation}
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
<MenuOption text={label} icon={mdiDeleteOutline} on:click={handleTrash} />
|
<MenuOption text={label} icon={mdiDeleteOutline} onClick={handleTrash} />
|
||||||
{:else if loading}
|
{:else if loading}
|
||||||
<CircleIconButton title={$t('loading')} icon={mdiTimerSand} />
|
<CircleIconButton title={$t('loading')} icon={mdiTimerSand} />
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
<MenuOption text={$t('download')} icon={menuItemIcon} on:click={handleDownloadFiles} />
|
<MenuOption text={$t('download')} icon={menuItemIcon} onClick={handleDownloadFiles} />
|
||||||
{:else}
|
{:else}
|
||||||
<CircleIconButton title={$t('download')} icon={mdiCloudDownloadOutline} on:click={handleDownloadFiles} />
|
<CircleIconButton title={$t('download')} icon={mdiCloudDownloadOutline} on:click={handleDownloadFiles} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
<MenuOption {text} {icon} on:click={handleFavorite} />
|
<MenuOption {text} {icon} onClick={handleFavorite} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !menuItem}
|
{#if !menuItem}
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if menuItem}
|
{#if menuItem}
|
||||||
<MenuOption text={$t('remove_from_album')} icon={mdiImageRemoveOutline} on:click={removeFromAlbum} />
|
<MenuOption text={$t('remove_from_album')} icon={mdiImageRemoveOutline} onClick={removeFromAlbum} />
|
||||||
{:else}
|
{:else}
|
||||||
<CircleIconButton title={$t('remove_from_album')} icon={mdiDeleteOutline} on:click={removeFromAlbum} />
|
<CircleIconButton title={$t('remove_from_album')} icon={mdiDeleteOutline} on:click={removeFromAlbum} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if unstack}
|
{#if unstack}
|
||||||
<MenuOption text={$t('unstack')} icon={mdiImageMinusOutline} on:click={handleUnstack} />
|
<MenuOption text={$t('unstack')} icon={mdiImageMinusOutline} onClick={handleUnstack} />
|
||||||
{:else}
|
{:else}
|
||||||
<MenuOption text={$t('stack')} icon={mdiImageMultipleOutline} on:click={handleStack} />
|
<MenuOption text={$t('stack')} icon={mdiImageMultipleOutline} onClick={handleStack} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,24 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import { generateId } from '$lib/utils/generate-id';
|
import { generateId } from '$lib/utils/generate-id';
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
import { optionClickCallbackStore, selectedIdStore } from '$lib/stores/context-menu.store';
|
import { optionClickCallbackStore, selectedIdStore } from '$lib/stores/context-menu.store';
|
||||||
|
|
||||||
export let text = '';
|
export let text: string;
|
||||||
export let subtitle = '';
|
export let subtitle = '';
|
||||||
export let icon = '';
|
export let icon = '';
|
||||||
|
export let activeColor = 'bg-slate-300';
|
||||||
|
export let textColor = 'text-immich-fg dark:text-immich-dark-bg';
|
||||||
|
export let onClick: () => void;
|
||||||
|
|
||||||
let id: string = generateId();
|
let id: string = generateId();
|
||||||
|
|
||||||
$: isActive = $selectedIdStore === id;
|
$: isActive = $selectedIdStore === id;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
|
||||||
click: void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
$optionClickCallbackStore?.();
|
$optionClickCallbackStore?.();
|
||||||
dispatch('click');
|
onClick();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -29,27 +27,20 @@
|
|||||||
on:click={handleClick}
|
on:click={handleClick}
|
||||||
on:mouseover={() => ($selectedIdStore = id)}
|
on:mouseover={() => ($selectedIdStore = id)}
|
||||||
on:mouseleave={() => ($selectedIdStore = undefined)}
|
on:mouseleave={() => ($selectedIdStore = undefined)}
|
||||||
class="w-full p-4 text-left text-sm font-medium text-immich-fg focus:outline-none focus:ring-2 focus:ring-inset dark:text-immich-dark-bg cursor-pointer border-gray-200"
|
class="w-full p-4 text-left text-sm font-medium {textColor} focus:outline-none focus:ring-2 focus:ring-inset cursor-pointer border-gray-200 flex gap-2 items-center {isActive
|
||||||
class:bg-slate-300={isActive}
|
? activeColor
|
||||||
class:bg-slate-100={!isActive}
|
: 'bg-slate-100'}"
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
>
|
>
|
||||||
{#if text}
|
{#if icon}
|
||||||
{#if icon}
|
<Icon path={icon} ariaHidden={true} size="18" />
|
||||||
<p class="flex gap-2">
|
|
||||||
<Icon path={icon} ariaHidden={true} size="18" />
|
|
||||||
{text}
|
|
||||||
</p>
|
|
||||||
{:else}
|
|
||||||
{text}
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<slot />
|
|
||||||
{/if}
|
{/if}
|
||||||
|
<div>
|
||||||
<slot name="subtitle">
|
{text}
|
||||||
<p class="text-xs text-gray-500">
|
{#if subtitle}
|
||||||
{subtitle}
|
<p class="text-xs text-gray-500">
|
||||||
</p>
|
{subtitle}
|
||||||
</slot>
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -426,7 +426,7 @@
|
|||||||
<MenuOption
|
<MenuOption
|
||||||
text={$t('set_as_album_cover')}
|
text={$t('set_as_album_cover')}
|
||||||
icon={mdiImageOutline}
|
icon={mdiImageOutline}
|
||||||
on:click={() => updateThumbnailUsingCurrentSelection()}
|
onClick={() => updateThumbnailUsingCurrentSelection()}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<ArchiveAction menuItem unarchive={isAllArchived} onArchive={() => assetStore.triggerUpdate()} />
|
<ArchiveAction menuItem unarchive={isAllArchived} onArchive={() => assetStore.triggerUpdate()} />
|
||||||
@ -468,14 +468,10 @@
|
|||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiImageOutline}
|
icon={mdiImageOutline}
|
||||||
text={$t('select_album_cover')}
|
text={$t('select_album_cover')}
|
||||||
on:click={() => (viewMode = ViewMode.SELECT_THUMBNAIL)}
|
onClick={() => (viewMode = ViewMode.SELECT_THUMBNAIL)}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption icon={mdiCogOutline} text={$t('options')} onClick={() => (viewMode = ViewMode.OPTIONS)} />
|
||||||
icon={mdiCogOutline}
|
<MenuOption icon={mdiDeleteOutline} text={$t('delete_album')} onClick={() => handleRemoveAlbum()} />
|
||||||
text={$t('options')}
|
|
||||||
on:click={() => (viewMode = ViewMode.OPTIONS)}
|
|
||||||
/>
|
|
||||||
<MenuOption icon={mdiDeleteOutline} text={$t('delete_album')} on:click={() => handleRemoveAlbum()} />
|
|
||||||
</ButtonContextMenu>
|
</ButtonContextMenu>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -390,7 +390,7 @@
|
|||||||
<MenuOption
|
<MenuOption
|
||||||
icon={mdiAccountMultipleCheckOutline}
|
icon={mdiAccountMultipleCheckOutline}
|
||||||
text={$t('fix_incorrect_match')}
|
text={$t('fix_incorrect_match')}
|
||||||
on:click={handleReassignAssets}
|
onClick={handleReassignAssets}
|
||||||
/>
|
/>
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
@ -406,22 +406,22 @@
|
|||||||
<MenuOption
|
<MenuOption
|
||||||
text={$t('select_featured_photo')}
|
text={$t('select_featured_photo')}
|
||||||
icon={mdiAccountBoxOutline}
|
icon={mdiAccountBoxOutline}
|
||||||
on:click={() => (viewMode = ViewMode.SELECT_PERSON)}
|
onClick={() => (viewMode = ViewMode.SELECT_PERSON)}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption
|
||||||
text={data.person.isHidden ? $t('unhide_person') : $t('hide_person')}
|
text={data.person.isHidden ? $t('unhide_person') : $t('hide_person')}
|
||||||
icon={data.person.isHidden ? mdiEyeOutline : mdiEyeOffOutline}
|
icon={data.person.isHidden ? mdiEyeOutline : mdiEyeOffOutline}
|
||||||
on:click={() => toggleHidePerson()}
|
onClick={() => toggleHidePerson()}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption
|
||||||
text={$t('set_date_of_birth')}
|
text={$t('set_date_of_birth')}
|
||||||
icon={mdiCalendarEditOutline}
|
icon={mdiCalendarEditOutline}
|
||||||
on:click={() => (viewMode = ViewMode.BIRTH_DATE)}
|
onClick={() => (viewMode = ViewMode.BIRTH_DATE)}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption
|
||||||
text={$t('merge_people')}
|
text={$t('merge_people')}
|
||||||
icon={mdiAccountMultipleCheckOutline}
|
icon={mdiAccountMultipleCheckOutline}
|
||||||
on:click={() => (viewMode = ViewMode.MERGE_PEOPLE)}
|
onClick={() => (viewMode = ViewMode.MERGE_PEOPLE)}
|
||||||
/>
|
/>
|
||||||
</ButtonContextMenu>
|
</ButtonContextMenu>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
@ -380,29 +380,32 @@
|
|||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
title={$t('library_options')}
|
title={$t('library_options')}
|
||||||
>
|
>
|
||||||
<MenuOption on:click={() => onRenameClicked(index)} text={$t('rename')} />
|
<MenuOption onClick={() => onRenameClicked(index)} text={$t('rename')} />
|
||||||
<MenuOption on:click={() => onEditImportPathClicked(index)} text={$t('edit_import_paths')} />
|
<MenuOption onClick={() => onEditImportPathClicked(index)} text={$t('edit_import_paths')} />
|
||||||
<MenuOption on:click={() => onScanSettingClicked(index)} text={$t('scan_settings')} />
|
<MenuOption onClick={() => onScanSettingClicked(index)} text={$t('scan_settings')} />
|
||||||
<hr />
|
<hr />
|
||||||
<MenuOption on:click={() => onScanNewLibraryClicked(library)} text={$t('scan_new_library_files')} />
|
<MenuOption onClick={() => onScanNewLibraryClicked(library)} text={$t('scan_new_library_files')} />
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => onScanAllLibraryFilesClicked(library)}
|
onClick={() => onScanAllLibraryFilesClicked(library)}
|
||||||
text={$t('scan_all_library_files')}
|
text={$t('scan_all_library_files')}
|
||||||
subtitle={$t('only_refreshes_modified_files')}
|
subtitle={$t('only_refreshes_modified_files')}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => onForceScanAllLibraryFilesClicked(library)}
|
onClick={() => onForceScanAllLibraryFilesClicked(library)}
|
||||||
text={$t('force_re-scan_library_files')}
|
text={$t('force_re-scan_library_files')}
|
||||||
subtitle={$t('refreshes_every_file')}
|
subtitle={$t('refreshes_every_file')}
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
<MenuOption
|
<MenuOption
|
||||||
on:click={() => onRemoveOfflineFilesClicked(library)}
|
onClick={() => onRemoveOfflineFilesClicked(library)}
|
||||||
text={$t('remove_offline_files')}
|
text={$t('remove_offline_files')}
|
||||||
/>
|
/>
|
||||||
<MenuOption on:click={() => onDeleteLibraryClicked(library, index)}>
|
<MenuOption
|
||||||
<p class="text-red-600">{$t('delete_library')}</p>
|
text={$t('delete_library')}
|
||||||
</MenuOption>
|
activeColor="bg-red-200"
|
||||||
|
textColor="text-red-600"
|
||||||
|
onClick={() => onDeleteLibraryClicked(library, index)}
|
||||||
|
/>
|
||||||
</ButtonContextMenu>
|
</ButtonContextMenu>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user