@versatiles/svelte 0.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 +58 -0
- package/dist/AutoComplete/AutoComplete.svelte +159 -0
- package/dist/AutoComplete/AutoComplete.svelte.d.ts +25 -0
- package/dist/BBoxMap/BBoxMap.d.ts +15 -0
- package/dist/BBoxMap/BBoxMap.js +209 -0
- package/dist/BBoxMap/BBoxMap.svelte +155 -0
- package/dist/BBoxMap/BBoxMap.svelte.d.ts +20 -0
- package/dist/BBoxMap/README.md +70 -0
- package/dist/BBoxMap/bboxes.json +2195 -0
- package/dist/BBoxMap/data/countries.jsonl +258 -0
- package/dist/BBoxMap/data/eu.jsonl +1876 -0
- package/dist/BBoxMap/data/us.jsonl +52 -0
- package/dist/BBoxMap/data/world.jsonl +7 -0
- package/dist/BBoxMap/helpers/geojson2bboxes.d.ts +2 -0
- package/dist/BBoxMap/helpers/geojson2bboxes.js +183 -0
- package/dist/BBoxMap/helpers/merge_bboxes.d.ts +2 -0
- package/dist/BBoxMap/helpers/merge_bboxes.js +84 -0
- package/dist/BBoxMap/helpers/population.raw.br +0 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/utils/location.d.ts +3 -0
- package/dist/utils/location.js +31 -0
- package/dist/utils/style.d.ts +3 -0
- package/dist/utils/style.js +20 -0
- package/dist/utils/zones.d.ts +1 -0
- package/dist/utils/zones.js +402 -0
- package/package.json +70 -0
package/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# create-svelte
|
2
|
+
|
3
|
+
Everything you need to build a Svelte library, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
|
4
|
+
|
5
|
+
Read more about creating a library [in the docs](https://kit.svelte.dev/docs/packaging).
|
6
|
+
|
7
|
+
## Creating a project
|
8
|
+
|
9
|
+
If you're seeing this, you've probably already done this step. Congrats!
|
10
|
+
|
11
|
+
```bash
|
12
|
+
# create a new project in the current directory
|
13
|
+
npm create svelte@latest
|
14
|
+
|
15
|
+
# create a new project in my-app
|
16
|
+
npm create svelte@latest my-app
|
17
|
+
```
|
18
|
+
|
19
|
+
## Developing
|
20
|
+
|
21
|
+
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
npm run dev
|
25
|
+
|
26
|
+
# or start the server and open the app in a new browser tab
|
27
|
+
npm run dev -- --open
|
28
|
+
```
|
29
|
+
|
30
|
+
Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
|
31
|
+
|
32
|
+
## Building
|
33
|
+
|
34
|
+
To build your library:
|
35
|
+
|
36
|
+
```bash
|
37
|
+
npm run package
|
38
|
+
```
|
39
|
+
|
40
|
+
To create a production version of your showcase app:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
npm run build
|
44
|
+
```
|
45
|
+
|
46
|
+
You can preview the production build with `npm run preview`.
|
47
|
+
|
48
|
+
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
49
|
+
|
50
|
+
## Publishing
|
51
|
+
|
52
|
+
Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
|
53
|
+
|
54
|
+
To publish your library to [npm](https://www.npmjs.com):
|
55
|
+
|
56
|
+
```bash
|
57
|
+
npm publish
|
58
|
+
```
|
@@ -0,0 +1,159 @@
|
|
1
|
+
<!-- AutoComplete.svelte -->
|
2
|
+
<script generics="T">import { createEventDispatcher } from "svelte";
|
3
|
+
const dispatch = createEventDispatcher();
|
4
|
+
export let placeholder = "";
|
5
|
+
export let minChar = 0;
|
6
|
+
export let maxItems = 10;
|
7
|
+
export let initialText = "";
|
8
|
+
export let items;
|
9
|
+
let input;
|
10
|
+
let isOpen = false;
|
11
|
+
let results = [];
|
12
|
+
let inputText = initialText;
|
13
|
+
let selectedIndex = 0;
|
14
|
+
const regExpEscape = (s) => s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
|
15
|
+
if (inputText.length >= minChar) {
|
16
|
+
const r = filterResults();
|
17
|
+
if (r.length > 0) {
|
18
|
+
const { key, value } = r[0];
|
19
|
+
inputText = key;
|
20
|
+
setTimeout(() => dispatch("change", JSON.parse(JSON.stringify(value))), 0);
|
21
|
+
} else {
|
22
|
+
inputText = "";
|
23
|
+
}
|
24
|
+
}
|
25
|
+
function onChange() {
|
26
|
+
if (inputText.length >= minChar) {
|
27
|
+
results = filterResults();
|
28
|
+
selectedIndex = 0;
|
29
|
+
isOpen = true;
|
30
|
+
} else {
|
31
|
+
isOpen = false;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
function onFocus() {
|
35
|
+
input.setSelectionRange(0, 1e3);
|
36
|
+
}
|
37
|
+
function filterResults() {
|
38
|
+
const searchText = inputText.trim();
|
39
|
+
const searchTextUpper = searchText.toUpperCase();
|
40
|
+
const searchReg = RegExp(regExpEscape(searchText), "i");
|
41
|
+
return items.filter((item) => item.key.toUpperCase().includes(searchTextUpper)).slice(0, maxItems).map((item) => ({
|
42
|
+
...item,
|
43
|
+
_label: item.key.replace(searchReg, "<span>$&</span>")
|
44
|
+
}));
|
45
|
+
}
|
46
|
+
function onKeyDown(event) {
|
47
|
+
switch (event.key) {
|
48
|
+
case "ArrowDown":
|
49
|
+
if (selectedIndex < results.length - 1) selectedIndex += 1;
|
50
|
+
break;
|
51
|
+
case "ArrowUp":
|
52
|
+
if (selectedIndex > 0) selectedIndex -= 1;
|
53
|
+
break;
|
54
|
+
case "Enter":
|
55
|
+
event.preventDefault();
|
56
|
+
close(selectedIndex);
|
57
|
+
break;
|
58
|
+
case "Escape":
|
59
|
+
event.preventDefault();
|
60
|
+
close();
|
61
|
+
break;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
function close(index = -1) {
|
65
|
+
isOpen = false;
|
66
|
+
if (index > -1 && results[index]) {
|
67
|
+
const { key, value } = results[index];
|
68
|
+
inputText = key;
|
69
|
+
dispatch("change", JSON.parse(JSON.stringify(value)));
|
70
|
+
}
|
71
|
+
}
|
72
|
+
</script>
|
73
|
+
|
74
|
+
<svelte:window on:click={() => close()} />
|
75
|
+
|
76
|
+
<div class="autocomplete">
|
77
|
+
<input
|
78
|
+
type="text"
|
79
|
+
bind:value={inputText}
|
80
|
+
{placeholder}
|
81
|
+
autocomplete="off"
|
82
|
+
on:input={onChange}
|
83
|
+
on:keydown={onKeyDown}
|
84
|
+
on:focusin={onFocus}
|
85
|
+
on:click={(e) => e.stopPropagation()}
|
86
|
+
bind:this={input}
|
87
|
+
aria-activedescendant={isOpen ? `result-${selectedIndex}` : undefined}
|
88
|
+
aria-autocomplete="list"
|
89
|
+
aria-controls="autocomplete-results"
|
90
|
+
/>
|
91
|
+
<div class="autocomplete-results" class:hide-results={!isOpen}>
|
92
|
+
{#each results as result, i}
|
93
|
+
<button
|
94
|
+
on:click={() => close(i)}
|
95
|
+
class={i === selectedIndex ? ' is-active' : ''}
|
96
|
+
role="option"
|
97
|
+
aria-selected={i === selectedIndex}
|
98
|
+
>
|
99
|
+
{@html result._label}
|
100
|
+
</button>
|
101
|
+
{/each}
|
102
|
+
</div>
|
103
|
+
</div>
|
104
|
+
|
105
|
+
<style>
|
106
|
+
.autocomplete {
|
107
|
+
--bg-color: var(--autocomplete-bg-color, light-dark(white, black));
|
108
|
+
--fg-color: var(--autocomplete-text-color, light-dark(black, white));
|
109
|
+
position: relative;
|
110
|
+
border-radius: 0.5em;
|
111
|
+
background: color-mix(in srgb, var(--bg-color) 80%, transparent);
|
112
|
+
box-sizing: border-box;
|
113
|
+
line-height: normal;
|
114
|
+
}
|
115
|
+
|
116
|
+
input {
|
117
|
+
width: 100%;
|
118
|
+
display: block;
|
119
|
+
box-sizing: border-box;
|
120
|
+
padding: 0.3em 0.6em;
|
121
|
+
border: none;
|
122
|
+
background: none;
|
123
|
+
color: var(--fg-color);
|
124
|
+
}
|
125
|
+
|
126
|
+
.autocomplete-results {
|
127
|
+
padding: 0;
|
128
|
+
margin: 0;
|
129
|
+
background: none;
|
130
|
+
width: 100%;
|
131
|
+
display: block;
|
132
|
+
border-radius: 0 0 0.5em 0.5em;
|
133
|
+
}
|
134
|
+
|
135
|
+
.autocomplete-results.hide-results {
|
136
|
+
display: none;
|
137
|
+
}
|
138
|
+
|
139
|
+
button {
|
140
|
+
padding: 0.2rem 0.5rem;
|
141
|
+
cursor: pointer;
|
142
|
+
border: none;
|
143
|
+
display: block;
|
144
|
+
background: transparent;
|
145
|
+
font-weight: normal;
|
146
|
+
color: color-mix(in srgb, var(--fg-color) 50%, transparent);
|
147
|
+
width: 100%;
|
148
|
+
text-align: left;
|
149
|
+
}
|
150
|
+
|
151
|
+
button > :global(span) {
|
152
|
+
color: var(--fg-color);
|
153
|
+
}
|
154
|
+
|
155
|
+
button.is-active,
|
156
|
+
button:hover {
|
157
|
+
background-color: color-mix(in srgb, var(--fg-color) 15%, transparent);
|
158
|
+
}
|
159
|
+
</style>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
declare class __sveltets_Render<T> {
|
3
|
+
props(): {
|
4
|
+
placeholder?: string;
|
5
|
+
minChar?: number;
|
6
|
+
maxItems?: number;
|
7
|
+
initialText?: string;
|
8
|
+
items: {
|
9
|
+
key: string;
|
10
|
+
value: T;
|
11
|
+
}[];
|
12
|
+
};
|
13
|
+
events(): {
|
14
|
+
change: CustomEvent<any>;
|
15
|
+
} & {
|
16
|
+
[evt: string]: CustomEvent<any>;
|
17
|
+
};
|
18
|
+
slots(): {};
|
19
|
+
}
|
20
|
+
export type AutoCompleteProps<T> = ReturnType<__sveltets_Render<T>['props']>;
|
21
|
+
export type AutoCompleteEvents<T> = ReturnType<__sveltets_Render<T>['events']>;
|
22
|
+
export type AutoCompleteSlots<T> = ReturnType<__sveltets_Render<T>['slots']>;
|
23
|
+
export default class AutoComplete<T> extends SvelteComponent<AutoCompleteProps<T>, AutoCompleteEvents<T>, AutoCompleteSlots<T>> {
|
24
|
+
}
|
25
|
+
export {};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import type { GeoJSON } from 'geojson';
|
2
|
+
import type { LngLat, Point } from 'maplibre-gl';
|
3
|
+
export type BBox = [number, number, number, number];
|
4
|
+
export type BBoxDrag = '0_' | '1_' | '_0' | '_1' | '00' | '01' | '10' | '11' | false;
|
5
|
+
export declare function getBBoxDrag(point: Point, bboxPixel: BBox): BBoxDrag;
|
6
|
+
export declare function dragBBox(bbox: BBox, drag: BBoxDrag, lngLat: LngLat): {
|
7
|
+
bbox: BBox;
|
8
|
+
drag: BBoxDrag;
|
9
|
+
};
|
10
|
+
export declare function getCursor(drag: BBoxDrag): string | false;
|
11
|
+
export declare function getBBoxGeometry(bbox: BBox): GeoJSON;
|
12
|
+
export declare function getBBoxes(): {
|
13
|
+
key: string;
|
14
|
+
value: BBox;
|
15
|
+
}[];
|
@@ -0,0 +1,209 @@
|
|
1
|
+
import bboxes from './bboxes.json';
|
2
|
+
export function getBBoxDrag(point, bboxPixel) {
|
3
|
+
const maxDistance = 5;
|
4
|
+
const { x, y } = point;
|
5
|
+
const [x0, y0, x1, y1] = bboxPixel;
|
6
|
+
// Don't think outside the box
|
7
|
+
if (x < x0 - maxDistance)
|
8
|
+
return false;
|
9
|
+
if (x > x1 + maxDistance)
|
10
|
+
return false;
|
11
|
+
if (y < y0 - maxDistance)
|
12
|
+
return false;
|
13
|
+
if (y > y1 + maxDistance)
|
14
|
+
return false;
|
15
|
+
const drag = [
|
16
|
+
Math.abs(x0 - x) < maxDistance,
|
17
|
+
Math.abs(y0 - y) < maxDistance,
|
18
|
+
Math.abs(x1 - x) < maxDistance,
|
19
|
+
Math.abs(y1 - y) < maxDistance
|
20
|
+
];
|
21
|
+
if (drag[0] && drag[2]) {
|
22
|
+
if (Math.abs(x0 - x) < Math.abs(x1 - x)) {
|
23
|
+
drag[2] = false;
|
24
|
+
}
|
25
|
+
else {
|
26
|
+
drag[0] = false;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
if (drag[1] && drag[3]) {
|
30
|
+
if (Math.abs(y0 - y) < Math.abs(y1 - y)) {
|
31
|
+
drag[3] = false;
|
32
|
+
}
|
33
|
+
else {
|
34
|
+
drag[1] = false;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
if (drag[0]) {
|
38
|
+
if (drag[1])
|
39
|
+
return '01';
|
40
|
+
if (drag[3])
|
41
|
+
return '00';
|
42
|
+
return '0_';
|
43
|
+
}
|
44
|
+
else if (drag[1]) {
|
45
|
+
if (drag[2])
|
46
|
+
return '11';
|
47
|
+
return '_1';
|
48
|
+
}
|
49
|
+
else if (drag[2]) {
|
50
|
+
if (drag[3])
|
51
|
+
return '10';
|
52
|
+
return '1_';
|
53
|
+
}
|
54
|
+
else if (drag[3]) {
|
55
|
+
return '_0';
|
56
|
+
}
|
57
|
+
else
|
58
|
+
return false;
|
59
|
+
}
|
60
|
+
export function dragBBox(bbox, drag, lngLat) {
|
61
|
+
const x = Math.round(lngLat.lng * 1e3) / 1e3;
|
62
|
+
const y = Math.round(lngLat.lat * 1e3) / 1e3;
|
63
|
+
switch (drag) {
|
64
|
+
case '_0':
|
65
|
+
bbox[1] = y;
|
66
|
+
break;
|
67
|
+
case '_1':
|
68
|
+
bbox[3] = y;
|
69
|
+
break;
|
70
|
+
case '0_':
|
71
|
+
bbox[0] = x;
|
72
|
+
break;
|
73
|
+
case '00':
|
74
|
+
bbox[0] = x;
|
75
|
+
bbox[1] = y;
|
76
|
+
break;
|
77
|
+
case '01':
|
78
|
+
bbox[0] = x;
|
79
|
+
bbox[3] = y;
|
80
|
+
break;
|
81
|
+
case '1_':
|
82
|
+
bbox[2] = x;
|
83
|
+
break;
|
84
|
+
case '10':
|
85
|
+
bbox[2] = x;
|
86
|
+
bbox[1] = y;
|
87
|
+
break;
|
88
|
+
case '11':
|
89
|
+
bbox[2] = x;
|
90
|
+
bbox[3] = y;
|
91
|
+
break;
|
92
|
+
}
|
93
|
+
if (bbox[2] < bbox[0]) {
|
94
|
+
// flip horizontal
|
95
|
+
const t = bbox[0];
|
96
|
+
bbox[0] = bbox[2];
|
97
|
+
bbox[2] = t;
|
98
|
+
switch (drag) {
|
99
|
+
case '0_':
|
100
|
+
drag = '1_';
|
101
|
+
break;
|
102
|
+
case '00':
|
103
|
+
drag = '10';
|
104
|
+
break;
|
105
|
+
case '01':
|
106
|
+
drag = '11';
|
107
|
+
break;
|
108
|
+
case '1_':
|
109
|
+
drag = '0_';
|
110
|
+
break;
|
111
|
+
case '10':
|
112
|
+
drag = '00';
|
113
|
+
break;
|
114
|
+
case '11':
|
115
|
+
drag = '01';
|
116
|
+
break;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
if (bbox[3] < bbox[1]) {
|
120
|
+
// flip vertical
|
121
|
+
const t = bbox[1];
|
122
|
+
bbox[1] = bbox[3];
|
123
|
+
bbox[3] = t;
|
124
|
+
switch (drag) {
|
125
|
+
case '_0':
|
126
|
+
drag = '_1';
|
127
|
+
break;
|
128
|
+
case '_1':
|
129
|
+
drag = '_0';
|
130
|
+
break;
|
131
|
+
case '00':
|
132
|
+
drag = '01';
|
133
|
+
break;
|
134
|
+
case '01':
|
135
|
+
drag = '00';
|
136
|
+
break;
|
137
|
+
case '10':
|
138
|
+
drag = '11';
|
139
|
+
break;
|
140
|
+
case '11':
|
141
|
+
drag = '10';
|
142
|
+
break;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
return { drag, bbox };
|
146
|
+
}
|
147
|
+
export function getCursor(drag) {
|
148
|
+
switch (drag) {
|
149
|
+
case '_0':
|
150
|
+
return 'ns-resize';
|
151
|
+
case '_1':
|
152
|
+
return 'ns-resize';
|
153
|
+
case '0_':
|
154
|
+
return 'ew-resize';
|
155
|
+
case '00':
|
156
|
+
return 'nesw-resize';
|
157
|
+
case '01':
|
158
|
+
return 'nwse-resize';
|
159
|
+
case '1_':
|
160
|
+
return 'ew-resize';
|
161
|
+
case '10':
|
162
|
+
return 'nwse-resize';
|
163
|
+
case '11':
|
164
|
+
return 'nesw-resize';
|
165
|
+
}
|
166
|
+
return false;
|
167
|
+
}
|
168
|
+
export function getBBoxGeometry(bbox) {
|
169
|
+
return {
|
170
|
+
type: 'FeatureCollection',
|
171
|
+
features: [polygon(getRing([-180, -86, 180, 86]), getRing(bbox)), linestring(getRing(bbox))]
|
172
|
+
};
|
173
|
+
function polygon(...coordinates) {
|
174
|
+
return {
|
175
|
+
type: 'Feature',
|
176
|
+
geometry: { type: 'Polygon', coordinates },
|
177
|
+
properties: {}
|
178
|
+
};
|
179
|
+
}
|
180
|
+
function linestring(coordinates) {
|
181
|
+
return {
|
182
|
+
type: 'Feature',
|
183
|
+
geometry: { type: 'LineString', coordinates },
|
184
|
+
properties: {}
|
185
|
+
};
|
186
|
+
}
|
187
|
+
function getRing(bbox) {
|
188
|
+
const x0 = Math.min(bbox[0], bbox[2]);
|
189
|
+
const x1 = Math.max(bbox[0], bbox[2]);
|
190
|
+
const y0 = Math.min(bbox[1], bbox[3]);
|
191
|
+
const y1 = Math.max(bbox[1], bbox[3]);
|
192
|
+
return [
|
193
|
+
[x0, y0],
|
194
|
+
[x1, y0],
|
195
|
+
[x1, y1],
|
196
|
+
[x0, y1],
|
197
|
+
[x0, y0]
|
198
|
+
];
|
199
|
+
}
|
200
|
+
}
|
201
|
+
export function getBBoxes() {
|
202
|
+
return bboxes.map((e) => {
|
203
|
+
const key = e[0];
|
204
|
+
const value = e.slice(1, 5);
|
205
|
+
value[2] = Math.round((value[2] + value[0]) * 1e5) / 1e5;
|
206
|
+
value[3] = Math.round((value[3] + value[1]) * 1e5) / 1e5;
|
207
|
+
return { key, value };
|
208
|
+
});
|
209
|
+
}
|
@@ -0,0 +1,155 @@
|
|
1
|
+
<!-- BBoxMap.svelte -->
|
2
|
+
<script>import { onMount } from "svelte";
|
3
|
+
import maplibregl, {} from "maplibre-gl";
|
4
|
+
import "maplibre-gl/dist/maplibre-gl.css";
|
5
|
+
import { dragBBox, getBBoxDrag, getBBoxes, getBBoxGeometry, getCursor } from "./BBoxMap.js";
|
6
|
+
import AutoComplete from "../AutoComplete/AutoComplete.svelte";
|
7
|
+
import { getMapStyle, isDarkMode } from "../utils/style.js";
|
8
|
+
import { getCountry } from "../utils/location.js";
|
9
|
+
const bboxes = getBBoxes();
|
10
|
+
let container;
|
11
|
+
const worldBBox = [-180, -85, 180, 85];
|
12
|
+
const startTime = Date.now();
|
13
|
+
export let selectedBBox = worldBBox;
|
14
|
+
let map;
|
15
|
+
let initialCountry = getCountry();
|
16
|
+
onMount(() => {
|
17
|
+
const darkMode = isDarkMode(container);
|
18
|
+
map = new maplibregl.Map({
|
19
|
+
container,
|
20
|
+
style: getMapStyle(darkMode),
|
21
|
+
bounds: selectedBBox,
|
22
|
+
renderWorldCopies: false,
|
23
|
+
dragRotate: false,
|
24
|
+
attributionControl: { compact: false }
|
25
|
+
});
|
26
|
+
map.setPadding({ top: 31 + 5, right: 5, bottom: 5, left: 5 });
|
27
|
+
const canvas = map.getCanvasContainer();
|
28
|
+
map.on("load", () => {
|
29
|
+
map.addSource("bbox", { type: "geojson", data: getBBoxGeometry(selectedBBox) });
|
30
|
+
map.addLayer({
|
31
|
+
id: "bbox-line",
|
32
|
+
type: "line",
|
33
|
+
source: "bbox",
|
34
|
+
filter: ["==", "$type", "LineString"],
|
35
|
+
layout: { "line-cap": "round", "line-join": "round" },
|
36
|
+
paint: { "line-color": darkMode ? "#FFFFFF" : "#000000", "line-width": 0.5 }
|
37
|
+
});
|
38
|
+
map.addLayer({
|
39
|
+
id: "bbox-fill",
|
40
|
+
type: "fill",
|
41
|
+
source: "bbox",
|
42
|
+
filter: ["==", "$type", "Polygon"],
|
43
|
+
paint: { "fill-color": darkMode ? "#FFFFFF" : "#000000", "fill-opacity": 0.2 }
|
44
|
+
});
|
45
|
+
});
|
46
|
+
function getDrag(point) {
|
47
|
+
const { x: x0, y: y1 } = map.project([selectedBBox[0], selectedBBox[1]]);
|
48
|
+
const { x: x1, y: y0 } = map.project([selectedBBox[2], selectedBBox[3]]);
|
49
|
+
return getBBoxDrag(point, [x0, y0, x1, y1]);
|
50
|
+
}
|
51
|
+
let lastDrag = false;
|
52
|
+
let dragging = false;
|
53
|
+
map.on("mousemove", (e) => {
|
54
|
+
if (dragging) {
|
55
|
+
if (e.originalEvent.buttons % 2) {
|
56
|
+
const { drag, bbox } = dragBBox(selectedBBox, lastDrag, e.lngLat);
|
57
|
+
lastDrag = drag;
|
58
|
+
selectedBBox = bbox;
|
59
|
+
redrawBBox();
|
60
|
+
e.preventDefault();
|
61
|
+
} else {
|
62
|
+
dragging = false;
|
63
|
+
}
|
64
|
+
} else {
|
65
|
+
const drag = getDrag(e.point);
|
66
|
+
if (drag !== lastDrag) {
|
67
|
+
lastDrag = drag;
|
68
|
+
canvas.style.cursor = getCursor(drag) || "grab";
|
69
|
+
}
|
70
|
+
}
|
71
|
+
});
|
72
|
+
map.on("mousedown", (e) => {
|
73
|
+
if (e.originalEvent.buttons % 2) {
|
74
|
+
const drag = getDrag(e.point);
|
75
|
+
lastDrag = drag;
|
76
|
+
if (drag) {
|
77
|
+
dragging = true;
|
78
|
+
e.preventDefault();
|
79
|
+
}
|
80
|
+
}
|
81
|
+
});
|
82
|
+
map.on("mouseup", () => dragging = false);
|
83
|
+
return () => map.remove();
|
84
|
+
});
|
85
|
+
function redrawBBox() {
|
86
|
+
const bboxSource = map.getSource("bbox");
|
87
|
+
bboxSource.setData(getBBoxGeometry(selectedBBox));
|
88
|
+
}
|
89
|
+
function flyTo(bbox) {
|
90
|
+
selectedBBox = bbox ?? worldBBox;
|
91
|
+
if (map) {
|
92
|
+
if (map.getSource("bbox")) redrawBBox();
|
93
|
+
const transform = map.cameraForBounds(selectedBBox);
|
94
|
+
if (transform == null) return;
|
95
|
+
transform.zoom = transform.zoom ?? 0 - 0.5;
|
96
|
+
transform.bearing = 0;
|
97
|
+
transform.pitch = 0;
|
98
|
+
if (Date.now() - startTime < 1e3) {
|
99
|
+
map.jumpTo(transform);
|
100
|
+
} else {
|
101
|
+
map.flyTo({ ...transform, essential: true, speed: 5 });
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
</script>
|
106
|
+
|
107
|
+
<div class="container">
|
108
|
+
{#if bboxes}
|
109
|
+
<div class="input">
|
110
|
+
<AutoComplete
|
111
|
+
items={bboxes}
|
112
|
+
placeholder="Find country, region or city …"
|
113
|
+
initialText={initialCountry}
|
114
|
+
on:change={(e) => flyTo(e.detail)}
|
115
|
+
/>
|
116
|
+
</div>
|
117
|
+
{/if}
|
118
|
+
<div class="map" bind:this={container}></div>
|
119
|
+
</div>
|
120
|
+
|
121
|
+
<style>
|
122
|
+
.container {
|
123
|
+
--bg-color: var(--bboxmap-bg-color, light-dark(white, black));
|
124
|
+
--fg-color: var(--bboxmap-text-color, light-dark(black, white));
|
125
|
+
width: 100%;
|
126
|
+
height: 100%;
|
127
|
+
position: relative;
|
128
|
+
min-height: 6em;
|
129
|
+
}
|
130
|
+
.map {
|
131
|
+
position: absolute;
|
132
|
+
top: 0px;
|
133
|
+
left: 0px;
|
134
|
+
bottom: 0px;
|
135
|
+
right: 0px;
|
136
|
+
}
|
137
|
+
:global(.maplibregl-ctrl-attrib) {
|
138
|
+
background-color: color-mix(in srgb, var(--bg-color) 50%, transparent) !important;
|
139
|
+
color: var(--fg-color) !important;
|
140
|
+
opacity: 0.5;
|
141
|
+
font-size: 0.85em;
|
142
|
+
padding: 0.1em !important;
|
143
|
+
line-height: normal !important;
|
144
|
+
}
|
145
|
+
:global(.maplibregl-ctrl-attrib a) {
|
146
|
+
color: var(--fg-color) !important;
|
147
|
+
}
|
148
|
+
.input {
|
149
|
+
position: absolute;
|
150
|
+
top: 0.5em;
|
151
|
+
left: 0.5em;
|
152
|
+
right: 0.5em;
|
153
|
+
z-index: 10;
|
154
|
+
}
|
155
|
+
</style>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
3
|
+
import type { BBox } from './BBoxMap.js';
|
4
|
+
declare const __propDef: {
|
5
|
+
props: {
|
6
|
+
selectedBBox?: BBox;
|
7
|
+
};
|
8
|
+
events: {
|
9
|
+
[evt: string]: CustomEvent<any>;
|
10
|
+
};
|
11
|
+
slots: {};
|
12
|
+
exports?: {} | undefined;
|
13
|
+
bindings?: string | undefined;
|
14
|
+
};
|
15
|
+
export type BBoxMapProps = typeof __propDef.props;
|
16
|
+
export type BBoxMapEvents = typeof __propDef.events;
|
17
|
+
export type BBoxMapSlots = typeof __propDef.slots;
|
18
|
+
export default class BBoxMap extends SvelteComponent<BBoxMapProps, BBoxMapEvents, BBoxMapSlots> {
|
19
|
+
}
|
20
|
+
export {};
|