mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	fix(web): prevent thumbhashes from covering search bar (#20720)
The thumbhash had a z-index setting which meant it would cover the search bar, and would always cause weird animations when scrolling up in search results. This is fixable by removing the z-index and moving it in front the other elements to get a naturally higher higher z-index preference.
This commit is contained in:
		
							parent
							
								
									f1c494ef97
								
							
						
					
					
						commit
						f36efd128b
					
				@ -1,22 +0,0 @@
 | 
			
		||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
 | 
			
		||||
import { render } from '@testing-library/svelte';
 | 
			
		||||
 | 
			
		||||
describe('ImageThumbnail component', () => {
 | 
			
		||||
  beforeAll(() => {
 | 
			
		||||
    Element.prototype.animate = vi.fn().mockImplementation(() => ({
 | 
			
		||||
      cancel: () => {},
 | 
			
		||||
    }));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('shows thumbhash while image is loading', () => {
 | 
			
		||||
    const sut = render(ImageThumbnail, {
 | 
			
		||||
      url: 'http://localhost/img.png',
 | 
			
		||||
      altText: 'test',
 | 
			
		||||
      base64ThumbHash: '1QcSHQRnh493V4dIh4eXh1h4kJUI',
 | 
			
		||||
      widthStyle: '250px',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const thumbhash = sut.getByTestId('thumbhash');
 | 
			
		||||
    expect(thumbhash).not.toBeFalsy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -45,4 +45,15 @@ describe('Thumbnail component', () => {
 | 
			
		||||
    const tabbables = getTabbable(container!);
 | 
			
		||||
    expect(tabbables.length).toBe(0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('shows thumbhash while image is loading', () => {
 | 
			
		||||
    const asset = assetFactory.build({ originalPath: 'image.jpg', originalMimeType: 'image/jpeg' });
 | 
			
		||||
    const sut = render(Thumbnail, {
 | 
			
		||||
      asset,
 | 
			
		||||
      selected: true,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const thumbhash = sut.getByTestId('thumbhash');
 | 
			
		||||
    expect(thumbhash).not.toBeFalsy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,10 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import { thumbhash } from '$lib/actions/thumbhash';
 | 
			
		||||
  import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
 | 
			
		||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
			
		||||
  import { cancelImageUrl } from '$lib/utils/sw-messaging';
 | 
			
		||||
  import { TUNABLES } from '$lib/utils/tunables';
 | 
			
		||||
  import { mdiEyeOffOutline } from '@mdi/js';
 | 
			
		||||
  import type { ActionReturn } from 'svelte/action';
 | 
			
		||||
  import type { ClassValue } from 'svelte/elements';
 | 
			
		||||
  import { fade } from 'svelte/transition';
 | 
			
		||||
 | 
			
		||||
  interface Props {
 | 
			
		||||
    url: string;
 | 
			
		||||
@ -15,7 +12,6 @@
 | 
			
		||||
    title?: string | null;
 | 
			
		||||
    heightStyle?: string | undefined;
 | 
			
		||||
    widthStyle: string;
 | 
			
		||||
    base64ThumbHash?: string | null;
 | 
			
		||||
    curve?: boolean;
 | 
			
		||||
    shadow?: boolean;
 | 
			
		||||
    circle?: boolean;
 | 
			
		||||
@ -33,7 +29,6 @@
 | 
			
		||||
    title = null,
 | 
			
		||||
    heightStyle = undefined,
 | 
			
		||||
    widthStyle,
 | 
			
		||||
    base64ThumbHash = null,
 | 
			
		||||
    curve = false,
 | 
			
		||||
    shadow = false,
 | 
			
		||||
    circle = false,
 | 
			
		||||
@ -45,10 +40,6 @@
 | 
			
		||||
    brokenAssetClass = '',
 | 
			
		||||
  }: Props = $props();
 | 
			
		||||
 | 
			
		||||
  let {
 | 
			
		||||
    IMAGE_THUMBNAIL: { THUMBHASH_FADE_DURATION },
 | 
			
		||||
  } = TUNABLES;
 | 
			
		||||
 | 
			
		||||
  let loaded = $state(false);
 | 
			
		||||
  let errored = $state(false);
 | 
			
		||||
 | 
			
		||||
@ -100,7 +91,6 @@
 | 
			
		||||
    alt={loaded || errored ? altText : ''}
 | 
			
		||||
    {title}
 | 
			
		||||
    class={['object-cover', optionalClasses, imageClass]}
 | 
			
		||||
    class:opacity-0={!thumbhash && !loaded}
 | 
			
		||||
    draggable="false"
 | 
			
		||||
  />
 | 
			
		||||
{/if}
 | 
			
		||||
@ -110,19 +100,3 @@
 | 
			
		||||
    <Icon {title} path={mdiEyeOffOutline} size="2em" class={hiddenIconClass} />
 | 
			
		||||
  </div>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
{#if base64ThumbHash && (!loaded || errored)}
 | 
			
		||||
  <canvas
 | 
			
		||||
    use:thumbhash={{ base64ThumbHash }}
 | 
			
		||||
    data-testid="thumbhash"
 | 
			
		||||
    style:width={widthStyle}
 | 
			
		||||
    style:height={heightStyle}
 | 
			
		||||
    {title}
 | 
			
		||||
    class="absolute top-0 object-cover"
 | 
			
		||||
    class:rounded-xl={curve}
 | 
			
		||||
    class:shadow-lg={shadow}
 | 
			
		||||
    class:rounded-full={circle}
 | 
			
		||||
    draggable="false"
 | 
			
		||||
    out:fade={{ duration: THUMBHASH_FADE_DURATION }}
 | 
			
		||||
  ></canvas>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
@ -230,15 +230,6 @@
 | 
			
		||||
    ]}
 | 
			
		||||
    data-outline
 | 
			
		||||
  ></div>
 | 
			
		||||
  {#if (!loaded || thumbError) && asset.thumbhash}
 | 
			
		||||
    <canvas
 | 
			
		||||
      use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
 | 
			
		||||
      class="absolute object-cover z-1"
 | 
			
		||||
      style:width="{width}px"
 | 
			
		||||
      style:height="{height}px"
 | 
			
		||||
      out:fade={{ duration: THUMBHASH_FADE_DURATION }}
 | 
			
		||||
    ></canvas>
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  <div
 | 
			
		||||
    class={['group absolute -top-[0px] -bottom-[0px]', { 'cursor-not-allowed': disabled, 'cursor-pointer': !disabled }]}
 | 
			
		||||
@ -352,7 +343,21 @@
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      {/if}
 | 
			
		||||
 | 
			
		||||
      {#if (!loaded || thumbError) && asset.thumbhash}
 | 
			
		||||
        <canvas
 | 
			
		||||
          use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
 | 
			
		||||
          data-testid="thumbhash"
 | 
			
		||||
          class="absolute top-0 object-cover"
 | 
			
		||||
          style:width="{width}px"
 | 
			
		||||
          style:height="{height}px"
 | 
			
		||||
          class:rounded-xl={selected}
 | 
			
		||||
          draggable="false"
 | 
			
		||||
          out:fade={{ duration: THUMBHASH_FADE_DURATION }}
 | 
			
		||||
        ></canvas>
 | 
			
		||||
      {/if}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    {#if selectionCandidate}
 | 
			
		||||
      <div
 | 
			
		||||
        class="absolute top-0 h-full w-full bg-immich-primary opacity-40"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user