mirror of
https://github.com/immich-app/immich.git
synced 2025-04-24 13:16:41 +02:00
feat(web): increase usage of CircleIconButton (#9256)
This commit is contained in:
parent
5b87abb021
commit
48b490f5e9
@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Badge from '$lib/components/elements/badge.svelte';
|
import Badge from '$lib/components/elements/badge.svelte';
|
||||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
|
import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
|
||||||
@ -16,6 +15,7 @@
|
|||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import JobTileButton from './job-tile-button.svelte';
|
import JobTileButton from './job-tile-button.svelte';
|
||||||
import JobTileStatus from './job-tile-status.svelte';
|
import JobTileStatus from './job-tile-status.svelte';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let title: string;
|
export let title: string;
|
||||||
export let subtitle: string | undefined;
|
export let subtitle: string | undefined;
|
||||||
@ -56,16 +56,19 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
{#if jobCounts.failed > 0}
|
{#if jobCounts.failed > 0}
|
||||||
<Badge color="primary">
|
<Badge color="primary">
|
||||||
<span class="text-sm">
|
<div class="flex flex-row gap-1">
|
||||||
{jobCounts.failed.toLocaleString($locale)} failed
|
<span class="text-sm">
|
||||||
</span>
|
{jobCounts.failed.toLocaleString($locale)} failed
|
||||||
<Button
|
</span>
|
||||||
size="tiny"
|
<CircleIconButton
|
||||||
shadow={false}
|
color="primary"
|
||||||
on:click={() => dispatch('command', { command: JobCommand.ClearFailed, force: false })}
|
icon={mdiClose}
|
||||||
>
|
title="Clear message"
|
||||||
<Icon path={mdiClose} size="18" />
|
size="12"
|
||||||
</Button>
|
padding="1"
|
||||||
|
on:click={() => dispatch('command', { command: JobCommand.ClearFailed, force: false })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Badge>
|
</Badge>
|
||||||
{/if}
|
{/if}
|
||||||
{#if jobCounts.delayed > 0}
|
{#if jobCounts.delayed > 0}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
data-testid="context-button-parent"
|
data-testid="context-button-parent"
|
||||||
>
|
>
|
||||||
<CircleIconButton
|
<CircleIconButton
|
||||||
color="light"
|
color="opaque"
|
||||||
title="Show album options"
|
title="Show album options"
|
||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
size="20"
|
size="20"
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
class="w-full h-14 flex p-4 text-white items-center justify-center rounded-full gap-4 bg-immich-dark-bg bg-opacity-60"
|
class="w-full h-14 flex p-4 text-white items-center justify-center rounded-full gap-4 bg-immich-dark-bg bg-opacity-60"
|
||||||
>
|
>
|
||||||
<button class={disabled ? 'cursor-not-allowed' : ''} on:click={() => dispatch('favorite')} {disabled}>
|
<button class={disabled ? 'cursor-not-allowed' : ''} on:click={() => dispatch('favorite')} {disabled}>
|
||||||
<!-- svelte-ignore missing-declaration -->
|
|
||||||
<div class="items-center justify-center">
|
<div class="items-center justify-center">
|
||||||
<Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} />
|
<Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -160,12 +160,7 @@
|
|||||||
bind:clientHeight={activityHeight}
|
bind:clientHeight={activityHeight}
|
||||||
>
|
>
|
||||||
<div class="flex place-items-center gap-2">
|
<div class="flex place-items-center gap-2">
|
||||||
<button
|
<CircleIconButton on:click={() => dispatch('close')} icon={mdiClose} title="Close" />
|
||||||
class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
|
||||||
on:click={() => dispatch('close')}
|
|
||||||
>
|
|
||||||
<Icon path={mdiClose} size="24" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<p class="text-lg text-immich-fg dark:text-immich-dark-fg">Activity</p>
|
<p class="text-lg text-immich-fg dark:text-immich-dark-fg">Activity</p>
|
||||||
</div>
|
</div>
|
||||||
@ -193,10 +188,13 @@
|
|||||||
</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]" title="Delete comment">
|
<div class="flex items-start w-fit pt-[5px]">
|
||||||
<button on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}>
|
<CircleIconButton
|
||||||
<Icon path={mdiDotsVertical} />
|
icon={mdiDotsVertical}
|
||||||
</button>
|
title="Comment options"
|
||||||
|
size="16"
|
||||||
|
on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div>
|
<div>
|
||||||
@ -242,10 +240,13 @@
|
|||||||
</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" title="Delete like">
|
<div class="flex items-start w-fit">
|
||||||
<button on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}>
|
<CircleIconButton
|
||||||
<Icon path={mdiDotsVertical} />
|
icon={mdiDotsVertical}
|
||||||
</button>
|
title="Reaction options"
|
||||||
|
size="16"
|
||||||
|
on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div>
|
<div>
|
||||||
|
@ -169,13 +169,7 @@
|
|||||||
|
|
||||||
<section class="relative p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
|
<section class="relative p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
|
||||||
<div class="flex place-items-center gap-2">
|
<div class="flex place-items-center gap-2">
|
||||||
<button
|
<CircleIconButton icon={mdiClose} title="Close" on:click={() => dispatch('close')} />
|
||||||
class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
|
||||||
on:click={() => dispatch('close')}
|
|
||||||
>
|
|
||||||
<Icon path={mdiClose} size="24" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<p class="text-lg text-immich-fg dark:text-immich-dark-fg">Info</p>
|
<p class="text-lg text-immich-fg dark:text-immich-dark-fg">Info</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -401,9 +395,13 @@
|
|||||||
<p class="break-all flex place-items-center gap-2">
|
<p class="break-all flex place-items-center gap-2">
|
||||||
{asset.originalFileName}
|
{asset.originalFileName}
|
||||||
{#if isOwner}
|
{#if isOwner}
|
||||||
<button title="Show File Location" on:click={toggleAssetPath} class="-translate-y-[2px]">
|
<CircleIconButton
|
||||||
<Icon path={mdiInformationOutline} />
|
icon={mdiInformationOutline}
|
||||||
</button>
|
title="Show file location"
|
||||||
|
size="16"
|
||||||
|
padding="2"
|
||||||
|
on:click={toggleAssetPath}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex gap-2 text-sm">
|
<div class="flex gap-2 text-sm">
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { mdiClose, mdiMagnify } from '@mdi/js';
|
import { mdiClose, mdiMagnify } from '@mdi/js';
|
||||||
import Icon from './icon.svelte';
|
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import type { SearchOptions } from '$lib/utils/dipatch';
|
import type { SearchOptions } from '$lib/utils/dipatch';
|
||||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let name: string;
|
export let name: string;
|
||||||
export let roundedBottom = true;
|
export let roundedBottom = true;
|
||||||
export let showLoadingSpinner: boolean;
|
export let showLoadingSpinner: boolean;
|
||||||
export let placeholder: string;
|
export let placeholder: string;
|
||||||
|
|
||||||
|
let inputRef: HTMLElement;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ search: SearchOptions; reset: void }>();
|
const dispatch = createEventDispatcher<{ search: SearchOptions; reset: void }>();
|
||||||
|
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
name = '';
|
name = '';
|
||||||
dispatch('reset');
|
dispatch('reset');
|
||||||
|
inputRef?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = (event: KeyboardEvent) => {
|
const handleSearch = (event: KeyboardEvent) => {
|
||||||
@ -29,16 +32,19 @@
|
|||||||
? 'rounded-2xl'
|
? 'rounded-2xl'
|
||||||
: 'rounded-t-lg'} bg-gray-200 p-2 dark:bg-immich-dark-gray gap-2 place-items-center h-full"
|
: 'rounded-t-lg'} bg-gray-200 p-2 dark:bg-immich-dark-gray gap-2 place-items-center h-full"
|
||||||
>
|
>
|
||||||
<button type="button" on:click={() => dispatch('search', { force: true })}>
|
<CircleIconButton
|
||||||
<div class="w-fit">
|
icon={mdiMagnify}
|
||||||
<Icon path={mdiMagnify} size="24" />
|
title="Search"
|
||||||
</div>
|
size="16"
|
||||||
</button>
|
padding="2"
|
||||||
|
on:click={() => dispatch('search', { force: true })}
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
class="w-full gap-2 bg-gray-200 dark:bg-immich-dark-gray dark:text-white"
|
class="w-full gap-2 bg-gray-200 dark:bg-immich-dark-gray dark:text-white"
|
||||||
type="text"
|
type="text"
|
||||||
{placeholder}
|
{placeholder}
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
|
bind:this={inputRef}
|
||||||
on:keydown={handleSearch}
|
on:keydown={handleSearch}
|
||||||
on:input={() => dispatch('search', { force: false })}
|
on:input={() => dispatch('search', { force: false })}
|
||||||
/>
|
/>
|
||||||
@ -48,8 +54,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if name}
|
{#if name}
|
||||||
<button on:click={resetSearch}>
|
<CircleIconButton icon={mdiClose} title="Clear value" size="16" padding="2" on:click={resetSearch} />
|
||||||
<Icon path={mdiClose} />
|
|
||||||
</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
import { linear } from 'svelte/easing';
|
import { linear } from 'svelte/easing';
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||||
import Icon from '../elements/icon.svelte';
|
|
||||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||||
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
|
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let peopleWithFaces: AssetFaceResponseDto[];
|
export let peopleWithFaces: AssetFaceResponseDto[];
|
||||||
export let allPeople: PersonResponseDto[];
|
export let allPeople: PersonResponseDto[];
|
||||||
@ -119,38 +119,19 @@
|
|||||||
<div class="flex place-items-center justify-between gap-2">
|
<div class="flex place-items-center justify-between gap-2">
|
||||||
{#if !searchFaces}
|
{#if !searchFaces}
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<CircleIconButton icon={mdiArrowLeftThin} title="Back" on:click={handleBackButton} />
|
||||||
class="flex place-content-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
|
||||||
on:click={handleBackButton}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<Icon path={mdiArrowLeftThin} size="24" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Select face</p>
|
<p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Select face</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<button
|
<CircleIconButton
|
||||||
class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
icon={mdiMagnify}
|
||||||
title="Search existing person"
|
title="Search for existing person"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
searchFaces = true;
|
searchFaces = true;
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<div>
|
|
||||||
<Icon path={mdiMagnify} size="24" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{#if !isShowLoadingNewPerson}
|
{#if !isShowLoadingNewPerson}
|
||||||
<button
|
<CircleIconButton icon={mdiPlus} title="Create new person" on:click={handleCreatePerson} />
|
||||||
class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
|
||||||
on:click={handleCreatePerson}
|
|
||||||
title="Create new person"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<Icon path={mdiPlus} size="24" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex place-content-center place-items-center">
|
<div class="flex place-content-center place-items-center">
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
@ -158,14 +139,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<CircleIconButton icon={mdiArrowLeftThin} title="Back" on:click={handleBackButton} />
|
||||||
class="flex place-content-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
|
||||||
on:click={handleBackButton}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<Icon path={mdiArrowLeftThin} size="24" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<div class="w-full flex">
|
<div class="w-full flex">
|
||||||
<SearchPeople
|
<SearchPeople
|
||||||
type="input"
|
type="input"
|
||||||
@ -179,14 +153,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<CircleIconButton icon={mdiClose} title="Cancel search" on:click={() => (searchFaces = false)} />
|
||||||
class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
|
||||||
on:click={() => (searchFaces = false)}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<Icon path={mdiClose} size="24" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 py-4 text-sm">
|
<div class="px-4 py-4 text-sm">
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
|
|
||||||
<div class="absolute right-2 top-2" class:hidden={!showVerticalDots}>
|
<div class="absolute right-2 top-2" class:hidden={!showVerticalDots}>
|
||||||
<CircleIconButton
|
<CircleIconButton
|
||||||
color="light"
|
color="opaque"
|
||||||
icon={mdiDotsVertical}
|
icon={mdiDotsVertical}
|
||||||
title="Show person options"
|
title="Show person options"
|
||||||
size="20"
|
size="20"
|
||||||
|
@ -15,14 +15,14 @@
|
|||||||
type AssetFaceResponseDto,
|
type AssetFaceResponseDto,
|
||||||
type PersonResponseDto,
|
type PersonResponseDto,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { mdiArrowLeftThin, mdiRestart } from '@mdi/js';
|
import { mdiArrowLeftThin, mdiMinus, mdiRestart } from '@mdi/js';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import { linear } from 'svelte/easing';
|
import { linear } from 'svelte/easing';
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||||
import Icon from '../elements/icon.svelte';
|
|
||||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||||
import AssignFaceSidePanel from './assign-face-side-panel.svelte';
|
import AssignFaceSidePanel from './assign-face-side-panel.svelte';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let assetId: string;
|
export let assetId: string;
|
||||||
export let assetType: AssetTypeEnum;
|
export let assetType: AssetTypeEnum;
|
||||||
@ -42,7 +42,7 @@
|
|||||||
let isShowLoadingPeople = false;
|
let isShowLoadingPeople = false;
|
||||||
|
|
||||||
// search people
|
// search people
|
||||||
let showSeletecFaces = false;
|
let showSelectedFaces = false;
|
||||||
let allPeople: PersonResponseDto[] = [];
|
let allPeople: PersonResponseDto[] = [];
|
||||||
|
|
||||||
// timers
|
// timers
|
||||||
@ -159,21 +159,21 @@
|
|||||||
if (newFeaturePhoto && personToUpdate) {
|
if (newFeaturePhoto && personToUpdate) {
|
||||||
selectedPersonToCreate[personToUpdate.id] = newFeaturePhoto;
|
selectedPersonToCreate[personToUpdate.id] = newFeaturePhoto;
|
||||||
}
|
}
|
||||||
showSeletecFaces = false;
|
showSelectedFaces = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReassignFace = (person: PersonResponseDto | null) => {
|
const handleReassignFace = (person: PersonResponseDto | null) => {
|
||||||
const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
|
const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
|
||||||
if (person && personToUpdate) {
|
if (person && personToUpdate) {
|
||||||
selectedPersonToReassign[personToUpdate.id] = person;
|
selectedPersonToReassign[personToUpdate.id] = person;
|
||||||
showSeletecFaces = false;
|
showSelectedFaces = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePersonPicker = (person: PersonResponseDto | null) => {
|
const handlePersonPicker = (person: PersonResponseDto | null) => {
|
||||||
if (person) {
|
if (person) {
|
||||||
editedPerson = person;
|
editedPerson = person;
|
||||||
showSeletecFaces = true;
|
showSelectedFaces = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -184,14 +184,7 @@
|
|||||||
>
|
>
|
||||||
<div class="flex place-items-center justify-between gap-2">
|
<div class="flex place-items-center justify-between gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<CircleIconButton icon={mdiArrowLeftThin} title="Back" on:click={handleBackButton} />
|
||||||
class="flex place-content-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
|
|
||||||
on:click={handleBackButton}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<Icon path={mdiArrowLeftThin} size="24" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Edit faces</p>
|
<p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Edit faces</p>
|
||||||
</div>
|
</div>
|
||||||
{#if !isShowLoadingDone}
|
{#if !isShowLoadingDone}
|
||||||
@ -273,21 +266,27 @@
|
|||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full bg-blue-700">
|
<div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full">
|
||||||
{#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]}
|
{#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]}
|
||||||
<button on:click={() => handleReset(face.id)} class="flex h-full w-full">
|
<CircleIconButton
|
||||||
<div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform">
|
color="primary"
|
||||||
<div>
|
icon={mdiRestart}
|
||||||
<Icon path={mdiRestart} size={18} />
|
title="Reset"
|
||||||
</div>
|
size="18"
|
||||||
</div>
|
padding="1"
|
||||||
</button>
|
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
|
||||||
|
on:click={() => handleReset(face.id)}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<button on:click={() => handlePersonPicker(face.person)} class="flex h-full w-full">
|
<CircleIconButton
|
||||||
<div
|
color="primary"
|
||||||
class="absolute left-1/2 top-1/2 h-[2px] w-[14px] translate-x-[-50%] translate-y-[-50%] transform bg-white"
|
icon={mdiMinus}
|
||||||
/>
|
title="Select new face"
|
||||||
</button>
|
size="18"
|
||||||
|
padding="1"
|
||||||
|
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
|
||||||
|
on:click={() => handlePersonPicker(face.person)}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -299,14 +298,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{#if showSeletecFaces}
|
{#if showSelectedFaces}
|
||||||
<AssignFaceSidePanel
|
<AssignFaceSidePanel
|
||||||
{peopleWithFaces}
|
{peopleWithFaces}
|
||||||
{allPeople}
|
{allPeople}
|
||||||
{editedPerson}
|
{editedPerson}
|
||||||
{assetType}
|
{assetType}
|
||||||
{assetId}
|
{assetId}
|
||||||
on:close={() => (showSeletecFaces = false)}
|
on:close={() => (showSelectedFaces = false)}
|
||||||
on:createPerson={(event) => handleCreatePerson(event.detail)}
|
on:createPerson={(event) => handleCreatePerson(event.detail)}
|
||||||
on:reassign={(event) => handleReassignFace(event.detail)}
|
on:reassign={(event) => handleReassignFace(event.detail)}
|
||||||
/>
|
/>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
import { validate, type LibraryResponseDto } from '@immich/sdk';
|
import { validate, type LibraryResponseDto } from '@immich/sdk';
|
||||||
import type { ValidateLibraryImportPathResponseDto } from '@immich/sdk';
|
import type { ValidateLibraryImportPathResponseDto } from '@immich/sdk';
|
||||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let library: LibraryResponseDto;
|
export let library: LibraryResponseDto;
|
||||||
|
|
||||||
@ -209,17 +210,17 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="w-4/5 text-ellipsis px-4 text-sm">{validatedPath.importPath}</td>
|
<td class="w-4/5 text-ellipsis px-4 text-sm">{validatedPath.importPath}</td>
|
||||||
<td class="w-1/5 text-ellipsis px-4 text-sm flex flex-row">
|
<td class="w-1/5 text-ellipsis flex justify-center">
|
||||||
<button
|
<CircleIconButton
|
||||||
type="button"
|
color="primary"
|
||||||
|
icon={mdiPencilOutline}
|
||||||
|
title="Edit import path"
|
||||||
|
size="16"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
editImportPath = listIndex;
|
editImportPath = listIndex;
|
||||||
editedImportPath = validatedPath.importPath;
|
editedImportPath = validatedPath.importPath;
|
||||||
}}
|
}}
|
||||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
/>
|
||||||
>
|
|
||||||
<Icon path={mdiPencilOutline} size="16" />
|
|
||||||
</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
|
||||||
import { LibraryType, type LibraryResponseDto } from '@immich/sdk';
|
import { LibraryType, type LibraryResponseDto } from '@immich/sdk';
|
||||||
import { mdiPencilOutline } from '@mdi/js';
|
import { mdiPencilOutline } from '@mdi/js';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import { handleError } from '../../utils/handle-error';
|
import { handleError } from '../../utils/handle-error';
|
||||||
import Button from '../elements/buttons/button.svelte';
|
import Button from '../elements/buttons/button.svelte';
|
||||||
import LibraryExclusionPatternForm from './library-exclusion-pattern-form.svelte';
|
import LibraryExclusionPatternForm from './library-exclusion-pattern-form.svelte';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let library: Partial<LibraryResponseDto>;
|
export let library: Partial<LibraryResponseDto>;
|
||||||
|
|
||||||
@ -138,17 +138,17 @@
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<td class="w-3/4 text-ellipsis px-4 text-sm">{exclusionPattern}</td>
|
<td class="w-3/4 text-ellipsis px-4 text-sm">{exclusionPattern}</td>
|
||||||
<td class="w-1/4 text-ellipsis px-4 text-sm">
|
<td class="w-1/4 text-ellipsis flex justify-center">
|
||||||
<button
|
<CircleIconButton
|
||||||
type="button"
|
color="primary"
|
||||||
|
icon={mdiPencilOutline}
|
||||||
|
title="Edit exclusion pattern"
|
||||||
|
size="16"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
editExclusionPattern = listIndex;
|
editExclusionPattern = listIndex;
|
||||||
editedExclusionPattern = exclusionPattern;
|
editedExclusionPattern = exclusionPattern;
|
||||||
}}
|
}}
|
||||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
/>
|
||||||
>
|
|
||||||
<Icon path={mdiPencilOutline} size="16" />
|
|
||||||
</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -340,9 +340,12 @@
|
|||||||
class:opacity-0={galleryInView}
|
class:opacity-0={galleryInView}
|
||||||
class:opacity-100={!galleryInView}
|
class:opacity-100={!galleryInView}
|
||||||
>
|
>
|
||||||
<button on:click={() => memoryGallery.scrollIntoView({ behavior: 'smooth' })}>
|
<CircleIconButton
|
||||||
<CircleIconButton title="Show gallery" icon={mdiChevronDown} color="light" />
|
title="Show gallery"
|
||||||
</button>
|
icon={mdiChevronDown}
|
||||||
|
color="light"
|
||||||
|
on:click={() => memoryGallery.scrollIntoView({ behavior: 'smooth' })}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<IntersectionObserver
|
<IntersectionObserver
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
import UserAvatar from '../user-avatar.svelte';
|
import UserAvatar from '../user-avatar.svelte';
|
||||||
import AvatarSelector from './avatar-selector.svelte';
|
import AvatarSelector from './avatar-selector.svelte';
|
||||||
import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
|
import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
let isShowSelectAvatar = false;
|
let isShowSelectAvatar = false;
|
||||||
|
|
||||||
@ -61,15 +62,16 @@
|
|||||||
{#key $user}
|
{#key $user}
|
||||||
<UserAvatar user={$user} size="xl" />
|
<UserAvatar user={$user} size="xl" />
|
||||||
{/key}
|
{/key}
|
||||||
<div
|
<div class="absolute z-10 bottom-0 right-0 rounded-full w-6 h-6">
|
||||||
class="absolute z-10 bottom-0 right-0 rounded-full w-6 h-6 border dark:border-immich-dark-primary bg-immich-primary"
|
<CircleIconButton
|
||||||
>
|
color="primary"
|
||||||
<button
|
icon={mdiPencil}
|
||||||
class="flex items-center justify-center w-full h-full text-white"
|
title="Edit avatar"
|
||||||
|
class="border"
|
||||||
|
size="12"
|
||||||
|
padding="2"
|
||||||
on:click={() => (isShowSelectAvatar = true)}
|
on:click={() => (isShowSelectAvatar = true)}
|
||||||
>
|
/>
|
||||||
<Icon path={mdiPencil} />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
|
import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let notification: Notification;
|
export let notification: Notification;
|
||||||
|
|
||||||
@ -78,9 +79,14 @@
|
|||||||
{notification.type.toString()}
|
{notification.type.toString()}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<button on:click|stopPropagation={discard}>
|
<CircleIconButton
|
||||||
<Icon path={mdiWindowClose} size="20" />
|
icon={mdiWindowClose}
|
||||||
</button>
|
title="Close"
|
||||||
|
class="dark:text-immich-dark-gray"
|
||||||
|
size="20"
|
||||||
|
padding="2"
|
||||||
|
on:click={discard}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message">
|
<p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import type { SessionResponseDto } from '@immich/sdk';
|
import type { SessionResponseDto } from '@immich/sdk';
|
||||||
@ -64,14 +65,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if !device.current}
|
{#if !device.current}
|
||||||
<div class="flex flex-col justify-center text-sm">
|
<div>
|
||||||
<button
|
<CircleIconButton
|
||||||
on:click={() => dispatcher('delete')}
|
color="primary"
|
||||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
icon={mdiTrashCanOutline}
|
||||||
title="Log out"
|
title="Log out"
|
||||||
>
|
size="16"
|
||||||
<Icon path={mdiTrashCanOutline} size="16" />
|
on:click={() => dispatcher('delete')}
|
||||||
</button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { createApiKey, deleteApiKey, getApiKeys, updateApiKey, type ApiKeyResponseDto } from '@immich/sdk';
|
import { createApiKey, deleteApiKey, getApiKeys, updateApiKey, type ApiKeyResponseDto } from '@immich/sdk';
|
||||||
import { mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
|
import { mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
|
||||||
@ -10,6 +9,7 @@
|
|||||||
import APIKeySecret from '../forms/api-key-secret.svelte';
|
import APIKeySecret from '../forms/api-key-secret.svelte';
|
||||||
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
||||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let keys: ApiKeyResponseDto[];
|
export let keys: ApiKeyResponseDto[];
|
||||||
|
|
||||||
@ -143,19 +143,21 @@
|
|||||||
<td class="w-1/3 text-ellipsis px-4 text-sm"
|
<td class="w-1/3 text-ellipsis px-4 text-sm"
|
||||||
>{new Date(key.createdAt).toLocaleDateString($locale, format)}
|
>{new Date(key.createdAt).toLocaleDateString($locale, format)}
|
||||||
</td>
|
</td>
|
||||||
<td class="w-1/3 text-ellipsis px-4 text-sm">
|
<td class="flex flex-row flex-wrap justify-center gap-x-2 gap-y-1 w-1/3">
|
||||||
<button
|
<CircleIconButton
|
||||||
|
color="primary"
|
||||||
|
icon={mdiPencilOutline}
|
||||||
|
title="Edit key"
|
||||||
|
size="16"
|
||||||
on:click={() => (editKey = key)}
|
on:click={() => (editKey = key)}
|
||||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
/>
|
||||||
>
|
<CircleIconButton
|
||||||
<Icon path={mdiPencilOutline} size="16" />
|
color="primary"
|
||||||
</button>
|
icon={mdiTrashCanOutline}
|
||||||
<button
|
title="Delete key"
|
||||||
|
size="16"
|
||||||
on:click={() => (deleteKey = key)}
|
on:click={() => (deleteKey = key)}
|
||||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
/>
|
||||||
>
|
|
||||||
<Icon path={mdiTrashCanOutline} size="16" />
|
|
||||||
</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/key}
|
{/key}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
import { copyToClipboard } from '$lib/utils';
|
import { copyToClipboard } from '$lib/utils';
|
||||||
import { mdiCodeTags, mdiContentCopy, mdiMessage, mdiPartyPopper } from '@mdi/js';
|
import { mdiCodeTags, mdiContentCopy, mdiMessage, mdiPartyPopper } from '@mdi/js';
|
||||||
|
|
||||||
@ -36,12 +37,12 @@
|
|||||||
🚨 Error - Something went wrong
|
🚨 Error - Something went wrong
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button
|
<CircleIconButton
|
||||||
|
color="primary"
|
||||||
|
icon={mdiContentCopy}
|
||||||
|
title="Copy error"
|
||||||
on:click={() => handleCopy()}
|
on:click={() => handleCopy()}
|
||||||
class="rounded-full bg-immich-primary px-3 py-2 text-sm text-white shadow-md transition-colors hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-immich-dark-gray dark:hover:bg-immich-dark-primary/80"
|
/>
|
||||||
>
|
|
||||||
<Icon path={mdiContentCopy} size={24} />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
import { fade, slide } from 'svelte/transition';
|
import { fade, slide } from 'svelte/transition';
|
||||||
import LinkButton from '../../../lib/components/elements/buttons/link-button.svelte';
|
import LinkButton from '../../../lib/components/elements/buttons/link-button.svelte';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
@ -386,12 +387,13 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<td class=" text-ellipsis px-4 text-sm">
|
<td class=" text-ellipsis px-4 text-sm">
|
||||||
<button
|
<CircleIconButton
|
||||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
color="primary"
|
||||||
on:click|stopPropagation|preventDefault={(e) => showMenu(e, library, index)}
|
icon={mdiDotsVertical}
|
||||||
>
|
title="Library options"
|
||||||
<Icon path={mdiDotsVertical} size="16" />
|
size="16"
|
||||||
</button>
|
on:click={(e) => showMenu(e, library, index)}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if showContextMenu}
|
{#if showContextMenu}
|
||||||
<Portal target="body">
|
<Portal target="body">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user