mirror of
https://github.com/immich-app/immich.git
synced 2025-12-07 01:10:00 +03:00
Compare commits
2 Commits
fix/asset-
...
new-search
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41c20244b5 | ||
|
|
e4f1c4ee64 |
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"metadata": "Metadata",
|
||||
"about": "About",
|
||||
"account": "Account",
|
||||
"account_settings": "Account Settings",
|
||||
@@ -756,6 +757,7 @@
|
||||
"connected_device": "Connected device",
|
||||
"connected_to": "Connected to",
|
||||
"contain": "Contain",
|
||||
"content": "Content",
|
||||
"context": "Context",
|
||||
"continue": "Continue",
|
||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
||||
|
||||
@@ -81,9 +81,7 @@
|
||||
</script>
|
||||
|
||||
<div id="camera-selection">
|
||||
<p class="uppercase immich-form-label">{$t('camera')}</p>
|
||||
|
||||
<div class="grid grid-auto-fit-40 gap-5 mt-1">
|
||||
<div class="grid grid-auto-fit-40 gap-5">
|
||||
<div class="w-full">
|
||||
<Combobox
|
||||
label={$t('make')}
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
|
||||
<div id="display-options-selection">
|
||||
<fieldset>
|
||||
<legend class="uppercase immich-form-label">{$t('display_options')}</legend>
|
||||
<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1">
|
||||
<div class="flex flex-wrap gap-x-5 gap-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="not-in-album-checkbox" size="tiny" bind:checked={filters.isNotInAlbum} />
|
||||
<Label label={$t('not_in_any_album')} for="not-in-album-checkbox" />
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
columns?: 1 | 2;
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
let { title, columns = 1, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<fieldset>
|
||||
<legend class="mb-3 text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">
|
||||
{title}
|
||||
</legend>
|
||||
|
||||
<div
|
||||
class="grid gap-3"
|
||||
class:grid-cols-1={columns === 1}
|
||||
class:md:grid-cols-2={columns === 2}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { Icon } from '@immich/ui';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
icon: string;
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
let { title, icon, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<section class="rounded-xl border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-800/50">
|
||||
<header class="mb-3 flex items-center gap-2">
|
||||
<Icon {icon} size="18" class="text-immich-primary dark:text-immich-dark-primary" />
|
||||
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-200">{title}</h3>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</section>
|
||||
@@ -74,9 +74,7 @@
|
||||
</script>
|
||||
|
||||
<div id="location-selection">
|
||||
<p class="uppercase immich-form-label">{$t('place')}</p>
|
||||
|
||||
<div class="grid grid-auto-fit-40 gap-5 mt-1">
|
||||
<div class="grid grid-auto-fit-40 gap-5">
|
||||
<div class="w-full">
|
||||
<Combobox
|
||||
label={$t('country')}
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
|
||||
<div id="media-type-selection">
|
||||
<fieldset>
|
||||
<legend class="uppercase immich-form-label">{$t('media_type')}</legend>
|
||||
<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1">
|
||||
<div class="flex flex-wrap gap-x-5 gap-y-2">
|
||||
<RadioButton name="media-type" id="type-all" bind:group={filteredMedia} label={$t('all')} value={MediaType.All} />
|
||||
<RadioButton
|
||||
name="media-type"
|
||||
|
||||
@@ -62,8 +62,7 @@
|
||||
: filterPeople(people, name).slice(0, numberOfPeople)}
|
||||
|
||||
<div id="people-selection" class="max-h-60 -mb-4 overflow-y-auto immich-scrollbar">
|
||||
<div class="flex items-center w-full justify-between gap-6">
|
||||
<p class="uppercase immich-form-label py-3">{$t('people')}</p>
|
||||
<div class="flex items-center w-full justify-end gap-6">
|
||||
<SearchBar bind:name placeholder={$t('filter_people')} showLoadingSpinner={false} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,15 +19,13 @@
|
||||
</script>
|
||||
|
||||
<div class="grid grid-auto-fit-40 gap-5">
|
||||
<label class="immich-form-label" for="start-date">
|
||||
<div class="[&_label]:uppercase">
|
||||
<Combobox
|
||||
label={$t('rating')}
|
||||
placeholder={$t('search_rating')}
|
||||
{options}
|
||||
selectedOption={rating === undefined ? undefined : options[rating]}
|
||||
onSelect={(r) => (rating = r === undefined ? undefined : Number.parseInt(r.value))}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<div class="[&_label]:uppercase">
|
||||
<Combobox
|
||||
label=""
|
||||
placeholder={$t('search_rating')}
|
||||
{options}
|
||||
selectedOption={rating === undefined ? undefined : options[rating]}
|
||||
onSelect={(r) => (rating = r === undefined ? undefined : Number.parseInt(r.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,14 +41,14 @@
|
||||
</script>
|
||||
|
||||
{#if $preferences?.tags?.enabled}
|
||||
<div id="location-selection">
|
||||
<div id="tags-selection">
|
||||
<form autocomplete="off" id="create-tag-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="[&_label]:uppercase">
|
||||
<Combobox
|
||||
label=""
|
||||
disabled={selectedTags === null}
|
||||
onSelect={handleSelect}
|
||||
label={$t('tags')}
|
||||
defaultFirstOption
|
||||
options={allTags.map((tag) => ({ id: tag.id, label: tag.value, value: tag.id }))}
|
||||
bind:selectedOption
|
||||
|
||||
@@ -25,18 +25,29 @@
|
||||
} 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 SearchFilterGroup from '$lib/components/shared-components/search-bar/search-filter-group.svelte';
|
||||
import SearchFilterSection from '$lib/components/shared-components/search-bar/search-filter-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';
|
||||
import { AssetTypeEnum, AssetVisibility, type MetadataSearchDto, type SmartSearchDto } from '@immich/sdk';
|
||||
import { Button, HStack, Modal, ModalBody, ModalFooter } from '@immich/ui';
|
||||
import { mdiTune } from '@mdi/js';
|
||||
import {
|
||||
mdiAccountMultiple,
|
||||
mdiCalendarRange,
|
||||
mdiCamera,
|
||||
mdiCog,
|
||||
mdiImageMultiple,
|
||||
mdiMagnify,
|
||||
mdiMapMarker,
|
||||
mdiStar,
|
||||
mdiTune,
|
||||
} from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
|
||||
@@ -187,47 +198,54 @@
|
||||
<Modal icon={mdiTune} size="giant" title={$t('search_options')} {onClose}>
|
||||
<ModalBody>
|
||||
<form id={formId} autocomplete="off" {onsubmit} {onreset}>
|
||||
<div class="flex flex-col gap-4 pb-10" tabindex="-1">
|
||||
<!-- PEOPLE -->
|
||||
<SearchPeopleSection bind:selectedPeople={filter.personIds} />
|
||||
<div class="flex flex-col gap-6" tabindex="-1">
|
||||
<SearchFilterGroup title={$t('content')}>
|
||||
<SearchFilterSection title={$t('search_type')} icon={mdiMagnify}>
|
||||
<SearchTextSection bind:query={filter.query} bind:queryType={filter.queryType} />
|
||||
</SearchFilterSection>
|
||||
|
||||
<!-- TEXT -->
|
||||
<SearchTextSection bind:query={filter.query} bind:queryType={filter.queryType} />
|
||||
<SearchFilterSection title={$t('people')} icon={mdiAccountMultiple}>
|
||||
<SearchPeopleSection bind:selectedPeople={filter.personIds} />
|
||||
</SearchFilterSection>
|
||||
</SearchFilterGroup>
|
||||
|
||||
<!-- TAGS -->
|
||||
<SearchTagsSection bind:selectedTags={filter.tagIds} />
|
||||
<SearchFilterGroup title={$t('metadata')} columns={2}>
|
||||
<SearchFilterSection title={$t('place')} icon={mdiMapMarker}>
|
||||
<SearchLocationSection bind:filters={filter.location} />
|
||||
</SearchFilterSection>
|
||||
|
||||
<!-- LOCATION -->
|
||||
<SearchLocationSection bind:filters={filter.location} />
|
||||
<SearchFilterSection title={$t('camera')} icon={mdiCamera}>
|
||||
<SearchCameraSection bind:filters={filter.camera} />
|
||||
</SearchFilterSection>
|
||||
|
||||
<!-- CAMERA MODEL -->
|
||||
<SearchCameraSection bind:filters={filter.camera} />
|
||||
<SearchFilterSection title={$t('date_range')} icon={mdiCalendarRange}>
|
||||
<SearchDateSection bind:filters={filter.date} />
|
||||
</SearchFilterSection>
|
||||
|
||||
<!-- DATE RANGE -->
|
||||
<SearchDateSection bind:filters={filter.date} />
|
||||
{#if $preferences?.ratings.enabled}
|
||||
<SearchFilterSection title={$t('rating')} icon={mdiStar}>
|
||||
<SearchRatingsSection bind:rating={filter.rating} />
|
||||
</SearchFilterSection>
|
||||
{/if}
|
||||
</SearchFilterGroup>
|
||||
|
||||
<!-- RATING -->
|
||||
{#if $preferences?.ratings.enabled}
|
||||
<SearchRatingsSection bind:rating={filter.rating} />
|
||||
{/if}
|
||||
<SearchFilterGroup title={$t('options')} columns={2}>
|
||||
<SearchFilterSection title={$t('media_type')} icon={mdiImageMultiple}>
|
||||
<SearchMediaSection bind:filteredMedia={filter.mediaType} />
|
||||
</SearchFilterSection>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-x-5 gap-y-10">
|
||||
<!-- MEDIA TYPE -->
|
||||
<SearchMediaSection bind:filteredMedia={filter.mediaType} />
|
||||
|
||||
<!-- DISPLAY OPTIONS -->
|
||||
<SearchDisplaySection bind:filters={filter.display} />
|
||||
</div>
|
||||
<SearchFilterSection title={$t('display_options')} icon={mdiCog}>
|
||||
<SearchDisplaySection bind:filters={filter.display} />
|
||||
</SearchFilterSection>
|
||||
</SearchFilterGroup>
|
||||
</div>
|
||||
</form>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<HStack fullWidth>
|
||||
<Button shape="round" size="large" type="reset" color="secondary" fullWidth form={formId}
|
||||
>{$t('clear_all')}</Button
|
||||
>
|
||||
<Button shape="round" size="large" type="submit" fullWidth form={formId}>{$t('search')}</Button>
|
||||
<Button shape="round" type="reset" color="secondary" fullWidth form={formId}>{$t('clear_all')}</Button>
|
||||
<Button shape="round" type="submit" fullWidth form={formId}>{$t('search')}</Button>
|
||||
</HStack>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user