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:
Alex
2025-05-19 09:32:23 -05:00
committed by GitHub
parent 2431e04a09
commit c8641d24f6
42 changed files with 871 additions and 928 deletions

View File

@@ -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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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}

View File

@@ -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>

View File

@@ -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(

View File

@@ -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}

View File

@@ -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}

View File

@@ -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', {