From ba6687dde95cae2e51452ad6c9dbb0b03832ef32 Mon Sep 17 00:00:00 2001 From: Yaros Date: Thu, 4 Dec 2025 05:10:12 +0100 Subject: [PATCH] feat(web): search type selection dropdown (#24091) * feat(web): search type selection dropdown * chore: implement suggestions * lint --------- Co-authored-by: Alex --- .../search-bar/search-bar.svelte | 77 ++++++++++++++++--- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/web/src/lib/components/shared-components/search-bar/search-bar.svelte b/web/src/lib/components/shared-components/search-bar/search-bar.svelte index ea1147fe06..a88da2cebf 100644 --- a/web/src/lib/components/shared-components/search-bar/search-bar.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-bar.svelte @@ -9,9 +9,9 @@ import { generateId } from '$lib/utils/generate-id'; import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk'; - import { IconButton, modalManager } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiClose, mdiMagnify, mdiTune } from '@mdi/js'; - import { onDestroy, tick } from 'svelte'; + import { onDestroy, onMount, tick } from 'svelte'; import { t } from 'svelte-i18n'; import SearchHistoryBox from './search-history-box.svelte'; @@ -31,6 +31,8 @@ let isSearchSuggestions = $state(false); let selectedId: string | undefined = $state(); let close: (() => Promise) | undefined; + let showSearchTypeDropdown = $state(false); + let currentSearchType = $state('smart'); const listboxId = generateId(); const searchTypeId = generateId(); @@ -70,6 +72,7 @@ const onFocusIn = () => { searchStore.isSearchEnabled = true; + getSearchType(); }; const onFocusOut = () => { @@ -98,6 +101,9 @@ const searchResult = await result.onClose; close = undefined; + // Refresh search type after modal closes + getSearchType(); + if (!searchResult) { return; } @@ -139,6 +145,7 @@ const onEscape = () => { closeDropdown(); + closeSearchTypeDropdown(); }; const onArrow = async (direction: 1 | -1) => { @@ -168,6 +175,20 @@ searchHistoryBox?.clearSelection(); }; + const toggleSearchTypeDropdown = () => { + showSearchTypeDropdown = !showSearchTypeDropdown; + }; + + const closeSearchTypeDropdown = () => { + showSearchTypeDropdown = false; + }; + + const selectSearchType = (type: string) => { + localStorage.setItem('searchQueryType', type); + currentSearchType = type; + showSearchTypeDropdown = false; + }; + const onsubmit = (event: Event) => { event.preventDefault(); onSubmit(); @@ -180,17 +201,18 @@ case 'metadata': case 'description': case 'ocr': { + currentSearchType = searchType; return searchType; } default: { + currentSearchType = 'smart'; return 'smart'; } } } function getSearchTypeText(): string { - const searchType = getSearchType(); - switch (searchType) { + switch (currentSearchType) { case 'smart': { return $t('context'); } @@ -203,8 +225,22 @@ case 'ocr': { return $t('ocr'); } + default: { + return $t('context'); + } } } + + onMount(() => { + getSearchType(); + }); + + const searchTypes = [ + { value: 'smart', label: () => $t('context') }, + { value: 'metadata', label: () => $t('filename') }, + { value: 'description', label: () => $t('description') }, + { value: 'ocr', label: () => $t('ocr') }, + ] as const; 0} > -

- {getSearchTypeText()} -

+
+ + + {#if showSearchTypeDropdown} +
+ {#each searchTypes as searchType (searchType.value)} + + {/each} +
+ {/if} +
{/if}