1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-24 08:52:28 +02:00

feat(web): coordinate input for asset location (#11291)

This commit is contained in:
Michel Heusschen 2024-07-23 14:01:10 +02:00 committed by GitHub
parent 8725656fd2
commit 7d3db11a5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 126 additions and 8 deletions

View File

@ -0,0 +1,50 @@
import NumberRangeInput from '$lib/components/shared-components/number-range-input.svelte';
import { act, render, type RenderResult } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
describe('NumberRangeInput component', () => {
const user = userEvent.setup();
let sut: RenderResult<NumberRangeInput>;
let input: HTMLInputElement;
beforeEach(() => {
sut = render(NumberRangeInput, { id: '', min: -90, max: 90, onInput: () => {} });
input = sut.getByRole('spinbutton') as HTMLInputElement;
});
it('updates value', async () => {
expect(input.value).toBe('');
await act(() => sut.component.$set({ value: 10 }));
expect(input.value).toBe('10');
});
it('restricts minimum value', async () => {
await user.type(input, '-91');
expect(input.value).toBe('-90');
});
it('restricts maximum value', async () => {
await user.type(input, '09990');
expect(input.value).toBe('90');
});
it('allows entering negative numbers', async () => {
await user.type(input, '-10');
expect(input.value).toBe('-10');
});
it('allows entering zero', async () => {
await user.type(input, '0');
expect(input.value).toBe('0');
});
it('allows entering decimal numbers', async () => {
await user.type(input, '-0.09001');
expect(input.value).toBe('-0.09001');
});
it('ignores text input', async () => {
await user.type(input, 'test');
expect(input.value).toBe('');
});
});

View File

@ -12,6 +12,7 @@
import SearchBar from '../elements/search-bar.svelte';
import { listNavigation } from '$lib/actions/list-navigation';
import { t } from 'svelte-i18n';
import CoordinatesInput from '$lib/components/shared-components/coordinates-input.svelte';
export let asset: AssetResponseDto | undefined = undefined;
@ -34,9 +35,9 @@
confirm: Point;
}>();
$: lat = asset?.exifInfo?.latitude || 0;
$: lng = asset?.exifInfo?.longitude || 0;
$: zoom = lat && lng ? 15 : 1;
$: lat = asset?.exifInfo?.latitude ?? undefined;
$: lng = asset?.exifInfo?.longitude ?? undefined;
$: zoom = lat !== undefined && lng !== undefined ? 15 : 1;
$: {
if (places) {
@ -148,7 +149,7 @@
{/if}
</div>
</div>
<label for="datetime">{$t('pick_a_location')}</label>
<span>{$t('pick_a_location')}</span>
<div class="h-[500px] min-h-[300px] w-full">
{#await import('../shared-components/map/map.svelte')}
{#await delay(timeToLoadTheMap) then}
@ -157,10 +158,9 @@
<LoadingSpinner />
</div>
{/await}
{:then component}
<svelte:component
this={component.default}
mapMarkers={lat && lng && asset
{:then { default: Map }}
<Map
mapMarkers={lat !== undefined && lng !== undefined && asset
? [
{
id: asset.id,
@ -181,5 +181,16 @@
/>
{/await}
</div>
<div class="grid sm:grid-cols-2 gap-4 text-sm text-left mt-4">
<CoordinatesInput
lat={point ? point.lat : lat}
lng={point ? point.lng : lng}
onUpdate={(lat, lng) => {
point = { lat, lng };
addClipMapMarker(lng, lat);
}}
/>
</div>
</div>
</ConfirmDialog>

View File

@ -0,0 +1,27 @@
<script lang="ts">
import NumberRangeInput from '$lib/components/shared-components/number-range-input.svelte';
import { generateId } from '$lib/utils/generate-id';
import { t } from 'svelte-i18n';
export let lat: number | null | undefined = undefined;
export let lng: number | null | undefined = undefined;
export let onUpdate: (lat: number, lng: number) => void;
const id = generateId();
const onInput = () => {
if (lat != null && lng != null) {
onUpdate(lat, lng);
}
};
</script>
<div>
<label class="immich-form-label" for="latitude-input-{id}">{$t('latitude')}</label>
<NumberRangeInput id="latitude-input-{id}" min={-90} max={90} {onInput} bind:value={lat} />
</div>
<div>
<label class="immich-form-label" for="longitude-input-{id}">{$t('longitude')}</label>
<NumberRangeInput id="longitude-input-{id}" min={-180} max={180} {onInput} bind:value={lng} />
</div>

View File

@ -0,0 +1,28 @@
<script lang="ts">
import { clamp } from 'lodash-es';
export let id: string;
export let min: number;
export let max: number;
export let step: number | string = 'any';
export let required = true;
export let value: number | null = null;
export let onInput: (value: number | null) => void;
</script>
<input
type="number"
class="immich-form-input w-full"
{id}
{min}
{max}
{step}
{required}
bind:value
on:input={() => {
if (value !== null && (value < min || value > max)) {
value = clamp(value, min, max);
}
onInput(value);
}}
/>

View File

@ -740,6 +740,7 @@
"language_setting_description": "Select your preferred language",
"last_seen": "Last seen",
"latest_version": "Latest Version",
"latitude": "Latitude",
"leave": "Leave",
"let_others_respond": "Let others respond",
"level": "Level",
@ -786,6 +787,7 @@
"login_has_been_disabled": "Login has been disabled.",
"logout_all_device_confirmation": "Are you sure you want to log out all devices?",
"logout_this_device_confirmation": "Are you sure you want to log out this device?",
"longitude": "Longitude",
"look": "Look",
"loop_videos": "Loop videos",
"loop_videos_description": "Enable to automatically loop a video in the detail viewer.",