mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-08-31 15:43:25 +00:00
try to detect 512px custom tiles
This commit is contained in:
@@ -1,435 +1,422 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as Card from '$lib/components/ui/card';
|
import * as Card from '$lib/components/ui/card';
|
||||||
import { Input } from '$lib/components/ui/input';
|
import { Input } from '$lib/components/ui/input';
|
||||||
import { Label } from '$lib/components/ui/label';
|
import { Label } from '$lib/components/ui/label';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { Separator } from '$lib/components/ui/separator';
|
import { Separator } from '$lib/components/ui/separator';
|
||||||
import * as RadioGroup from '$lib/components/ui/radio-group';
|
import * as RadioGroup from '$lib/components/ui/radio-group';
|
||||||
import {
|
import {
|
||||||
CirclePlus,
|
CirclePlus,
|
||||||
CircleX,
|
CircleX,
|
||||||
Minus,
|
Minus,
|
||||||
Pencil,
|
Pencil,
|
||||||
Plus,
|
Plus,
|
||||||
Save,
|
Save,
|
||||||
Trash2,
|
Trash2,
|
||||||
Move,
|
Move,
|
||||||
Map,
|
Map,
|
||||||
Layers2
|
Layers2
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import { settings } from '$lib/db';
|
import { settings } from '$lib/db';
|
||||||
import { defaultBasemap, type CustomLayer } from '$lib/assets/layers';
|
import { defaultBasemap, type CustomLayer } from '$lib/assets/layers';
|
||||||
import { map } from '$lib/stores';
|
import { map } from '$lib/stores';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import Sortable from 'sortablejs/Sortable';
|
import Sortable from 'sortablejs/Sortable';
|
||||||
import { customBasemapUpdate } from './utils';
|
import { customBasemapUpdate } from './utils';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
customLayers,
|
customLayers,
|
||||||
selectedBasemapTree,
|
selectedBasemapTree,
|
||||||
selectedOverlayTree,
|
selectedOverlayTree,
|
||||||
currentBasemap,
|
currentBasemap,
|
||||||
previousBasemap,
|
previousBasemap,
|
||||||
currentOverlays,
|
currentOverlays,
|
||||||
previousOverlays,
|
previousOverlays,
|
||||||
customBasemapOrder,
|
customBasemapOrder,
|
||||||
customOverlayOrder
|
customOverlayOrder
|
||||||
} = settings;
|
} = settings;
|
||||||
|
|
||||||
let name: string = '';
|
let name: string = '';
|
||||||
let tileUrls: string[] = [''];
|
let tileUrls: string[] = [''];
|
||||||
let maxZoom: number = 20;
|
let maxZoom: number = 20;
|
||||||
let layerType: 'basemap' | 'overlay' = 'basemap';
|
let layerType: 'basemap' | 'overlay' = 'basemap';
|
||||||
let resourceType: 'raster' | 'vector' = 'raster';
|
let resourceType: 'raster' | 'vector' = 'raster';
|
||||||
|
|
||||||
let basemapContainer: HTMLElement;
|
let basemapContainer: HTMLElement;
|
||||||
let overlayContainer: HTMLElement;
|
let overlayContainer: HTMLElement;
|
||||||
|
|
||||||
let basemapSortable: Sortable;
|
let basemapSortable: Sortable;
|
||||||
let overlaySortable: Sortable;
|
let overlaySortable: Sortable;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if ($customBasemapOrder.length === 0) {
|
if ($customBasemapOrder.length === 0) {
|
||||||
$customBasemapOrder = Object.keys($customLayers).filter(
|
$customBasemapOrder = Object.keys($customLayers).filter(
|
||||||
(id) => $customLayers[id].layerType === 'basemap'
|
(id) => $customLayers[id].layerType === 'basemap'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ($customOverlayOrder.length === 0) {
|
if ($customOverlayOrder.length === 0) {
|
||||||
$customOverlayOrder = Object.keys($customLayers).filter(
|
$customOverlayOrder = Object.keys($customLayers).filter(
|
||||||
(id) => $customLayers[id].layerType === 'overlay'
|
(id) => $customLayers[id].layerType === 'overlay'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
basemapSortable = Sortable.create(basemapContainer, {
|
basemapSortable = Sortable.create(basemapContainer, {
|
||||||
onSort: (e) => {
|
onSort: (e) => {
|
||||||
$customBasemapOrder = basemapSortable.toArray();
|
$customBasemapOrder = basemapSortable.toArray();
|
||||||
$selectedBasemapTree.basemaps['custom'] = $customBasemapOrder.reduce((acc, id) => {
|
$selectedBasemapTree.basemaps['custom'] = $customBasemapOrder.reduce((acc, id) => {
|
||||||
acc[id] = true;
|
acc[id] = true;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
overlaySortable = Sortable.create(overlayContainer, {
|
overlaySortable = Sortable.create(overlayContainer, {
|
||||||
onSort: (e) => {
|
onSort: (e) => {
|
||||||
$customOverlayOrder = overlaySortable.toArray();
|
$customOverlayOrder = overlaySortable.toArray();
|
||||||
$selectedOverlayTree.overlays['custom'] = $customOverlayOrder.reduce((acc, id) => {
|
$selectedOverlayTree.overlays['custom'] = $customOverlayOrder.reduce((acc, id) => {
|
||||||
acc[id] = true;
|
acc[id] = true;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
basemapSortable.sort($customBasemapOrder);
|
basemapSortable.sort($customBasemapOrder);
|
||||||
overlaySortable.sort($customOverlayOrder);
|
overlaySortable.sort($customOverlayOrder);
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
basemapSortable.destroy();
|
basemapSortable.destroy();
|
||||||
overlaySortable.destroy();
|
overlaySortable.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
$: if (tileUrls[0].length > 0) {
|
$: if (tileUrls[0].length > 0) {
|
||||||
if (
|
if (
|
||||||
tileUrls[0].includes('.json') ||
|
tileUrls[0].includes('.json') ||
|
||||||
(tileUrls[0].includes('api.mapbox.com/styles') && !tileUrls[0].includes('tiles'))
|
(tileUrls[0].includes('api.mapbox.com/styles') && !tileUrls[0].includes('tiles'))
|
||||||
) {
|
) {
|
||||||
resourceType = 'vector';
|
resourceType = 'vector';
|
||||||
} else {
|
} else {
|
||||||
resourceType = 'raster';
|
resourceType = 'raster';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLayer() {
|
function createLayer() {
|
||||||
if (selectedLayerId && $customLayers[selectedLayerId].layerType !== layerType) {
|
if (selectedLayerId && $customLayers[selectedLayerId].layerType !== layerType) {
|
||||||
deleteLayer(selectedLayerId);
|
deleteLayer(selectedLayerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof maxZoom === 'string') {
|
if (typeof maxZoom === 'string') {
|
||||||
maxZoom = parseInt(maxZoom);
|
maxZoom = parseInt(maxZoom);
|
||||||
}
|
}
|
||||||
|
let is512 = tileUrls.some((url) => url.includes('512'));
|
||||||
|
|
||||||
let layerId = selectedLayerId ?? getLayerId();
|
let layerId = selectedLayerId ?? getLayerId();
|
||||||
let layer: CustomLayer = {
|
let layer: CustomLayer = {
|
||||||
id: layerId,
|
id: layerId,
|
||||||
name: name,
|
name: name,
|
||||||
tileUrls: tileUrls.map((url) => decodeURI(url.trim())),
|
tileUrls: tileUrls.map((url) => decodeURI(url.trim())),
|
||||||
maxZoom: maxZoom,
|
maxZoom: maxZoom,
|
||||||
layerType: layerType,
|
layerType: layerType,
|
||||||
resourceType: resourceType,
|
resourceType: resourceType,
|
||||||
value: ''
|
value: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
if (resourceType === 'vector') {
|
if (resourceType === 'vector') {
|
||||||
layer.value = layer.tileUrls[0];
|
layer.value = layer.tileUrls[0];
|
||||||
} else {
|
} else {
|
||||||
layer.value = {
|
layer.value = {
|
||||||
version: 8,
|
version: 8,
|
||||||
sources: {
|
sources: {
|
||||||
[layerId]: {
|
[layerId]: {
|
||||||
type: 'raster',
|
type: 'raster',
|
||||||
tiles: layer.tileUrls,
|
tiles: layer.tileUrls,
|
||||||
tileSize: 256,
|
tileSize: is512 ? 512 : 256,
|
||||||
maxzoom: maxZoom
|
maxzoom: maxZoom
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
layers: [
|
layers: [
|
||||||
{
|
{
|
||||||
id: layerId,
|
id: layerId,
|
||||||
type: 'raster',
|
type: 'raster',
|
||||||
source: layerId
|
source: layerId
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
$customLayers[layerId] = layer;
|
$customLayers[layerId] = layer;
|
||||||
addLayer(layerId);
|
addLayer(layerId);
|
||||||
selectedLayerId = undefined;
|
selectedLayerId = undefined;
|
||||||
setDataFromSelectedLayer();
|
setDataFromSelectedLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLayerId() {
|
function getLayerId() {
|
||||||
for (let id = 0; ; id++) {
|
for (let id = 0; ; id++) {
|
||||||
if (!$customLayers.hasOwnProperty(`custom-${id}`)) {
|
if (!$customLayers.hasOwnProperty(`custom-${id}`)) {
|
||||||
return `custom-${id}`;
|
return `custom-${id}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLayer(layerId: string) {
|
function addLayer(layerId: string) {
|
||||||
if (layerType === 'basemap') {
|
if (layerType === 'basemap') {
|
||||||
selectedBasemapTree.update(($tree) => {
|
selectedBasemapTree.update(($tree) => {
|
||||||
if (!$tree.basemaps.hasOwnProperty('custom')) {
|
if (!$tree.basemaps.hasOwnProperty('custom')) {
|
||||||
$tree.basemaps['custom'] = {};
|
$tree.basemaps['custom'] = {};
|
||||||
}
|
}
|
||||||
$tree.basemaps['custom'][layerId] = true;
|
$tree.basemaps['custom'][layerId] = true;
|
||||||
return $tree;
|
return $tree;
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($currentBasemap === layerId) {
|
if ($currentBasemap === layerId) {
|
||||||
$customBasemapUpdate++;
|
$customBasemapUpdate++;
|
||||||
} else {
|
} else {
|
||||||
$currentBasemap = layerId;
|
$currentBasemap = layerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$customBasemapOrder.includes(layerId)) {
|
if (!$customBasemapOrder.includes(layerId)) {
|
||||||
$customBasemapOrder = [...$customBasemapOrder, layerId];
|
$customBasemapOrder = [...$customBasemapOrder, layerId];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selectedOverlayTree.update(($tree) => {
|
selectedOverlayTree.update(($tree) => {
|
||||||
if (!$tree.overlays.hasOwnProperty('custom')) {
|
if (!$tree.overlays.hasOwnProperty('custom')) {
|
||||||
$tree.overlays['custom'] = {};
|
$tree.overlays['custom'] = {};
|
||||||
}
|
}
|
||||||
$tree.overlays['custom'][layerId] = true;
|
$tree.overlays['custom'][layerId] = true;
|
||||||
return $tree;
|
return $tree;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$currentOverlays.overlays['custom'] &&
|
$currentOverlays.overlays['custom'] &&
|
||||||
$currentOverlays.overlays['custom'][layerId] &&
|
$currentOverlays.overlays['custom'][layerId] &&
|
||||||
$map
|
$map
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
$map.removeImport(layerId);
|
$map.removeImport(layerId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// No reliable way to check if the map is ready to remove sources and layers
|
// No reliable way to check if the map is ready to remove sources and layers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$currentOverlays.overlays.hasOwnProperty('custom')) {
|
if (!$currentOverlays.overlays.hasOwnProperty('custom')) {
|
||||||
$currentOverlays.overlays['custom'] = {};
|
$currentOverlays.overlays['custom'] = {};
|
||||||
}
|
}
|
||||||
$currentOverlays.overlays['custom'][layerId] = true;
|
$currentOverlays.overlays['custom'][layerId] = true;
|
||||||
|
|
||||||
if (!$customOverlayOrder.includes(layerId)) {
|
if (!$customOverlayOrder.includes(layerId)) {
|
||||||
$customOverlayOrder = [...$customOverlayOrder, layerId];
|
$customOverlayOrder = [...$customOverlayOrder, layerId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryDeleteLayer(node: any, id: string): any {
|
function tryDeleteLayer(node: any, id: string): any {
|
||||||
if (node.hasOwnProperty(id)) {
|
if (node.hasOwnProperty(id)) {
|
||||||
delete node[id];
|
delete node[id];
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteLayer(layerId: string) {
|
function deleteLayer(layerId: string) {
|
||||||
let layer = $customLayers[layerId];
|
let layer = $customLayers[layerId];
|
||||||
if (layer.layerType === 'basemap') {
|
if (layer.layerType === 'basemap') {
|
||||||
if (layerId === $currentBasemap) {
|
if (layerId === $currentBasemap) {
|
||||||
$currentBasemap = defaultBasemap;
|
$currentBasemap = defaultBasemap;
|
||||||
}
|
}
|
||||||
if (layerId === $previousBasemap) {
|
if (layerId === $previousBasemap) {
|
||||||
$previousBasemap = defaultBasemap;
|
$previousBasemap = defaultBasemap;
|
||||||
}
|
}
|
||||||
|
|
||||||
$selectedBasemapTree.basemaps['custom'] = tryDeleteLayer(
|
$selectedBasemapTree.basemaps['custom'] = tryDeleteLayer(
|
||||||
$selectedBasemapTree.basemaps['custom'],
|
$selectedBasemapTree.basemaps['custom'],
|
||||||
layerId
|
layerId
|
||||||
);
|
);
|
||||||
if (Object.keys($selectedBasemapTree.basemaps['custom']).length === 0) {
|
if (Object.keys($selectedBasemapTree.basemaps['custom']).length === 0) {
|
||||||
$selectedBasemapTree.basemaps = tryDeleteLayer(
|
$selectedBasemapTree.basemaps = tryDeleteLayer($selectedBasemapTree.basemaps, 'custom');
|
||||||
$selectedBasemapTree.basemaps,
|
}
|
||||||
'custom'
|
$customBasemapOrder = $customBasemapOrder.filter((id) => id !== layerId);
|
||||||
);
|
} else {
|
||||||
}
|
$currentOverlays.overlays['custom'][layerId] = false;
|
||||||
$customBasemapOrder = $customBasemapOrder.filter((id) => id !== layerId);
|
if ($previousOverlays.overlays['custom']) {
|
||||||
} else {
|
$previousOverlays.overlays['custom'] = tryDeleteLayer(
|
||||||
$currentOverlays.overlays['custom'][layerId] = false;
|
$previousOverlays.overlays['custom'],
|
||||||
if ($previousOverlays.overlays['custom']) {
|
layerId
|
||||||
$previousOverlays.overlays['custom'] = tryDeleteLayer(
|
);
|
||||||
$previousOverlays.overlays['custom'],
|
}
|
||||||
layerId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$selectedOverlayTree.overlays['custom'] = tryDeleteLayer(
|
$selectedOverlayTree.overlays['custom'] = tryDeleteLayer(
|
||||||
$selectedOverlayTree.overlays['custom'],
|
$selectedOverlayTree.overlays['custom'],
|
||||||
layerId
|
layerId
|
||||||
);
|
);
|
||||||
if (Object.keys($selectedOverlayTree.overlays['custom']).length === 0) {
|
if (Object.keys($selectedOverlayTree.overlays['custom']).length === 0) {
|
||||||
$selectedOverlayTree.overlays = tryDeleteLayer(
|
$selectedOverlayTree.overlays = tryDeleteLayer($selectedOverlayTree.overlays, 'custom');
|
||||||
$selectedOverlayTree.overlays,
|
}
|
||||||
'custom'
|
$customOverlayOrder = $customOverlayOrder.filter((id) => id !== layerId);
|
||||||
);
|
|
||||||
}
|
|
||||||
$customOverlayOrder = $customOverlayOrder.filter((id) => id !== layerId);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$currentOverlays.overlays['custom'] &&
|
$currentOverlays.overlays['custom'] &&
|
||||||
$currentOverlays.overlays['custom'][layerId] &&
|
$currentOverlays.overlays['custom'][layerId] &&
|
||||||
$map
|
$map
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
$map.removeImport(layerId);
|
$map.removeImport(layerId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// No reliable way to check if the map is ready to remove sources and layers
|
// No reliable way to check if the map is ready to remove sources and layers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$customLayers = tryDeleteLayer($customLayers, layerId);
|
$customLayers = tryDeleteLayer($customLayers, layerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedLayerId: string | undefined = undefined;
|
let selectedLayerId: string | undefined = undefined;
|
||||||
|
|
||||||
function setDataFromSelectedLayer() {
|
function setDataFromSelectedLayer() {
|
||||||
if (selectedLayerId) {
|
if (selectedLayerId) {
|
||||||
const layer = $customLayers[selectedLayerId];
|
const layer = $customLayers[selectedLayerId];
|
||||||
name = layer.name;
|
name = layer.name;
|
||||||
tileUrls = layer.tileUrls;
|
tileUrls = layer.tileUrls;
|
||||||
maxZoom = layer.maxZoom;
|
maxZoom = layer.maxZoom;
|
||||||
layerType = layer.layerType;
|
layerType = layer.layerType;
|
||||||
resourceType = layer.resourceType;
|
resourceType = layer.resourceType;
|
||||||
} else {
|
} else {
|
||||||
name = '';
|
name = '';
|
||||||
tileUrls = [''];
|
tileUrls = [''];
|
||||||
maxZoom = 20;
|
maxZoom = 20;
|
||||||
layerType = 'basemap';
|
layerType = 'basemap';
|
||||||
resourceType = 'raster';
|
resourceType = 'raster';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: selectedLayerId, setDataFromSelectedLayer();
|
$: selectedLayerId, setDataFromSelectedLayer();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
{#if $customBasemapOrder.length > 0}
|
{#if $customBasemapOrder.length > 0}
|
||||||
<div class="flex flex-row items-center gap-1 font-semibold mb-2">
|
<div class="flex flex-row items-center gap-1 font-semibold mb-2">
|
||||||
<Map size="16" />
|
<Map size="16" />
|
||||||
{$_('layers.label.basemaps')}
|
{$_('layers.label.basemaps')}
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<Separator />
|
<Separator />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div
|
<div
|
||||||
bind:this={basemapContainer}
|
bind:this={basemapContainer}
|
||||||
class="ml-1.5 flex flex-col gap-1 {$customBasemapOrder.length > 0 ? 'mb-2' : ''}"
|
class="ml-1.5 flex flex-col gap-1 {$customBasemapOrder.length > 0 ? 'mb-2' : ''}"
|
||||||
>
|
>
|
||||||
{#each $customBasemapOrder as id (id)}
|
{#each $customBasemapOrder as id (id)}
|
||||||
<div class="flex flex-row items-center gap-2" data-id={id}>
|
<div class="flex flex-row items-center gap-2" data-id={id}>
|
||||||
<Move size="12" />
|
<Move size="12" />
|
||||||
<span class="grow">{$customLayers[id].name}</span>
|
<span class="grow">{$customLayers[id].name}</span>
|
||||||
<Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7">
|
<Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7">
|
||||||
<Pencil size="16" />
|
<Pencil size="16" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7">
|
<Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7">
|
||||||
<Trash2 size="16" />
|
<Trash2 size="16" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if $customOverlayOrder.length > 0}
|
{#if $customOverlayOrder.length > 0}
|
||||||
<div class="flex flex-row items-center gap-1 font-semibold mb-2">
|
<div class="flex flex-row items-center gap-1 font-semibold mb-2">
|
||||||
<Layers2 size="16" />
|
<Layers2 size="16" />
|
||||||
{$_('layers.label.overlays')}
|
{$_('layers.label.overlays')}
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<Separator />
|
<Separator />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div
|
<div
|
||||||
bind:this={overlayContainer}
|
bind:this={overlayContainer}
|
||||||
class="ml-1.5 flex flex-col gap-1 {$customOverlayOrder.length > 0 ? 'mb-2' : ''}"
|
class="ml-1.5 flex flex-col gap-1 {$customOverlayOrder.length > 0 ? 'mb-2' : ''}"
|
||||||
>
|
>
|
||||||
{#each $customOverlayOrder as id (id)}
|
{#each $customOverlayOrder as id (id)}
|
||||||
<div class="flex flex-row items-center gap-2" data-id={id}>
|
<div class="flex flex-row items-center gap-2" data-id={id}>
|
||||||
<Move size="12" />
|
<Move size="12" />
|
||||||
<span class="grow">{$customLayers[id].name}</span>
|
<span class="grow">{$customLayers[id].name}</span>
|
||||||
<Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7">
|
<Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7">
|
||||||
<Pencil size="16" />
|
<Pencil size="16" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7">
|
<Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7">
|
||||||
<Trash2 size="16" />
|
<Trash2 size="16" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card.Root>
|
<Card.Root>
|
||||||
<Card.Header class="p-3">
|
<Card.Header class="p-3">
|
||||||
<Card.Title class="text-base">
|
<Card.Title class="text-base">
|
||||||
{#if selectedLayerId}
|
{#if selectedLayerId}
|
||||||
{$_('layers.custom_layers.edit')}
|
{$_('layers.custom_layers.edit')}
|
||||||
{:else}
|
{:else}
|
||||||
{$_('layers.custom_layers.new')}
|
{$_('layers.custom_layers.new')}
|
||||||
{/if}
|
{/if}
|
||||||
</Card.Title>
|
</Card.Title>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="p-3 pt-0">
|
<Card.Content class="p-3 pt-0">
|
||||||
<fieldset class="flex flex-col gap-2">
|
<fieldset class="flex flex-col gap-2">
|
||||||
<Label for="name">{$_('menu.metadata.name')}</Label>
|
<Label for="name">{$_('menu.metadata.name')}</Label>
|
||||||
<Input bind:value={name} id="name" class="h-8" />
|
<Input bind:value={name} id="name" class="h-8" />
|
||||||
<Label for="url">{$_('layers.custom_layers.urls')}</Label>
|
<Label for="url">{$_('layers.custom_layers.urls')}</Label>
|
||||||
{#each tileUrls as url, i}
|
{#each tileUrls as url, i}
|
||||||
<div class="flex flex-row gap-2">
|
<div class="flex flex-row gap-2">
|
||||||
<Input
|
<Input
|
||||||
bind:value={tileUrls[i]}
|
bind:value={tileUrls[i]}
|
||||||
id="url"
|
id="url"
|
||||||
class="h-8"
|
class="h-8"
|
||||||
placeholder={$_('layers.custom_layers.url_placeholder')}
|
placeholder={$_('layers.custom_layers.url_placeholder')}
|
||||||
/>
|
/>
|
||||||
{#if tileUrls.length > 1}
|
{#if tileUrls.length > 1}
|
||||||
<Button
|
<Button
|
||||||
on:click={() =>
|
on:click={() => (tileUrls = tileUrls.filter((_, index) => index !== i))}
|
||||||
(tileUrls = tileUrls.filter((_, index) => index !== i))}
|
variant="outline"
|
||||||
variant="outline"
|
class="p-1 h-8"
|
||||||
class="p-1 h-8"
|
>
|
||||||
>
|
<Minus size="16" />
|
||||||
<Minus size="16" />
|
</Button>
|
||||||
</Button>
|
{/if}
|
||||||
{/if}
|
{#if i === tileUrls.length - 1}
|
||||||
{#if i === tileUrls.length - 1}
|
<Button
|
||||||
<Button
|
on:click={() => (tileUrls = [...tileUrls, ''])}
|
||||||
on:click={() => (tileUrls = [...tileUrls, ''])}
|
variant="outline"
|
||||||
variant="outline"
|
class="p-1 h-8"
|
||||||
class="p-1 h-8"
|
>
|
||||||
>
|
<Plus size="16" />
|
||||||
<Plus size="16" />
|
</Button>
|
||||||
</Button>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
{/each}
|
||||||
{/each}
|
{#if resourceType === 'raster'}
|
||||||
{#if resourceType === 'raster'}
|
<Label for="maxZoom">{$_('layers.custom_layers.max_zoom')}</Label>
|
||||||
<Label for="maxZoom">{$_('layers.custom_layers.max_zoom')}</Label>
|
<Input type="number" bind:value={maxZoom} id="maxZoom" min={0} max={22} class="h-8" />
|
||||||
<Input
|
{/if}
|
||||||
type="number"
|
<Label>{$_('layers.custom_layers.layer_type')}</Label>
|
||||||
bind:value={maxZoom}
|
<RadioGroup.Root bind:value={layerType} class="flex flex-row">
|
||||||
id="maxZoom"
|
<div class="flex items-center space-x-2">
|
||||||
min={0}
|
<RadioGroup.Item value="basemap" id="basemap" />
|
||||||
max={22}
|
<Label for="basemap">{$_('layers.custom_layers.basemap')}</Label>
|
||||||
class="h-8"
|
</div>
|
||||||
/>
|
<div class="flex items-center space-x-2">
|
||||||
{/if}
|
<RadioGroup.Item value="overlay" id="overlay" />
|
||||||
<Label>{$_('layers.custom_layers.layer_type')}</Label>
|
<Label for="overlay">{$_('layers.custom_layers.overlay')}</Label>
|
||||||
<RadioGroup.Root bind:value={layerType} class="flex flex-row">
|
</div>
|
||||||
<div class="flex items-center space-x-2">
|
</RadioGroup.Root>
|
||||||
<RadioGroup.Item value="basemap" id="basemap" />
|
{#if selectedLayerId}
|
||||||
<Label for="basemap">{$_('layers.custom_layers.basemap')}</Label>
|
<div class="mt-2 flex flex-row gap-2">
|
||||||
</div>
|
<Button variant="outline" on:click={createLayer} class="grow">
|
||||||
<div class="flex items-center space-x-2">
|
<Save size="16" class="mr-1" />
|
||||||
<RadioGroup.Item value="overlay" id="overlay" />
|
{$_('layers.custom_layers.update')}
|
||||||
<Label for="overlay">{$_('layers.custom_layers.overlay')}</Label>
|
</Button>
|
||||||
</div>
|
<Button variant="outline" on:click={() => (selectedLayerId = undefined)}>
|
||||||
</RadioGroup.Root>
|
<CircleX size="16" />
|
||||||
{#if selectedLayerId}
|
</Button>
|
||||||
<div class="mt-2 flex flex-row gap-2">
|
</div>
|
||||||
<Button variant="outline" on:click={createLayer} class="grow">
|
{:else}
|
||||||
<Save size="16" class="mr-1" />
|
<Button variant="outline" class="mt-2" on:click={createLayer}>
|
||||||
{$_('layers.custom_layers.update')}
|
<CirclePlus size="16" class="mr-1" />
|
||||||
</Button>
|
{$_('layers.custom_layers.create')}
|
||||||
<Button variant="outline" on:click={() => (selectedLayerId = undefined)}>
|
</Button>
|
||||||
<CircleX size="16" />
|
{/if}
|
||||||
</Button>
|
</fieldset>
|
||||||
</div>
|
</Card.Content>
|
||||||
{:else}
|
</Card.Root>
|
||||||
<Button variant="outline" class="mt-2" on:click={createLayer}>
|
|
||||||
<CirclePlus size="16" class="mr-1" />
|
|
||||||
{$_('layers.custom_layers.create')}
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
</fieldset>
|
|
||||||
</Card.Content>
|
|
||||||
</Card.Root>
|
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user