From 8db666bc384c86de193a908b29a90be6e7a6182b Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Thu, 8 May 2025 22:36:05 +0200 Subject: [PATCH] refactor: search filter modal (#18159) --- .../search-bar/search-bar.svelte | 69 +++++++++---------- .../SearchFilterModal.svelte} | 33 ++++----- 2 files changed, 49 insertions(+), 53 deletions(-) rename web/src/lib/{components/shared-components/search-bar/search-filter-modal.svelte => modals/SearchFilterModal.svelte} (81%) 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 e01b0edf1b..29a63fe049 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 @@ -1,19 +1,20 @@ <script lang="ts"> - import { AppRoute } from '$lib/constants'; import { goto } from '$app/navigation'; - import { searchStore } from '$lib/stores/search.svelte'; - import { mdiClose, mdiMagnify, mdiTune } from '@mdi/js'; - import SearchHistoryBox from './search-history-box.svelte'; - import SearchFilterModal from './search-filter-modal.svelte'; - import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk'; - import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; - import { handlePromiseError } from '$lib/utils'; - import { shortcuts } from '$lib/actions/shortcut'; import { focusOutside } from '$lib/actions/focus-outside'; + import { shortcuts } from '$lib/actions/shortcut'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; - import { t } from 'svelte-i18n'; + import { AppRoute } from '$lib/constants'; + import { modalManager } from '$lib/managers/modal-manager.svelte'; + import SearchFilterModal from '$lib/modals/SearchFilterModal.svelte'; + import { searchStore } from '$lib/stores/search.svelte'; + import { handlePromiseError } from '$lib/utils'; import { generateId } from '$lib/utils/generate-id'; + import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; + import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk'; + import { mdiClose, mdiMagnify, mdiTune } from '@mdi/js'; import { onDestroy, tick } from 'svelte'; + import { t } from 'svelte-i18n'; + import SearchHistoryBox from './search-history-box.svelte'; interface Props { value?: string; @@ -28,10 +29,10 @@ let input = $state<HTMLInputElement>(); let searchHistoryBox = $state<ReturnType<typeof SearchHistoryBox>>(); let showSuggestions = $state(false); - let showFilter = $state(false); let isSearchSuggestions = $state(false); let selectedId: string | undefined = $state(); let isFocus = $state(false); + let close: (() => Promise<void>) | undefined; const listboxId = generateId(); @@ -43,7 +44,6 @@ const params = getMetadataSearchQuery(payload); closeDropdown(); - showFilter = false; searchStore.isSearchEnabled = false; await goto(`${AppRoute.SEARCH}?${params}`); }; @@ -83,13 +83,27 @@ await handleSearch(searchPayload); }; - const onFilterClick = () => { - showFilter = !showFilter; + const onFilterClick = async () => { value = ''; - if (showFilter) { - closeDropdown(); + if (close) { + await close(); + close = undefined; + return; } + + const result = modalManager.open(SearchFilterModal, { searchQuery }); + close = result.close; + closeDropdown(); + + const searchResult = await result.onClose; + close = undefined; + + if (!searchResult) { + return; + } + + await handleSearch(searchResult); }; const onSubmit = () => { @@ -122,7 +136,6 @@ const onEscape = () => { closeDropdown(); - showFilter = false; }; const onArrow = async (direction: 1 | -1) => { @@ -221,9 +234,7 @@ class="w-full transition-all border-2 px-14 py-4 max-md:py-2 text-immich-fg/75 dark:text-immich-dark-fg {grayTheme ? 'dark:bg-immich-dark-gray' : 'dark:bg-immich-dark-bg'} {showSuggestions && isSearchSuggestions ? 'rounded-t-3xl' : 'rounded-3xl bg-gray-200'} - {searchStore.isSearchEnabled && !showFilter - ? 'border-gray-200 dark:border-gray-700 bg-white' - : 'border-transparent'}" + {searchStore.isSearchEnabled ? 'border-gray-200 dark:border-gray-700 bg-white' : 'border-transparent'}" placeholder={$t('search_your_photos')} required pattern="^(?!m:$).*$" @@ -231,7 +242,6 @@ bind:this={input} onfocus={openDropdown} oninput={onInput} - disabled={showFilter} role="combobox" aria-controls={listboxId} aria-activedescendant={selectedId ?? ''} @@ -285,22 +295,7 @@ </div> {/if} <div class="absolute inset-y-0 start-0 flex items-center ps-2"> - <CircleIconButton - type="submit" - disabled={showFilter} - title={$t('search')} - icon={mdiMagnify} - size="20" - onclick={() => {}} - /> + <CircleIconButton type="submit" title={$t('search')} icon={mdiMagnify} size="20" onclick={() => {}} /> </div> </form> - - {#if showFilter} - <SearchFilterModal - {searchQuery} - onSearch={(payload) => handleSearch(payload)} - onClose={() => (showFilter = false)} - /> - {/if} </div> diff --git a/web/src/lib/components/shared-components/search-bar/search-filter-modal.svelte b/web/src/lib/modals/SearchFilterModal.svelte similarity index 81% rename from web/src/lib/components/shared-components/search-bar/search-filter-modal.svelte rename to web/src/lib/modals/SearchFilterModal.svelte index d5c9b02a2d..1b2f32731e 100644 --- a/web/src/lib/components/shared-components/search-bar/search-filter-modal.svelte +++ b/web/src/lib/modals/SearchFilterModal.svelte @@ -1,8 +1,8 @@ <script lang="ts" module> import { MediaType, QueryType, validQueryTypes } from '$lib/constants'; - import type { SearchDateFilter } from './search-date-section.svelte'; - import type { SearchDisplayFilters } from './search-display-section.svelte'; - import type { SearchLocationFilter } from './search-location-section.svelte'; + import type { SearchDateFilter } from '../components/shared-components/search-bar/search-date-section.svelte'; + import type { SearchDisplayFilters } from '../components/shared-components/search-bar/search-display-section.svelte'; + import type { SearchLocationFilter } from '../components/shared-components/search-bar/search-location-section.svelte'; export type SearchFilter = { query: string; @@ -20,6 +20,17 @@ <script lang="ts"> import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; + import SearchCameraSection, { + type SearchCameraFilter, + } from '$lib/components/shared-components/search-bar/search-camera-section.svelte'; + import SearchDateSection from '$lib/components/shared-components/search-bar/search-date-section.svelte'; + import SearchDisplaySection from '$lib/components/shared-components/search-bar/search-display-section.svelte'; + import SearchLocationSection from '$lib/components/shared-components/search-bar/search-location-section.svelte'; + import SearchMediaSection from '$lib/components/shared-components/search-bar/search-media-section.svelte'; + import SearchPeopleSection from '$lib/components/shared-components/search-bar/search-people-section.svelte'; + import SearchRatingsSection from '$lib/components/shared-components/search-bar/search-ratings-section.svelte'; + import SearchTagsSection from '$lib/components/shared-components/search-bar/search-tags-section.svelte'; + import SearchTextSection from '$lib/components/shared-components/search-bar/search-text-section.svelte'; import { preferences } from '$lib/stores/user.store'; import { parseUtcDate } from '$lib/utils/date-time'; import { generateId } from '$lib/utils/generate-id'; @@ -28,23 +39,13 @@ import { mdiTune } from '@mdi/js'; import { t } from 'svelte-i18n'; import { SvelteSet } from 'svelte/reactivity'; - import SearchCameraSection, { type SearchCameraFilter } from './search-camera-section.svelte'; - import SearchDateSection from './search-date-section.svelte'; - import SearchDisplaySection from './search-display-section.svelte'; - import SearchLocationSection from './search-location-section.svelte'; - import SearchMediaSection from './search-media-section.svelte'; - import SearchPeopleSection from './search-people-section.svelte'; - import SearchRatingsSection from './search-ratings-section.svelte'; - import SearchTagsSection from './search-tags-section.svelte'; - import SearchTextSection from './search-text-section.svelte'; interface Props { searchQuery: MetadataSearchDto | SmartSearchDto; - onClose: () => void; - onSearch: (search: SmartSearchDto | MetadataSearchDto) => void; + onClose: (search?: SmartSearchDto | MetadataSearchDto) => void; } - let { searchQuery, onClose, onSearch }: Props = $props(); + let { searchQuery, onClose }: Props = $props(); const parseOptionalDate = (dateString?: string) => (dateString ? parseUtcDate(dateString) : undefined); const toStartOfDayDate = (dateString: string) => parseUtcDate(dateString)?.startOf('day').toISODate() || undefined; @@ -141,7 +142,7 @@ rating: filter.rating, }; - onSearch(payload); + onClose(payload); }; const onreset = (event: Event) => {