mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:39:37 -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:
		
							parent
							
								
									a2934b8830
								
							
						
					
					
						commit
						878932f87e
					
				@ -319,35 +319,30 @@
 | 
				
			|||||||
<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}
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
 | 
					    <div class="px-4 sm:px-6 py-4 space-y-10 max-h-[calc(100dvh-12rem)] overflow-y-auto immich-scrollbar">
 | 
				
			||||||
      <!-- PEOPLE -->
 | 
					      <!-- PEOPLE -->
 | 
				
			||||||
    <div id="people-selection" class="my-4">
 | 
					      {#if suggestions.people.length > 0}
 | 
				
			||||||
      <div class="flex justify-between place-items-center gap-6">
 | 
					        <div id="people-selection" class="-mb-4">
 | 
				
			||||||
        <div class="flex-1">
 | 
					          <div class="flex items-center gap-6">
 | 
				
			||||||
            <p class="immich-form-label">PEOPLE</p>
 | 
					            <p class="immich-form-label">PEOPLE</p>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      {#if suggestions.people.length > 0}
 | 
					          <div class="flex -mx-1 max-h-64 gap-1 mt-2 flex-wrap overflow-y-auto immich-scrollbar">
 | 
				
			||||||
        <div class="flex gap-1 mt-4 flex-wrap max-h-[300px] overflow-y-auto immich-scrollbar transition-all">
 | 
					 | 
				
			||||||
            {#each peopleList as person (person.id)}
 | 
					            {#each peopleList as person (person.id)}
 | 
				
			||||||
              <button
 | 
					              <button
 | 
				
			||||||
                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="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,
 | 
					                  (p) => p.id === person.id,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                ? 'dark:border-slate-500 border-slate-300 bg-slate-200 dark:bg-slate-800 dark:text-white'
 | 
					                  ? 'dark:border-slate-500 border-slate-400 bg-slate-200 dark:bg-slate-800 dark:text-white'
 | 
				
			||||||
                  : ''}"
 | 
					                  : ''}"
 | 
				
			||||||
                on:click={() => handlePeopleSelection(person.id)}
 | 
					                on:click={() => handlePeopleSelection(person.id)}
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
@ -363,12 +358,12 @@
 | 
				
			|||||||
            {/each}
 | 
					            {/each}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          {#if showAllPeople || suggestions.people.length > peopleList.length}
 | 
				
			||||||
            <div class="flex justify-center mt-2">
 | 
					            <div class="flex justify-center mt-2">
 | 
				
			||||||
              <Button
 | 
					              <Button
 | 
				
			||||||
                shadow={false}
 | 
					                shadow={false}
 | 
				
			||||||
                color="text-primary"
 | 
					                color="text-primary"
 | 
				
			||||||
            type="button"
 | 
					                class="flex gap-2 place-items-center"
 | 
				
			||||||
            class="flex gap-2 place-items-center place-content-center"
 | 
					 | 
				
			||||||
                on:click={() => (showAllPeople = !showAllPeople)}
 | 
					                on:click={() => (showAllPeople = !showAllPeople)}
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
                {#if showAllPeople}
 | 
					                {#if showAllPeople}
 | 
				
			||||||
@ -382,27 +377,28 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          {/if}
 | 
					          {/if}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					      {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <hr class="border-slate-300 dark:border-slate-700" />
 | 
					 | 
				
			||||||
      <!-- CONTEXT -->
 | 
					      <!-- CONTEXT -->
 | 
				
			||||||
    <div class="my-4">
 | 
					      <div>
 | 
				
			||||||
      <label class="immich-form-label" for="context">CONTEXT</label>
 | 
					        <label class="immich-form-label" for="context">
 | 
				
			||||||
 | 
					          <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}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 | 
					        </label>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <hr class="border-slate-300 dark:border-slate-700" />
 | 
					 | 
				
			||||||
      <!-- LOCATION -->
 | 
					      <!-- LOCATION -->
 | 
				
			||||||
    <div id="location-selection" class="my-4">
 | 
					      <div id="location-selection">
 | 
				
			||||||
        <p class="immich-form-label">PLACE</p>
 | 
					        <p class="immich-form-label">PLACE</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="flex justify-between gap-5 mt-3">
 | 
					        <div class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5 mt-1">
 | 
				
			||||||
          <div class="w-full">
 | 
					          <div class="w-full">
 | 
				
			||||||
            <label class="text-sm text-black dark:text-white" for="search-place-country">Country</label>
 | 
					            <label class="text-sm text-black dark:text-white" for="search-place-country">Country</label>
 | 
				
			||||||
            <Combobox
 | 
					            <Combobox
 | 
				
			||||||
@ -442,12 +438,11 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <hr class="border-slate-300 dark:border-slate-700" />
 | 
					 | 
				
			||||||
      <!-- CAMERA MODEL -->
 | 
					      <!-- CAMERA MODEL -->
 | 
				
			||||||
    <div id="camera-selection" class="my-4">
 | 
					      <div id="camera-selection">
 | 
				
			||||||
        <p class="immich-form-label">CAMERA</p>
 | 
					        <p class="immich-form-label">CAMERA</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="flex justify-between gap-5 mt-3">
 | 
					        <div class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5 mt-1">
 | 
				
			||||||
          <div class="w-full">
 | 
					          <div class="w-full">
 | 
				
			||||||
            <label class="text-sm text-black dark:text-white" for="search-camera-make">Make</label>
 | 
					            <label class="text-sm text-black dark:text-white" for="search-camera-make">Make</label>
 | 
				
			||||||
            <Combobox
 | 
					            <Combobox
 | 
				
			||||||
@ -474,108 +469,105 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <hr class="border-slate-300 dark:border-slate-700" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <!-- DATE RANGE -->
 | 
					      <!-- DATE RANGE -->
 | 
				
			||||||
    <div id="date-range-selection" class="my-4 flex justify-between gap-5">
 | 
					      <div id="date-range-selection" class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5">
 | 
				
			||||||
      <div class="mb-3 flex-1 mt">
 | 
					        <label class="immich-form-label" for="start-date">
 | 
				
			||||||
        <label class="immich-form-label" for="start-date">START DATE</label>
 | 
					          <span>START DATE</span>
 | 
				
			||||||
          <input
 | 
					          <input
 | 
				
			||||||
          class="immich-form-input w-full mt-3 hover:cursor-pointer"
 | 
					            class="immich-form-input w-full mt-1 hover:cursor-pointer"
 | 
				
			||||||
            type="date"
 | 
					            type="date"
 | 
				
			||||||
            id="start-date"
 | 
					            id="start-date"
 | 
				
			||||||
            name="start-date"
 | 
					            name="start-date"
 | 
				
			||||||
 | 
					            max={filter.date.takenBefore}
 | 
				
			||||||
            bind:value={filter.date.takenAfter}
 | 
					            bind:value={filter.date.takenAfter}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
      </div>
 | 
					        </label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="mb-3 flex-1">
 | 
					        <label class="immich-form-label" for="end-date">
 | 
				
			||||||
        <label class="immich-form-label" for="end-date">END DATE</label>
 | 
					          <span>END DATE</span>
 | 
				
			||||||
          <input
 | 
					          <input
 | 
				
			||||||
          class="immich-form-input w-full mt-3 hover:cursor-pointer"
 | 
					            class="immich-form-input w-full mt-1 hover:cursor-pointer"
 | 
				
			||||||
            type="date"
 | 
					            type="date"
 | 
				
			||||||
            id="end-date"
 | 
					            id="end-date"
 | 
				
			||||||
            name="end-date"
 | 
					            name="end-date"
 | 
				
			||||||
            placeholder=""
 | 
					            placeholder=""
 | 
				
			||||||
 | 
					            min={filter.date.takenAfter}
 | 
				
			||||||
            bind:value={filter.date.takenBefore}
 | 
					            bind:value={filter.date.takenBefore}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
      </div>
 | 
					        </label>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <hr class="border-slate-300 dark:border-slate-700" />
 | 
					      <div class="grid md:grid-cols-2 gap-x-5 gap-y-8">
 | 
				
			||||||
    <div class="py-3 grid grid-cols-[repeat(auto-fill,minmax(21rem,1fr))] gap-x-16 gap-y-8">
 | 
					 | 
				
			||||||
        <!-- MEDIA TYPE -->
 | 
					        <!-- MEDIA TYPE -->
 | 
				
			||||||
        <div id="media-type-selection">
 | 
					        <div id="media-type-selection">
 | 
				
			||||||
          <p class="immich-form-label">MEDIA TYPE</p>
 | 
					          <p class="immich-form-label">MEDIA TYPE</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="flex gap-5 mt-3">
 | 
					          <div class="flex gap-5 mt-1 text-base">
 | 
				
			||||||
          <label
 | 
					            <label for="type-all" class="flex items-center gap-1">
 | 
				
			||||||
            for="type-all"
 | 
					 | 
				
			||||||
            class="text-base flex place-items-center gap-1 hover:cursor-pointer text-black dark:text-white"
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
              <input
 | 
					              <input
 | 
				
			||||||
                bind:group={filter.mediaType}
 | 
					                bind:group={filter.mediaType}
 | 
				
			||||||
                value={MediaType.All}
 | 
					                value={MediaType.All}
 | 
				
			||||||
                type="radio"
 | 
					                type="radio"
 | 
				
			||||||
                name="radio-type"
 | 
					                name="radio-type"
 | 
				
			||||||
                id="type-all"
 | 
					                id="type-all"
 | 
				
			||||||
            />All</label
 | 
					                class="size-4"
 | 
				
			||||||
          >
 | 
					              />
 | 
				
			||||||
 | 
					              <span class="pt-0.5">All</span>
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <label
 | 
					            <label for="type-image" class="flex items-center gap-1">
 | 
				
			||||||
            for="type-image"
 | 
					 | 
				
			||||||
            class="text-base flex place-items-center gap-1 hover:cursor-pointer text-black dark:text-white"
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
              <input
 | 
					              <input
 | 
				
			||||||
                bind:group={filter.mediaType}
 | 
					                bind:group={filter.mediaType}
 | 
				
			||||||
                value={MediaType.Image}
 | 
					                value={MediaType.Image}
 | 
				
			||||||
                type="radio"
 | 
					                type="radio"
 | 
				
			||||||
                name="media-type"
 | 
					                name="media-type"
 | 
				
			||||||
                id="type-image"
 | 
					                id="type-image"
 | 
				
			||||||
            />Image</label
 | 
					                class="size-4"
 | 
				
			||||||
          >
 | 
					              />
 | 
				
			||||||
 | 
					              <span class="pt-0.5">Image</span>
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <label
 | 
					            <label for="type-video" class="flex items-center gap-1">
 | 
				
			||||||
            for="type-video"
 | 
					 | 
				
			||||||
            class="text-base flex place-items-center gap-1 hover:cursor-pointer text-black dark:text-white"
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
              <input
 | 
					              <input
 | 
				
			||||||
                bind:group={filter.mediaType}
 | 
					                bind:group={filter.mediaType}
 | 
				
			||||||
                value={MediaType.Video}
 | 
					                value={MediaType.Video}
 | 
				
			||||||
                type="radio"
 | 
					                type="radio"
 | 
				
			||||||
                name="radio-type"
 | 
					                name="radio-type"
 | 
				
			||||||
                id="type-video"
 | 
					                id="type-video"
 | 
				
			||||||
            />Video</label
 | 
					                class="size-4"
 | 
				
			||||||
          >
 | 
					              />
 | 
				
			||||||
 | 
					              <span class="pt-0.5">Video</span>
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- DISPLAY OPTIONS -->
 | 
					        <!-- DISPLAY OPTIONS -->
 | 
				
			||||||
      <div id="display-options-selection">
 | 
					        <div id="display-options-selection" class="text-sm">
 | 
				
			||||||
          <p class="immich-form-label">DISPLAY OPTIONS</p>
 | 
					          <p class="immich-form-label">DISPLAY OPTIONS</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="flex gap-5 mt-3">
 | 
					          <div class="flex flex-wrap gap-x-5 gap-y-2 mt-1">
 | 
				
			||||||
          <label class="flex items-center mb-2">
 | 
					            <label class="flex items-center gap-2">
 | 
				
			||||||
            <input type="checkbox" class="form-checkbox h-5 w-5 color" bind:checked={filter.isNotInAlbum} />
 | 
					              <input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filter.isNotInAlbum} />
 | 
				
			||||||
            <span class="ml-2 text-sm text-black dark:text-white pt-1">Not in any album</span>
 | 
					              <span class="pt-1">Not in any album</span>
 | 
				
			||||||
            </label>
 | 
					            </label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <label class="flex items-center mb-2">
 | 
					            <label class="flex items-center gap-2">
 | 
				
			||||||
            <input type="checkbox" class="form-checkbox h-5 w-5 color" bind:checked={filter.isArchive} />
 | 
					              <input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filter.isArchive} />
 | 
				
			||||||
            <span class="ml-2 text-sm text-black dark:text-white pt-1">Archive</span>
 | 
					              <span class="pt-1">Archive</span>
 | 
				
			||||||
            </label>
 | 
					            </label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <label class="flex items-center mb-2">
 | 
					            <label class="flex items-center gap-2">
 | 
				
			||||||
            <input type="checkbox" class="form-checkbox h-5 w-5 color" bind:checked={filter.isFavorite} />
 | 
					              <input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filter.isFavorite} />
 | 
				
			||||||
            <span class="ml-2 text-sm text-black dark:text-white pt-1">Favorite</span>
 | 
					              <span class="pt-1">Favorite</span>
 | 
				
			||||||
            </label>
 | 
					            </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>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user