chore: tailwindcss v4 and z-war clean up (#18358)
* chore: styling tweak * replace full-screen-modal, update docs * scrubber * fix: control app bar in memory viewer * face lift * pr feedback * clean up
This commit is contained in:
@@ -453,150 +453,6 @@
|
||||
|
||||
<div class="flex overflow-hidden" use:scrollMemoryClearer={{ routeStartsWith: AppRoute.ALBUMS }}>
|
||||
<div class="relative w-full shrink">
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {assetStore} {assetInteraction} />
|
||||
<ButtonContextMenu icon={mdiPlus} title={$t('add_to')}>
|
||||
<AddToAlbum />
|
||||
<AddToAlbum shared />
|
||||
</ButtonContextMenu>
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
onFavorite={(ids, isFavorite) =>
|
||||
assetStore.updateAssetOperation(ids, (asset) => {
|
||||
asset.isFavorite = isFavorite;
|
||||
return { remove: false };
|
||||
})}
|
||||
></FavoriteAction>
|
||||
{/if}
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<ChangeDate menuItem />
|
||||
<ChangeDescription menuItem />
|
||||
<ChangeLocation menuItem />
|
||||
{#if assetInteraction.selectedAssets.length === 1}
|
||||
<MenuOption
|
||||
text={$t('set_as_album_cover')}
|
||||
icon={mdiImageOutline}
|
||||
onClick={() => updateThumbnailUsingCurrentSelection()}
|
||||
/>
|
||||
{/if}
|
||||
<ArchiveAction menuItem unarchive={assetInteraction.isAllArchived} />
|
||||
{/if}
|
||||
|
||||
{#if $preferences.tags.enabled && assetInteraction.isAllUserOwned}
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
|
||||
{#if isOwned || assetInteraction.isAllUserOwned}
|
||||
<RemoveFromAlbum menuItem bind:album onRemove={handleRemoveAssets} />
|
||||
{/if}
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<DeleteAssets menuItem onAssetDelete={handleRemoveAssets} />
|
||||
{/if}
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{:else}
|
||||
{#if viewMode === AlbumPageViewMode.VIEW}
|
||||
<ControlAppBar showBackButton backIcon={mdiArrowLeft} onClose={() => goto(backUrl)}>
|
||||
{#snippet trailing()}
|
||||
{#if isEditor}
|
||||
<CircleIconButton
|
||||
title={$t('add_photos')}
|
||||
onclick={async () => {
|
||||
assetStore.suspendTransitions = true;
|
||||
viewMode = AlbumPageViewMode.SELECT_ASSETS;
|
||||
oldAt = { at: $gridScrollTarget?.at };
|
||||
await navigate(
|
||||
{ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: { at: null } },
|
||||
{ replaceState: true },
|
||||
);
|
||||
}}
|
||||
icon={mdiImagePlusOutline}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if isOwned}
|
||||
<CircleIconButton title={$t('share')} onclick={handleShare} icon={mdiShareVariantOutline} />
|
||||
{/if}
|
||||
|
||||
<AlbumMap {album} />
|
||||
|
||||
{#if album.assetCount > 0}
|
||||
<CircleIconButton title={$t('slideshow')} onclick={handleStartSlideshow} icon={mdiPresentationPlay} />
|
||||
<CircleIconButton title={$t('download')} onclick={handleDownloadAlbum} icon={mdiFolderDownloadOutline} />
|
||||
{/if}
|
||||
|
||||
{#if isOwned}
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('album_options')}>
|
||||
{#if album.assetCount > 0}
|
||||
<MenuOption
|
||||
icon={mdiImageOutline}
|
||||
text={$t('select_album_cover')}
|
||||
onClick={() => (viewMode = AlbumPageViewMode.SELECT_THUMBNAIL)}
|
||||
/>
|
||||
<MenuOption
|
||||
icon={mdiCogOutline}
|
||||
text={$t('options')}
|
||||
onClick={() => (viewMode = AlbumPageViewMode.OPTIONS)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<MenuOption icon={mdiDeleteOutline} text={$t('delete_album')} onClick={() => handleRemoveAlbum()} />
|
||||
</ButtonContextMenu>
|
||||
{/if}
|
||||
|
||||
{#if isCreatingSharedAlbum && album.albumUsers.length === 0}
|
||||
<Button size="small" disabled={album.assetCount === 0} onclick={handleShare}>
|
||||
{$t('share')}
|
||||
</Button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</ControlAppBar>
|
||||
{/if}
|
||||
|
||||
{#if viewMode === AlbumPageViewMode.SELECT_ASSETS}
|
||||
<ControlAppBar onClose={handleCloseSelectAssets}>
|
||||
{#snippet leading()}
|
||||
<p class="text-lg dark:text-immich-dark-fg">
|
||||
{#if !timelineInteraction.selectionActive}
|
||||
{$t('add_to_album')}
|
||||
{:else}
|
||||
{$t('selected_count', { values: { count: timelineInteraction.selectedAssets.length } })}
|
||||
{/if}
|
||||
</p>
|
||||
{/snippet}
|
||||
|
||||
{#snippet trailing()}
|
||||
<button
|
||||
type="button"
|
||||
onclick={handleSelectFromComputer}
|
||||
class="rounded-lg px-6 py-2 text-sm font-medium text-immich-primary transition-all hover:bg-immich-primary/10 dark:text-immich-dark-primary dark:hover:bg-immich-dark-primary/25"
|
||||
>
|
||||
{$t('select_from_computer')}
|
||||
</button>
|
||||
<Button size="small" disabled={!timelineInteraction.selectionActive} onclick={handleAddAssets}
|
||||
>{$t('done')}</Button
|
||||
>
|
||||
{/snippet}
|
||||
</ControlAppBar>
|
||||
{/if}
|
||||
|
||||
{#if viewMode === AlbumPageViewMode.SELECT_THUMBNAIL}
|
||||
<ControlAppBar onClose={() => (viewMode = AlbumPageViewMode.VIEW)}>
|
||||
{#snippet leading()}
|
||||
{$t('select_album_cover')}
|
||||
{/snippet}
|
||||
</ControlAppBar>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<main class="relative h-dvh overflow-hidden px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
|
||||
<AssetGrid
|
||||
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
|
||||
@@ -685,7 +541,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (viewMode = AlbumPageViewMode.SELECT_ASSETS)}
|
||||
class="mt-5 bg-subtle flex w-full place-items-center gap-6 rounded-md border px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
|
||||
class="mt-5 bg-subtle flex w-full place-items-center gap-6 rounded-2xl border px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 dark:hover:bg-gray-500/20 hover:text-immich-primary dark:border-none dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
|
||||
>
|
||||
<span class="text-text-immich-primary dark:text-immich-dark-primary"
|
||||
><Icon path={mdiPlus} size="24" />
|
||||
@@ -710,6 +566,150 @@
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
<CreateSharedLink />
|
||||
<SelectAllAssets {assetStore} {assetInteraction} />
|
||||
<ButtonContextMenu icon={mdiPlus} title={$t('add_to')}>
|
||||
<AddToAlbum />
|
||||
<AddToAlbum shared />
|
||||
</ButtonContextMenu>
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<FavoriteAction
|
||||
removeFavorite={assetInteraction.isAllFavorite}
|
||||
onFavorite={(ids, isFavorite) =>
|
||||
assetStore.updateAssetOperation(ids, (asset) => {
|
||||
asset.isFavorite = isFavorite;
|
||||
return { remove: false };
|
||||
})}
|
||||
></FavoriteAction>
|
||||
{/if}
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')} offset={{ x: 175, y: 25 }}>
|
||||
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<ChangeDate menuItem />
|
||||
<ChangeDescription menuItem />
|
||||
<ChangeLocation menuItem />
|
||||
{#if assetInteraction.selectedAssets.length === 1}
|
||||
<MenuOption
|
||||
text={$t('set_as_album_cover')}
|
||||
icon={mdiImageOutline}
|
||||
onClick={() => updateThumbnailUsingCurrentSelection()}
|
||||
/>
|
||||
{/if}
|
||||
<ArchiveAction menuItem unarchive={assetInteraction.isAllArchived} />
|
||||
{/if}
|
||||
|
||||
{#if $preferences.tags.enabled && assetInteraction.isAllUserOwned}
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
|
||||
{#if isOwned || assetInteraction.isAllUserOwned}
|
||||
<RemoveFromAlbum menuItem bind:album onRemove={handleRemoveAssets} />
|
||||
{/if}
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<DeleteAssets menuItem onAssetDelete={handleRemoveAssets} />
|
||||
{/if}
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{:else}
|
||||
{#if viewMode === AlbumPageViewMode.VIEW}
|
||||
<ControlAppBar showBackButton backIcon={mdiArrowLeft} onClose={() => goto(backUrl)}>
|
||||
{#snippet trailing()}
|
||||
{#if isEditor}
|
||||
<CircleIconButton
|
||||
title={$t('add_photos')}
|
||||
onclick={async () => {
|
||||
assetStore.suspendTransitions = true;
|
||||
viewMode = AlbumPageViewMode.SELECT_ASSETS;
|
||||
oldAt = { at: $gridScrollTarget?.at };
|
||||
await navigate(
|
||||
{ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: { at: null } },
|
||||
{ replaceState: true },
|
||||
);
|
||||
}}
|
||||
icon={mdiImagePlusOutline}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if isOwned}
|
||||
<CircleIconButton title={$t('share')} onclick={handleShare} icon={mdiShareVariantOutline} />
|
||||
{/if}
|
||||
|
||||
<AlbumMap {album} />
|
||||
|
||||
{#if album.assetCount > 0}
|
||||
<CircleIconButton title={$t('slideshow')} onclick={handleStartSlideshow} icon={mdiPresentationPlay} />
|
||||
<CircleIconButton title={$t('download')} onclick={handleDownloadAlbum} icon={mdiFolderDownloadOutline} />
|
||||
{/if}
|
||||
|
||||
{#if isOwned}
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('album_options')} offset={{ x: 175, y: 25 }}>
|
||||
{#if album.assetCount > 0}
|
||||
<MenuOption
|
||||
icon={mdiImageOutline}
|
||||
text={$t('select_album_cover')}
|
||||
onClick={() => (viewMode = AlbumPageViewMode.SELECT_THUMBNAIL)}
|
||||
/>
|
||||
<MenuOption
|
||||
icon={mdiCogOutline}
|
||||
text={$t('options')}
|
||||
onClick={() => (viewMode = AlbumPageViewMode.OPTIONS)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<MenuOption icon={mdiDeleteOutline} text={$t('delete_album')} onClick={() => handleRemoveAlbum()} />
|
||||
</ButtonContextMenu>
|
||||
{/if}
|
||||
|
||||
{#if isCreatingSharedAlbum && album.albumUsers.length === 0}
|
||||
<Button size="small" disabled={album.assetCount === 0} onclick={handleShare}>
|
||||
{$t('share')}
|
||||
</Button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</ControlAppBar>
|
||||
{/if}
|
||||
|
||||
{#if viewMode === AlbumPageViewMode.SELECT_ASSETS}
|
||||
<ControlAppBar onClose={handleCloseSelectAssets}>
|
||||
{#snippet leading()}
|
||||
<p class="text-lg dark:text-immich-dark-fg">
|
||||
{#if !timelineInteraction.selectionActive}
|
||||
{$t('add_to_album')}
|
||||
{:else}
|
||||
{$t('selected_count', { values: { count: timelineInteraction.selectedAssets.length } })}
|
||||
{/if}
|
||||
</p>
|
||||
{/snippet}
|
||||
|
||||
{#snippet trailing()}
|
||||
<button
|
||||
type="button"
|
||||
onclick={handleSelectFromComputer}
|
||||
class="rounded-lg px-6 py-2 text-sm font-medium text-immich-primary transition-all hover:bg-immich-primary/10 dark:text-immich-dark-primary dark:hover:bg-immich-dark-primary/25"
|
||||
>
|
||||
{$t('select_from_computer')}
|
||||
</button>
|
||||
<Button size="small" disabled={!timelineInteraction.selectionActive} onclick={handleAddAssets}
|
||||
>{$t('done')}</Button
|
||||
>
|
||||
{/snippet}
|
||||
</ControlAppBar>
|
||||
{/if}
|
||||
|
||||
{#if viewMode === AlbumPageViewMode.SELECT_THUMBNAIL}
|
||||
<ControlAppBar onClose={() => (viewMode = AlbumPageViewMode.VIEW)}>
|
||||
{#snippet leading()}
|
||||
{$t('select_album_cover')}
|
||||
{/snippet}
|
||||
</ControlAppBar>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{#if album.albumUsers.length > 0 && album && isShowActivity && $user && !$showAssetViewer}
|
||||
<div class="flex">
|
||||
|
||||
@@ -40,6 +40,20 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
<AssetGrid
|
||||
enableRouting={true}
|
||||
{assetStore}
|
||||
{assetInteraction}
|
||||
removeAction={AssetAction.UNARCHIVE}
|
||||
onEscape={handleEscape}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_archived_assets_message')} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</UserPageLayout>
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
@@ -73,17 +87,3 @@
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{/if}
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
<AssetGrid
|
||||
enableRouting={true}
|
||||
{assetStore}
|
||||
{assetInteraction}
|
||||
removeAction={AssetAction.UNARCHIVE}
|
||||
onEscape={handleEscape}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_archived_assets_message')} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -44,6 +44,21 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
<AssetGrid
|
||||
enableRouting={true}
|
||||
withStacked={true}
|
||||
{assetStore}
|
||||
{assetInteraction}
|
||||
removeAction={AssetAction.UNFAVORITE}
|
||||
onEscape={handleEscape}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_favorites_message')} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</UserPageLayout>
|
||||
|
||||
<!-- Multiselection mode app bar -->
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
@@ -74,18 +89,3 @@
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{/if}
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
<AssetGrid
|
||||
enableRouting={true}
|
||||
withStacked={true}
|
||||
{assetStore}
|
||||
{assetInteraction}
|
||||
removeAction={AssetAction.UNFAVORITE}
|
||||
onEscape={handleEscape}
|
||||
>
|
||||
{#snippet empty()}
|
||||
<EmptyPlaceholder text={$t('no_favorites_message')} />
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -90,6 +90,44 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<UserPageLayout title={data.meta.title}>
|
||||
{#snippet sidebar()}
|
||||
<Sidebar>
|
||||
<SkipLink target={`#${headerId}`} text={$t('skip_to_folders')} breakpoint="md" />
|
||||
<section>
|
||||
<div class="text-xs ps-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div>
|
||||
<div class="h-full">
|
||||
<TreeItems
|
||||
icons={{ default: mdiFolderOutline, active: mdiFolder }}
|
||||
items={tree}
|
||||
active={currentPath}
|
||||
getLink={getLinkForPath}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</Sidebar>
|
||||
{/snippet}
|
||||
|
||||
<Breadcrumbs {pathSegments} icon={mdiFolderHome} title={$t('folders')} getLink={getLinkForPath} />
|
||||
|
||||
<section class="mt-2 h-[calc(100%-(--spacing(20)))] overflow-auto immich-scrollbar">
|
||||
<TreeItemThumbnails items={currentTreeItems} icon={mdiFolder} onClick={handleNavigateToFolder} />
|
||||
|
||||
<!-- Assets -->
|
||||
{#if data.pathAssets && data.pathAssets.length > 0}
|
||||
<div bind:clientHeight={viewport.height} bind:clientWidth={viewport.width} class="mt-2">
|
||||
<GalleryViewer
|
||||
assets={data.pathAssets}
|
||||
{assetInteraction}
|
||||
{viewport}
|
||||
showAssetName={true}
|
||||
pageHeaderOffset={54}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
</UserPageLayout>
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
<div class="fixed top-0 start-0 w-full">
|
||||
<AssetSelectControlBar
|
||||
@@ -132,41 +170,3 @@
|
||||
</AssetSelectControlBar>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<UserPageLayout title={data.meta.title}>
|
||||
{#snippet sidebar()}
|
||||
<Sidebar>
|
||||
<SkipLink target={`#${headerId}`} text={$t('skip_to_folders')} breakpoint="md" />
|
||||
<section>
|
||||
<div class="text-xs ps-4 mb-2 dark:text-white">{$t('explorer').toUpperCase()}</div>
|
||||
<div class="h-full">
|
||||
<TreeItems
|
||||
icons={{ default: mdiFolderOutline, active: mdiFolder }}
|
||||
items={tree}
|
||||
active={currentPath}
|
||||
getLink={getLinkForPath}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</Sidebar>
|
||||
{/snippet}
|
||||
|
||||
<Breadcrumbs {pathSegments} icon={mdiFolderHome} title={$t('folders')} getLink={getLinkForPath} />
|
||||
|
||||
<section class="mt-2 h-[calc(100%-(--spacing(20)))] overflow-auto immich-scrollbar">
|
||||
<TreeItemThumbnails items={currentTreeItems} icon={mdiFolder} onClick={handleNavigateToFolder} />
|
||||
|
||||
<!-- Assets -->
|
||||
{#if data.pathAssets && data.pathAssets.length > 0}
|
||||
<div bind:clientHeight={viewport.height} bind:clientWidth={viewport.width} class="mt-2">
|
||||
<GalleryViewer
|
||||
assets={data.pathAssets}
|
||||
{assetInteraction}
|
||||
{viewport}
|
||||
showAssetName={true}
|
||||
pageHeaderOffset={54}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
</UserPageLayout>
|
||||
|
||||
@@ -51,26 +51,9 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- Multi-selection mode app bar -->
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
<SelectAllAssets withText {assetStore} {assetInteraction} />
|
||||
<SetVisibilityAction unlock onVisibilitySet={handleMoveOffLockedFolder} />
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem />
|
||||
<ChangeDate menuItem />
|
||||
<ChangeLocation menuItem />
|
||||
<DeleteAssets menuItem force onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{/if}
|
||||
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
{#snippet buttons()}
|
||||
<Button size="small" variant="filled" color="warning" leadingIcon={mdiLockOutline} onclick={handleLock}>
|
||||
<Button size="small" variant="ghost" color="primary" leadingIcon={mdiLockOutline} onclick={handleLock}>
|
||||
{$t('lock')}
|
||||
</Button>
|
||||
{/snippet}
|
||||
@@ -87,3 +70,20 @@
|
||||
{/snippet}
|
||||
</AssetGrid>
|
||||
</UserPageLayout>
|
||||
|
||||
<!-- Multi-selection mode app bar -->
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
<SelectAllAssets withText {assetStore} {assetInteraction} />
|
||||
<SetVisibilityAction unlock onVisibilitySet={handleMoveOffLockedFolder} />
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem />
|
||||
<ChangeDate menuItem />
|
||||
<ChangeLocation menuItem />
|
||||
<DeleteAssets menuItem force onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{/if}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
</script>
|
||||
|
||||
<main class="grid h-dvh pt-18">
|
||||
<AssetGrid enableRouting={true} {assetStore} {assetInteraction} onEscape={handleEscape} />
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
@@ -64,5 +66,4 @@
|
||||
{/snippet}
|
||||
</ControlAppBar>
|
||||
{/if}
|
||||
<AssetGrid enableRouting={true} {assetStore} {assetInteraction} onEscape={handleEscape} />
|
||||
</main>
|
||||
|
||||
@@ -421,11 +421,11 @@
|
||||
class="flex flex-col justify-center text-start px-4 text-immich-primary dark:text-immich-dark-primary"
|
||||
>
|
||||
<p class="w-40 sm:w-72 font-medium truncate">{person.name || $t('add_a_name')}</p>
|
||||
<p class="text-sm text-gray-500 dark:text-immich-gray">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{$t('assets_count', { values: { count: numberOfAssets } })}
|
||||
</p>
|
||||
{#if person.birthDate}
|
||||
<p class="text-sm text-gray-500 dark:text-immich-gray">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{$t('person_birthdate', {
|
||||
values: {
|
||||
date: DateTime.fromISO(person.birthDate).toLocaleString(
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import SkipLink from '$lib/components/elements/buttons/skip-link.svelte';
|
||||
import UserPageLayout, { headerId } from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import AssetGrid from '$lib/components/photos-page/asset-grid.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
@@ -20,7 +19,7 @@
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { buildTree, normalizeTreePath } from '$lib/utils/tree-utils';
|
||||
import { deleteTag, getAllTags, updateTag, upsertTags, type TagResponseDto } from '@immich/sdk';
|
||||
import { Button, HStack, Text } from '@immich/ui';
|
||||
import { Button, HStack, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
|
||||
import { mdiPencil, mdiPlus, mdiTag, mdiTagMultiple, mdiTrashCanOutline } from '@mdi/js';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -192,47 +191,55 @@
|
||||
</UserPageLayout>
|
||||
|
||||
{#if isNewOpen}
|
||||
<FullScreenModal title={$t('create_tag')} icon={mdiTag} onClose={handleCancel}>
|
||||
<div class="text-immich-primary dark:text-immich-dark-primary">
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
{$t('create_tag_description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form {onsubmit} autocomplete="off" id="create-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('tag').toUpperCase()}
|
||||
bind:value={newTagValue}
|
||||
required={true}
|
||||
autofocus={true}
|
||||
/>
|
||||
<Modal size="small" title={$t('create_tag')} icon={mdiTag} onClose={handleCancel}>
|
||||
<ModalBody>
|
||||
<div class="text-immich-primary dark:text-immich-dark-primary">
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
{$t('create_tag_description')}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="secondary" fullWidth shape="round" onclick={() => handleCancel()}>{$t('cancel')}</Button>
|
||||
<Button type="submit" fullWidth shape="round" form="create-tag-form">{$t('create')}</Button>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
<form {onsubmit} autocomplete="off" id="create-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label={$t('tag').toUpperCase()}
|
||||
bind:value={newTagValue}
|
||||
required={true}
|
||||
autofocus={true}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<div class="flex w-full gap-2">
|
||||
<Button color="secondary" fullWidth shape="round" onclick={() => handleCancel()}>{$t('cancel')}</Button>
|
||||
<Button type="submit" fullWidth shape="round" form="create-tag-form">{$t('create')}</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
{#if isEditOpen}
|
||||
<FullScreenModal title={$t('edit_tag')} icon={mdiTag} onClose={handleCancel}>
|
||||
<form {onsubmit} autocomplete="off" id="edit-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.COLOR}
|
||||
label={$t('color').toUpperCase()}
|
||||
bind:value={newTagColor}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<Modal title={$t('edit_tag')} icon={mdiTag} onClose={handleCancel}>
|
||||
<ModalBody>
|
||||
<form {onsubmit} autocomplete="off" id="edit-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.COLOR}
|
||||
label={$t('color').toUpperCase()}
|
||||
bind:value={newTagColor}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</ModalBody>
|
||||
|
||||
{#snippet stickyBottom()}
|
||||
<Button color="secondary" fullWidth shape="round" onclick={() => handleCancel()}>{$t('cancel')}</Button>
|
||||
<Button type="submit" fullWidth shape="round" form="edit-tag-form">{$t('save')}</Button>
|
||||
{/snippet}
|
||||
</FullScreenModal>
|
||||
<ModalFooter>
|
||||
<div class="flex w-full gap-2">
|
||||
<Button color="secondary" fullWidth shape="round" onclick={() => handleCancel()}>{$t('cancel')}</Button>
|
||||
<Button type="submit" fullWidth shape="round" form="edit-tag-form">{$t('save')}</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
@@ -90,17 +90,6 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
<SelectAllAssets {assetStore} {assetInteraction} />
|
||||
<DeleteAssets force onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
<RestoreAssets onRestore={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
</AssetSelectControlBar>
|
||||
{/if}
|
||||
|
||||
{#if $featureFlags.loaded && $featureFlags.trash}
|
||||
<UserPageLayout hideNavbar={assetInteraction.selectionActive} title={data.meta.title} scrollbar={false}>
|
||||
{#snippet buttons()}
|
||||
@@ -138,3 +127,14 @@
|
||||
</AssetGrid>
|
||||
</UserPageLayout>
|
||||
{/if}
|
||||
|
||||
{#if assetInteraction.selectionActive}
|
||||
<AssetSelectControlBar
|
||||
assets={assetInteraction.selectedAssets}
|
||||
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||
>
|
||||
<SelectAllAssets {assetStore} {assetInteraction} />
|
||||
<DeleteAssets force onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
<RestoreAssets onRestore={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
</AssetSelectControlBar>
|
||||
{/if}
|
||||
|
||||
@@ -249,47 +249,49 @@
|
||||
<div>
|
||||
<Card color="secondary">
|
||||
<CardHeader>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2 px-4 py-2 text-primary">
|
||||
<Icon icon={mdiAccountOutline} size="1.5rem" />
|
||||
<CardTitle>{$t('profile')}</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Stack gap={2}>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('name')}</Heading>
|
||||
<Text>{user.name}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('email')}</Heading>
|
||||
<Text>{user.email}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('created_at')}</Heading>
|
||||
<Text>{user.createdAt}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('updated_at')}</Heading>
|
||||
<Text>{user.updatedAt}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('id')}</Heading>
|
||||
<Code>{user.id}</Code>
|
||||
</div>
|
||||
</Stack>
|
||||
<div class="px-4 pb-7">
|
||||
<Stack gap={2}>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('name')}</Heading>
|
||||
<Text>{user.name}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('email')}</Heading>
|
||||
<Text>{user.email}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('created_at')}</Heading>
|
||||
<Text>{user.createdAt}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('updated_at')}</Heading>
|
||||
<Text>{user.updatedAt}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Heading tag="h3" size="tiny">{$t('id')}</Heading>
|
||||
<Code>{user.id}</Code>
|
||||
</div>
|
||||
</Stack>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<Card color="secondary">
|
||||
<CardHeader>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2 px-4 py-2 text-primary">
|
||||
<Icon icon={mdiFeatureSearchOutline} size="1.5rem" />
|
||||
<CardTitle>{$t('features')}</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div>
|
||||
<Stack gap={2}>
|
||||
<div class="px-4 pb-4">
|
||||
<Stack gap={3}>
|
||||
<Field readOnly label={$t('email_notifications')}>
|
||||
<Switch checked={userPreferences.emailNotifications.enabled} color="primary" />
|
||||
</Field>
|
||||
@@ -320,13 +322,13 @@
|
||||
</Card>
|
||||
<Card color="secondary">
|
||||
<CardHeader>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2 px-4 py-2 text-primary">
|
||||
<Icon icon={mdiChartPieOutline} size="1.5rem" />
|
||||
<CardTitle>{$t('storage_quota')}</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div>
|
||||
<div class="px-4 pb-4">
|
||||
{#if user.quotaSizeInBytes !== null && user.quotaSizeInBytes >= 0}
|
||||
<Text>
|
||||
{$t('storage_usage', {
|
||||
|
||||
Reference in New Issue
Block a user