@versatiles/svelte 0.3.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -6
- package/dist/components/BBoxMap/AutoComplete.svelte +176 -0
- package/dist/components/{AutoComplete.svelte.d.ts → BBoxMap/AutoComplete.svelte.d.ts} +12 -18
- package/dist/components/BBoxMap/BBoxMap.svelte +43 -54
- package/dist/components/BBoxMap/BBoxMap.svelte.d.ts +6 -17
- package/dist/components/BasicMap/BasicMap.svelte +34 -37
- package/dist/components/BasicMap/BasicMap.svelte.d.ts +9 -24
- package/dist/components/LocatorMap/LocatorMap.svelte +61 -20
- package/dist/components/MapEditor/Editor.svelte +25 -0
- package/dist/components/MapEditor/Editor.svelte.d.ts +7 -0
- package/dist/components/MapEditor/EditorLine.svelte +27 -0
- package/dist/components/MapEditor/EditorLine.svelte.d.ts +7 -0
- package/dist/components/MapEditor/EditorMarker.svelte +42 -0
- package/dist/components/MapEditor/EditorMarker.svelte.d.ts +7 -0
- package/dist/components/MapEditor/MapEditor.svelte +99 -0
- package/dist/components/MapEditor/MapEditor.svelte.d.ts +4 -0
- package/dist/components/MapEditor/editor.scss +16 -0
- package/dist/components/MapEditor/lib/element_abstract.d.ts +20 -0
- package/dist/components/MapEditor/lib/element_abstract.js +58 -0
- package/dist/components/MapEditor/lib/element_line.d.ts +14 -0
- package/dist/components/MapEditor/lib/element_line.js +76 -0
- package/dist/components/MapEditor/lib/element_marker.d.ts +20 -0
- package/dist/components/MapEditor/lib/element_marker.js +191 -0
- package/dist/components/MapEditor/lib/geocoder.d.ts +1 -0
- package/dist/components/MapEditor/lib/geocoder.js +8 -0
- package/dist/components/MapEditor/lib/geometry_manager.d.ts +18 -0
- package/dist/components/MapEditor/lib/geometry_manager.js +117 -0
- package/dist/components/MapEditor/lib/map_layer.d.ts +14 -0
- package/dist/components/MapEditor/lib/map_layer.js +61 -0
- package/dist/components/MapEditor/lib/types.d.ts +112 -0
- package/dist/components/MapEditor/lib/types.js +1 -0
- package/dist/components/MapEditor/lib/utils.d.ts +2 -0
- package/dist/components/MapEditor/lib/utils.js +11 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/utils/draw/bbox.d.ts +4 -10
- package/dist/utils/draw/bbox.js +62 -62
- package/dist/utils/map_style.d.ts +5 -2
- package/dist/utils/map_style.js +13 -6
- package/package.json +10 -7
- package/dist/components/AutoComplete.svelte +0 -196
- package/dist/utils/draw/abstract.d.ts +0 -5
- package/dist/utils/draw/abstract.js +0 -2
- package/dist/utils/draw/marker.d.ts +0 -24
- package/dist/utils/draw/marker.js +0 -97
- package/dist/utils/draw/style.d.ts +0 -19
- package/dist/utils/draw/style.js +0 -40
package/README.md
CHANGED
@@ -23,17 +23,22 @@ npm i @versatiles/svelte
|
|
23
23
|
</tr>
|
24
24
|
<tr>
|
25
25
|
<th>BasicMap</th>
|
26
|
-
<td><img src="./screenshots/basic-map-light.png"></td>
|
27
|
-
<td><img src="./screenshots/basic-map-dark.png"></td>
|
26
|
+
<td><img width="512" src="./screenshots/basic-map-light.png"></td>
|
27
|
+
<td><img width="512" src="./screenshots/basic-map-dark.png"></td>
|
28
28
|
</tr>
|
29
29
|
<tr>
|
30
30
|
<th>BBoxMap</th>
|
31
|
-
<td><img src="./screenshots/bbox-map-light.png"></td>
|
32
|
-
<td><img src="./screenshots/bbox-map-dark.png"></td>
|
31
|
+
<td><img width="512" src="./screenshots/bbox-map-light.png"></td>
|
32
|
+
<td><img width="512" src="./screenshots/bbox-map-dark.png"></td>
|
33
33
|
</tr>
|
34
34
|
<tr>
|
35
35
|
<th>LocatorMap</th>
|
36
|
-
<td><img src="./screenshots/locator-map-light.png"></td>
|
37
|
-
<td><img src="./screenshots/locator-map-dark.png"></td>
|
36
|
+
<td><img width="512" src="./screenshots/locator-map-light.png"></td>
|
37
|
+
<td><img width="512" src="./screenshots/locator-map-dark.png"></td>
|
38
|
+
</tr>
|
39
|
+
<tr>
|
40
|
+
<th>MapEditor</th>
|
41
|
+
<td><img width="512" src="./screenshots/map-editor-light.png"></td>
|
42
|
+
<td><img width="512" src="./screenshots/map-editor-dark.png"></td>
|
38
43
|
</tr>
|
39
44
|
</table>
|
@@ -0,0 +1,176 @@
|
|
1
|
+
<!-- AutoComplete.svelte -->
|
2
|
+
<script lang="ts" generics="T">import { isDarkMode } from '../../utils/map_style.js';
|
3
|
+
/* eslint svelte/no-at-html-tags: off */
|
4
|
+
let { change, initialInputText, inputText = $bindable(''), items = [], maxItems = 10, minChar = 3, placeholder = '' } = $props();
|
5
|
+
import { onMount } from 'svelte';
|
6
|
+
let inputElement; // Reference to DOM element
|
7
|
+
let autocompleteElement; // Reference to DOM element
|
8
|
+
let isOpen = $state(false);
|
9
|
+
let results = $state([]);
|
10
|
+
let selectedIndex = $state(0);
|
11
|
+
// Escape special characters in search string for use in regex
|
12
|
+
const regExpEscape = (s) => s.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
|
13
|
+
if (inputText.length >= minChar) {
|
14
|
+
const r = filterResults();
|
15
|
+
if (r.length > 0) {
|
16
|
+
const { key, value } = r[0];
|
17
|
+
inputText = key;
|
18
|
+
change(JSON.parse(JSON.stringify(value)));
|
19
|
+
}
|
20
|
+
else {
|
21
|
+
inputText = '';
|
22
|
+
}
|
23
|
+
}
|
24
|
+
// Handle input change
|
25
|
+
function onChange() {
|
26
|
+
if (inputText.length >= minChar) {
|
27
|
+
results = filterResults();
|
28
|
+
selectedIndex = 0;
|
29
|
+
isOpen = true;
|
30
|
+
}
|
31
|
+
else {
|
32
|
+
isOpen = false;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
function onFocus() {
|
36
|
+
inputElement.setSelectionRange(0, 1000);
|
37
|
+
}
|
38
|
+
// Filter results based on search query
|
39
|
+
function filterResults() {
|
40
|
+
const searchText = inputText.trim();
|
41
|
+
const searchTextUpper = searchText.toUpperCase();
|
42
|
+
const searchReg = RegExp(regExpEscape(searchText), 'i');
|
43
|
+
return items
|
44
|
+
.filter((item) => item.key.toUpperCase().includes(searchTextUpper))
|
45
|
+
.slice(0, maxItems)
|
46
|
+
.map((item) => ({
|
47
|
+
...item,
|
48
|
+
_label: item.key.replace(searchReg, '<span>$&</span>')
|
49
|
+
}));
|
50
|
+
}
|
51
|
+
// Handle keyboard navigation
|
52
|
+
function onKeyDown(event) {
|
53
|
+
switch (event.key) {
|
54
|
+
case 'ArrowDown':
|
55
|
+
if (selectedIndex < results.length - 1)
|
56
|
+
selectedIndex += 1;
|
57
|
+
break;
|
58
|
+
case 'ArrowUp':
|
59
|
+
if (selectedIndex > 0)
|
60
|
+
selectedIndex -= 1;
|
61
|
+
break;
|
62
|
+
case 'Enter':
|
63
|
+
event.preventDefault();
|
64
|
+
close(selectedIndex);
|
65
|
+
break;
|
66
|
+
case 'Escape':
|
67
|
+
event.preventDefault();
|
68
|
+
close();
|
69
|
+
break;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
// Close the autocomplete and select an item
|
73
|
+
function close(index = -1) {
|
74
|
+
isOpen = false;
|
75
|
+
if (index >= 0 && results[index]) {
|
76
|
+
const { key, value } = results[index];
|
77
|
+
inputText = key;
|
78
|
+
change(JSON.parse(JSON.stringify(value)));
|
79
|
+
}
|
80
|
+
}
|
81
|
+
onMount(() => {
|
82
|
+
const darkMode = isDarkMode(autocompleteElement);
|
83
|
+
autocompleteElement.style.setProperty('--bg-color', darkMode ? '#000' : '#fff');
|
84
|
+
autocompleteElement.style.setProperty('--fg-color', darkMode ? '#fff' : '#000');
|
85
|
+
if (initialInputText) {
|
86
|
+
inputText = initialInputText;
|
87
|
+
results = filterResults();
|
88
|
+
close(0);
|
89
|
+
}
|
90
|
+
});
|
91
|
+
</script>
|
92
|
+
|
93
|
+
<svelte:window on:click={() => close()} />
|
94
|
+
|
95
|
+
<div class="autocomplete" bind:this={autocompleteElement}>
|
96
|
+
<input
|
97
|
+
type="text"
|
98
|
+
bind:value={inputText}
|
99
|
+
{placeholder}
|
100
|
+
autocomplete="off"
|
101
|
+
oninput={onChange}
|
102
|
+
onkeydown={onKeyDown}
|
103
|
+
onfocusin={onFocus}
|
104
|
+
onclick={(e) => e.stopPropagation()}
|
105
|
+
bind:this={inputElement}
|
106
|
+
aria-activedescendant={isOpen ? `result-${selectedIndex}` : undefined}
|
107
|
+
aria-autocomplete="list"
|
108
|
+
aria-controls="autocomplete-results"
|
109
|
+
/>
|
110
|
+
<div class="autocomplete-results" class:hide-results={!isOpen}>
|
111
|
+
{#each results as result, i}
|
112
|
+
<button
|
113
|
+
onclick={() => close(i)}
|
114
|
+
class={i === selectedIndex ? ' is-active' : ''}
|
115
|
+
role="option"
|
116
|
+
aria-selected={i === selectedIndex}
|
117
|
+
>
|
118
|
+
{@html result._label}
|
119
|
+
</button>
|
120
|
+
{/each}
|
121
|
+
</div>
|
122
|
+
</div>
|
123
|
+
|
124
|
+
<style>
|
125
|
+
.autocomplete {
|
126
|
+
position: relative;
|
127
|
+
border-radius: 0.5em;
|
128
|
+
background: color-mix(in srgb, var(--bg-color) 80%, transparent);
|
129
|
+
box-sizing: border-box;
|
130
|
+
line-height: normal;
|
131
|
+
}
|
132
|
+
|
133
|
+
input {
|
134
|
+
width: 100%;
|
135
|
+
display: block;
|
136
|
+
box-sizing: border-box;
|
137
|
+
padding: 0.3em 0.6em;
|
138
|
+
border: none;
|
139
|
+
background: none;
|
140
|
+
color: var(--fg-color);
|
141
|
+
}
|
142
|
+
|
143
|
+
.autocomplete-results {
|
144
|
+
padding: 0;
|
145
|
+
margin: 0;
|
146
|
+
background: none;
|
147
|
+
width: 100%;
|
148
|
+
display: block;
|
149
|
+
border-radius: 0 0 0.5em 0.5em;
|
150
|
+
}
|
151
|
+
|
152
|
+
.autocomplete-results.hide-results {
|
153
|
+
display: none;
|
154
|
+
}
|
155
|
+
|
156
|
+
button {
|
157
|
+
padding: 0.2rem 0.5rem;
|
158
|
+
cursor: pointer;
|
159
|
+
border: none;
|
160
|
+
display: block;
|
161
|
+
background: transparent;
|
162
|
+
font-weight: normal;
|
163
|
+
color: color-mix(in srgb, var(--fg-color) 50%, transparent);
|
164
|
+
width: 100%;
|
165
|
+
text-align: left;
|
166
|
+
}
|
167
|
+
|
168
|
+
button > :global(span) {
|
169
|
+
color: var(--fg-color);
|
170
|
+
}
|
171
|
+
|
172
|
+
button.is-active,
|
173
|
+
button:hover {
|
174
|
+
background-color: color-mix(in srgb, var(--fg-color) 15%, transparent);
|
175
|
+
}
|
176
|
+
</style>
|
@@ -1,32 +1,26 @@
|
|
1
1
|
declare class __sveltets_Render<T> {
|
2
2
|
props(): {
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
items
|
3
|
+
change: (value: T) => void;
|
4
|
+
initialInputText?: string;
|
5
|
+
inputText?: string;
|
6
|
+
items?: {
|
7
7
|
key: string;
|
8
8
|
value: T;
|
9
|
-
}[];
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
change: CustomEvent<any>;
|
14
|
-
} & {
|
15
|
-
[evt: string]: CustomEvent<any>;
|
9
|
+
}[] | undefined;
|
10
|
+
maxItems?: number;
|
11
|
+
minChar?: number;
|
12
|
+
placeholder?: string;
|
16
13
|
};
|
14
|
+
events(): {};
|
17
15
|
slots(): {};
|
18
|
-
bindings():
|
19
|
-
exports(): {
|
20
|
-
setInputText: (text: string) => void;
|
21
|
-
};
|
16
|
+
bindings(): "inputText";
|
17
|
+
exports(): {};
|
22
18
|
}
|
23
19
|
interface $$IsomorphicComponent {
|
24
20
|
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
25
21
|
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
26
22
|
} & ReturnType<__sveltets_Render<T>['exports']>;
|
27
|
-
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {
|
28
|
-
$$events?: ReturnType<__sveltets_Render<T>['events']>;
|
29
|
-
}): ReturnType<__sveltets_Render<T>['exports']>;
|
23
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
30
24
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
31
25
|
}
|
32
26
|
declare const AutoComplete: $$IsomorphicComponent;
|
@@ -1,55 +1,44 @@
|
|
1
1
|
<!-- BBoxMap.svelte -->
|
2
|
-
<script lang="ts">
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
if (Date.now() - startTime < 1000) {
|
43
|
-
map.jumpTo(transform);
|
44
|
-
} else {
|
45
|
-
map.flyTo({ ...transform, essential: true, speed: 5 });
|
46
|
-
}
|
47
|
-
}
|
48
|
-
}
|
49
|
-
|
50
|
-
$: if (autoComplete) {
|
51
|
-
autoComplete?.setInputText(getCountryName() ?? '');
|
52
|
-
}
|
2
|
+
<script lang="ts">import 'maplibre-gl/dist/maplibre-gl.css';
|
3
|
+
import AutoComplete from './AutoComplete.svelte';
|
4
|
+
import { getCountryName } from '../../utils/location.js';
|
5
|
+
import BasicMap from '../BasicMap/BasicMap.svelte';
|
6
|
+
import { isDarkMode } from '../../utils/map_style.js';
|
7
|
+
import { loadBBoxes } from './BBoxMap.js';
|
8
|
+
import { BBoxDrawer } from '../../utils/draw/bbox.js';
|
9
|
+
let { selectedBBox = $bindable() } = $props();
|
10
|
+
const startTime = Date.now();
|
11
|
+
let bboxDrawer;
|
12
|
+
let map = $state();
|
13
|
+
let bboxes = $state();
|
14
|
+
let mapContainer;
|
15
|
+
async function onMapLoad(_map) {
|
16
|
+
map = _map;
|
17
|
+
mapContainer = map.getContainer();
|
18
|
+
map.setPadding({ top: 31 + 5, right: 5, bottom: 5, left: 5 });
|
19
|
+
bboxDrawer = new BBoxDrawer(map, [-180, -86, 180, 86], isDarkMode(mapContainer) ? '#FFFFFF' : '#000000');
|
20
|
+
bboxes = await loadBBoxes();
|
21
|
+
bboxDrawer.bbox.subscribe((bbox) => {
|
22
|
+
selectedBBox = bbox;
|
23
|
+
});
|
24
|
+
}
|
25
|
+
function flyToBBox(bbox) {
|
26
|
+
if (!map || !bbox)
|
27
|
+
return;
|
28
|
+
bboxDrawer.setGeometry(bbox);
|
29
|
+
const transform = map.cameraForBounds(bboxDrawer.getBounds());
|
30
|
+
if (transform == null)
|
31
|
+
return;
|
32
|
+
transform.zoom = transform.zoom ?? 0 - 0.5;
|
33
|
+
transform.bearing = 0;
|
34
|
+
transform.pitch = 0;
|
35
|
+
if (Date.now() - startTime < 1000) {
|
36
|
+
map.jumpTo(transform);
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
map.flyTo({ ...transform, essential: true, speed: 5 });
|
40
|
+
}
|
41
|
+
}
|
53
42
|
</script>
|
54
43
|
|
55
44
|
<div class="container">
|
@@ -58,12 +47,12 @@
|
|
58
47
|
<AutoComplete
|
59
48
|
items={bboxes}
|
60
49
|
placeholder="Find country, region or city …"
|
61
|
-
|
62
|
-
|
50
|
+
change={(bbox) => flyToBBox(bbox)}
|
51
|
+
initialInputText={getCountryName() ?? ''}
|
63
52
|
/>
|
64
53
|
</div>
|
65
54
|
{/if}
|
66
|
-
<BasicMap {
|
55
|
+
<BasicMap {onMapLoad}></BasicMap>
|
67
56
|
</div>
|
68
57
|
|
69
58
|
<style>
|
@@ -1,19 +1,8 @@
|
|
1
1
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
$$slots?: Slots;
|
9
|
-
}): Exports & {
|
10
|
-
$set?: any;
|
11
|
-
$on?: any;
|
12
|
-
};
|
13
|
-
z_$$bindings?: Bindings;
|
14
|
-
}
|
15
|
-
declare const BBoxMap: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
16
|
-
[evt: string]: CustomEvent<any>;
|
17
|
-
}, {}, {}, string>;
|
18
|
-
type BBoxMap = InstanceType<typeof BBoxMap>;
|
2
|
+
import type { BBox } from 'geojson';
|
3
|
+
type $$ComponentProps = {
|
4
|
+
selectedBBox?: BBox;
|
5
|
+
};
|
6
|
+
declare const BBoxMap: import("svelte").Component<$$ComponentProps, {}, "selectedBBox">;
|
7
|
+
type BBoxMap = ReturnType<typeof BBoxMap>;
|
19
8
|
export default BBoxMap;
|
@@ -1,41 +1,38 @@
|
|
1
1
|
<!-- BasicMap.svelte -->
|
2
|
-
<script lang="ts">
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
dispatch('mapReady', { map });
|
38
|
-
});
|
2
|
+
<script lang="ts">import 'maplibre-gl/dist/maplibre-gl.css';
|
3
|
+
import { getMapStyle, isDarkMode } from '../../utils/map_style.js';
|
4
|
+
// Props
|
5
|
+
let { style = 'position:absolute; left:0px; top:0px; width:100%; height:100%;', styleOptions = { transitionDuration: 0 }, mapOptions = {}, map = $bindable(), onMapInit, onMapLoad } = $props();
|
6
|
+
let container;
|
7
|
+
$effect(() => {
|
8
|
+
if (container)
|
9
|
+
init();
|
10
|
+
});
|
11
|
+
async function init() {
|
12
|
+
if (map)
|
13
|
+
return;
|
14
|
+
let MaplibreMap = (await import('maplibre-gl')).Map;
|
15
|
+
if (!container)
|
16
|
+
throw Error();
|
17
|
+
const darkMode = isDarkMode(container);
|
18
|
+
container.style.setProperty('--bg-color', darkMode ? '#000' : '#fff');
|
19
|
+
container.style.setProperty('--fg-color', darkMode ? '#fff' : '#000');
|
20
|
+
map = new MaplibreMap({
|
21
|
+
container,
|
22
|
+
style: getMapStyle(darkMode, styleOptions),
|
23
|
+
renderWorldCopies: false,
|
24
|
+
dragRotate: false,
|
25
|
+
attributionControl: { compact: false },
|
26
|
+
fadeDuration: 0,
|
27
|
+
...mapOptions
|
28
|
+
});
|
29
|
+
if (onMapInit)
|
30
|
+
onMapInit(map);
|
31
|
+
map.on('load', () => {
|
32
|
+
if (onMapLoad)
|
33
|
+
onMapLoad(map);
|
34
|
+
});
|
35
|
+
}
|
39
36
|
</script>
|
40
37
|
|
41
38
|
<div class="map" {style} bind:this={container}></div>
|
@@ -1,29 +1,14 @@
|
|
1
1
|
import type { Map as MaplibreMapType, MapOptions } from 'maplibre-gl';
|
2
2
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
3
|
-
import
|
4
|
-
|
5
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
6
|
-
$$bindings?: Bindings;
|
7
|
-
} & Exports;
|
8
|
-
(internal: unknown, props: Props & {
|
9
|
-
$$events?: Events;
|
10
|
-
$$slots?: Slots;
|
11
|
-
}): Exports & {
|
12
|
-
$set?: any;
|
13
|
-
$on?: any;
|
14
|
-
};
|
15
|
-
z_$$bindings?: Bindings;
|
16
|
-
}
|
17
|
-
declare const BasicMap: $$__sveltets_2_IsomorphicComponent<{
|
3
|
+
import { getMapStyle } from '../../utils/map_style.js';
|
4
|
+
type $$ComponentProps = {
|
18
5
|
style?: string;
|
19
|
-
|
20
|
-
map?: MaplibreMapType | undefined;
|
21
|
-
styleOptions?: StyleBuilderOptions;
|
6
|
+
styleOptions?: Parameters<typeof getMapStyle>[1];
|
22
7
|
mapOptions?: Partial<MapOptions>;
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
type BasicMap =
|
8
|
+
map?: MaplibreMapType;
|
9
|
+
onMapInit?: (map: MaplibreMapType) => void;
|
10
|
+
onMapLoad?: (map: MaplibreMapType) => void;
|
11
|
+
};
|
12
|
+
declare const BasicMap: import("svelte").Component<$$ComponentProps, {}, "map">;
|
13
|
+
type BasicMap = ReturnType<typeof BasicMap>;
|
29
14
|
export default BasicMap;
|
@@ -1,27 +1,68 @@
|
|
1
1
|
<!-- LocatorMap.svelte -->
|
2
|
-
<script lang="ts">
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
2
|
+
<script lang="ts">import 'maplibre-gl/dist/maplibre-gl.css';
|
3
|
+
import BasicMap from '../BasicMap/BasicMap.svelte';
|
4
|
+
let map;
|
5
|
+
function onMapInit(_map) {
|
6
|
+
map = _map;
|
7
|
+
map.on('load', async () => {
|
8
|
+
let coordinates = [0, 0];
|
9
|
+
const initialHash = parseHash();
|
10
|
+
if (initialHash) {
|
11
|
+
console.log('initialHash', initialHash);
|
12
|
+
coordinates = [initialHash[1], initialHash[2]];
|
13
|
+
map.setZoom(initialHash[0]);
|
14
|
+
map.setCenter(coordinates);
|
15
|
+
}
|
16
|
+
else {
|
17
|
+
map.on('move', () => {
|
18
|
+
const { lng, lat } = map.getCenter();
|
19
|
+
source.setData({
|
20
|
+
type: 'Feature',
|
21
|
+
geometry: { type: 'Point', coordinates: [lng, lat] },
|
22
|
+
properties: {}
|
23
|
+
});
|
24
|
+
});
|
25
|
+
map.on('moveend', () => updateHash());
|
26
|
+
}
|
27
|
+
map.addSource('marker', {
|
28
|
+
type: 'geojson',
|
29
|
+
data: { type: 'Feature', geometry: { type: 'Point', coordinates }, properties: {} }
|
30
|
+
});
|
31
|
+
const source = map.getSource('marker');
|
32
|
+
map.addLayer({
|
33
|
+
id: 'marker',
|
34
|
+
source: 'marker',
|
35
|
+
type: 'symbol',
|
36
|
+
layout: {
|
37
|
+
'icon-image': 'basics:icon-embassy',
|
38
|
+
'icon-size': 1,
|
39
|
+
'icon-overlap': 'always'
|
40
|
+
},
|
41
|
+
paint: {
|
42
|
+
'icon-color': '#FF0000',
|
43
|
+
'icon-halo-color': '#FFFFFF',
|
44
|
+
'icon-halo-width': 1,
|
45
|
+
'icon-halo-blur': 0
|
46
|
+
}
|
47
|
+
});
|
48
|
+
function parseHash() {
|
49
|
+
const hash = window.location.hash
|
50
|
+
.replace(/[^0-9/.]+/g, '')
|
51
|
+
.split('/')
|
52
|
+
.map(parseFloat);
|
53
|
+
return hash.length === 3 ? hash : undefined;
|
54
|
+
}
|
55
|
+
function updateHash() {
|
56
|
+
const center = map.getCenter();
|
57
|
+
const zoom = map.getZoom();
|
58
|
+
window.location.hash = `#${zoom.toFixed(2)}/${center.lng.toFixed(6)}/${center.lat.toFixed(6)}`;
|
59
|
+
}
|
60
|
+
});
|
61
|
+
}
|
21
62
|
</script>
|
22
63
|
|
23
64
|
<div class="container">
|
24
|
-
<BasicMap {
|
65
|
+
<BasicMap {onMapInit}></BasicMap>
|
25
66
|
</div>
|
26
67
|
|
27
68
|
<style>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<script lang="ts">import {} from './editor.scss';
|
2
|
+
import EditorMarker from './EditorMarker.svelte';
|
3
|
+
import EditorLine from './EditorLine.svelte';
|
4
|
+
import { LineElement } from './lib/element_line.js';
|
5
|
+
import { MarkerElement } from './lib/element_marker.js';
|
6
|
+
const { element } = $props();
|
7
|
+
let name = $state(element.name);
|
8
|
+
$effect(() => {
|
9
|
+
name = element.name;
|
10
|
+
});
|
11
|
+
$effect(() => {
|
12
|
+
//element.name = name;
|
13
|
+
});
|
14
|
+
</script>
|
15
|
+
|
16
|
+
{#if element}
|
17
|
+
<label for="input-name">Name</label>
|
18
|
+
<input id="input-name" type="text" bind:value={name} />
|
19
|
+
{#if element instanceof MarkerElement}
|
20
|
+
<EditorMarker {element} />
|
21
|
+
{/if}
|
22
|
+
{#if element instanceof LineElement}
|
23
|
+
<EditorLine {element} />
|
24
|
+
{/if}
|
25
|
+
{/if}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import type { AbstractElement } from './lib/element_abstract.js';
|
2
|
+
type $$ComponentProps = {
|
3
|
+
element: AbstractElement;
|
4
|
+
};
|
5
|
+
declare const Editor: import("svelte").Component<$$ComponentProps, {}, "">;
|
6
|
+
type Editor = ReturnType<typeof Editor>;
|
7
|
+
export default Editor;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<script lang="ts">import {} from './editor.scss';
|
2
|
+
import { get } from 'svelte/store';
|
3
|
+
import { dashArrays } from './lib/element_line.js';
|
4
|
+
const dashedList = Array.from(dashArrays.keys()).sort();
|
5
|
+
let { element } = $props();
|
6
|
+
let color = $state(get(element.color));
|
7
|
+
let width = $state(get(element.width));
|
8
|
+
let dashed = $state(get(element.dashed));
|
9
|
+
$effect(() => element.color.set(color));
|
10
|
+
$effect(() => element.width.set(width));
|
11
|
+
$effect(() => element.dashed.set(dashed));
|
12
|
+
</script>
|
13
|
+
|
14
|
+
<div class="row">
|
15
|
+
<label for="dashed-input">Dashed</label>
|
16
|
+
<select id="dashed-input" bind:value={dashed}>
|
17
|
+
{#each dashedList as d}
|
18
|
+
<option value={d}>{d}</option>
|
19
|
+
{/each}
|
20
|
+
</select>
|
21
|
+
|
22
|
+
<label for="color-input">Color</label>
|
23
|
+
<input id="color-input" type="color" bind:value={color} />
|
24
|
+
|
25
|
+
<label for="width-input">Width</label>
|
26
|
+
<input id="width-input" type="range" min="0.5" max="5" step="0.5" bind:value={width} />
|
27
|
+
</div>
|