0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-02-11 01:18:24 -05:00

feat(web): improve search filter design (#7367)

* feat(web): improve search filter design

* restore position of people toggle button

* consistent colors for media type inputs
This commit is contained in:
Michel Heusschen 2024-02-24 04:32:56 +01:00 committed by GitHub
parent a2934b8830
commit 878932f87e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -319,263 +319,255 @@
<div <div
bind:clientWidth={filterBoxWidth} bind:clientWidth={filterBoxWidth}
transition:fly={{ y: 25, duration: 250 }} transition:fly={{ y: 25, duration: 250 }}
class="absolute w-full rounded-b-3xl border border-gray-200 bg-white shadow-2xl transition-all dark:border-gray-800 dark:bg-immich-dark-gray dark:text-gray-300 px-6 pt-6 overflow-y-auto max-h-[90vh] immich-scrollbar" class="absolute w-full rounded-b-3xl border border-t-0 border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-immich-dark-gray dark:text-gray-300"
> >
<p class="text-xs py-2">FILTERS</p>
<hr class="border-slate-300 dark:border-slate-700 py-2" />
<form <form
id="search-filter-form relative" id="search-filter-form"
autocomplete="off" autocomplete="off"
class="hover:cursor-auto"
on:submit|preventDefault={search} on:submit|preventDefault={search}
on:reset|preventDefault={resetForm} on:reset|preventDefault={resetForm}
> >
<!-- PEOPLE --> <div class="px-4 sm:px-6 py-4 space-y-10 max-h-[calc(100dvh-12rem)] overflow-y-auto immich-scrollbar">
<div id="people-selection" class="my-4"> <!-- PEOPLE -->
<div class="flex justify-between place-items-center gap-6">
<div class="flex-1">
<p class="immich-form-label">PEOPLE</p>
</div>
</div>
{#if suggestions.people.length > 0} {#if suggestions.people.length > 0}
<div class="flex gap-1 mt-4 flex-wrap max-h-[300px] overflow-y-auto immich-scrollbar transition-all"> <div id="people-selection" class="-mb-4">
{#each peopleList as person (person.id)} <div class="flex items-center gap-6">
<button <p class="immich-form-label">PEOPLE</p>
type="button" </div>
class="w-20 text-center rounded-3xl border-2 border-transparent hover:bg-immich-gray dark:hover:bg-immich-dark-primary/20 p-2 transition-all {filter.people.some(
(p) => p.id === person.id,
)
? 'dark:border-slate-500 border-slate-300 bg-slate-200 dark:bg-slate-800 dark:text-white'
: ''}"
on:click={() => handlePeopleSelection(person.id)}
>
<ImageThumbnail
circle
shadow
url={getPeopleThumbnailUrl(person.id)}
altText={person.name}
widthStyle="100%"
/>
<p class="mt-2 line-clamp-2 text-sm font-medium dark:text-white">{person.name}</p>
</button>
{/each}
</div>
<div class="flex justify-center mt-2"> <div class="flex -mx-1 max-h-64 gap-1 mt-2 flex-wrap overflow-y-auto immich-scrollbar">
<Button {#each peopleList as person (person.id)}
shadow={false} <button
color="text-primary" type="button"
type="button" class="w-20 text-center rounded-3xl border-2 border-transparent hover:bg-immich-gray dark:hover:bg-immich-dark-primary/20 p-2 transition-all {filter.people.some(
class="flex gap-2 place-items-center place-content-center" (p) => p.id === person.id,
on:click={() => (showAllPeople = !showAllPeople)} )
> ? 'dark:border-slate-500 border-slate-400 bg-slate-200 dark:bg-slate-800 dark:text-white'
{#if showAllPeople} : ''}"
<span><Icon path={mdiClose} /></span> on:click={() => handlePeopleSelection(person.id)}
Collapse >
{:else} <ImageThumbnail
<span><Icon path={mdiArrowRight} /></span> circle
See all people shadow
{/if} url={getPeopleThumbnailUrl(person.id)}
</Button> altText={person.name}
widthStyle="100%"
/>
<p class="mt-2 line-clamp-2 text-sm font-medium dark:text-white">{person.name}</p>
</button>
{/each}
</div>
{#if showAllPeople || suggestions.people.length > peopleList.length}
<div class="flex justify-center mt-2">
<Button
shadow={false}
color="text-primary"
class="flex gap-2 place-items-center"
on:click={() => (showAllPeople = !showAllPeople)}
>
{#if showAllPeople}
<span><Icon path={mdiClose} /></span>
Collapse
{:else}
<span><Icon path={mdiArrowRight} /></span>
See all people
{/if}
</Button>
</div>
{/if}
</div> </div>
{/if} {/if}
</div>
<hr class="border-slate-300 dark:border-slate-700" /> <!-- CONTEXT -->
<!-- CONTEXT --> <div>
<div class="my-4"> <label class="immich-form-label" for="context">
<label class="immich-form-label" for="context">CONTEXT</label> <span>CONTEXT</span>
<input <input
class="immich-form-input hover:cursor-text w-full mt-3" class="immich-form-input hover:cursor-text w-full mt-1"
type="text" type="text"
id="context" id="context"
name="context" name="context"
placeholder="Sunrise on the beach" placeholder="Sunrise on the beach"
bind:value={filter.context} bind:value={filter.context}
/>
</div>
<hr class="border-slate-300 dark:border-slate-700" />
<!-- LOCATION -->
<div id="location-selection" class="my-4">
<p class="immich-form-label">PLACE</p>
<div class="flex justify-between gap-5 mt-3">
<div class="w-full">
<label class="text-sm text-black dark:text-white" for="search-place-country">Country</label>
<Combobox
id="search-place-country"
options={suggestions.country}
bind:selectedOption={filter.location.country}
placeholder="Search country..."
on:click={() => updateSuggestion(SearchSuggestionType.Country, {})}
/> />
</div> </label>
<div class="w-full">
<label class="text-sm text-black dark:text-white" for="search-place-state">State</label>
<Combobox
id="search-place-state"
options={suggestions.state}
bind:selectedOption={filter.location.state}
placeholder="Search state..."
on:click={() => updateSuggestion(SearchSuggestionType.State, { country: filter.location.country?.value })}
/>
</div>
<div class="w-full">
<label class="text-sm text-black dark:text-white" for="search-place-city">City</label>
<Combobox
id="search-place-city"
options={suggestions.city}
bind:selectedOption={filter.location.city}
placeholder="Search city..."
on:click={() =>
updateSuggestion(SearchSuggestionType.City, {
country: filter.location.country?.value,
state: filter.location.state?.value,
})}
/>
</div>
</div>
</div>
<hr class="border-slate-300 dark:border-slate-700" />
<!-- CAMERA MODEL -->
<div id="camera-selection" class="my-4">
<p class="immich-form-label">CAMERA</p>
<div class="flex justify-between gap-5 mt-3">
<div class="w-full">
<label class="text-sm text-black dark:text-white" for="search-camera-make">Make</label>
<Combobox
id="search-camera-make"
options={suggestions.make}
bind:selectedOption={filter.camera.make}
placeholder="Search camera make..."
on:click={() =>
updateSuggestion(SearchSuggestionType.CameraMake, { cameraModel: filter.camera.model?.value })}
/>
</div>
<div class="w-full">
<label class="text-sm text-black dark:text-white" for="search-camera-model">Model</label>
<Combobox
id="search-camera-model"
options={suggestions.model}
bind:selectedOption={filter.camera.model}
placeholder="Search camera model..."
on:click={() =>
updateSuggestion(SearchSuggestionType.CameraModel, { cameraMake: filter.camera.make?.value })}
/>
</div>
</div>
</div>
<hr class="border-slate-300 dark:border-slate-700" />
<!-- DATE RANGE -->
<div id="date-range-selection" class="my-4 flex justify-between gap-5">
<div class="mb-3 flex-1 mt">
<label class="immich-form-label" for="start-date">START DATE</label>
<input
class="immich-form-input w-full mt-3 hover:cursor-pointer"
type="date"
id="start-date"
name="start-date"
bind:value={filter.date.takenAfter}
/>
</div> </div>
<div class="mb-3 flex-1"> <!-- LOCATION -->
<label class="immich-form-label" for="end-date">END DATE</label> <div id="location-selection">
<input <p class="immich-form-label">PLACE</p>
class="immich-form-input w-full mt-3 hover:cursor-pointer"
type="date"
id="end-date"
name="end-date"
placeholder=""
bind:value={filter.date.takenBefore}
/>
</div>
</div>
<hr class="border-slate-300 dark:border-slate-700" /> <div class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5 mt-1">
<div class="py-3 grid grid-cols-[repeat(auto-fill,minmax(21rem,1fr))] gap-x-16 gap-y-8"> <div class="w-full">
<!-- MEDIA TYPE --> <label class="text-sm text-black dark:text-white" for="search-place-country">Country</label>
<div id="media-type-selection"> <Combobox
<p class="immich-form-label">MEDIA TYPE</p> id="search-place-country"
options={suggestions.country}
bind:selectedOption={filter.location.country}
placeholder="Search country..."
on:click={() => updateSuggestion(SearchSuggestionType.Country, {})}
/>
</div>
<div class="flex gap-5 mt-3"> <div class="w-full">
<label <label class="text-sm text-black dark:text-white" for="search-place-state">State</label>
for="type-all" <Combobox
class="text-base flex place-items-center gap-1 hover:cursor-pointer text-black dark:text-white" id="search-place-state"
> options={suggestions.state}
<input bind:selectedOption={filter.location.state}
bind:group={filter.mediaType} placeholder="Search state..."
value={MediaType.All} on:click={() => updateSuggestion(SearchSuggestionType.State, { country: filter.location.country?.value })}
type="radio" />
name="radio-type" </div>
id="type-all"
/>All</label
>
<label <div class="w-full">
for="type-image" <label class="text-sm text-black dark:text-white" for="search-place-city">City</label>
class="text-base flex place-items-center gap-1 hover:cursor-pointer text-black dark:text-white" <Combobox
> id="search-place-city"
<input options={suggestions.city}
bind:group={filter.mediaType} bind:selectedOption={filter.location.city}
value={MediaType.Image} placeholder="Search city..."
type="radio" on:click={() =>
name="media-type" updateSuggestion(SearchSuggestionType.City, {
id="type-image" country: filter.location.country?.value,
/>Image</label state: filter.location.state?.value,
> })}
/>
<label </div>
for="type-video"
class="text-base flex place-items-center gap-1 hover:cursor-pointer text-black dark:text-white"
>
<input
bind:group={filter.mediaType}
value={MediaType.Video}
type="radio"
name="radio-type"
id="type-video"
/>Video</label
>
</div> </div>
</div> </div>
<!-- DISPLAY OPTIONS --> <!-- CAMERA MODEL -->
<div id="display-options-selection"> <div id="camera-selection">
<p class="immich-form-label">DISPLAY OPTIONS</p> <p class="immich-form-label">CAMERA</p>
<div class="flex gap-5 mt-3"> <div class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5 mt-1">
<label class="flex items-center mb-2"> <div class="w-full">
<input type="checkbox" class="form-checkbox h-5 w-5 color" bind:checked={filter.isNotInAlbum} /> <label class="text-sm text-black dark:text-white" for="search-camera-make">Make</label>
<span class="ml-2 text-sm text-black dark:text-white pt-1">Not in any album</span> <Combobox
</label> id="search-camera-make"
options={suggestions.make}
bind:selectedOption={filter.camera.make}
placeholder="Search camera make..."
on:click={() =>
updateSuggestion(SearchSuggestionType.CameraMake, { cameraModel: filter.camera.model?.value })}
/>
</div>
<label class="flex items-center mb-2"> <div class="w-full">
<input type="checkbox" class="form-checkbox h-5 w-5 color" bind:checked={filter.isArchive} /> <label class="text-sm text-black dark:text-white" for="search-camera-model">Model</label>
<span class="ml-2 text-sm text-black dark:text-white pt-1">Archive</span> <Combobox
</label> id="search-camera-model"
options={suggestions.model}
bind:selectedOption={filter.camera.model}
placeholder="Search camera model..."
on:click={() =>
updateSuggestion(SearchSuggestionType.CameraModel, { cameraMake: filter.camera.make?.value })}
/>
</div>
</div>
</div>
<label class="flex items-center mb-2"> <!-- DATE RANGE -->
<input type="checkbox" class="form-checkbox h-5 w-5 color" bind:checked={filter.isFavorite} /> <div id="date-range-selection" class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5">
<span class="ml-2 text-sm text-black dark:text-white pt-1">Favorite</span> <label class="immich-form-label" for="start-date">
</label> <span>START DATE</span>
<input
class="immich-form-input w-full mt-1 hover:cursor-pointer"
type="date"
id="start-date"
name="start-date"
max={filter.date.takenBefore}
bind:value={filter.date.takenAfter}
/>
</label>
<label class="immich-form-label" for="end-date">
<span>END DATE</span>
<input
class="immich-form-input w-full mt-1 hover:cursor-pointer"
type="date"
id="end-date"
name="end-date"
placeholder=""
min={filter.date.takenAfter}
bind:value={filter.date.takenBefore}
/>
</label>
</div>
<div class="grid md:grid-cols-2 gap-x-5 gap-y-8">
<!-- MEDIA TYPE -->
<div id="media-type-selection">
<p class="immich-form-label">MEDIA TYPE</p>
<div class="flex gap-5 mt-1 text-base">
<label for="type-all" class="flex items-center gap-1">
<input
bind:group={filter.mediaType}
value={MediaType.All}
type="radio"
name="radio-type"
id="type-all"
class="size-4"
/>
<span class="pt-0.5">All</span>
</label>
<label for="type-image" class="flex items-center gap-1">
<input
bind:group={filter.mediaType}
value={MediaType.Image}
type="radio"
name="media-type"
id="type-image"
class="size-4"
/>
<span class="pt-0.5">Image</span>
</label>
<label for="type-video" class="flex items-center gap-1">
<input
bind:group={filter.mediaType}
value={MediaType.Video}
type="radio"
name="radio-type"
id="type-video"
class="size-4"
/>
<span class="pt-0.5">Video</span>
</label>
</div>
</div>
<!-- DISPLAY OPTIONS -->
<div id="display-options-selection" class="text-sm">
<p class="immich-form-label">DISPLAY OPTIONS</p>
<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1">
<label class="flex items-center gap-2">
<input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filter.isNotInAlbum} />
<span class="pt-1">Not in any album</span>
</label>
<label class="flex items-center gap-2">
<input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filter.isArchive} />
<span class="pt-1">Archive</span>
</label>
<label class="flex items-center gap-2">
<input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filter.isFavorite} />
<span class="pt-1">Favorite</span>
</label>
</div>
</div> </div>
</div> </div>
</div> </div>
<div <div
id="button-row" id="button-row"
class="flex justify-end gap-4 py-4 sticky bottom-0 dark:border-gray-800 dark:bg-immich-dark-gray" class="flex justify-end gap-4 border-t dark:border-gray-800 dark:bg-immich-dark-gray px-4 sm:py-6 py-4 mt-2 rounded-b-3xl"
> >
<Button type="reset" color="gray">CLEAR ALL</Button> <Button type="reset" color="gray">CLEAR ALL</Button>
<Button type="submit">SEARCH</Button> <Button type="submit">SEARCH</Button>