@versatiles/svelte 2.1.7 → 2.2.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.
@@ -34,19 +34,6 @@
34
34
  // Escape special characters in search string for use in regex
35
35
  const regExpEscape = (s: string) => s.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
36
36
 
37
- $effect(() => {
38
- if (inputText.length >= minChar) {
39
- const r = filterResults();
40
- if (r.length > 0) {
41
- const { key, value } = r[0];
42
- inputText = key;
43
- change(JSON.parse(JSON.stringify(value)));
44
- } else {
45
- inputText = '';
46
- }
47
- }
48
- });
49
-
50
37
  // Handle input change
51
38
  function onChange() {
52
39
  if (inputText.length >= minChar) {
@@ -59,7 +46,7 @@
59
46
  }
60
47
 
61
48
  function onFocus() {
62
- inputElement.setSelectionRange(0, 1000);
49
+ inputElement.setSelectionRange(0, inputElement.value.length);
63
50
  }
64
51
 
65
52
  // Filter results based on search query
@@ -118,7 +105,7 @@
118
105
  });
119
106
  </script>
120
107
 
121
- <svelte:window on:click={() => close()} />
108
+ <svelte:window onclick={() => close()} />
122
109
 
123
110
  <div class="autocomplete" bind:this={autocompleteElement}>
124
111
  <input
@@ -135,7 +122,7 @@
135
122
  aria-autocomplete="list"
136
123
  aria-controls="autocomplete-results"
137
124
  />
138
- <div class="autocomplete-results" class:hide-results={!isOpen}>
125
+ <div class="autocomplete-results" class:hide-results={!isOpen} id="autocomplete-results" role="listbox">
139
126
  {#each results as result, i (result.key)}
140
127
  <button
141
128
  onclick={() => close(i)}
@@ -4,11 +4,19 @@
4
4
  import { getCountryName } from '../../utils/location.js';
5
5
  import BasicMap from '../BasicMap/BasicMap.svelte';
6
6
  import { isDarkMode } from '../../utils/map_style.js';
7
+ import { onDestroy } from 'svelte';
7
8
  import { loadBBoxes } from './BBoxMap.js';
8
9
  import { BBoxDrawer, isSameBBox, type BBox } from './lib/bbox_drawer.js';
9
10
 
10
- let { selectedBBox = $bindable() }: { selectedBBox?: BBox } = $props();
11
+ let {
12
+ selectedBBox = $bindable(),
13
+ onMapLoad
14
+ }: {
15
+ selectedBBox?: BBox;
16
+ onMapLoad?: (map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) => void;
17
+ } = $props();
11
18
 
19
+ const bboxPadding = 20;
12
20
  const startTime = Date.now();
13
21
  let bboxDrawer: BBoxDrawer | undefined;
14
22
  let map: MaplibreMapType | undefined = $state();
@@ -50,9 +58,8 @@
50
58
  if (!bboxDrawer || !map || !selectedBBox) return;
51
59
  if (disableZoomTimeout) return;
52
60
 
53
- const transform = map.cameraForBounds(selectedBBox) as CameraOptions;
61
+ const transform = map.cameraForBounds(selectedBBox, { padding: bboxPadding }) as CameraOptions;
54
62
  if (transform == null) return;
55
- transform.zoom = transform.zoom ?? 0 - 0.5;
56
63
  transform.bearing = 0;
57
64
  transform.pitch = 0;
58
65
 
@@ -82,20 +89,44 @@
82
89
  if (disableZoomTimeout) clearTimeout(disableZoomTimeout);
83
90
  disableZoomTimeout = setTimeout(() => (disableZoomTimeout = undefined), 100);
84
91
  }
92
+
93
+ function selectVisibleArea() {
94
+ if (!map) return;
95
+ const pad = map.getPadding();
96
+ const { clientWidth: w, clientHeight: h } = map.getContainer();
97
+ const nw = map.unproject([(pad.left ?? 0) + bboxPadding, (pad.top ?? 0) + bboxPadding]);
98
+ const se = map.unproject([w - (pad.right ?? 0) - bboxPadding, h - (pad.bottom ?? 0) - bboxPadding]);
99
+ const round = (v: number) => Math.round(v * 1e3) / 1e3;
100
+ selectedBBox = [round(nw.lng), round(se.lat), round(se.lng), round(nw.lat)];
101
+ }
102
+
103
+ onDestroy(() => {
104
+ bboxDrawer?.destroy();
105
+ });
85
106
  </script>
86
107
 
87
108
  <div class="container">
88
109
  {#if bboxes}
89
- <div class="input">
90
- <AutoComplete
91
- items={bboxes}
92
- placeholder="Find country, region or city …"
93
- change={(bbox) => (selectedBBox = bbox)}
94
- initialInputText={getInitialInputText()}
95
- />
110
+ <div class="toolbar">
111
+ <div class="input">
112
+ <AutoComplete
113
+ items={bboxes}
114
+ placeholder="Find country, region or city …"
115
+ change={(bbox) => (selectedBBox = bbox)}
116
+ initialInputText={getInitialInputText()}
117
+ />
118
+ </div>
119
+ <button class="select-visible" onclick={selectVisibleArea} title="Use visible area as bounding box">
120
+ <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2">
121
+ <path d="M3 8V5a2 2 0 0 1 2-2h3" />
122
+ <path d="M21 8V5a2 2 0 0 0-2-2h-3" />
123
+ <path d="M3 16v3a2 2 0 0 0 2 2h3" />
124
+ <path d="M21 16v3a2 2 0 0 1-2 2h-3" />
125
+ </svg>
126
+ </button>
96
127
  </div>
97
128
  {/if}
98
- <BasicMap {onMapInit}></BasicMap>
129
+ <BasicMap {onMapInit} {onMapLoad}></BasicMap>
99
130
  </div>
100
131
 
101
132
  <style>
@@ -107,11 +138,34 @@
107
138
  top: 0;
108
139
  min-height: 6em;
109
140
  }
110
- .input {
141
+ .toolbar {
111
142
  position: absolute;
112
143
  top: 0.5em;
113
144
  left: 0.5em;
114
145
  right: 0.5em;
115
146
  z-index: 10;
147
+ display: flex;
148
+ gap: 0.3em;
149
+ align-items: flex-start;
150
+ }
151
+ .input {
152
+ flex: 1;
153
+ min-width: 0;
154
+ }
155
+ .select-visible {
156
+ flex-shrink: 0;
157
+ padding: 0.25em;
158
+ border: none;
159
+ border-radius: 0.5em;
160
+ background: color-mix(in srgb, var(--bg-color) 80%, transparent);
161
+ color: var(--fg-color);
162
+ cursor: pointer;
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ line-height: 0;
167
+ }
168
+ .select-visible:hover {
169
+ background: color-mix(in srgb, var(--bg-color) 95%, transparent);
116
170
  }
117
171
  </style>
@@ -1,6 +1,8 @@
1
+ import type { Map as MaplibreMapType } from 'maplibre-gl';
1
2
  import { type BBox } from './lib/bbox_drawer.js';
2
3
  type $$ComponentProps = {
3
4
  selectedBBox?: BBox;
5
+ onMapLoad?: (map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) => void;
4
6
  };
5
7
  declare const BBoxMap: import("svelte").Component<$$ComponentProps, {}, "selectedBBox">;
6
8
  type BBoxMap = ReturnType<typeof BBoxMap>;
@@ -14,10 +14,15 @@ export declare class BBoxDrawer extends EventHandler<{
14
14
  }> {
15
15
  #private;
16
16
  private sourceId;
17
+ private lineLayerId;
18
+ private fillLayerId;
17
19
  private dragPoint;
18
20
  private isDragging;
19
21
  private map;
20
22
  private canvas;
23
+ private handleMouseMove;
24
+ private handleMouseDown;
25
+ private handleMouseUp;
21
26
  private insideOut;
22
27
  constructor(map: maplibregl.Map, initialBBox: BBox, color: string, insideOut?: boolean);
23
28
  private updateDragPoint;
@@ -28,6 +33,7 @@ export declare class BBoxDrawer extends EventHandler<{
28
33
  private getAsPixel;
29
34
  private checkDragPointAt;
30
35
  private getCursor;
36
+ destroy(): void;
31
37
  private doDrag;
32
38
  }
33
39
  export declare function isSameBBox(a: geojson.BBox, b: geojson.BBox): boolean;
@@ -2,7 +2,7 @@ import { EventHandler } from '../../../utils/event_handler.js';
2
2
  // prettier-ignore
3
3
  export const DragPointMap = new Map([
4
4
  ['n', { cursor: 'ns-resize', flipH: 'n', flipV: 's' }],
5
- ['ne', { cursor: 'nesw-resize', flipH: 'n', flipV: 'se' }],
5
+ ['ne', { cursor: 'nesw-resize', flipH: 'nw', flipV: 'se' }],
6
6
  ['e', { cursor: 'ew-resize', flipH: 'w', flipV: 'e' }],
7
7
  ['se', { cursor: 'nwse-resize', flipH: 'sw', flipV: 'ne' }],
8
8
  ['s', { cursor: 'ns-resize', flipH: 's', flipV: 'n' }],
@@ -14,10 +14,15 @@ export const DragPointMap = new Map([
14
14
  const worldBBox = [-180, -85, 180, 85];
15
15
  export class BBoxDrawer extends EventHandler {
16
16
  sourceId;
17
+ lineLayerId;
18
+ fillLayerId;
17
19
  dragPoint = false;
18
20
  isDragging = false;
19
21
  map;
20
22
  canvas;
23
+ handleMouseMove;
24
+ handleMouseDown;
25
+ handleMouseUp;
21
26
  insideOut;
22
27
  #bbox;
23
28
  constructor(map, initialBBox, color, insideOut) {
@@ -25,10 +30,13 @@ export class BBoxDrawer extends EventHandler {
25
30
  this.#bbox = [...initialBBox];
26
31
  this.insideOut = insideOut ?? true;
27
32
  this.map = map;
28
- this.sourceId = 'bbox_' + Math.random().toString(36).slice(2);
33
+ const uid = Math.random().toString(36).slice(2);
34
+ this.sourceId = 'bbox_' + uid;
35
+ this.lineLayerId = 'bbox-line_' + uid;
36
+ this.fillLayerId = 'bbox-fill_' + uid;
29
37
  map.addSource(this.sourceId, { type: 'geojson', data: this.getAsFeatureCollection() });
30
38
  map.addLayer({
31
- id: 'bbox-line_' + Math.random().toString(36).slice(2),
39
+ id: this.lineLayerId,
32
40
  type: 'line',
33
41
  source: this.sourceId,
34
42
  filter: ['==', '$type', 'LineString'],
@@ -36,7 +44,7 @@ export class BBoxDrawer extends EventHandler {
36
44
  paint: { 'line-color': color }
37
45
  });
38
46
  map.addLayer({
39
- id: 'bbox-fill_' + Math.random().toString(36).slice(2),
47
+ id: this.fillLayerId,
40
48
  type: 'fill',
41
49
  source: this.sourceId,
42
50
  filter: ['==', '$type', 'Polygon'],
@@ -44,7 +52,7 @@ export class BBoxDrawer extends EventHandler {
44
52
  paint: { 'fill-color': color, 'fill-opacity': 0.2 }
45
53
  });
46
54
  this.canvas = map.getCanvasContainer();
47
- map.on('mousemove', (e) => {
55
+ this.handleMouseMove = (e) => {
48
56
  if (e.originalEvent.buttons % 2 === 0)
49
57
  return this.checkDragPointAt(e.point);
50
58
  if (!this.isDragging)
@@ -55,8 +63,8 @@ export class BBoxDrawer extends EventHandler {
55
63
  this.redraw();
56
64
  e.preventDefault();
57
65
  this.emit('drag', [...this.#bbox]);
58
- });
59
- map.on('mousedown', (e) => {
66
+ };
67
+ this.handleMouseDown = (e) => {
60
68
  if (this.isDragging)
61
69
  return;
62
70
  if (e.originalEvent.buttons % 2) {
@@ -66,12 +74,17 @@ export class BBoxDrawer extends EventHandler {
66
74
  if (this.isDragging)
67
75
  e.preventDefault();
68
76
  }
69
- });
70
- map.on('mouseup', () => {
77
+ };
78
+ this.handleMouseUp = () => {
79
+ if (!this.isDragging)
80
+ return;
71
81
  this.isDragging = false;
72
82
  this.updateDragPoint(false);
73
83
  this.emit('dragEnd', [...this.#bbox]);
74
- });
84
+ };
85
+ map.on('mousemove', this.handleMouseMove);
86
+ map.on('mousedown', this.handleMouseDown);
87
+ map.on('mouseup', this.handleMouseUp);
75
88
  }
76
89
  updateDragPoint(dragPoint) {
77
90
  if (this.dragPoint === dragPoint)
@@ -146,6 +159,19 @@ export class BBoxDrawer extends EventHandler {
146
159
  getCursor(drag) {
147
160
  return DragPointMap.get(drag)?.cursor ?? 'default';
148
161
  }
162
+ destroy() {
163
+ this.map.off('mousemove', this.handleMouseMove);
164
+ this.map.off('mousedown', this.handleMouseDown);
165
+ this.map.off('mouseup', this.handleMouseUp);
166
+ if (this.map.getLayer(this.lineLayerId))
167
+ this.map.removeLayer(this.lineLayerId);
168
+ if (this.map.getLayer(this.fillLayerId))
169
+ this.map.removeLayer(this.fillLayerId);
170
+ if (this.map.getSource(this.sourceId))
171
+ this.map.removeSource(this.sourceId);
172
+ this.canvas.style.cursor = '';
173
+ this.clear();
174
+ }
149
175
  doDrag(lngLat) {
150
176
  this.#bbox = ((bbox) => {
151
177
  const x = Math.round(lngLat.lng * 1e3) / 1e3;
@@ -64,7 +64,6 @@
64
64
  if (!map!.loaded()) return;
65
65
  triggeredMapReady = true;
66
66
  if (onMapLoad) onMapLoad(map!, maplibre);
67
- setTimeout(() => console.log('map_ready'), 100);
68
67
  }
69
68
  }
70
69
  </script>
@@ -3,6 +3,12 @@
3
3
  import BasicMap from '../BasicMap/BasicMap.svelte';
4
4
  let map: MaplibreMapType;
5
5
 
6
+ let {
7
+ onMapLoad
8
+ }: {
9
+ onMapLoad?: (map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) => void;
10
+ } = $props();
11
+
6
12
  function onMapInit(_map: MaplibreMapType) {
7
13
  map = _map;
8
14
  map.on('load', async () => {
@@ -66,7 +72,7 @@
66
72
  </script>
67
73
 
68
74
  <div class="container">
69
- <BasicMap {onMapInit}></BasicMap>
75
+ <BasicMap {onMapInit} {onMapLoad}></BasicMap>
70
76
  </div>
71
77
 
72
78
  <style>
@@ -1,18 +1,7 @@
1
- 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> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const LocatorMap: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type LocatorMap = InstanceType<typeof LocatorMap>;
1
+ import type { Map as MaplibreMapType } from 'maplibre-gl';
2
+ type $$ComponentProps = {
3
+ onMapLoad?: (map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) => void;
4
+ };
5
+ declare const LocatorMap: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type LocatorMap = ReturnType<typeof LocatorMap>;
18
7
  export default LocatorMap;
@@ -7,6 +7,12 @@
7
7
  import { GeometryManagerInteractive } from './lib/geometry_manager_interactive.js';
8
8
  import { StateReader } from './lib/state/reader.js';
9
9
 
10
+ let {
11
+ onMapLoad
12
+ }: {
13
+ onMapLoad?: (map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) => void;
14
+ } = $props();
15
+
10
16
  let showSidebar = $state(false);
11
17
  let geometryManager: GeometryManager | GeometryManagerInteractive | undefined = $state();
12
18
 
@@ -52,6 +58,7 @@
52
58
  <div class="container">
53
59
  <BasicMap
54
60
  {onMapInit}
61
+ {onMapLoad}
55
62
  emptyStyle={true}
56
63
  mapOptions={{ attributionControl: false }}
57
64
  styleOptions={{ darkMode: false }}
@@ -1,3 +1,7 @@
1
- declare const MapEditor: import("svelte").Component<Record<string, never>, {}, "">;
1
+ import type { Map as MaplibreMapType } from 'maplibre-gl';
2
+ type $$ComponentProps = {
3
+ onMapLoad?: (map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) => void;
4
+ };
5
+ declare const MapEditor: import("svelte").Component<$$ComponentProps, {}, "">;
2
6
  type MapEditor = ReturnType<typeof MapEditor>;
3
7
  export default MapEditor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versatiles/svelte",
3
- "version": "2.1.7",
3
+ "version": "2.2.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "build": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && vite build && npm run package",
@@ -42,42 +42,42 @@
42
42
  "svelte": "^5.45.8"
43
43
  },
44
44
  "devDependencies": {
45
- "@playwright/test": "^1.57.0",
45
+ "@playwright/test": "^1.58.1",
46
46
  "@sveltejs/adapter-static": "^3.0.10",
47
- "@sveltejs/kit": "^2.50.0",
47
+ "@sveltejs/kit": "^2.50.2",
48
48
  "@sveltejs/package": "^2.5.7",
49
49
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
50
- "@turf/turf": "^7.3.2",
50
+ "@turf/turf": "^7.3.3",
51
51
  "@types/eslint": "^9.6.1",
52
- "@types/node": "^25.0.9",
53
- "@versatiles/release-tool": "^2.5.0",
54
- "@vitest/coverage-v8": "^4.0.17",
52
+ "@types/node": "^25.2.1",
53
+ "@versatiles/release-tool": "^2.7.0",
54
+ "@vitest/coverage-v8": "^4.0.18",
55
55
  "cookie": "^1.1.1",
56
56
  "eslint": "^9.39.2",
57
57
  "eslint-config-prettier": "^10.1.8",
58
58
  "eslint-plugin-svelte": "^3.14.0",
59
59
  "geojson": "^0.5.0",
60
- "globals": "^17.0.0",
61
- "happy-dom": "^20.3.4",
60
+ "globals": "^17.3.0",
61
+ "happy-dom": "^20.5.0",
62
62
  "husky": "^9.1.7",
63
- "prettier": "^3.8.0",
63
+ "prettier": "^3.8.1",
64
64
  "prettier-plugin-svelte": "^3.4.1",
65
- "publint": "^0.3.16",
66
- "sass-embedded": "^1.97.2",
67
- "svelte": "^5.47.1",
68
- "svelte-check": "^4.3.5",
65
+ "publint": "^0.3.17",
66
+ "sass-embedded": "^1.97.3",
67
+ "svelte": "^5.49.2",
68
+ "svelte-check": "^4.3.6",
69
69
  "svelte-preprocess": "^6.0.3",
70
70
  "tsx": "^4.21.0",
71
71
  "typescript": "^5.9.3",
72
- "typescript-eslint": "^8.53.1",
72
+ "typescript-eslint": "^8.54.0",
73
73
  "vite": "^7.3.1",
74
- "vitest": "^4.0.17"
74
+ "vitest": "^4.0.18"
75
75
  },
76
76
  "svelte": "./dist/index.js",
77
77
  "types": "./dist/index.d.ts",
78
78
  "type": "module",
79
79
  "dependencies": {
80
80
  "@versatiles/style": "^5.8.4",
81
- "maplibre-gl": "^5.16.0"
81
+ "maplibre-gl": "^5.17.0"
82
82
  }
83
83
  }