@versatiles/svelte 2.1.8 → 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.
@@ -46,7 +46,7 @@
46
46
  }
47
47
 
48
48
  function onFocus() {
49
- inputElement.setSelectionRange(0, 1000);
49
+ inputElement.setSelectionRange(0, inputElement.value.length);
50
50
  }
51
51
 
52
52
  // Filter results based on search query
@@ -105,7 +105,7 @@
105
105
  });
106
106
  </script>
107
107
 
108
- <svelte:window on:click={() => close()} />
108
+ <svelte:window onclick={() => close()} />
109
109
 
110
110
  <div class="autocomplete" bind:this={autocompleteElement}>
111
111
  <input
@@ -122,7 +122,7 @@
122
122
  aria-autocomplete="list"
123
123
  aria-controls="autocomplete-results"
124
124
  />
125
- <div class="autocomplete-results" class:hide-results={!isOpen}>
125
+ <div class="autocomplete-results" class:hide-results={!isOpen} id="autocomplete-results" role="listbox">
126
126
  {#each results as result, i (result.key)}
127
127
  <button
128
128
  onclick={() => close(i)}
@@ -4,6 +4,7 @@
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
 
@@ -15,6 +16,7 @@
15
16
  onMapLoad?: (map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) => void;
16
17
  } = $props();
17
18
 
19
+ const bboxPadding = 20;
18
20
  const startTime = Date.now();
19
21
  let bboxDrawer: BBoxDrawer | undefined;
20
22
  let map: MaplibreMapType | undefined = $state();
@@ -56,9 +58,8 @@
56
58
  if (!bboxDrawer || !map || !selectedBBox) return;
57
59
  if (disableZoomTimeout) return;
58
60
 
59
- const transform = map.cameraForBounds(selectedBBox) as CameraOptions;
61
+ const transform = map.cameraForBounds(selectedBBox, { padding: bboxPadding }) as CameraOptions;
60
62
  if (transform == null) return;
61
- transform.zoom = transform.zoom ?? 0 - 0.5;
62
63
  transform.bearing = 0;
63
64
  transform.pitch = 0;
64
65
 
@@ -88,17 +89,41 @@
88
89
  if (disableZoomTimeout) clearTimeout(disableZoomTimeout);
89
90
  disableZoomTimeout = setTimeout(() => (disableZoomTimeout = undefined), 100);
90
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
+ });
91
106
  </script>
92
107
 
93
108
  <div class="container">
94
109
  {#if bboxes}
95
- <div class="input">
96
- <AutoComplete
97
- items={bboxes}
98
- placeholder="Find country, region or city …"
99
- change={(bbox) => (selectedBBox = bbox)}
100
- initialInputText={getInitialInputText()}
101
- />
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>
102
127
  </div>
103
128
  {/if}
104
129
  <BasicMap {onMapInit} {onMapLoad}></BasicMap>
@@ -113,11 +138,34 @@
113
138
  top: 0;
114
139
  min-height: 6em;
115
140
  }
116
- .input {
141
+ .toolbar {
117
142
  position: absolute;
118
143
  top: 0.5em;
119
144
  left: 0.5em;
120
145
  right: 0.5em;
121
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);
122
170
  }
123
171
  </style>
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versatiles/svelte",
3
- "version": "2.1.8",
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
  }