@versatiles/svelte 0.3.0 → 1.0.0

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.
Files changed (47) hide show
  1. package/README.md +11 -6
  2. package/dist/components/BBoxMap/AutoComplete.svelte +176 -0
  3. package/dist/components/{AutoComplete.svelte.d.ts → BBoxMap/AutoComplete.svelte.d.ts} +12 -18
  4. package/dist/components/BBoxMap/BBoxMap.svelte +43 -54
  5. package/dist/components/BBoxMap/BBoxMap.svelte.d.ts +4 -17
  6. package/dist/components/BasicMap/BasicMap.svelte +34 -37
  7. package/dist/components/BasicMap/BasicMap.svelte.d.ts +9 -24
  8. package/dist/components/LocatorMap/LocatorMap.svelte +61 -20
  9. package/dist/components/MapEditor/Editor.svelte +25 -0
  10. package/dist/components/MapEditor/Editor.svelte.d.ts +7 -0
  11. package/dist/components/MapEditor/EditorLine.svelte +27 -0
  12. package/dist/components/MapEditor/EditorLine.svelte.d.ts +7 -0
  13. package/dist/components/MapEditor/EditorMarker.svelte +42 -0
  14. package/dist/components/MapEditor/EditorMarker.svelte.d.ts +7 -0
  15. package/dist/components/MapEditor/MapEditor.svelte +99 -0
  16. package/dist/components/MapEditor/MapEditor.svelte.d.ts +4 -0
  17. package/dist/components/MapEditor/editor.scss +16 -0
  18. package/dist/components/MapEditor/lib/element_abstract.d.ts +20 -0
  19. package/dist/components/MapEditor/lib/element_abstract.js +58 -0
  20. package/dist/components/MapEditor/lib/element_line.d.ts +14 -0
  21. package/dist/components/MapEditor/lib/element_line.js +76 -0
  22. package/dist/components/MapEditor/lib/element_marker.d.ts +20 -0
  23. package/dist/components/MapEditor/lib/element_marker.js +191 -0
  24. package/dist/components/MapEditor/lib/geocoder.d.ts +1 -0
  25. package/dist/components/MapEditor/lib/geocoder.js +8 -0
  26. package/dist/components/MapEditor/lib/geometry_manager.d.ts +18 -0
  27. package/dist/components/MapEditor/lib/geometry_manager.js +117 -0
  28. package/dist/components/MapEditor/lib/map_layer.d.ts +14 -0
  29. package/dist/components/MapEditor/lib/map_layer.js +61 -0
  30. package/dist/components/MapEditor/lib/types.d.ts +112 -0
  31. package/dist/components/MapEditor/lib/types.js +1 -0
  32. package/dist/components/MapEditor/lib/utils.d.ts +2 -0
  33. package/dist/components/MapEditor/lib/utils.js +11 -0
  34. package/dist/index.d.ts +2 -1
  35. package/dist/index.js +2 -1
  36. package/dist/utils/draw/bbox.d.ts +2 -9
  37. package/dist/utils/draw/bbox.js +8 -13
  38. package/dist/utils/map_style.d.ts +5 -2
  39. package/dist/utils/map_style.js +13 -6
  40. package/package.json +9 -6
  41. package/dist/components/AutoComplete.svelte +0 -196
  42. package/dist/utils/draw/abstract.d.ts +0 -5
  43. package/dist/utils/draw/abstract.js +0 -2
  44. package/dist/utils/draw/marker.d.ts +0 -24
  45. package/dist/utils/draw/marker.js +0 -97
  46. package/dist/utils/draw/style.d.ts +0 -19
  47. 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
- placeholder?: string;
4
- minChar?: number;
5
- maxItems?: number;
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
- setInputText?: ((text: string) => void) | undefined;
11
- };
12
- events(): {
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(): string;
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
- import type { CameraOptions, Map as MaplibreMapType } from 'maplibre-gl';
4
- import 'maplibre-gl/dist/maplibre-gl.css';
5
- import AutoComplete from '../AutoComplete.svelte';
6
- import { getCountryName } from '../../utils/location.js';
7
- import BasicMap from '../BasicMap/BasicMap.svelte';
8
- import { isDarkMode } from '../../utils/map_style.js';
9
- import type { BBox } from 'geojson';
10
- import { loadBBoxes } from './BBoxMap.js';
11
- import { BBoxDrawer } from '../../utils/draw/bbox.js';
12
-
13
- let bboxes: { key: string; value: BBox }[] | undefined = undefined;
14
- let mapContainer: HTMLDivElement;
15
- let autoComplete: AutoComplete<BBox> | undefined = undefined;
16
- const startTime = Date.now();
17
- let bbox: BBoxDrawer;
18
- let map: MaplibreMapType; // Declare map instance at the top level
19
-
20
- function handleMapReady(event: CustomEvent) {
21
- map = event.detail.map;
22
- map.setPadding({ top: 31 + 5, right: 5, bottom: 5, left: 5 });
23
-
24
- map.on('load', async () => {
25
- bbox = new BBoxDrawer(map, {
26
- color: isDarkMode(mapContainer) ? '#FFFFFF' : '#000000'
27
- });
28
- bboxes = await loadBBoxes();
29
- });
30
- }
31
-
32
- function flyTo(newBBox: BBox) {
33
- if (bbox) {
34
- bbox.setGeometry(newBBox);
35
-
36
- const transform = map.cameraForBounds(bbox.getBounds()) as CameraOptions;
37
- if (transform == null) return;
38
- transform.zoom = transform.zoom ?? 0 - 0.5;
39
- transform.bearing = 0;
40
- transform.pitch = 0;
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 bbox;
12
+ let map = $state();
13
+ let bboxes = $state();
14
+ let mapContainer;
15
+ function onMapInit(_map) {
16
+ map = _map;
17
+ mapContainer = map.getContainer();
18
+ map.setPadding({ top: 31 + 5, right: 5, bottom: 5, left: 5 });
19
+ map.on('load', async () => {
20
+ bbox = new BBoxDrawer(map, [-180, -86, 180, 86], isDarkMode(mapContainer) ? '#FFFFFF' : '#000000');
21
+ bboxes = await loadBBoxes();
22
+ });
23
+ }
24
+ function setBBox(newBBox) {
25
+ selectedBBox = newBBox;
26
+ if (bbox && map) {
27
+ bbox.setGeometry(newBBox);
28
+ const transform = map.cameraForBounds(bbox.getBounds());
29
+ if (transform == null)
30
+ return;
31
+ transform.zoom = transform.zoom ?? 0 - 0.5;
32
+ transform.bearing = 0;
33
+ transform.pitch = 0;
34
+ if (Date.now() - startTime < 1000) {
35
+ map.jumpTo(transform);
36
+ }
37
+ else {
38
+ map.flyTo({ ...transform, essential: true, speed: 5 });
39
+ }
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
- on:change={(e) => flyTo(e.detail)}
62
- bind:this={autoComplete}
50
+ change={setBBox}
51
+ initialInputText={getCountryName() ?? ''}
63
52
  />
64
53
  </div>
65
54
  {/if}
66
- <BasicMap {map} bind:container={mapContainer} on:mapReady={handleMapReady}></BasicMap>
55
+ <BasicMap {onMapInit}></BasicMap>
67
56
  </div>
68
57
 
69
58
  <style>
@@ -1,19 +1,6 @@
1
1
  import 'maplibre-gl/dist/maplibre-gl.css';
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: {
7
- $$events?: Events;
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
+ declare const BBoxMap: import("svelte").Component<{
3
+ selectedBBox?: any;
4
+ }, {}, "selectedBBox">;
5
+ type BBoxMap = ReturnType<typeof BBoxMap>;
19
6
  export default BBoxMap;
@@ -1,41 +1,38 @@
1
1
  <!-- BasicMap.svelte -->
2
- <script lang="ts">
3
- import type { Map as MaplibreMapType, MapOptions } from 'maplibre-gl';
4
- import { onMount, createEventDispatcher } from 'svelte';
5
- import 'maplibre-gl/dist/maplibre-gl.css';
6
- import { getMapStyle, isDarkMode } from '../../utils/map_style.js';
7
- import type { StyleBuilderOptions } from '@versatiles/style';
8
-
9
- // Props
10
- export let style: string = 'position:absolute; left:0px; top:0px; width:100%; height:100%;';
11
- export let container: HTMLDivElement | undefined = undefined;
12
- export let map: MaplibreMapType | undefined = undefined;
13
- export let styleOptions: StyleBuilderOptions = {};
14
- export let mapOptions: Partial<MapOptions> = {};
15
-
16
- // Create the event dispatcher
17
- const dispatch = createEventDispatcher();
18
-
19
- onMount(async (): Promise<void> => {
20
- let MaplibreMap: typeof MaplibreMapType = (await import('maplibre-gl')).Map;
21
-
22
- if (!container) throw Error();
23
-
24
- const darkMode = isDarkMode(container);
25
- container.style.setProperty('--bg-color', darkMode ? '#000' : '#fff');
26
- container.style.setProperty('--fg-color', darkMode ? '#fff' : '#000');
27
-
28
- map = new MaplibreMap({
29
- container,
30
- style: getMapStyle(darkMode, styleOptions),
31
- renderWorldCopies: false,
32
- dragRotate: false,
33
- attributionControl: { compact: false },
34
- ...mapOptions
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 type { StyleBuilderOptions } from '@versatiles/style';
4
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
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
- container?: HTMLDivElement | undefined;
20
- map?: MaplibreMapType | undefined;
21
- styleOptions?: StyleBuilderOptions;
6
+ styleOptions?: Parameters<typeof getMapStyle>[1];
22
7
  mapOptions?: Partial<MapOptions>;
23
- }, {
24
- mapReady: CustomEvent<any>;
25
- } & {
26
- [evt: string]: CustomEvent<any>;
27
- }, {}, {}, string>;
28
- type BasicMap = InstanceType<typeof 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
- import type { Map as MaplibreMapType } from 'maplibre-gl';
4
- import 'maplibre-gl/dist/maplibre-gl.css';
5
- import BasicMap from '../BasicMap/BasicMap.svelte';
6
- //import SpriteLibrary from '../../utils/sprite_library.js';
7
- import { MarkerDrawer } from '../../utils/draw/marker.js';
8
-
9
- let mapContainer: HTMLDivElement;
10
- let map: MaplibreMapType;
11
- //let spriteLibrary = new SpriteLibrary();
12
- let markers = [];
13
-
14
- function handleMapReady(event: CustomEvent) {
15
- map = event.detail.map;
16
- map.on('load', async () => {
17
- //const list = await spriteLibrary.getSpriteList();
18
- markers.push(new MarkerDrawer(map, { point: [25, 22] }));
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 {map} bind:container={mapContainer} on:mapReady={handleMapReady}></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>