@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 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 {};