chore: search filter animation improvement and reactive search cue example (#30866)
* chore: better animation for search form and search cue * better search cue example * chore: revert mise.lock changes
This commit is contained in:
@@ -1845,6 +1845,7 @@
|
|||||||
"search": "Search",
|
"search": "Search",
|
||||||
"search_albums": "Search albums",
|
"search_albums": "Search albums",
|
||||||
"search_by_context": "Search by context",
|
"search_by_context": "Search by context",
|
||||||
|
"search_by_context_example": "Describe the photo you want to find, e.g. \"Children jumping on a trampoline\"",
|
||||||
"search_by_description": "Search by description",
|
"search_by_description": "Search by description",
|
||||||
"search_by_description_example": "Hiking day in Sapa",
|
"search_by_description_example": "Hiking day in Sapa",
|
||||||
"search_by_filename": "Search by file name or extension",
|
"search_by_filename": "Search by file name or extension",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import SearchFilters from './SearchFilters.svelte';
|
import SearchFilters from './SearchFilters.svelte';
|
||||||
import { searchManager } from '$lib/managers/search-manager.svelte';
|
import { searchManager } from '$lib/managers/search-manager.svelte';
|
||||||
|
import { getSearchTypePlaceholder } from './search-bar-utils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
grayTheme: boolean;
|
grayTheme: boolean;
|
||||||
@@ -19,6 +20,9 @@
|
|||||||
let { grayTheme }: Props = $props();
|
let { grayTheme }: Props = $props();
|
||||||
|
|
||||||
let showClearIcon = $derived(searchManager.filter.query.length > 0);
|
let showClearIcon = $derived(searchManager.filter.query.length > 0);
|
||||||
|
let placeholder = $derived(
|
||||||
|
searchStore.isSearchEnabled ? getSearchTypePlaceholder(searchManager.filter.queryType) : $t('search_your_photos'),
|
||||||
|
);
|
||||||
|
|
||||||
let input = $state<HTMLInputElement>();
|
let input = $state<HTMLInputElement>();
|
||||||
let searchFilters = $state<ReturnType<typeof SearchFilters>>();
|
let searchFilters = $state<ReturnType<typeof SearchFilters>>();
|
||||||
@@ -145,7 +149,7 @@
|
|||||||
{grayTheme || showSuggestions ? 'dark:bg-immich-dark-gray' : 'dark:bg-immich-dark-bg'}
|
{grayTheme || showSuggestions ? 'dark:bg-immich-dark-gray' : 'dark:bg-immich-dark-bg'}
|
||||||
{showSuggestions ? 'rounded-t-3xl shadow-[0_8px_20px_rgba(0,0,0,0.12)]' : 'rounded-3xl bg-gray-200'}
|
{showSuggestions ? 'rounded-t-3xl shadow-[0_8px_20px_rgba(0,0,0,0.12)]' : 'rounded-3xl bg-gray-200'}
|
||||||
{searchStore.isSearchEnabled ? 'border-light-200 bg-white dark:border-dark-600' : 'border-transparent'}"
|
{searchStore.isSearchEnabled ? 'border-light-200 bg-white dark:border-dark-600' : 'border-transparent'}"
|
||||||
placeholder={$t('search_your_photos')}
|
{placeholder}
|
||||||
required
|
required
|
||||||
pattern="^(?!m:$).*$"
|
pattern="^(?!m:$).*$"
|
||||||
bind:value={searchManager.filter.query}
|
bind:value={searchManager.filter.query}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
let searchHistory = $state<SearchHistorySection>();
|
let searchHistory = $state<SearchHistorySection>();
|
||||||
|
|
||||||
let activeFilter = $state('type');
|
let activeFilter = $state('type');
|
||||||
|
let showAdvanced = $state(false);
|
||||||
let peoplePromise = $state<Promise<PersonResponseDto[]>>();
|
let peoplePromise = $state<Promise<PersonResponseDto[]>>();
|
||||||
let people = $state<PersonResponseDto[]>();
|
let people = $state<PersonResponseDto[]>();
|
||||||
let tagsPromise = $state<Promise<TagResponseDto[]>>();
|
let tagsPromise = $state<Promise<TagResponseDto[]>>();
|
||||||
@@ -187,7 +188,7 @@
|
|||||||
<div role="listbox" {id}>
|
<div role="listbox" {id}>
|
||||||
{#if isOpen}
|
{#if isOpen}
|
||||||
<div
|
<div
|
||||||
transition:fly={{ y: 25, duration: 150 }}
|
transition:fly={{ y: 25, duration: 250 }}
|
||||||
class="absolute z-1 w-full rounded-b-3xl bg-white shadow-[0_8px_20px_rgba(0,0,0,0.12)] transition-all dark:bg-immich-dark-gray dark:text-gray-300"
|
class="absolute z-1 w-full rounded-b-3xl bg-white shadow-[0_8px_20px_rgba(0,0,0,0.12)] transition-all dark:bg-immich-dark-gray dark:text-gray-300"
|
||||||
>
|
>
|
||||||
<SearchHistorySection
|
<SearchHistorySection
|
||||||
@@ -212,16 +213,7 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if activeFilter === 'advanced'}
|
{#if activeFilter}
|
||||||
<div class="my-5 h-px w-full bg-light-200 dark:bg-dark-600"></div>
|
|
||||||
<div class="px-5">
|
|
||||||
<SearchCameraSection />
|
|
||||||
{#if authManager.authenticated && authManager.preferences.ratings.enabled}
|
|
||||||
<SearchRatingsSection />
|
|
||||||
{/if}
|
|
||||||
<SearchDisplaySection />
|
|
||||||
</div>
|
|
||||||
{:else if activeFilter}
|
|
||||||
<div class="px-5 pt-5">
|
<div class="px-5 pt-5">
|
||||||
{#if activeFilter === 'type'}
|
{#if activeFilter === 'type'}
|
||||||
<SearchTextSection />
|
<SearchTextSection />
|
||||||
@@ -238,15 +230,31 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div
|
||||||
|
class="grid transition-[grid-template-rows] duration-200 ease-in-out {showAdvanced
|
||||||
|
? 'grid-rows-[1fr]'
|
||||||
|
: 'grid-rows-[0fr]'}"
|
||||||
|
inert={!showAdvanced}
|
||||||
|
>
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="my-5 h-px w-full bg-light-200 dark:bg-dark-600"></div>
|
||||||
|
<div class="px-5">
|
||||||
|
<SearchCameraSection />
|
||||||
|
{#if authManager.authenticated && authManager.preferences.ratings.enabled}
|
||||||
|
<SearchRatingsSection />
|
||||||
|
{/if}
|
||||||
|
<SearchDisplaySection />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="my-5 h-px w-full bg-light-200 dark:bg-dark-600"></div>
|
<div class="my-5 h-px w-full bg-light-200 dark:bg-dark-600"></div>
|
||||||
<div class="flex gap-2 px-5 pb-5">
|
<div class="flex gap-2 px-5 pb-5">
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant={advancedFiltersSet ? 'outline' : 'ghost'}
|
variant={advancedFiltersSet ? 'outline' : 'ghost'}
|
||||||
leadingIcon={mdiTune}
|
leadingIcon={mdiTune}
|
||||||
trailingIcon={activeFilter === 'advanced' ? mdiChevronUp : mdiChevronDown}
|
trailingIcon={showAdvanced ? mdiChevronUp : mdiChevronDown}
|
||||||
onclick={() => (activeFilter = activeFilter === 'advanced' ? '' : 'advanced')}
|
onclick={() => (showAdvanced = !showAdvanced)}>{$t('advanced_filters')}</Button
|
||||||
>{$t('advanced_filters')}</Button
|
|
||||||
>
|
>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -95,6 +95,30 @@ export const getSearchTypeTitle = (type: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getSearchTypePlaceholder = (type: string) => {
|
||||||
|
const $t = get(t);
|
||||||
|
switch (type) {
|
||||||
|
case 'metadata': {
|
||||||
|
return $t('search_by_filename_example');
|
||||||
|
}
|
||||||
|
case 'description': {
|
||||||
|
return $t('search_by_description_example');
|
||||||
|
}
|
||||||
|
case 'fullPath': {
|
||||||
|
return $t('search_by_full_path_example');
|
||||||
|
}
|
||||||
|
case 'ocr': {
|
||||||
|
return $t('search_by_ocr_example');
|
||||||
|
}
|
||||||
|
case 'smart': {
|
||||||
|
return $t('search_by_context_example');
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return $t('search_your_photos');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const getSearchPlacesTitle = (city?: string, state?: string, country?: string) =>
|
export const getSearchPlacesTitle = (city?: string, state?: string, country?: string) =>
|
||||||
[city, state, country].filter(Boolean).join(', ') || undefined;
|
[city, state, country].filter(Boolean).join(', ') || undefined;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user