mirror of
https://github.com/immich-app/immich.git
synced 2025-01-27 22:22:45 +01:00
feat: zooming and virtual keyboard working for iPadOS/Safari
This commit is contained in:
parent
fdd0729c4a
commit
cac29bac0d
1 changed files with 25 additions and 14 deletions
|
@ -21,7 +21,7 @@
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import { mdiMagnify, mdiUnfoldMoreHorizontal, mdiClose } from '@mdi/js';
|
import { mdiMagnify, mdiUnfoldMoreHorizontal, mdiClose } from '@mdi/js';
|
||||||
import { createEventDispatcher, onDestroy, onMount, tick } from 'svelte';
|
import { createEventDispatcher, onMount, tick } from 'svelte';
|
||||||
import type { FormEventHandler } from 'svelte/elements';
|
import type { FormEventHandler } from 'svelte/elements';
|
||||||
import { shortcuts } from '$lib/actions/shortcut';
|
import { shortcuts } from '$lib/actions/shortcut';
|
||||||
import { focusOutside } from '$lib/actions/focus-outside';
|
import { focusOutside } from '$lib/actions/focus-outside';
|
||||||
|
@ -52,6 +52,7 @@
|
||||||
let selectedIndex: number | undefined;
|
let selectedIndex: number | undefined;
|
||||||
let optionRefs: HTMLElement[] = [];
|
let optionRefs: HTMLElement[] = [];
|
||||||
let input: HTMLInputElement;
|
let input: HTMLInputElement;
|
||||||
|
let dropdown: HTMLUListElement;
|
||||||
let bounds: DOMRect | undefined;
|
let bounds: DOMRect | undefined;
|
||||||
let scrollableAncestor: Element | null;
|
let scrollableAncestor: Element | null;
|
||||||
let dropdownDirection: 'bottom' | 'top' = 'bottom';
|
let dropdownDirection: 'bottom' | 'top' = 'bottom';
|
||||||
|
@ -81,11 +82,15 @@
|
||||||
observer.observe(input);
|
observer.observe(input);
|
||||||
scrollableAncestor = input.closest('.overflow-y-auto, .overflow-y-scroll');
|
scrollableAncestor = input.closest('.overflow-y-auto, .overflow-y-scroll');
|
||||||
scrollableAncestor?.addEventListener('scroll', onPositionChange);
|
scrollableAncestor?.addEventListener('scroll', onPositionChange);
|
||||||
});
|
window.visualViewport?.addEventListener('resize', onPositionChange);
|
||||||
|
window.visualViewport?.addEventListener('scroll', onPositionChange);
|
||||||
|
|
||||||
onDestroy(() => {
|
return () => {
|
||||||
scrollableAncestor?.removeEventListener('scroll', onPositionChange);
|
observer.disconnect();
|
||||||
observer.disconnect();
|
scrollableAncestor?.removeEventListener('scroll', onPositionChange);
|
||||||
|
window.visualViewport?.removeEventListener('resize', onPositionChange);
|
||||||
|
window.visualViewport?.removeEventListener('scroll', onPositionChange);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
|
@ -155,21 +160,28 @@
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportHeight = window.innerHeight;
|
const vv = window.visualViewport;
|
||||||
|
const viewportHeight = vv?.height || 0;
|
||||||
|
const left = boundary.left + (vv?.offsetLeft || 0);
|
||||||
|
const offsetTop = vv?.offsetTop || 0;
|
||||||
|
|
||||||
if (dropdownDirection === 'top') {
|
if (dropdownDirection === 'top') {
|
||||||
|
const dropdownHeight = dropdown?.clientHeight || 0;
|
||||||
|
const availableHeight = boundary.top - dropdownOffset;
|
||||||
|
const adjustTop = Math.max(availableHeight - dropdownHeight, 0);
|
||||||
return {
|
return {
|
||||||
bottom: `${viewportHeight - boundary.top}px`,
|
top: `${dropdownOffset + offsetTop + adjustTop}px`,
|
||||||
left: `${boundary.left}px`,
|
left: `${left}px`,
|
||||||
width: `${boundary.width}px`,
|
width: `${boundary.width}px`,
|
||||||
maxHeight: maxHeight(boundary.top - dropdownOffset),
|
maxHeight: maxHeight(availableHeight),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const top = boundary.bottom + offsetTop;
|
||||||
const availableHeight = viewportHeight - boundary.bottom;
|
const availableHeight = viewportHeight - boundary.bottom;
|
||||||
return {
|
return {
|
||||||
top: `${boundary.bottom}px`,
|
top: `${top}px`,
|
||||||
left: `${boundary.left}px`,
|
left: `${left}px`,
|
||||||
width: `${boundary.width}px`,
|
width: `${boundary.width}px`,
|
||||||
maxHeight: maxHeight(availableHeight - dropdownOffset),
|
maxHeight: maxHeight(availableHeight - dropdownOffset),
|
||||||
};
|
};
|
||||||
|
@ -191,7 +203,7 @@
|
||||||
return 'bottom';
|
return 'bottom';
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportHeight = window.innerHeight;
|
const viewportHeight = window.visualViewport?.height || 0;
|
||||||
const heightBelow = viewportHeight - boundary.bottom;
|
const heightBelow = viewportHeight - boundary.bottom;
|
||||||
const heightAbove = boundary.top;
|
const heightAbove = boundary.top;
|
||||||
|
|
||||||
|
@ -201,7 +213,6 @@
|
||||||
const getInputPosition = () => input?.getBoundingClientRect();
|
const getInputPosition = () => input?.getBoundingClientRect();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:resize={onPositionChange} />
|
|
||||||
<label class="immich-form-label" class:sr-only={hideLabel} for={inputId}>{label}</label>
|
<label class="immich-form-label" class:sr-only={hideLabel} for={inputId}>{label}</label>
|
||||||
<div
|
<div
|
||||||
class="relative w-full dark:text-gray-300 text-gray-700 text-base"
|
class="relative w-full dark:text-gray-300 text-gray-700 text-base"
|
||||||
|
@ -308,11 +319,11 @@
|
||||||
class:shadow={dropdownDirection === 'bottom'}
|
class:shadow={dropdownDirection === 'bottom'}
|
||||||
class:border={isOpen}
|
class:border={isOpen}
|
||||||
style:top={position?.top}
|
style:top={position?.top}
|
||||||
style:bottom={position?.bottom}
|
|
||||||
style:left={position?.left}
|
style:left={position?.left}
|
||||||
style:width={position?.width}
|
style:width={position?.width}
|
||||||
style:max-height={position?.maxHeight}
|
style:max-height={position?.maxHeight}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
bind:this={dropdown}
|
||||||
>
|
>
|
||||||
{#if isOpen}
|
{#if isOpen}
|
||||||
{#if filteredOptions.length === 0}
|
{#if filteredOptions.length === 0}
|
||||||
|
|
Loading…
Reference in a new issue