mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
chore(web): migration svelte 5 syntax (#13883)
This commit is contained in:
parent
9203a61709
commit
0b3742cf13
310 changed files with 6435 additions and 4176 deletions
|
|
@ -1,4 +1,4 @@
|
|||
<script lang="ts" context="module">
|
||||
<script lang="ts" module>
|
||||
void maplibregl.setRTLTextPlugin(mapboxRtlUrl, true);
|
||||
</script>
|
||||
|
||||
|
|
@ -6,12 +6,13 @@
|
|||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { Theme } from '$lib/constants';
|
||||
import { colorTheme, mapSettings } from '$lib/stores/preferences.store';
|
||||
import { serverConfig } from '$lib/stores/server-config.store';
|
||||
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||
import { getServerConfig, type MapMarkerResponseDto } from '@immich/sdk';
|
||||
import { type MapMarkerResponseDto } from '@immich/sdk';
|
||||
import mapboxRtlUrl from '@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js?url';
|
||||
import { mdiCog, mdiMap, mdiMapMarker } from '@mdi/js';
|
||||
import type { Feature, GeoJsonProperties, Geometry, Point } from 'geojson';
|
||||
import type { GeoJSONSource, LngLatLike, StyleSpecification } from 'maplibre-gl';
|
||||
import type { GeoJSONSource, LngLatLike } from 'maplibre-gl';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { t } from 'svelte-i18n';
|
||||
import {
|
||||
|
|
@ -30,14 +31,43 @@
|
|||
type Map,
|
||||
} from 'svelte-maplibre';
|
||||
|
||||
export let mapMarkers: MapMarkerResponseDto[];
|
||||
export let showSettingsModal: boolean | undefined = undefined;
|
||||
export let zoom: number | undefined = undefined;
|
||||
export let center: LngLatLike | undefined = undefined;
|
||||
export let hash = false;
|
||||
export let simplified = false;
|
||||
export let clickable = false;
|
||||
export let useLocationPin = false;
|
||||
interface Props {
|
||||
mapMarkers: MapMarkerResponseDto[];
|
||||
showSettingsModal?: boolean | undefined;
|
||||
zoom?: number | undefined;
|
||||
center?: LngLatLike | undefined;
|
||||
hash?: boolean;
|
||||
simplified?: boolean;
|
||||
clickable?: boolean;
|
||||
useLocationPin?: boolean;
|
||||
onOpenInMapView?: (() => Promise<void> | void) | undefined;
|
||||
onSelect?: (assetIds: string[]) => void;
|
||||
onClickPoint?: ({ lat, lng }: { lat: number; lng: number }) => void;
|
||||
popup?: import('svelte').Snippet<[{ marker: MapMarkerResponseDto }]>;
|
||||
}
|
||||
|
||||
let {
|
||||
mapMarkers = $bindable(),
|
||||
showSettingsModal = $bindable(undefined),
|
||||
zoom = undefined,
|
||||
center = $bindable(undefined),
|
||||
hash = false,
|
||||
simplified = false,
|
||||
clickable = false,
|
||||
useLocationPin = false,
|
||||
onOpenInMapView = undefined,
|
||||
onSelect = () => {},
|
||||
onClickPoint = () => {},
|
||||
popup,
|
||||
}: Props = $props();
|
||||
|
||||
let map: maplibregl.Map | undefined = $state();
|
||||
let marker: maplibregl.Marker | null = null;
|
||||
|
||||
const theme = $derived($mapSettings.allowDarkMode ? $colorTheme.value : Theme.LIGHT);
|
||||
const styleUrl = $derived(theme === Theme.DARK ? $serverConfig.mapDarkStyleUrl : $serverConfig.mapLightStyleUrl);
|
||||
const style = $derived(fetch(styleUrl).then((response) => response.json()));
|
||||
|
||||
export function addClipMapMarker(lng: number, lat: number) {
|
||||
if (map) {
|
||||
if (marker) {
|
||||
|
|
@ -46,26 +76,9 @@
|
|||
|
||||
center = { lng, lat };
|
||||
marker = new maplibregl.Marker().setLngLat([lng, lat]).addTo(map);
|
||||
map.setZoom(15);
|
||||
}
|
||||
}
|
||||
|
||||
export let onOpenInMapView: (() => Promise<void> | void) | undefined = undefined;
|
||||
export let onSelect: (assetIds: string[]) => void = () => {};
|
||||
export let onClickPoint: ({ lat, lng }: { lat: number; lng: number }) => void = () => {};
|
||||
|
||||
let map: maplibregl.Map;
|
||||
let marker: maplibregl.Marker | null = null;
|
||||
|
||||
// svelte-ignore reactive_declaration_non_reactive_property
|
||||
$: style = (async () => {
|
||||
const config = await getServerConfig();
|
||||
const theme = $mapSettings.allowDarkMode ? $colorTheme.value : Theme.LIGHT;
|
||||
const styleUrl = theme === Theme.DARK ? config.mapDarkStyleUrl : config.mapLightStyleUrl;
|
||||
const style = await fetch(styleUrl).then((response) => response.json());
|
||||
return style as StyleSpecification;
|
||||
})();
|
||||
|
||||
function handleAssetClick(assetId: string, map: Map | null) {
|
||||
if (!map) {
|
||||
return;
|
||||
|
|
@ -93,7 +106,9 @@
|
|||
marker.remove();
|
||||
}
|
||||
|
||||
marker = new maplibregl.Marker().setLngLat([lng, lat]).addTo(map);
|
||||
if (map) {
|
||||
marker = new maplibregl.Marker().setLngLat([lng, lat]).addTo(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,92 +150,96 @@
|
|||
{zoom}
|
||||
attributionControl={false}
|
||||
diffStyleUpdates={true}
|
||||
let:map
|
||||
on:load={(event) => event.detail.setMaxZoom(18)}
|
||||
on:load={(event) => event.detail.on('click', handleMapClick)}
|
||||
bind:map
|
||||
>
|
||||
<NavigationControl position="top-left" showCompass={!simplified} />
|
||||
{#snippet children({ map }: { map: maplibregl.Map })}
|
||||
<NavigationControl position="top-left" showCompass={!simplified} />
|
||||
|
||||
{#if !simplified}
|
||||
<GeolocateControl position="top-left" />
|
||||
<FullscreenControl position="top-left" />
|
||||
<ScaleControl />
|
||||
<AttributionControl compact={false} />
|
||||
{/if}
|
||||
{#if !simplified}
|
||||
<GeolocateControl position="top-left" />
|
||||
<FullscreenControl position="top-left" />
|
||||
<ScaleControl />
|
||||
<AttributionControl compact={false} />
|
||||
{/if}
|
||||
|
||||
{#if showSettingsModal !== undefined}
|
||||
<Control>
|
||||
<ControlGroup>
|
||||
<ControlButton on:click={() => (showSettingsModal = true)}><Icon path={mdiCog} size="100%" /></ControlButton>
|
||||
</ControlGroup>
|
||||
</Control>
|
||||
{/if}
|
||||
{#if showSettingsModal !== undefined}
|
||||
<Control>
|
||||
<ControlGroup>
|
||||
<ControlButton on:click={() => (showSettingsModal = true)}><Icon path={mdiCog} size="100%" /></ControlButton
|
||||
>
|
||||
</ControlGroup>
|
||||
</Control>
|
||||
{/if}
|
||||
|
||||
{#if onOpenInMapView}
|
||||
<Control position="top-right">
|
||||
<ControlGroup>
|
||||
<ControlButton on:click={() => onOpenInMapView()}>
|
||||
<Icon title={$t('open_in_map_view')} path={mdiMap} size="100%" />
|
||||
</ControlButton>
|
||||
</ControlGroup>
|
||||
</Control>
|
||||
{/if}
|
||||
{#if onOpenInMapView}
|
||||
<Control position="top-right">
|
||||
<ControlGroup>
|
||||
<ControlButton on:click={() => onOpenInMapView()}>
|
||||
<Icon title={$t('open_in_map_view')} path={mdiMap} size="100%" />
|
||||
</ControlButton>
|
||||
</ControlGroup>
|
||||
</Control>
|
||||
{/if}
|
||||
|
||||
<GeoJSON
|
||||
data={{
|
||||
type: 'FeatureCollection',
|
||||
features: mapMarkers.map((marker) => asFeature(marker)),
|
||||
}}
|
||||
id="geojson"
|
||||
cluster={{ radius: 500, maxZoom: 24 }}
|
||||
>
|
||||
<MarkerLayer
|
||||
applyToClusters
|
||||
asButton
|
||||
let:feature
|
||||
on:click={(event) => handlePromiseError(handleClusterClick(event.detail.feature.properties?.cluster_id, map))}
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[40px] h-[40px] bg-immich-primary text-immich-gray flex justify-center items-center font-mono font-bold shadow-lg hover:bg-immich-dark-primary transition-all duration-200 hover:text-immich-dark-bg opacity-90"
|
||||
>
|
||||
{feature.properties?.point_count}
|
||||
</div>
|
||||
</MarkerLayer>
|
||||
<MarkerLayer
|
||||
applyToClusters={false}
|
||||
asButton
|
||||
let:feature
|
||||
on:click={(event) => {
|
||||
if (!$$slots.popup) {
|
||||
handleAssetClick(event.detail.feature.properties?.id, map);
|
||||
}
|
||||
<GeoJSON
|
||||
data={{
|
||||
type: 'FeatureCollection',
|
||||
features: mapMarkers.map((marker) => asFeature(marker)),
|
||||
}}
|
||||
id="geojson"
|
||||
cluster={{ radius: 500, maxZoom: 24 }}
|
||||
>
|
||||
{#if useLocationPin}
|
||||
<Icon
|
||||
path={mdiMapMarker}
|
||||
size="50px"
|
||||
class="location-pin dark:text-immich-dark-primary text-immich-primary"
|
||||
/>
|
||||
{:else}
|
||||
<img
|
||||
src={getAssetThumbnailUrl(feature.properties?.id)}
|
||||
class="rounded-full w-[60px] h-[60px] border-2 border-immich-primary shadow-lg hover:border-immich-dark-primary transition-all duration-200 hover:scale-150 object-cover bg-immich-primary"
|
||||
alt={feature.properties?.city && feature.properties.country
|
||||
? $t('map_marker_for_images', {
|
||||
values: { city: feature.properties.city, country: feature.properties.country },
|
||||
})
|
||||
: $t('map_marker_with_image')}
|
||||
/>
|
||||
{/if}
|
||||
{#if $$slots.popup}
|
||||
<Popup offset={[0, -30]} openOn="click" closeOnClickOutside>
|
||||
<slot name="popup" marker={asMarker(feature)} />
|
||||
</Popup>
|
||||
{/if}
|
||||
</MarkerLayer>
|
||||
</GeoJSON>
|
||||
<MarkerLayer
|
||||
applyToClusters
|
||||
asButton
|
||||
on:click={(event) => handlePromiseError(handleClusterClick(event.detail.feature.properties?.cluster_id, map))}
|
||||
>
|
||||
{#snippet children({ feature }: { feature: maplibregl.Feature })}
|
||||
<div
|
||||
class="rounded-full w-[40px] h-[40px] bg-immich-primary text-immich-gray flex justify-center items-center font-mono font-bold shadow-lg hover:bg-immich-dark-primary transition-all duration-200 hover:text-immich-dark-bg opacity-90"
|
||||
>
|
||||
{feature.properties?.point_count}
|
||||
</div>
|
||||
{/snippet}
|
||||
</MarkerLayer>
|
||||
<MarkerLayer
|
||||
applyToClusters={false}
|
||||
asButton
|
||||
on:click={(event) => {
|
||||
if (!popup) {
|
||||
handleAssetClick(event.detail.feature.properties?.id, map);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#snippet children({ feature }: { feature: Feature<Geometry, GeoJsonProperties> })}
|
||||
{#if useLocationPin}
|
||||
<Icon
|
||||
path={mdiMapMarker}
|
||||
size="50px"
|
||||
class="location-pin dark:text-immich-dark-primary text-immich-primary"
|
||||
/>
|
||||
{:else}
|
||||
<img
|
||||
src={getAssetThumbnailUrl(feature.properties?.id)}
|
||||
class="rounded-full w-[60px] h-[60px] border-2 border-immich-primary shadow-lg hover:border-immich-dark-primary transition-all duration-200 hover:scale-150 object-cover bg-immich-primary"
|
||||
alt={feature.properties?.city && feature.properties.country
|
||||
? $t('map_marker_for_images', {
|
||||
values: { city: feature.properties.city, country: feature.properties.country },
|
||||
})
|
||||
: $t('map_marker_with_image')}
|
||||
/>
|
||||
{/if}
|
||||
{#if popup}
|
||||
<Popup offset={[0, -30]} openOn="click" closeOnClickOutside>
|
||||
{@render popup?.({ marker: asMarker(feature) })}
|
||||
</Popup>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</MarkerLayer>
|
||||
</GeoJSON>
|
||||
{/snippet}
|
||||
</MapLibre>
|
||||
<style>
|
||||
.location-pin {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue