@konce-pt/map 0.12.0 → 0.14.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.
- package/README.md +10 -10
- package/dist/cluster.d.ts +6 -6
- package/dist/cluster.js +7 -7
- package/dist/countries.d.ts +5 -5
- package/dist/countries.js +5 -5
- package/dist/engine.d.ts +12 -12
- package/dist/engine.js +1 -1
- package/dist/engines/google.engine.d.ts +10 -10
- package/dist/engines/google.engine.js +25 -25
- package/dist/engines/leaflet.engine.d.ts +2 -2
- package/dist/engines/leaflet.engine.js +10 -10
- package/dist/format.d.ts +3 -3
- package/dist/format.js +4 -4
- package/dist/geo.d.ts +8 -8
- package/dist/geo.js +8 -8
- package/dist/hours.d.ts +7 -7
- package/dist/hours.js +9 -9
- package/dist/index.js +1 -1
- package/dist/marker-html.d.ts +7 -7
- package/dist/marker-html.js +10 -10
- package/dist/types.d.ts +24 -24
- package/dist/types.js +1 -1
- package/dist/url.d.ts +2 -2
- package/dist/url.js +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://ui.konce.pt/images/koncept_ui_lib.png" alt="Koncept UI
|
|
2
|
+
<img src="https://ui.konce.pt/images/koncept_ui_lib.png" alt="Koncept UI - Angular component library" width="100%" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
# @konce-pt/map
|
|
6
6
|
|
|
7
7
|
Rdzeń mapy [Koncept UI](https://gitlab.com/konce-pt/koncept-ui): odwzorowanie i arytmetyka
|
|
8
|
-
prostokątów, klastrowanie znaczników, godziny otwarcia, HTML znaczników, formatowanie
|
|
8
|
+
prostokątów, klastrowanie znaczników, godziny otwarcia, HTML znaczników, formatowanie - oraz
|
|
9
9
|
adaptery silników Leaflet i Google. Czysty TypeScript, **zero zależności runtime**
|
|
10
10
|
(`leaflet` to peer opcjonalny, ładowany dynamicznym `import()`).
|
|
11
11
|
|
|
@@ -20,7 +20,7 @@ npm i @konce-pt/map
|
|
|
20
20
|
```ts
|
|
21
21
|
import { clusterLocations, zoomForBounds, isOpenAt, createLeafletEngine } from '@konce-pt/map';
|
|
22
22
|
|
|
23
|
-
// Klastrowanie na siatce pikseli
|
|
23
|
+
// Klastrowanie na siatce pikseli - wynik zależy od zoomu, nie od samych współrzędnych.
|
|
24
24
|
clusterLocations(locations, 12, { gridSize: 60 });
|
|
25
25
|
|
|
26
26
|
// Zoom, przy którym prostokąt mieści się w kontenerze.
|
|
@@ -35,16 +35,16 @@ const engine = createLeafletEngine({ maxZoom: 19 });
|
|
|
35
35
|
|
|
36
36
|
## API
|
|
37
37
|
|
|
38
|
-
- **Geometria**
|
|
38
|
+
- **Geometria** - `project`, `unproject`, `boundsOf`, `boundsCenter`, `boundsContain`, `padBounds`,
|
|
39
39
|
`isFittableBounds`, `haversine`, `metersPerPixel`, `zoomForBounds`, `clampLat`, `normalizeLng`
|
|
40
|
-
- **Klastry**
|
|
41
|
-
- **Znaczniki**
|
|
40
|
+
- **Klastry** - `clusterLocations`, `dominantCategory`, `countByCategory`, `isSplittable`
|
|
41
|
+
- **Znaczniki** - `renderMarkerHtml`, `renderClusterHtml`, `renderUserMarkerHtml`, `colorVarOf`,
|
|
42
42
|
`contrastVarOf`, `foregroundVarOf`, `clusterTier`
|
|
43
|
-
- **Godziny**
|
|
44
|
-
- **Kraje i format**
|
|
43
|
+
- **Godziny** - `parseTime`, `formatTime`, `isOpenAt`, `isAlwaysOpen`, `nextChange`, `groupWeek`
|
|
44
|
+
- **Kraje i format** - `KPT_MAP_COUNTRIES`, `countryPreset`, `resolveCountryView`,
|
|
45
45
|
`formatDistance`, `formatLatLng`, `normalizeUrl`, `prettyUrl`
|
|
46
|
-
- **Silniki**
|
|
47
|
-
- **Typy**
|
|
46
|
+
- **Silniki** - `createLeafletEngine`, `createGoogleEngine`, `loadGoogleMapsApi`
|
|
47
|
+
- **Typy** - `KptMapEngine`, `KptMapLocation`, `KptMapCategory`, `KptMapPoi` i reszta kontraktu
|
|
48
48
|
|
|
49
49
|
Pełny opis: [`llms.txt`](https://ui.konce.pt/llms/map/llms.txt) (EN)
|
|
50
50
|
i [`llms-pl.txt`](https://ui.konce.pt/llms/map/llms-pl.txt) (PL).
|
package/dist/cluster.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { KptMapCluster, KptMapClusterEntry, KptMapLocation } from './types.ts';
|
|
2
2
|
export interface KptClusterOptions {
|
|
3
|
-
/** Aktualny zoom mapy
|
|
3
|
+
/** Aktualny zoom mapy - decyduje o gęstości siatki. */
|
|
4
4
|
zoom: number;
|
|
5
5
|
/** Bok komórki siatki w pikselach ekranu. Domyślnie 60. */
|
|
6
6
|
gridSize?: number;
|
|
@@ -9,7 +9,7 @@ export interface KptClusterOptions {
|
|
|
9
9
|
/** Ile punktów musi trafić do komórki, żeby powstała grupa. Domyślnie 2. */
|
|
10
10
|
minPoints?: number;
|
|
11
11
|
/**
|
|
12
|
-
* Kolejność kategorii
|
|
12
|
+
* Kolejność kategorii - rozstrzyga remis przy wyborze kategorii dominującej.
|
|
13
13
|
* Bez niej remis rozstrzyga kolejność wystąpienia w danych.
|
|
14
14
|
*/
|
|
15
15
|
categoryOrder?: readonly string[];
|
|
@@ -24,18 +24,18 @@ export declare const KPT_CLUSTER_DEFAULTS: {
|
|
|
24
24
|
*
|
|
25
25
|
* Środek grupy uśredniamy w przestrzeni pikseli, nie w stopniach. Mercator rozciąga
|
|
26
26
|
* stopnie tym mocniej, im dalej od równika, więc średnia w stopniach wypada obok
|
|
27
|
-
* wizualnego środka kropek na ekranie
|
|
27
|
+
* wizualnego środka kropek na ekranie - a to jego ma pilnować znacznik grupy.
|
|
28
28
|
*/
|
|
29
29
|
export declare function clusterLocations<T>(locations: readonly KptMapLocation<T>[], options: KptClusterOptions): KptMapClusterEntry<T>[];
|
|
30
30
|
/**
|
|
31
31
|
* Najliczniejsza kategoria w grupie. Remis rozstrzyga `categoryOrder`, a bez niej
|
|
32
|
-
* kolejność pierwszego wystąpienia
|
|
32
|
+
* kolejność pierwszego wystąpienia - w obu wypadkach wynik jest deterministyczny.
|
|
33
33
|
*/
|
|
34
34
|
export declare function dominantCategory<T>(items: readonly KptMapLocation<T>[], categoryOrder?: readonly string[]): string;
|
|
35
|
-
/** Zlicza lokalizacje w rozbiciu na kategorie
|
|
35
|
+
/** Zlicza lokalizacje w rozbiciu na kategorie - licznik obok pozycji legendy. */
|
|
36
36
|
export declare function countByCategory<T>(locations: readonly KptMapLocation<T>[]): Record<string, number>;
|
|
37
37
|
/**
|
|
38
38
|
* Czy grupa da się rozbić przybliżeniem. Punkty o tej samej współrzędnej zostaną razem
|
|
39
|
-
* na każdym zoomie
|
|
39
|
+
* na każdym zoomie - wtedy kliknięcie ma otworzyć listę zamiast zoomować w nieskończoność.
|
|
40
40
|
*/
|
|
41
41
|
export declare function isSplittable<T>(cluster: KptMapCluster<T>): boolean;
|
package/dist/cluster.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Grupowanie znaczników
|
|
2
|
+
* Grupowanie znaczników - siatka w przestrzeni pikseli, O(n), deterministyczna.
|
|
3
3
|
* Pure TS: ten sam kod obsługuje silnik Leaflet i Google Maps, i przenosi się do Reacta.
|
|
4
4
|
*/
|
|
5
5
|
import { boundsOf, project, unproject } from "./geo.js";
|
|
@@ -13,7 +13,7 @@ export const KPT_CLUSTER_DEFAULTS = {
|
|
|
13
13
|
*
|
|
14
14
|
* Środek grupy uśredniamy w przestrzeni pikseli, nie w stopniach. Mercator rozciąga
|
|
15
15
|
* stopnie tym mocniej, im dalej od równika, więc średnia w stopniach wypada obok
|
|
16
|
-
* wizualnego środka kropek na ekranie
|
|
16
|
+
* wizualnego środka kropek na ekranie - a to jego ma pilnować znacznik grupy.
|
|
17
17
|
*/
|
|
18
18
|
export function clusterLocations(locations, options) {
|
|
19
19
|
const gridSize = options.gridSize ?? KPT_CLUSTER_DEFAULTS.gridSize;
|
|
@@ -22,7 +22,7 @@ export function clusterLocations(locations, options) {
|
|
|
22
22
|
const zoom = options.zoom;
|
|
23
23
|
if (locations.length === 0)
|
|
24
24
|
return [];
|
|
25
|
-
// Powyżej progu pokazujemy wszystko osobno
|
|
25
|
+
// Powyżej progu pokazujemy wszystko osobno - użytkownik przybliżył się celowo.
|
|
26
26
|
if (zoom > maxZoom || gridSize <= 0)
|
|
27
27
|
return [...locations];
|
|
28
28
|
const cells = new Map();
|
|
@@ -62,7 +62,7 @@ export function clusterLocations(locations, options) {
|
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
64
|
* Najliczniejsza kategoria w grupie. Remis rozstrzyga `categoryOrder`, a bez niej
|
|
65
|
-
* kolejność pierwszego wystąpienia
|
|
65
|
+
* kolejność pierwszego wystąpienia - w obu wypadkach wynik jest deterministyczny.
|
|
66
66
|
*/
|
|
67
67
|
export function dominantCategory(items, categoryOrder) {
|
|
68
68
|
const counts = new Map();
|
|
@@ -82,7 +82,7 @@ export function dominantCategory(items, categoryOrder) {
|
|
|
82
82
|
}
|
|
83
83
|
if (count < bestCount)
|
|
84
84
|
continue;
|
|
85
|
-
// Remis
|
|
85
|
+
// Remis - niższy indeks w `categoryOrder` wygrywa, a poza nią wcześniejsze wystąpienie.
|
|
86
86
|
const rank = (c) => {
|
|
87
87
|
const i = categoryOrder?.indexOf(c) ?? -1;
|
|
88
88
|
return i >= 0 ? i : Number.MAX_SAFE_INTEGER;
|
|
@@ -93,7 +93,7 @@ export function dominantCategory(items, categoryOrder) {
|
|
|
93
93
|
}
|
|
94
94
|
return best;
|
|
95
95
|
}
|
|
96
|
-
/** Zlicza lokalizacje w rozbiciu na kategorie
|
|
96
|
+
/** Zlicza lokalizacje w rozbiciu na kategorie - licznik obok pozycji legendy. */
|
|
97
97
|
export function countByCategory(locations) {
|
|
98
98
|
const out = {};
|
|
99
99
|
for (const loc of locations)
|
|
@@ -102,7 +102,7 @@ export function countByCategory(locations) {
|
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* Czy grupa da się rozbić przybliżeniem. Punkty o tej samej współrzędnej zostaną razem
|
|
105
|
-
* na każdym zoomie
|
|
105
|
+
* na każdym zoomie - wtedy kliknięcie ma otworzyć listę zamiast zoomować w nieskończoność.
|
|
106
106
|
*/
|
|
107
107
|
export function isSplittable(cluster) {
|
|
108
108
|
const { south, west, north, east } = cluster.bounds;
|
package/dist/countries.d.ts
CHANGED
|
@@ -3,25 +3,25 @@ export interface KptMapCountryPreset {
|
|
|
3
3
|
/** Kod ISO 3166-1 alpha-2 (albo `EU` dla całego kontynentu). */
|
|
4
4
|
code: string;
|
|
5
5
|
center: KptLatLng;
|
|
6
|
-
/** Zoom, przy którym kraj mieści się na typowym kontenerze (~
|
|
6
|
+
/** Zoom, przy którym kraj mieści się na typowym kontenerze (~1000x600 px). */
|
|
7
7
|
zoom: number;
|
|
8
8
|
bounds: KptMapBounds;
|
|
9
9
|
}
|
|
10
10
|
/** Kod kraju użyty, gdy aplikacja nie poda żadnego. */
|
|
11
11
|
export declare const KPT_MAP_DEFAULT_COUNTRY = "PL";
|
|
12
12
|
/**
|
|
13
|
-
* Zasięgi lądowe (bez terytoriów zamorskich) zaokrąglone do dwóch miejsc
|
|
13
|
+
* Zasięgi lądowe (bez terytoriów zamorskich) zaokrąglone do dwóch miejsc -
|
|
14
14
|
* to widok startowy, nie granica administracyjna.
|
|
15
15
|
*/
|
|
16
16
|
export declare const KPT_MAP_COUNTRIES: Record<string, KptMapCountryPreset>;
|
|
17
17
|
/** Preset kraju po kodzie (wielkość liter bez znaczenia); `undefined`, gdy nieznany. */
|
|
18
18
|
export declare function countryPreset(code: string): KptMapCountryPreset | undefined;
|
|
19
|
-
/** Kody wszystkich presetów
|
|
19
|
+
/** Kody wszystkich presetów - do listy wyboru w aplikacji. */
|
|
20
20
|
export declare function countryCodes(): string[];
|
|
21
21
|
/**
|
|
22
22
|
* Widok startowy: preset kraju, a przy nieznanym kodzie preset domyślny.
|
|
23
|
-
* Zawsze zwraca coś sensownego
|
|
23
|
+
* Zawsze zwraca coś sensownego - mapa nigdy nie startuje na `[0, 0]`.
|
|
24
24
|
*/
|
|
25
25
|
export declare function resolveCountryView(code: string | null | undefined): KptMapCountryPreset;
|
|
26
|
-
/** Środek prostokąta kraju
|
|
26
|
+
/** Środek prostokąta kraju - pomocnicze przy własnych `bounds`. */
|
|
27
27
|
export declare function centerOfBounds(bounds: KptMapBounds): KptLatLng;
|
package/dist/countries.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Presety widoku bazowego
|
|
2
|
+
* Presety widoku bazowego - pure TS. Domyślny kraj to Polska.
|
|
3
3
|
* Kto potrzebuje kraju spoza listy, podaje `[view]` albo `[bounds]` wprost.
|
|
4
4
|
*/
|
|
5
5
|
import { boundsCenter } from "./geo.js";
|
|
6
6
|
/** Kod kraju użyty, gdy aplikacja nie poda żadnego. */
|
|
7
7
|
export const KPT_MAP_DEFAULT_COUNTRY = 'PL';
|
|
8
8
|
/**
|
|
9
|
-
* Zasięgi lądowe (bez terytoriów zamorskich) zaokrąglone do dwóch miejsc
|
|
9
|
+
* Zasięgi lądowe (bez terytoriów zamorskich) zaokrąglone do dwóch miejsc -
|
|
10
10
|
* to widok startowy, nie granica administracyjna.
|
|
11
11
|
*/
|
|
12
12
|
export const KPT_MAP_COUNTRIES = {
|
|
@@ -33,18 +33,18 @@ export const KPT_MAP_COUNTRIES = {
|
|
|
33
33
|
export function countryPreset(code) {
|
|
34
34
|
return KPT_MAP_COUNTRIES[code?.toUpperCase()];
|
|
35
35
|
}
|
|
36
|
-
/** Kody wszystkich presetów
|
|
36
|
+
/** Kody wszystkich presetów - do listy wyboru w aplikacji. */
|
|
37
37
|
export function countryCodes() {
|
|
38
38
|
return Object.keys(KPT_MAP_COUNTRIES);
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Widok startowy: preset kraju, a przy nieznanym kodzie preset domyślny.
|
|
42
|
-
* Zawsze zwraca coś sensownego
|
|
42
|
+
* Zawsze zwraca coś sensownego - mapa nigdy nie startuje na `[0, 0]`.
|
|
43
43
|
*/
|
|
44
44
|
export function resolveCountryView(code) {
|
|
45
45
|
return (code ? countryPreset(code) : undefined) ?? KPT_MAP_COUNTRIES[KPT_MAP_DEFAULT_COUNTRY];
|
|
46
46
|
}
|
|
47
|
-
/** Środek prostokąta kraju
|
|
47
|
+
/** Środek prostokąta kraju - pomocnicze przy własnych `bounds`. */
|
|
48
48
|
export function centerOfBounds(bounds) {
|
|
49
49
|
return boundsCenter(bounds);
|
|
50
50
|
}
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KptLatLng, KptMapBounds, KptMapPoint, KptMapUserPosition } from './types.ts';
|
|
2
|
-
/** Rodzaj silnika
|
|
2
|
+
/** Rodzaj silnika - trafia do `data-engine` na hoście, przydatne w testach i stylach. */
|
|
3
3
|
export type KptMapEngineKind = 'leaflet' | 'google';
|
|
4
4
|
export interface KptMapEngineInit {
|
|
5
5
|
center: KptLatLng;
|
|
@@ -10,25 +10,25 @@ export interface KptMapEngineInit {
|
|
|
10
10
|
maxBounds?: KptMapBounds | null;
|
|
11
11
|
/** Szablon URL kafli, np. `https://tile.openstreetmap.org/{z}/{x}/{y}.png`. */
|
|
12
12
|
tileUrl: string;
|
|
13
|
-
/** Tekst atrybucji
|
|
13
|
+
/** Tekst atrybucji - wymagany licencyjnie przy OSM. */
|
|
14
14
|
attribution: string;
|
|
15
|
-
/** Silnik nie rysuje własnych kontrolek zoomu
|
|
15
|
+
/** Silnik nie rysuje własnych kontrolek zoomu - robi to warstwa Angulara. */
|
|
16
16
|
interactive?: boolean;
|
|
17
17
|
}
|
|
18
|
-
/** Znacznik gotowy do narysowania
|
|
18
|
+
/** Znacznik gotowy do narysowania - HTML pochodzi z `marker-html.ts`. */
|
|
19
19
|
export interface KptMapRenderedMarker {
|
|
20
20
|
/** Stabilne id: `id` lokalizacji albo `id` grupy. */
|
|
21
21
|
id: string;
|
|
22
22
|
at: KptLatLng;
|
|
23
23
|
html: string;
|
|
24
|
-
/** Rozmiar ikony w pikselach
|
|
24
|
+
/** Rozmiar ikony w pikselach - silnik potrzebuje go do zakotwiczenia. */
|
|
25
25
|
size: {
|
|
26
26
|
width: number;
|
|
27
27
|
height: number;
|
|
28
28
|
};
|
|
29
29
|
/** Punkt zakotwiczenia względem lewego górnego rogu ikony. */
|
|
30
30
|
anchor: KptMapPoint;
|
|
31
|
-
/** Kolejność rysowania
|
|
31
|
+
/** Kolejność rysowania - grupy nad pojedynczymi punktami. */
|
|
32
32
|
zIndex?: number;
|
|
33
33
|
}
|
|
34
34
|
export interface KptMapPointerEvent {
|
|
@@ -39,9 +39,9 @@ export interface KptMapPointerEvent {
|
|
|
39
39
|
originalEvent?: Event;
|
|
40
40
|
}
|
|
41
41
|
export interface KptMapEngineEvents {
|
|
42
|
-
/** Pan i zoom w toku
|
|
42
|
+
/** Pan i zoom w toku - używane do przeliczania pozycji popupu. */
|
|
43
43
|
move: () => void;
|
|
44
|
-
/** Koniec ruchu
|
|
44
|
+
/** Koniec ruchu - dopiero tu warto emitować `viewChange`. */
|
|
45
45
|
moveend: () => void;
|
|
46
46
|
zoomend: () => void;
|
|
47
47
|
click: (event: KptMapPointerEvent) => void;
|
|
@@ -64,7 +64,7 @@ export interface KptMapEngine {
|
|
|
64
64
|
width: number;
|
|
65
65
|
height: number;
|
|
66
66
|
};
|
|
67
|
-
/** Współrzędne
|
|
67
|
+
/** Współrzędne -> piksele WZGLĘDEM kontenera (pozycjonowanie popupu). */
|
|
68
68
|
project(at: KptLatLng): KptMapPoint;
|
|
69
69
|
unproject(point: KptMapPoint): KptLatLng;
|
|
70
70
|
/** Pełna wymiana zestawu znaczników; diff po `id` robi implementacja. */
|
|
@@ -73,12 +73,12 @@ export interface KptMapEngine {
|
|
|
73
73
|
setUserPosition(position: KptMapUserPosition | null, html: string): void;
|
|
74
74
|
setTileSource(tileUrl: string, attribution: string): void;
|
|
75
75
|
setMaxBounds(bounds: KptMapBounds | null): void;
|
|
76
|
-
/** Kontener zmienił rozmiar
|
|
76
|
+
/** Kontener zmienił rozmiar - bez tego Leaflet zostawia szare pola. */
|
|
77
77
|
invalidateSize(): void;
|
|
78
78
|
on<K extends keyof KptMapEngineEvents>(event: K, handler: KptMapEngineEvents[K]): void;
|
|
79
79
|
}
|
|
80
|
-
/** Fabryka silnika
|
|
80
|
+
/** Fabryka silnika - to ją dostarcza DI, żeby każda mapa miała własną instancję. */
|
|
81
81
|
export type KptMapEngineFactory = () => KptMapEngine;
|
|
82
|
-
/** Domyślne źródło kafli. Do produkcji podstaw własny serwer
|
|
82
|
+
/** Domyślne źródło kafli. Do produkcji podstaw własny serwer - patrz llms.txt. */
|
|
83
83
|
export declare const KPT_MAP_DEFAULT_TILE_URL = "https://tile.openstreetmap.org/{z}/{x}/{y}.png";
|
|
84
84
|
export declare const KPT_MAP_DEFAULT_ATTRIBUTION = "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors";
|
package/dist/engine.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/** Domyślne źródło kafli. Do produkcji podstaw własny serwer
|
|
1
|
+
/** Domyślne źródło kafli. Do produkcji podstaw własny serwer - patrz llms.txt. */
|
|
2
2
|
export const KPT_MAP_DEFAULT_TILE_URL = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
|
|
3
3
|
export const KPT_MAP_DEFAULT_ATTRIBUTION = '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { KptMapEngine } from '../engine.ts';
|
|
2
2
|
/**
|
|
3
|
-
* Namespace `google.maps`. Świadomie `any`
|
|
3
|
+
* Namespace `google.maps`. Świadomie `any` - pełne typy przychodzą z opcjonalnego
|
|
4
4
|
* `@types/google.maps`, a biblioteka nie może od nich zależeć przy kompilacji.
|
|
5
5
|
*/
|
|
6
6
|
export type KptGoogleMapsApi = any;
|
|
@@ -8,10 +8,10 @@ export interface KptGoogleOptions {
|
|
|
8
8
|
/** Klucz API z Google Cloud. Wymagany. */
|
|
9
9
|
apiKey: string;
|
|
10
10
|
/**
|
|
11
|
-
* Identyfikator stylu mapy z konsoli Google (*Map management
|
|
11
|
+
* Identyfikator stylu mapy z konsoli Google (*Map management -> Create Map ID*).
|
|
12
12
|
*
|
|
13
13
|
* **Wymagany.** Bez niego nie działa `AdvancedMarkerElement`, a znaczniki tej biblioteki
|
|
14
|
-
* to gotowy HTML z `marker-html.ts`
|
|
14
|
+
* to gotowy HTML z `marker-html.ts` - starsze `Marker` nie umie go przyjąć.
|
|
15
15
|
*/
|
|
16
16
|
mapId: string;
|
|
17
17
|
/** Wersja API. Domyślnie `weekly`. */
|
|
@@ -20,7 +20,7 @@ export interface KptGoogleOptions {
|
|
|
20
20
|
language?: string;
|
|
21
21
|
/** Region wpływający na nazewnictwo i granice sporne (ISO 3166-1 alpha-2). */
|
|
22
22
|
region?: string;
|
|
23
|
-
/** Dolny limit zoomu. Domyślnie 2
|
|
23
|
+
/** Dolny limit zoomu. Domyślnie 2 - niżej świat powtarza się w poziomie. */
|
|
24
24
|
minZoom?: number;
|
|
25
25
|
/** Górny limit zoomu. Domyślnie 21. */
|
|
26
26
|
maxZoom?: number;
|
|
@@ -28,24 +28,24 @@ export interface KptGoogleOptions {
|
|
|
28
28
|
scrollWheelZoom?: boolean;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Tyle z `KptGoogleOptions`, ile trzeba do wczytania samego API. Bez `mapId`
|
|
31
|
+
* Tyle z `KptGoogleOptions`, ile trzeba do wczytania samego API. Bez `mapId` - ten dotyczy
|
|
32
32
|
* wyglądu mapy, a API potrzebują też usługi, które żadnej mapy nie rysują.
|
|
33
33
|
*/
|
|
34
34
|
export type KptGoogleLoadOptions = Pick<KptGoogleOptions, 'apiKey' | 'version' | 'language' | 'region'>;
|
|
35
35
|
/**
|
|
36
|
-
* Ładuje Google Maps JavaScript API raz na aplikację. Gdy `google.maps` już istnieje
|
|
37
|
-
* aplikacja wstawiła skrypt sama albo stoi tu druga mapa
|
|
36
|
+
* Ładuje Google Maps JavaScript API raz na aplikację. Gdy `google.maps` już istnieje - bo
|
|
37
|
+
* aplikacja wstawiła skrypt sama albo stoi tu druga mapa - korzystamy z tego, co jest: drugie
|
|
38
38
|
* wczytanie Google i tak odrzuca, wypisując ostrzeżenie do konsoli.
|
|
39
39
|
*
|
|
40
40
|
* Publiczne, bo silnik nie jest jedynym, kto tego API potrzebuje. Geokoder Google **musi**
|
|
41
41
|
* iść przez SDK, a nie przez REST: web service `/maps/api/geocode/json` odrzuca klucze
|
|
42
|
-
* z restrykcją `Referer` (
|
|
42
|
+
* z restrykcją `Referer` ("API keys with referer restrictions cannot be used with this API"),
|
|
43
43
|
* a to jedyna restrykcja, jaką da się sensownie nałożyć na klucz leżący w bundlu przeglądarki.
|
|
44
44
|
* Wywołania z załadowanego SDK autoryzują się tak samo jak sama mapa.
|
|
45
45
|
*
|
|
46
|
-
* Adaptera geokodera biblioteka nadal nie dostarcza
|
|
46
|
+
* Adaptera geokodera biblioteka nadal nie dostarcza - tylko drogę do API, którą i tak
|
|
47
47
|
* otwiera silnikowi. Powody stoją przy `provideKptGoogleMap` i w `llms.txt`.
|
|
48
48
|
*/
|
|
49
49
|
export declare function loadGoogleMapsApi(options: KptGoogleLoadOptions): Promise<KptGoogleMapsApi>;
|
|
50
|
-
/** Fabryka silnika
|
|
50
|
+
/** Fabryka silnika - każda mapa dostaje własną instancję. */
|
|
51
51
|
export declare function createGoogleEngine(options: KptGoogleOptions): KptMapEngine;
|
|
@@ -6,17 +6,17 @@ const LOAD_ERROR = 'kpt-map: nie udało się załadować Google Maps JavaScript
|
|
|
6
6
|
const CALLBACK = '__kptGoogleMapsReady';
|
|
7
7
|
let modulePromise = null;
|
|
8
8
|
/**
|
|
9
|
-
* Ładuje Google Maps JavaScript API raz na aplikację. Gdy `google.maps` już istnieje
|
|
10
|
-
* aplikacja wstawiła skrypt sama albo stoi tu druga mapa
|
|
9
|
+
* Ładuje Google Maps JavaScript API raz na aplikację. Gdy `google.maps` już istnieje - bo
|
|
10
|
+
* aplikacja wstawiła skrypt sama albo stoi tu druga mapa - korzystamy z tego, co jest: drugie
|
|
11
11
|
* wczytanie Google i tak odrzuca, wypisując ostrzeżenie do konsoli.
|
|
12
12
|
*
|
|
13
13
|
* Publiczne, bo silnik nie jest jedynym, kto tego API potrzebuje. Geokoder Google **musi**
|
|
14
14
|
* iść przez SDK, a nie przez REST: web service `/maps/api/geocode/json` odrzuca klucze
|
|
15
|
-
* z restrykcją `Referer` (
|
|
15
|
+
* z restrykcją `Referer` ("API keys with referer restrictions cannot be used with this API"),
|
|
16
16
|
* a to jedyna restrykcja, jaką da się sensownie nałożyć na klucz leżący w bundlu przeglądarki.
|
|
17
17
|
* Wywołania z załadowanego SDK autoryzują się tak samo jak sama mapa.
|
|
18
18
|
*
|
|
19
|
-
* Adaptera geokodera biblioteka nadal nie dostarcza
|
|
19
|
+
* Adaptera geokodera biblioteka nadal nie dostarcza - tylko drogę do API, którą i tak
|
|
20
20
|
* otwiera silnikowi. Powody stoją przy `provideKptGoogleMap` i w `llms.txt`.
|
|
21
21
|
*/
|
|
22
22
|
export async function loadGoogleMapsApi(options) {
|
|
@@ -52,7 +52,7 @@ export async function loadGoogleMapsApi(options) {
|
|
|
52
52
|
});
|
|
53
53
|
return modulePromise;
|
|
54
54
|
}
|
|
55
|
-
/** Fabryka silnika
|
|
55
|
+
/** Fabryka silnika - każda mapa dostaje własną instancję. */
|
|
56
56
|
export function createGoogleEngine(options) {
|
|
57
57
|
return new GoogleEngine(options);
|
|
58
58
|
}
|
|
@@ -65,18 +65,18 @@ class GoogleEngine {
|
|
|
65
65
|
this.map = null;
|
|
66
66
|
this.host = null;
|
|
67
67
|
this.userMarker = null;
|
|
68
|
-
/** Krąg dokładności jest własnym `<div>`, nie `google.maps.Circle`
|
|
68
|
+
/** Krąg dokładności jest własnym `<div>`, nie `google.maps.Circle` - patrz `setUserPosition`. */
|
|
69
69
|
this.accuracyEl = null;
|
|
70
70
|
this.accuracyMeters = 0;
|
|
71
71
|
this.accuracyAt = null;
|
|
72
|
-
/** Znaczniki po `id` wraz z ich sprzątaniem
|
|
72
|
+
/** Znaczniki po `id` wraz z ich sprzątaniem - bez tego każdy przelicz przerysowywałby mapę. */
|
|
73
73
|
this.markers = new Map();
|
|
74
74
|
this.listeners = [];
|
|
75
75
|
this.handlers = {};
|
|
76
76
|
}
|
|
77
77
|
async init(host, opts) {
|
|
78
78
|
const maps = (this.maps = await loadGoogleMapsApi(this.options));
|
|
79
|
-
// Pod nazwą `Map` kryje się konstruktor Google, nie wbudowana kolekcja
|
|
79
|
+
// Pod nazwą `Map` kryje się konstruktor Google, nie wbudowana kolekcja - stąd alias.
|
|
80
80
|
const [{ Map: GoogleMap }, markerLib] = await Promise.all([
|
|
81
81
|
maps.importLibrary('maps'),
|
|
82
82
|
maps.importLibrary('marker'),
|
|
@@ -89,9 +89,9 @@ class GoogleEngine {
|
|
|
89
89
|
minZoom: opts.minZoom ?? this.options.minZoom ?? 2,
|
|
90
90
|
maxZoom: opts.maxZoom ?? this.options.maxZoom ?? 21,
|
|
91
91
|
mapId: this.options.mapId,
|
|
92
|
-
// Kontrolki rysuje warstwa Angulara
|
|
92
|
+
// Kontrolki rysuje warstwa Angulara - te same przyciski w obu silnikach.
|
|
93
93
|
disableDefaultUI: true,
|
|
94
|
-
// POI Google przechwytywałyby kliknięcia przeznaczone dla mapy (menu
|
|
94
|
+
// POI Google przechwytywałyby kliknięcia przeznaczone dla mapy (menu "dodaj lokalizację").
|
|
95
95
|
clickableIcons: false,
|
|
96
96
|
scrollwheel: this.options.scrollWheelZoom ?? true,
|
|
97
97
|
restriction: opts.maxBounds
|
|
@@ -109,7 +109,7 @@ class GoogleEngine {
|
|
|
109
109
|
});
|
|
110
110
|
this.on_(this.map, 'click', (e) => this.emit('click', this.toPointerEvent(e)));
|
|
111
111
|
this.on_(this.map, 'contextmenu', (e) => {
|
|
112
|
-
// Menu przeglądarki zasłoniłoby własne
|
|
112
|
+
// Menu przeglądarki zasłoniłoby własne "Dodaj lokalizację".
|
|
113
113
|
e.domEvent?.preventDefault();
|
|
114
114
|
this.emit('contextmenu', this.toPointerEvent(e));
|
|
115
115
|
});
|
|
@@ -132,7 +132,7 @@ class GoogleEngine {
|
|
|
132
132
|
for (const listener of this.listeners)
|
|
133
133
|
listener.remove?.();
|
|
134
134
|
this.listeners.length = 0;
|
|
135
|
-
// Google nie ma odpowiednika `map.remove()`
|
|
135
|
+
// Google nie ma odpowiednika `map.remove()` - kontener czyścimy sami.
|
|
136
136
|
if (this.map)
|
|
137
137
|
this.maps?.event.clearInstanceListeners(this.map);
|
|
138
138
|
this.host?.replaceChildren();
|
|
@@ -165,7 +165,7 @@ class GoogleEngine {
|
|
|
165
165
|
return this.map.getZoom();
|
|
166
166
|
}
|
|
167
167
|
/**
|
|
168
|
-
* Zasięg widoku. `getBounds()` bywa `undefined`, zanim mapa policzy pierwszy układ
|
|
168
|
+
* Zasięg widoku. `getBounds()` bywa `undefined`, zanim mapa policzy pierwszy układ -
|
|
169
169
|
* liczymy go wtedy z rdzenia, zamiast oddawać `null` przez kontrakt, który go nie przewiduje.
|
|
170
170
|
*/
|
|
171
171
|
getBounds() {
|
|
@@ -185,12 +185,12 @@ class GoogleEngine {
|
|
|
185
185
|
return { width: rect?.width ?? 0, height: rect?.height ?? 0 };
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
|
-
* Współrzędne
|
|
188
|
+
* Współrzędne -> piksele kontenera.
|
|
189
189
|
*
|
|
190
190
|
* Świadomie **nie** przez `map.getProjection()`: ten zwraca `null` do pierwszego
|
|
191
191
|
* `projection_changed` i tak czy owak wymaga ręcznego przeliczenia na piksele kontenera.
|
|
192
|
-
* Rdzeń (`core/geo.ts`) ma dokładnie tę matematykę
|
|
193
|
-
* i adapter Leafleta
|
|
192
|
+
* Rdzeń (`core/geo.ts`) ma dokładnie tę matematykę - tę samą, na której stoi grupowanie
|
|
193
|
+
* i adapter Leafleta - więc popup ląduje piksel w piksel tak samo w obu silnikach.
|
|
194
194
|
* Działa też dla zoomu ułamkowego, którego Google używa przy gestach.
|
|
195
195
|
*/
|
|
196
196
|
project(at) {
|
|
@@ -210,7 +210,7 @@ class GoogleEngine {
|
|
|
210
210
|
return unproject({ x: origin.x + point.x - size.width / 2, y: origin.y + point.y - size.height / 2 }, zoom);
|
|
211
211
|
}
|
|
212
212
|
/**
|
|
213
|
-
* Wymienia zestaw znaczników różnicowo
|
|
213
|
+
* Wymienia zestaw znaczników różnicowo - znaczniki o niezmienionym `id` zostają na miejscu,
|
|
214
214
|
* inaczej każdy przelicz gasiłby animacje i migotał.
|
|
215
215
|
*/
|
|
216
216
|
setMarkers(next) {
|
|
@@ -241,7 +241,7 @@ class GoogleEngine {
|
|
|
241
241
|
* `L.DomEvent.stopPropagation`: bez zatrzymania propagacji to samo kliknięcie doszłoby
|
|
242
242
|
* do mapy i `onMapClick()` zamknąłby popup w tej samej klatce, w której go otwieramy.
|
|
243
243
|
* Przy okazji odpada zależność od tego, jak `gmp-click` zachowuje się w danej wersji API.
|
|
244
|
-
* Dostępność bez zmian
|
|
244
|
+
* Dostępność bez zmian - etykietę niesie `aria-label` w HTML-u ikony.
|
|
245
245
|
*/
|
|
246
246
|
const onClick = (event) => {
|
|
247
247
|
event.stopPropagation();
|
|
@@ -286,13 +286,13 @@ class GoogleEngine {
|
|
|
286
286
|
map: this.map,
|
|
287
287
|
position: at,
|
|
288
288
|
content: this.buildUserContent(html),
|
|
289
|
-
// Nad znacznikami lokalizacji
|
|
289
|
+
// Nad znacznikami lokalizacji - własne położenie ma być zawsze widoczne.
|
|
290
290
|
zIndex: 1000,
|
|
291
291
|
});
|
|
292
292
|
}
|
|
293
293
|
this.resizeAccuracy();
|
|
294
294
|
}
|
|
295
|
-
/** Google dostarcza własne kafle i własną atrybucję
|
|
295
|
+
/** Google dostarcza własne kafle i własną atrybucję - nie ma czego podmieniać. */
|
|
296
296
|
setTileSource() {
|
|
297
297
|
// celowo puste
|
|
298
298
|
}
|
|
@@ -301,14 +301,14 @@ class GoogleEngine {
|
|
|
301
301
|
restriction: bounds ? { latLngBounds: this.toLatLngBounds(bounds), strictBounds: true } : null,
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
|
-
/** Google sam reaguje na zmianę rozmiaru kontenera
|
|
304
|
+
/** Google sam reaguje na zmianę rozmiaru kontenera - nie ma odpowiednika `invalidateSize`. */
|
|
305
305
|
invalidateSize() {
|
|
306
306
|
// celowo puste
|
|
307
307
|
}
|
|
308
308
|
on(event, handler) {
|
|
309
309
|
(this.handlers[event] ??= []).push(handler);
|
|
310
310
|
}
|
|
311
|
-
//
|
|
311
|
+
// -- prywatne ----------------------------------------------------------------
|
|
312
312
|
emit(event, ...args) {
|
|
313
313
|
for (const handler of this.handlers[event] ?? []) {
|
|
314
314
|
handler(...args);
|
|
@@ -321,7 +321,7 @@ class GoogleEngine {
|
|
|
321
321
|
/**
|
|
322
322
|
* Opakowanie znacznika. `AdvancedMarkerElement` kotwiczy treść **środkiem dolnej krawędzi**,
|
|
323
323
|
* a kontrakt daje kotwicę w pikselach od lewego górnego rogu ikony. Element zewnętrzny ma
|
|
324
|
-
* więc zerowe wymiary
|
|
324
|
+
* więc zerowe wymiary - jego "środek dolnej krawędzi" wypada dokładnie we współrzędnych -
|
|
325
325
|
* a wewnętrzny przesuwa ikonę o żądaną kotwicę.
|
|
326
326
|
*/
|
|
327
327
|
buildMarkerContent(m) {
|
|
@@ -339,7 +339,7 @@ class GoogleEngine {
|
|
|
339
339
|
inner.style.top = `${-m.anchor.y}px`;
|
|
340
340
|
inner.style.width = `${m.size.width}px`;
|
|
341
341
|
inner.style.height = `${m.size.height}px`;
|
|
342
|
-
// HTML pochodzi z `marker-html.ts`
|
|
342
|
+
// HTML pochodzi z `marker-html.ts` - z rdzenia biblioteki, nie od użytkownika.
|
|
343
343
|
inner.innerHTML = m.html;
|
|
344
344
|
}
|
|
345
345
|
/**
|
|
@@ -383,7 +383,7 @@ class GoogleEngine {
|
|
|
383
383
|
fromDomEvent(event, at) {
|
|
384
384
|
return { at, point: this.toContainerPoint(event), originalEvent: event };
|
|
385
385
|
}
|
|
386
|
-
/** Google podaje zdarzenie DOM bez pozycji w kontenerze
|
|
386
|
+
/** Google podaje zdarzenie DOM bez pozycji w kontenerze - liczymy ją z prostokąta hosta. */
|
|
387
387
|
toContainerPoint(event) {
|
|
388
388
|
const rect = this.host?.getBoundingClientRect();
|
|
389
389
|
if (!event || !rect)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { KptMapEngine } from '../engine.ts';
|
|
2
2
|
export interface KptLeafletOptions {
|
|
3
|
-
/** Dolny limit zoomu. Domyślnie 2
|
|
3
|
+
/** Dolny limit zoomu. Domyślnie 2 - niżej świat powtarza się w poziomie. */
|
|
4
4
|
minZoom?: number;
|
|
5
5
|
/** Górny limit zoomu. Domyślnie 19 (tyle daje OSM). */
|
|
6
6
|
maxZoom?: number;
|
|
@@ -9,5 +9,5 @@ export interface KptLeafletOptions {
|
|
|
9
9
|
/** Zoom kółkiem myszy. Domyślnie włączony. */
|
|
10
10
|
scrollWheelZoom?: boolean;
|
|
11
11
|
}
|
|
12
|
-
/** Fabryka silnika
|
|
12
|
+
/** Fabryka silnika - każda mapa dostaje własną instancję. */
|
|
13
13
|
export declare function createLeafletEngine(options?: KptLeafletOptions): KptMapEngine;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const MODULE_LOAD_ERROR = 'kpt-map: nie udało się załadować biblioteki `leaflet`. ' +
|
|
2
|
-
'To zależność opcjonalna
|
|
2
|
+
'To zależność opcjonalna - zainstaluj ją w aplikacji: `pnpm add leaflet` ' +
|
|
3
3
|
'(oraz dołącz `leaflet/dist/leaflet.css`).';
|
|
4
4
|
let modulePromise = null;
|
|
5
5
|
/**
|
|
6
6
|
* Ładuje Leaflet raz na aplikację. Leaflet 1.9 jest paczką UMD/CJS bez pola `exports`,
|
|
7
|
-
* więc pod bundlerem trafia raz jako `default`, a raz jako sam namespace
|
|
7
|
+
* więc pod bundlerem trafia raz jako `default`, a raz jako sam namespace - stąd interop.
|
|
8
8
|
*/
|
|
9
9
|
async function loadLeaflet() {
|
|
10
10
|
modulePromise ??= import('leaflet')
|
|
@@ -15,7 +15,7 @@ async function loadLeaflet() {
|
|
|
15
15
|
});
|
|
16
16
|
return modulePromise;
|
|
17
17
|
}
|
|
18
|
-
/** Fabryka silnika
|
|
18
|
+
/** Fabryka silnika - każda mapa dostaje własną instancję. */
|
|
19
19
|
export function createLeafletEngine(options = {}) {
|
|
20
20
|
return new LeafletEngine(options);
|
|
21
21
|
}
|
|
@@ -28,7 +28,7 @@ class LeafletEngine {
|
|
|
28
28
|
this.tiles = null;
|
|
29
29
|
this.userMarker = null;
|
|
30
30
|
this.userCircle = null;
|
|
31
|
-
/** Znaczniki po `id`
|
|
31
|
+
/** Znaczniki po `id` - bez tego każdy przelicz przerysowywałby całą mapę. */
|
|
32
32
|
this.markers = new Map();
|
|
33
33
|
this.handlers = {};
|
|
34
34
|
}
|
|
@@ -39,7 +39,7 @@ class LeafletEngine {
|
|
|
39
39
|
zoom: opts.zoom,
|
|
40
40
|
minZoom: opts.minZoom ?? this.options.minZoom ?? 2,
|
|
41
41
|
maxZoom: opts.maxZoom ?? this.options.maxZoom ?? 19,
|
|
42
|
-
// Kontrolki rysuje warstwa Angulara
|
|
42
|
+
// Kontrolki rysuje warstwa Angulara - domyślnych nie da się otokenizować
|
|
43
43
|
// bez walki z niewarstwowanym `leaflet.css`.
|
|
44
44
|
zoomControl: false,
|
|
45
45
|
attributionControl: true,
|
|
@@ -58,7 +58,7 @@ class LeafletEngine {
|
|
|
58
58
|
this.map.on('zoomend', () => this.emit('zoomend'));
|
|
59
59
|
this.map.on('click', (e) => this.emit('click', this.toPointerEvent(e)));
|
|
60
60
|
this.map.on('contextmenu', (e) => {
|
|
61
|
-
// Menu przeglądarki zasłoniłoby własne
|
|
61
|
+
// Menu przeglądarki zasłoniłoby własne "Dodaj lokalizację".
|
|
62
62
|
L.DomEvent.preventDefault(e.originalEvent);
|
|
63
63
|
this.emit('contextmenu', this.toPointerEvent(e));
|
|
64
64
|
});
|
|
@@ -108,7 +108,7 @@ class LeafletEngine {
|
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
110
110
|
* Wymienia zestaw znaczników różnicowo. Znaczniki, których `id` się nie zmieniło,
|
|
111
|
-
* zostają na miejscu
|
|
111
|
+
* zostają na miejscu - inaczej każdy przelicz gasiłby animacje i migotał.
|
|
112
112
|
*/
|
|
113
113
|
setMarkers(next) {
|
|
114
114
|
if (!this.map || !this.L)
|
|
@@ -171,7 +171,7 @@ class LeafletEngine {
|
|
|
171
171
|
this.userMarker.setIcon(icon);
|
|
172
172
|
}
|
|
173
173
|
else {
|
|
174
|
-
// Nad znacznikami lokalizacji
|
|
174
|
+
// Nad znacznikami lokalizacji - własne położenie ma być zawsze widoczne.
|
|
175
175
|
this.userMarker = new L.Marker(at, { icon, alt: '', keyboard: false, zIndexOffset: 1000 }).addTo(this.map);
|
|
176
176
|
}
|
|
177
177
|
// Promień w metrach; L.Circle sam przelicza go na piksele przy każdym zoomie.
|
|
@@ -198,7 +198,7 @@ class LeafletEngine {
|
|
|
198
198
|
attribution,
|
|
199
199
|
maxZoom: this.options.maxZoom ?? 19,
|
|
200
200
|
}).addTo(this.map);
|
|
201
|
-
// Kafle wchodzą pod znaczniki
|
|
201
|
+
// Kafle wchodzą pod znaczniki - bez tego nowa warstwa przykryłaby je.
|
|
202
202
|
this.tiles.bringToBack();
|
|
203
203
|
}
|
|
204
204
|
setMaxBounds(bounds) {
|
|
@@ -208,7 +208,7 @@ class LeafletEngine {
|
|
|
208
208
|
this.map.options.maxBoundsViscosity = bounds ? 1 : 0;
|
|
209
209
|
}
|
|
210
210
|
invalidateSize() {
|
|
211
|
-
// `animate: false`
|
|
211
|
+
// `animate: false` - to reakcja na zmianę layoutu, nie gest użytkownika.
|
|
212
212
|
this.map?.invalidateSize({ animate: false });
|
|
213
213
|
}
|
|
214
214
|
on(event, handler) {
|
package/dist/format.d.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Dystans po ludzku: poniżej kilometra w metrach zaokrąglonych do dziesiątek,
|
|
3
3
|
* wyżej w kilometrach z jednym miejscem po przecinku (`6,2 km` w `pl-PL`).
|
|
4
4
|
*
|
|
5
|
-
* Wartość ujemna albo nieskończona daje `null`
|
|
6
|
-
* zamiast malować
|
|
5
|
+
* Wartość ujemna albo nieskończona daje `null` - wołający chowa wtedy wiersz,
|
|
6
|
+
* zamiast malować "NaN km".
|
|
7
7
|
*/
|
|
8
8
|
export declare function formatDistance(metres: number | null | undefined, locale: string): string | null;
|
|
9
|
-
/** Współrzędne do wiersza karty
|
|
9
|
+
/** Współrzędne do wiersza karty - pięć miejsc po przecinku to ok. metr. */
|
|
10
10
|
export declare function formatLatLng(at: {
|
|
11
11
|
lat: number;
|
|
12
12
|
lng: number;
|
package/dist/format.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Formatowanie liczb dla warstwy prezentacji mapy
|
|
2
|
+
* Formatowanie liczb dla warstwy prezentacji mapy - pure TS.
|
|
3
3
|
* Wszystko przez `Intl`, więc separator dziesiętny idzie z locale,
|
|
4
4
|
* a nie z zaszytej na sztywno kropki.
|
|
5
5
|
*/
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Dystans po ludzku: poniżej kilometra w metrach zaokrąglonych do dziesiątek,
|
|
8
8
|
* wyżej w kilometrach z jednym miejscem po przecinku (`6,2 km` w `pl-PL`).
|
|
9
9
|
*
|
|
10
|
-
* Wartość ujemna albo nieskończona daje `null`
|
|
11
|
-
* zamiast malować
|
|
10
|
+
* Wartość ujemna albo nieskończona daje `null` - wołający chowa wtedy wiersz,
|
|
11
|
+
* zamiast malować "NaN km".
|
|
12
12
|
*/
|
|
13
13
|
export function formatDistance(metres, locale) {
|
|
14
14
|
if (metres == null || !Number.isFinite(metres) || metres < 0)
|
|
@@ -26,7 +26,7 @@ export function formatDistance(metres, locale) {
|
|
|
26
26
|
}).format(km);
|
|
27
27
|
return `${value} km`;
|
|
28
28
|
}
|
|
29
|
-
/** Współrzędne do wiersza karty
|
|
29
|
+
/** Współrzędne do wiersza karty - pięć miejsc po przecinku to ok. metr. */
|
|
30
30
|
export function formatLatLng(at, digits = 5) {
|
|
31
31
|
return `${at.lat.toFixed(digits)}, ${at.lng.toFixed(digits)}`;
|
|
32
32
|
}
|
package/dist/geo.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KptLatLng, KptMapBounds, KptMapPoint } from './types.ts';
|
|
2
|
-
/** Bok kafla w pikselach
|
|
2
|
+
/** Bok kafla w pikselach - stała obu silników. */
|
|
3
3
|
export declare const KPT_TILE_SIZE = 256;
|
|
4
4
|
/**
|
|
5
5
|
* Granica szerokości w Mercatorze. Powyżej niej projekcja ucieka do nieskończoności,
|
|
@@ -13,31 +13,31 @@ export declare function clampLat(lat: number): number;
|
|
|
13
13
|
/**
|
|
14
14
|
* Zawija długość geograficzną do zakresu [-180, 180].
|
|
15
15
|
*
|
|
16
|
-
* Wartości już w zakresie wracają nietknięte
|
|
16
|
+
* Wartości już w zakresie wracają nietknięte - także -180, bo w projekcji jest to
|
|
17
17
|
* lewa krawędź świata (x = 0), a nie ta sama kolumna pikseli co +180. Zawijanie
|
|
18
18
|
* dotyczy wyłącznie tego, co wychodzi poza zakres: 540° to ten sam południk co 180°.
|
|
19
19
|
*/
|
|
20
20
|
export declare function normalizeLng(lng: number): number;
|
|
21
21
|
/** Liczba pikseli świata na danym zoomie. */
|
|
22
22
|
export declare function worldSize(zoom: number): number;
|
|
23
|
-
/** Współrzędne
|
|
23
|
+
/** Współrzędne -> piksele świata na danym zoomie (origin w lewym górnym rogu). */
|
|
24
24
|
export declare function project(at: KptLatLng, zoom: number): KptMapPoint;
|
|
25
|
-
/** Piksele świata
|
|
25
|
+
/** Piksele świata -> współrzędne. Odwrotność `project`. */
|
|
26
26
|
export declare function unproject(point: KptMapPoint, zoom: number): KptLatLng;
|
|
27
|
-
/** Najmniejszy prostokąt obejmujący wszystkie punkty. Pusta lista
|
|
27
|
+
/** Najmniejszy prostokąt obejmujący wszystkie punkty. Pusta lista -> `null`. */
|
|
28
28
|
export declare function boundsOf(points: readonly KptLatLng[]): KptMapBounds | null;
|
|
29
29
|
/**
|
|
30
30
|
* Najmniejszy zasięg w stopniach, przy którym `fitBounds` ma jeszcze sens (~100 m).
|
|
31
31
|
*
|
|
32
32
|
* Geokodery obrysowują pojedynczy adres prostokątem rzędu 0,0001°. Wpasowanie takiego
|
|
33
33
|
* w kadr wjeżdża na górny limit zoomu, a wynik zależy od tego, jak dostawca obrysował
|
|
34
|
-
* budynek
|
|
34
|
+
* budynek - dwa sąsiednie adresy lądują w różnych przybliżeniach.
|
|
35
35
|
*/
|
|
36
36
|
export declare const KPT_MIN_FIT_SPAN = 0.001;
|
|
37
37
|
/**
|
|
38
38
|
* Czy prostokąt warto wpasowywać w kadr, zamiast potraktować go jak punkt.
|
|
39
39
|
*
|
|
40
|
-
* `false` dla zasięgu zdegenerowanego albo ciaśniejszego niż `minSpan` w OBU osiach
|
|
40
|
+
* `false` dla zasięgu zdegenerowanego albo ciaśniejszego niż `minSpan` w OBU osiach -
|
|
41
41
|
* długa, wąska ulica ma jeden wymiar mikroskopijny i dalej zasługuje na `fitBounds`.
|
|
42
42
|
*/
|
|
43
43
|
export declare function isFittableBounds(bounds: KptMapBounds, minSpan?: number): boolean;
|
|
@@ -56,7 +56,7 @@ export declare function haversine(a: KptLatLng, b: KptLatLng): number;
|
|
|
56
56
|
export declare function metersPerPixel(lat: number, zoom: number): number;
|
|
57
57
|
/**
|
|
58
58
|
* Największy zoom, przy którym `bounds` mieści się w kontenerze o zadanym rozmiarze.
|
|
59
|
-
* Zwraca liczbę zmiennoprzecinkową
|
|
59
|
+
* Zwraca liczbę zmiennoprzecinkową - zaokrąglenie w dół zostawiamy wołającemu.
|
|
60
60
|
*/
|
|
61
61
|
export declare function zoomForBounds(bounds: KptMapBounds, size: {
|
|
62
62
|
width: number;
|
package/dist/geo.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Bok kafla w pikselach
|
|
1
|
+
/** Bok kafla w pikselach - stała obu silników. */
|
|
2
2
|
export const KPT_TILE_SIZE = 256;
|
|
3
3
|
/**
|
|
4
4
|
* Granica szerokości w Mercatorze. Powyżej niej projekcja ucieka do nieskończoności,
|
|
@@ -14,7 +14,7 @@ export function clampLat(lat) {
|
|
|
14
14
|
/**
|
|
15
15
|
* Zawija długość geograficzną do zakresu [-180, 180].
|
|
16
16
|
*
|
|
17
|
-
* Wartości już w zakresie wracają nietknięte
|
|
17
|
+
* Wartości już w zakresie wracają nietknięte - także -180, bo w projekcji jest to
|
|
18
18
|
* lewa krawędź świata (x = 0), a nie ta sama kolumna pikseli co +180. Zawijanie
|
|
19
19
|
* dotyczy wyłącznie tego, co wychodzi poza zakres: 540° to ten sam południk co 180°.
|
|
20
20
|
*/
|
|
@@ -28,7 +28,7 @@ export function normalizeLng(lng) {
|
|
|
28
28
|
export function worldSize(zoom) {
|
|
29
29
|
return KPT_TILE_SIZE * Math.pow(2, zoom);
|
|
30
30
|
}
|
|
31
|
-
/** Współrzędne
|
|
31
|
+
/** Współrzędne -> piksele świata na danym zoomie (origin w lewym górnym rogu). */
|
|
32
32
|
export function project(at, zoom) {
|
|
33
33
|
const size = worldSize(zoom);
|
|
34
34
|
const lat = clampLat(at.lat);
|
|
@@ -38,7 +38,7 @@ export function project(at, zoom) {
|
|
|
38
38
|
y: size * (0.5 - Math.log((1 + sin) / (1 - sin)) / (4 * Math.PI)),
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
/** Piksele świata
|
|
41
|
+
/** Piksele świata -> współrzędne. Odwrotność `project`. */
|
|
42
42
|
export function unproject(point, zoom) {
|
|
43
43
|
const size = worldSize(zoom);
|
|
44
44
|
const lng = (point.x / size - 0.5) * 360;
|
|
@@ -46,7 +46,7 @@ export function unproject(point, zoom) {
|
|
|
46
46
|
const lat = (180 / Math.PI) * Math.atan(Math.sinh(n));
|
|
47
47
|
return { lat, lng };
|
|
48
48
|
}
|
|
49
|
-
/** Najmniejszy prostokąt obejmujący wszystkie punkty. Pusta lista
|
|
49
|
+
/** Najmniejszy prostokąt obejmujący wszystkie punkty. Pusta lista -> `null`. */
|
|
50
50
|
export function boundsOf(points) {
|
|
51
51
|
if (points.length === 0)
|
|
52
52
|
return null;
|
|
@@ -71,13 +71,13 @@ export function boundsOf(points) {
|
|
|
71
71
|
*
|
|
72
72
|
* Geokodery obrysowują pojedynczy adres prostokątem rzędu 0,0001°. Wpasowanie takiego
|
|
73
73
|
* w kadr wjeżdża na górny limit zoomu, a wynik zależy od tego, jak dostawca obrysował
|
|
74
|
-
* budynek
|
|
74
|
+
* budynek - dwa sąsiednie adresy lądują w różnych przybliżeniach.
|
|
75
75
|
*/
|
|
76
76
|
export const KPT_MIN_FIT_SPAN = 0.001;
|
|
77
77
|
/**
|
|
78
78
|
* Czy prostokąt warto wpasowywać w kadr, zamiast potraktować go jak punkt.
|
|
79
79
|
*
|
|
80
|
-
* `false` dla zasięgu zdegenerowanego albo ciaśniejszego niż `minSpan` w OBU osiach
|
|
80
|
+
* `false` dla zasięgu zdegenerowanego albo ciaśniejszego niż `minSpan` w OBU osiach -
|
|
81
81
|
* długa, wąska ulica ma jeden wymiar mikroskopijny i dalej zasługuje na `fitBounds`.
|
|
82
82
|
*/
|
|
83
83
|
export function isFittableBounds(bounds, minSpan = KPT_MIN_FIT_SPAN) {
|
|
@@ -122,7 +122,7 @@ export function metersPerPixel(lat, zoom) {
|
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
124
|
* Największy zoom, przy którym `bounds` mieści się w kontenerze o zadanym rozmiarze.
|
|
125
|
-
* Zwraca liczbę zmiennoprzecinkową
|
|
125
|
+
* Zwraca liczbę zmiennoprzecinkową - zaokrąglenie w dół zostawiamy wołającemu.
|
|
126
126
|
*/
|
|
127
127
|
export function zoomForBounds(bounds, size, paddingPx = 0) {
|
|
128
128
|
const w = Math.max(1, size.width - paddingPx * 2);
|
package/dist/hours.d.ts
CHANGED
|
@@ -9,33 +9,33 @@ export declare function isoWeekday(date: Date): KptMapWeekday;
|
|
|
9
9
|
* Czy miejsce jest otwarte w danym momencie.
|
|
10
10
|
*
|
|
11
11
|
* Sprawdza dwa dni: bieżący oraz poprzedni, bo przedział przez północ
|
|
12
|
-
* (`22:00
|
|
12
|
+
* (`22:00 - 01:00`) obejmuje wczesne godziny dnia następnego.
|
|
13
13
|
*/
|
|
14
14
|
export declare function isOpenAt(hours: KptMapOpeningHours | undefined, date: Date): boolean;
|
|
15
|
-
/** Czy rozkład to pełna doba przez siedem dni
|
|
15
|
+
/** Czy rozkład to pełna doba przez siedem dni - wtedy karta pisze po prostu "24/7". */
|
|
16
16
|
export declare function isAlwaysOpen(hours: KptMapOpeningHours | undefined): boolean;
|
|
17
17
|
/** Najbliższa zmiana stanu otwarte/zamknięte albo `null`, gdy w ciągu tygodnia jej nie ma. */
|
|
18
18
|
export declare function nextChange(hours: KptMapOpeningHours | undefined, date: Date): {
|
|
19
19
|
open: boolean;
|
|
20
20
|
at: Date;
|
|
21
21
|
} | null;
|
|
22
|
-
/** Skrócona nazwa dnia z `Intl`
|
|
22
|
+
/** Skrócona nazwa dnia z `Intl` - bez słownika, więc działa dla dowolnego locale. */
|
|
23
23
|
export declare function weekdayName(day: KptMapWeekday, locale: string): string;
|
|
24
24
|
/** Wiersz rozkładu: jeden dzień albo sklejony zakres dni o identycznych godzinach. */
|
|
25
25
|
export interface KptMapHoursGroup {
|
|
26
|
-
/** `pon.` albo `wt
|
|
26
|
+
/** `pon.` albo `wt.-sob.`, wg locale. */
|
|
27
27
|
label: string;
|
|
28
28
|
days: KptMapWeekday[];
|
|
29
29
|
closed: boolean;
|
|
30
|
-
/** Sformatowane przedziały, np. `['11:00
|
|
30
|
+
/** Sformatowane przedziały, np. `['11:00 - 24:00']`. Puste, gdy zamknięte. */
|
|
31
31
|
ranges: string[];
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Rozkład tygodnia z sąsiadującymi dniami sklejonymi w jeden wiersz.
|
|
35
35
|
*
|
|
36
|
-
* Niedziela i poniedziałek się nie sklejają, choć w tablicy sąsiadują
|
|
36
|
+
* Niedziela i poniedziałek się nie sklejają, choć w tablicy sąsiadują - tydzień
|
|
37
37
|
* czyta się liniowo od poniedziałku i zawijanie byłoby mylące.
|
|
38
38
|
*/
|
|
39
39
|
export declare function groupWeek(hours: KptMapOpeningHours | undefined, locale: string, separator?: string): KptMapHoursGroup[];
|
|
40
|
-
/** Siedem dni oznaczonych jako zamknięte
|
|
40
|
+
/** Siedem dni oznaczonych jako zamknięte - wartość startowa pola `hours` w formularzu. */
|
|
41
41
|
export declare function emptyWeek(): KptMapOpeningHours;
|
package/dist/hours.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Godziny otwarcia
|
|
2
|
+
* Godziny otwarcia - pure TS, bez Angulara i bez silnika mapowego.
|
|
3
3
|
* Ten plik przenosi się do portu React bez zmian.
|
|
4
4
|
*
|
|
5
5
|
* Umowa o czasie: `HH:MM` w czasie lokalnym miejsca, a porównania robimy
|
|
6
6
|
* na lokalnym czasie przeglądarki. Miejsce w innej strefie niż użytkownik
|
|
7
|
-
* pokaże więc status wg zegara użytkownika
|
|
7
|
+
* pokaże więc status wg zegara użytkownika - to świadome uproszczenie,
|
|
8
8
|
* bo model nie niesie strefy. Kto potrzebuje inaczej, liczy status u siebie.
|
|
9
9
|
*/
|
|
10
10
|
/** Minuta doby dla `HH:MM`; `24:00` to 1440. `null`, gdy zapis jest nieprawidłowy. */
|
|
@@ -50,7 +50,7 @@ function usableRanges(entry) {
|
|
|
50
50
|
* Czy miejsce jest otwarte w danym momencie.
|
|
51
51
|
*
|
|
52
52
|
* Sprawdza dwa dni: bieżący oraz poprzedni, bo przedział przez północ
|
|
53
|
-
* (`22:00
|
|
53
|
+
* (`22:00 - 01:00`) obejmuje wczesne godziny dnia następnego.
|
|
54
54
|
*/
|
|
55
55
|
export function isOpenAt(hours, date) {
|
|
56
56
|
if (!hours || hours.length === 0)
|
|
@@ -71,7 +71,7 @@ export function isOpenAt(hours, date) {
|
|
|
71
71
|
}
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
74
|
-
/** Czy rozkład to pełna doba przez siedem dni
|
|
74
|
+
/** Czy rozkład to pełna doba przez siedem dni - wtedy karta pisze po prostu "24/7". */
|
|
75
75
|
export function isAlwaysOpen(hours) {
|
|
76
76
|
if (!hours)
|
|
77
77
|
return false;
|
|
@@ -117,7 +117,7 @@ export function nextChange(hours, date) {
|
|
|
117
117
|
}
|
|
118
118
|
return null;
|
|
119
119
|
}
|
|
120
|
-
/** Skrócona nazwa dnia z `Intl`
|
|
120
|
+
/** Skrócona nazwa dnia z `Intl` - bez słownika, więc działa dla dowolnego locale. */
|
|
121
121
|
export function weekdayName(day, locale) {
|
|
122
122
|
// 2024-01-01 był poniedziałkiem, więc dzień ISO wypada na tę samą liczbę w miesiącu.
|
|
123
123
|
const date = new Date(Date.UTC(2024, 0, day));
|
|
@@ -132,10 +132,10 @@ function signatureOf(entry) {
|
|
|
132
132
|
/**
|
|
133
133
|
* Rozkład tygodnia z sąsiadującymi dniami sklejonymi w jeden wiersz.
|
|
134
134
|
*
|
|
135
|
-
* Niedziela i poniedziałek się nie sklejają, choć w tablicy sąsiadują
|
|
135
|
+
* Niedziela i poniedziałek się nie sklejają, choć w tablicy sąsiadują - tydzień
|
|
136
136
|
* czyta się liniowo od poniedziałku i zawijanie byłoby mylące.
|
|
137
137
|
*/
|
|
138
|
-
export function groupWeek(hours, locale, separator = '
|
|
138
|
+
export function groupWeek(hours, locale, separator = ' - ') {
|
|
139
139
|
if (!hours || hours.length === 0)
|
|
140
140
|
return [];
|
|
141
141
|
const groups = [];
|
|
@@ -160,11 +160,11 @@ export function groupWeek(hours, locale, separator = ' – ') {
|
|
|
160
160
|
for (const group of groups) {
|
|
161
161
|
const first = weekdayName(group.days[0], locale);
|
|
162
162
|
const last = weekdayName(group.days[group.days.length - 1], locale);
|
|
163
|
-
group.label = group.days.length === 1 ? first : `${first}
|
|
163
|
+
group.label = group.days.length === 1 ? first : `${first}-${last}`;
|
|
164
164
|
}
|
|
165
165
|
return groups;
|
|
166
166
|
}
|
|
167
|
-
/** Siedem dni oznaczonych jako zamknięte
|
|
167
|
+
/** Siedem dni oznaczonych jako zamknięte - wartość startowa pola `hours` w formularzu. */
|
|
168
168
|
export function emptyWeek() {
|
|
169
169
|
return Array.from({ length: 7 }, (_, i) => ({
|
|
170
170
|
day: (i + 1),
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Rdzeń mapy
|
|
2
|
+
* Rdzeń mapy - pure TS, bez frameworka. Obok obliczeń (geometria, klastrowanie, godziny)
|
|
3
3
|
* mieszkają tu adaptery silników: Leaflet i Google. Oba rozmawiają z resztą kodu wyłącznie
|
|
4
4
|
* przez interfejs `KptMapEngine`, więc port Angulara i Reacta dzielą całą tę warstwę.
|
|
5
5
|
*/
|
package/dist/marker-html.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { KptMapColor, KptMapMarkerShape } from './types.ts';
|
|
2
|
-
/** Rola koloru
|
|
2
|
+
/** Rola koloru -> zmienna CSS. Gotowe `var(--kpt-*)` przechodzi bez zmian; cokolwiek spoza tego
|
|
3
3
|
* kontraktu dostaje bezpieczny fallback zamiast trafić surowo do wyjścia. */
|
|
4
4
|
export declare function colorVarOf(color: KptMapColor): string;
|
|
5
|
-
/** Kolor treści na tle `color`
|
|
5
|
+
/** Kolor treści na tle `color` - dla ról znany, dla własnych zmiennych fallback na biel tokenową. */
|
|
6
6
|
export declare function contrastVarOf(color: KptMapColor): string;
|
|
7
7
|
export declare function foregroundVarOf(color: KptMapColor): string;
|
|
8
8
|
/** Ucieka znaki, które rozbiłyby atrybut albo wstrzyknęły znacznik. */
|
|
@@ -12,9 +12,9 @@ export interface KptMarkerHtmlInput {
|
|
|
12
12
|
color: KptMapColor;
|
|
13
13
|
/** Inner-SVG ikony (bez wrappera `<svg>`); bez niego znacznik nie ma glifu. */
|
|
14
14
|
iconSvgInner?: string;
|
|
15
|
-
/** Tekst dla czytnika ekranu
|
|
15
|
+
/** Tekst dla czytnika ekranu - zwykle tytuł lokalizacji. */
|
|
16
16
|
label: string;
|
|
17
|
-
/** Znacznik wybrany (otwarty popup)
|
|
17
|
+
/** Znacznik wybrany (otwarty popup) - dostaje klasę modyfikującą. */
|
|
18
18
|
selected?: boolean;
|
|
19
19
|
}
|
|
20
20
|
export declare function renderMarkerHtml(input: KptMarkerHtmlInput): string;
|
|
@@ -26,11 +26,11 @@ export interface KptClusterHtmlInput {
|
|
|
26
26
|
}
|
|
27
27
|
/** Koło z licznikiem; rozmiar rośnie schodkowo z liczebnością grupy. */
|
|
28
28
|
export declare function renderClusterHtml(input: KptClusterHtmlInput): string;
|
|
29
|
-
/** Próg wielkości grupy
|
|
29
|
+
/** Próg wielkości grupy - trzy stopnie wystarczą, żeby zobaczyć różnicę bez skalowania ciągłego. */
|
|
30
30
|
export declare function clusterTier(count: number): 'sm' | 'md' | 'lg';
|
|
31
|
-
/** Powyżej 999 licznik przestaje się mieścić
|
|
31
|
+
/** Powyżej 999 licznik przestaje się mieścić - skracamy. */
|
|
32
32
|
export declare function formatCount(count: number): string;
|
|
33
|
-
/** Znacznik pozycji użytkownika
|
|
33
|
+
/** Znacznik pozycji użytkownika - ikona podawana z zewnątrz, jak przy zwykłym znaczniku. */
|
|
34
34
|
export declare function renderUserMarkerHtml(input: {
|
|
35
35
|
label: string;
|
|
36
36
|
iconSvgInner?: string;
|
package/dist/marker-html.js
CHANGED
|
@@ -15,14 +15,14 @@ const ROLE_CONTRAST_VARS = {
|
|
|
15
15
|
muted: 'var(--kpt-color-on-muted)',
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
* Kształt jedynego wejścia spoza znanej roli, jakie kontrakt `KptMapColor` dopuszcza
|
|
18
|
+
* Kształt jedynego wejścia spoza znanej roli, jakie kontrakt `KptMapColor` dopuszcza -
|
|
19
19
|
* gotowy token `var(--kpt-*)`. Dane runtime (JSON/CMS) nie są tym kontraktem związane, więc
|
|
20
20
|
* zanim trafią do atrybutu `style="..."` budowanego konkatenacją stringów w `renderMarkerHtml`/
|
|
21
|
-
* `renderClusterHtml`, muszą zostać zweryfikowane
|
|
21
|
+
* `renderClusterHtml`, muszą zostać zweryfikowane - inaczej cudzysłów/`>` wyrywa się z atrybutu
|
|
22
22
|
* (HTML injection), a średnik wstrzykuje dodatkową deklarację CSS.
|
|
23
23
|
*/
|
|
24
24
|
const SAFE_VAR_TOKEN = /^var\(--kpt-[\w-]+\)$/;
|
|
25
|
-
/** Rola koloru
|
|
25
|
+
/** Rola koloru -> zmienna CSS. Gotowe `var(--kpt-*)` przechodzi bez zmian; cokolwiek spoza tego
|
|
26
26
|
* kontraktu dostaje bezpieczny fallback zamiast trafić surowo do wyjścia. */
|
|
27
27
|
export function colorVarOf(color) {
|
|
28
28
|
const known = ROLE_VARS[color];
|
|
@@ -30,7 +30,7 @@ export function colorVarOf(color) {
|
|
|
30
30
|
return known;
|
|
31
31
|
return SAFE_VAR_TOKEN.test(color) ? color : ROLE_VARS.muted;
|
|
32
32
|
}
|
|
33
|
-
/** Kolor treści na tle `color`
|
|
33
|
+
/** Kolor treści na tle `color` - dla ról znany, dla własnych zmiennych fallback na biel tokenową. */
|
|
34
34
|
export function contrastVarOf(color) {
|
|
35
35
|
return ROLE_CONTRAST_VARS[color] ?? 'var(--kpt-color-text-inverse)';
|
|
36
36
|
}
|
|
@@ -38,7 +38,7 @@ export function contrastVarOf(color) {
|
|
|
38
38
|
* Kolor **glifu stojącego wprost na powierzchni**, bez własnego tła.
|
|
39
39
|
*
|
|
40
40
|
* Różni się od `colorVarOf()` tylko dla roli `muted`: `--kpt-color-muted` to neutral-100,
|
|
41
|
-
* czyli token tła, a nie treści
|
|
41
|
+
* czyli token tła, a nie treści - goły znak w tym kolorze na jasnej powierzchni jest
|
|
42
42
|
* niewidoczny. Bierzemy więc czytelny szary tekstowy. Pozostałe role są nasycone
|
|
43
43
|
* i jako treść czytają się bez zmian.
|
|
44
44
|
*/
|
|
@@ -63,11 +63,11 @@ export function escapeHtml(value) {
|
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Pinezka o wysokości `--kpt-map-marker-size`, zakotwiczona czubkiem w punkcie.
|
|
66
|
-
* `viewBox`
|
|
66
|
+
* `viewBox` 24x32: 24 szerokości koła + ogon do 32.
|
|
67
67
|
*/
|
|
68
68
|
const PIN_PATH = 'M12 0C5.8 0 .8 5 .8 11.2c0 8 9.9 19.4 10.3 19.9a1.2 1.2 0 0 0 1.8 0c.4-.5 10.3-11.9 10.3-19.9C23.2 5 18.2 0 12 0z';
|
|
69
69
|
export function renderMarkerHtml(input) {
|
|
70
|
-
// `shape` trafia do atrybutu `class="..."` konkatenacją stringów
|
|
70
|
+
// `shape` trafia do atrybutu `class="..."` konkatenacją stringów - `KptMapMarkerShape` ogranicza
|
|
71
71
|
// to tylko compile-time, dane runtime (JSON/CMS) nie są tym związane (ten sam powód, dla którego
|
|
72
72
|
// `color`/`contrast` dostały escapowanie/whitelistę wyżej w pliku).
|
|
73
73
|
const shape = escapeHtml(input.shape ?? 'pin');
|
|
@@ -96,7 +96,7 @@ export function renderClusterHtml(input) {
|
|
|
96
96
|
` style="--_kpt-map-cluster-bg:${color};--_kpt-map-cluster-fg:${contrast}">` +
|
|
97
97
|
`<span class="kpt-map__cluster-count">${formatCount(input.count)}</span></div>`);
|
|
98
98
|
}
|
|
99
|
-
/** Próg wielkości grupy
|
|
99
|
+
/** Próg wielkości grupy - trzy stopnie wystarczą, żeby zobaczyć różnicę bez skalowania ciągłego. */
|
|
100
100
|
export function clusterTier(count) {
|
|
101
101
|
if (count < 10)
|
|
102
102
|
return 'sm';
|
|
@@ -104,11 +104,11 @@ export function clusterTier(count) {
|
|
|
104
104
|
return 'md';
|
|
105
105
|
return 'lg';
|
|
106
106
|
}
|
|
107
|
-
/** Powyżej 999 licznik przestaje się mieścić
|
|
107
|
+
/** Powyżej 999 licznik przestaje się mieścić - skracamy. */
|
|
108
108
|
export function formatCount(count) {
|
|
109
109
|
return count > 999 ? '999+' : String(count);
|
|
110
110
|
}
|
|
111
|
-
/** Znacznik pozycji użytkownika
|
|
111
|
+
/** Znacznik pozycji użytkownika - ikona podawana z zewnątrz, jak przy zwykłym znaczniku. */
|
|
112
112
|
export function renderUserMarkerHtml(input) {
|
|
113
113
|
const label = escapeHtml(input.label);
|
|
114
114
|
const glyph = input.iconSvgInner
|
package/dist/types.d.ts
CHANGED
|
@@ -10,20 +10,20 @@ export interface KptMapBounds {
|
|
|
10
10
|
north: number;
|
|
11
11
|
east: number;
|
|
12
12
|
}
|
|
13
|
-
/** Punkt w pikselach
|
|
13
|
+
/** Punkt w pikselach - kontenera mapy albo świata, zależnie od kontekstu. */
|
|
14
14
|
export interface KptMapPoint {
|
|
15
15
|
x: number;
|
|
16
16
|
y: number;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Kolor znacznika: rola z warstwy semantycznej tokenów albo gotowe `var(--kpt-*)`.
|
|
20
|
-
* Literały (`#c00`, `rgb(...)`) są celowo niedozwolone
|
|
20
|
+
* Literały (`#c00`, `rgb(...)`) są celowo niedozwolone - kontrakt repo wymaga tokenów.
|
|
21
21
|
*/
|
|
22
22
|
export type KptMapColorRole = 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'muted';
|
|
23
23
|
export type KptMapColor = KptMapColorRole | `var(--kpt-${string})`;
|
|
24
24
|
/** Kształt znacznika. */
|
|
25
25
|
export type KptMapMarkerShape = 'pin' | 'dot' | 'square';
|
|
26
|
-
/** Typ pola w schemie formularza
|
|
26
|
+
/** Typ pola w schemie formularza "dodaj lokalizację". */
|
|
27
27
|
export type KptMapFieldType = 'text' | 'textarea' | 'number' | 'date' | 'select' | 'multiselect' | 'checkbox' | 'tel' | 'email' | 'url'
|
|
28
28
|
/** Zdjęcie: wybór pliku (wartość jako `data:`) albo wklejony adres. */
|
|
29
29
|
| 'image'
|
|
@@ -59,12 +59,12 @@ export interface KptMapFieldDef {
|
|
|
59
59
|
*/
|
|
60
60
|
autofill?: KptMapAddressPart;
|
|
61
61
|
}
|
|
62
|
-
/** Rozbiór adresu zwracany przez geokoder. Wszystko opcjonalne
|
|
62
|
+
/** Rozbiór adresu zwracany przez geokoder. Wszystko opcjonalne - źródła bywają dziurawe. */
|
|
63
63
|
export interface KptMapAddress {
|
|
64
64
|
/** Nazwa ulicy bez numeru. */
|
|
65
65
|
street?: string;
|
|
66
66
|
houseNumber?: string;
|
|
67
|
-
/** Ulica z numerem, gotowa do jednego pola
|
|
67
|
+
/** Ulica z numerem, gotowa do jednego pola "Adres". */
|
|
68
68
|
address?: string;
|
|
69
69
|
city?: string;
|
|
70
70
|
postcode?: string;
|
|
@@ -81,23 +81,23 @@ export interface KptMapAddress {
|
|
|
81
81
|
/** Pole `KptMapAddress`, którym można wypełnić kontrolkę formularza. */
|
|
82
82
|
export type KptMapAddressPart = keyof KptMapAddress;
|
|
83
83
|
/**
|
|
84
|
-
* Podpowiedź z listy wyszukiwarki adresów (geokodowanie wprost: tekst
|
|
84
|
+
* Podpowiedź z listy wyszukiwarki adresów (geokodowanie wprost: tekst -> miejsce).
|
|
85
85
|
*
|
|
86
86
|
* Pochodzi z portu `KPT_MAP_GEOCODER`, więc kształt jest wspólny dla dowolnego
|
|
87
|
-
* dostawcy
|
|
87
|
+
* dostawcy - Nominatim, instancji własnej i komercyjnej.
|
|
88
88
|
*/
|
|
89
89
|
export interface KptMapSuggestion {
|
|
90
|
-
/** Stabilny w obrębie jednej odpowiedzi
|
|
90
|
+
/** Stabilny w obrębie jednej odpowiedzi - służy za klucz `track` listy podpowiedzi. */
|
|
91
91
|
id: string;
|
|
92
92
|
/** Pełna nazwa jednym ciągiem; to widzi użytkownik na liście. */
|
|
93
93
|
label: string;
|
|
94
94
|
/**
|
|
95
|
-
* Współrzędne, **gdy dostawca podaje je już na liście**
|
|
95
|
+
* Współrzędne, **gdy dostawca podaje je już na liście** - tak robi każdy geokoder
|
|
96
96
|
* pytany wprost (Nominatim, Photon, Google Geocoding API).
|
|
97
97
|
*
|
|
98
98
|
* Brak wartości znaczy, że podpowiedź trzeba jeszcze uzupełnić metodą `resolve()`
|
|
99
99
|
* portu. Tak działa Google Places Autocomplete: zwraca `placeId` i tekst, a `lat`/`lng`
|
|
100
|
-
* dociąga się osobnym Place Details
|
|
100
|
+
* dociąga się osobnym Place Details - jedno wywołanie na *wybór* użytkownika, a nie
|
|
101
101
|
* na każdy naciśnięty klawisz. Na tym stoi jego rozliczanie sesyjne, więc to nie jest
|
|
102
102
|
* dziwactwo jednego dostawcy, tylko wzorzec, który port musi umieć wyrazić.
|
|
103
103
|
*/
|
|
@@ -107,11 +107,11 @@ export interface KptMapSuggestion {
|
|
|
107
107
|
* zamiast dostać punktowy zoom w środek rynku.
|
|
108
108
|
*/
|
|
109
109
|
bounds?: KptMapBounds;
|
|
110
|
-
/** Rozbiór adresu, gdy dostawca go podaje
|
|
110
|
+
/** Rozbiór adresu, gdy dostawca go podaje - ten sam typ, co przy geokodowaniu wstecznym. */
|
|
111
111
|
address?: KptMapAddress;
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
* Wybrane miejsce
|
|
114
|
+
* Wybrane miejsce - podpowiedź z **pewnymi** współrzędnymi.
|
|
115
115
|
*
|
|
116
116
|
* To ten typ wychodzi z `selected` pola wyszukiwania i z `searchSelect` mapy: zanim
|
|
117
117
|
* zdarzenie poleci, brakujące `at` zostaje dociągnięte przez `resolve()`. Aplikacja
|
|
@@ -120,7 +120,7 @@ export interface KptMapSuggestion {
|
|
|
120
120
|
export interface KptMapSearchResult extends KptMapSuggestion {
|
|
121
121
|
at: KptLatLng;
|
|
122
122
|
}
|
|
123
|
-
/** Kategoria obiektu
|
|
123
|
+
/** Kategoria obiektu - kolor, ikona i schema formularza. */
|
|
124
124
|
export interface KptMapCategory {
|
|
125
125
|
id: string;
|
|
126
126
|
/** Etykieta w legendzie i domyślnym popupie. */
|
|
@@ -130,7 +130,7 @@ export interface KptMapCategory {
|
|
|
130
130
|
icon?: string;
|
|
131
131
|
/** Domyślnie `'pin'`. */
|
|
132
132
|
shape?: KptMapMarkerShape;
|
|
133
|
-
/** Schema formularza
|
|
133
|
+
/** Schema formularza "dodaj lokalizację" dla tej kategorii. */
|
|
134
134
|
fields?: KptMapFieldDef[];
|
|
135
135
|
/** Pokazać w legendzie. Domyślnie `true`. */
|
|
136
136
|
legend?: boolean;
|
|
@@ -140,7 +140,7 @@ export interface KptMapLocation<T = unknown> {
|
|
|
140
140
|
id: string;
|
|
141
141
|
at: KptLatLng;
|
|
142
142
|
categoryId: string;
|
|
143
|
-
/** Tytuł w popupie oraz `aria-label` znacznika
|
|
143
|
+
/** Tytuł w popupie oraz `aria-label` znacznika - bez niego znacznik jest niedostępny. */
|
|
144
144
|
title: string;
|
|
145
145
|
/** Dowolne dane aplikacji; trafiają do szablonu popupu jako `data`. */
|
|
146
146
|
data?: T;
|
|
@@ -152,10 +152,10 @@ export interface KptMapCluster<T = unknown> {
|
|
|
152
152
|
/** Środek ciężkości grupy. */
|
|
153
153
|
at: KptLatLng;
|
|
154
154
|
count: number;
|
|
155
|
-
/** Najliczniejsza kategoria w grupie
|
|
155
|
+
/** Najliczniejsza kategoria w grupie - źródło koloru przy `clusterColorBy: 'dominant'`. */
|
|
156
156
|
dominantCategoryId: string;
|
|
157
157
|
items: KptMapLocation<T>[];
|
|
158
|
-
/** Zasięg grupy
|
|
158
|
+
/** Zasięg grupy - do `fitBounds` po kliknięciu. */
|
|
159
159
|
bounds: KptMapBounds;
|
|
160
160
|
}
|
|
161
161
|
/** Element wyniku grupowania: albo grupa, albo pojedyncza lokalizacja. */
|
|
@@ -183,17 +183,17 @@ export interface KptMapCreateEvent {
|
|
|
183
183
|
/** Wartości pól wg schemy kategorii (albo z własnego szablonu formularza). */
|
|
184
184
|
value: Record<string, unknown>;
|
|
185
185
|
}
|
|
186
|
-
/** Widok mapy
|
|
186
|
+
/** Widok mapy - środek i zoom. */
|
|
187
187
|
export interface KptMapView {
|
|
188
188
|
center: KptLatLng;
|
|
189
189
|
zoom: number;
|
|
190
190
|
}
|
|
191
|
-
/** Dzień tygodnia wg ISO-8601: 1 = poniedziałek
|
|
191
|
+
/** Dzień tygodnia wg ISO-8601: 1 = poniedziałek ... 7 = niedziela. */
|
|
192
192
|
export type KptMapWeekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
193
193
|
/**
|
|
194
194
|
* Przedział otwarcia w dobie, `HH:MM` w czasie lokalnym miejsca.
|
|
195
195
|
*
|
|
196
|
-
* `to` mniejsze albo równe `from` znaczy
|
|
196
|
+
* `to` mniejsze albo równe `from` znaczy "przez północ" - przedział kończy się
|
|
197
197
|
* następnego dnia. `24:00` to prawidłowy koniec doby i nie jest przejściem przez północ.
|
|
198
198
|
*/
|
|
199
199
|
export interface KptMapHoursRange {
|
|
@@ -203,13 +203,13 @@ export interface KptMapHoursRange {
|
|
|
203
203
|
/** Godziny jednego dnia tygodnia. */
|
|
204
204
|
export interface KptMapDayHours {
|
|
205
205
|
day: KptMapWeekday;
|
|
206
|
-
/** Zamknięte przez całą dobę
|
|
206
|
+
/** Zamknięte przez całą dobę - `ranges` jest wtedy ignorowane. */
|
|
207
207
|
closed?: boolean;
|
|
208
208
|
ranges: KptMapHoursRange[];
|
|
209
209
|
}
|
|
210
|
-
/** Tygodniowy rozkład godzin. Brakujący dzień znaczy
|
|
210
|
+
/** Tygodniowy rozkład godzin. Brakujący dzień znaczy "zamknięte". */
|
|
211
211
|
export type KptMapOpeningHours = KptMapDayHours[];
|
|
212
|
-
/** Wiersz
|
|
212
|
+
/** Wiersz "ikona + etykieta + wartość" w karcie miejsca. */
|
|
213
213
|
export interface KptMapPoiFact {
|
|
214
214
|
/** Nazwa ikony z `KptIconRegistry`; bez niej wiersz jest bez glifu. */
|
|
215
215
|
icon?: string;
|
|
@@ -218,7 +218,7 @@ export interface KptMapPoiFact {
|
|
|
218
218
|
}
|
|
219
219
|
/**
|
|
220
220
|
* Dane karty miejsca (`kpt-map-poi-card`). Aplikacja mapuje na to swój
|
|
221
|
-
* `KptMapLocation.data`
|
|
221
|
+
* `KptMapLocation.data` - biblioteka nie narzuca kształtu danych domenowych.
|
|
222
222
|
*/
|
|
223
223
|
export interface KptMapPoi {
|
|
224
224
|
title: string;
|
package/dist/types.js
CHANGED
package/dist/url.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Adres bez schematu dostaje `https://`
|
|
3
|
-
* Schemat spoza allowlisty (np. `javascript:`) traktujemy jak brak schematu
|
|
2
|
+
* Adres bez schematu dostaje `https://` - inaczej przeglądarka potraktuje go jak ścieżkę względną.
|
|
3
|
+
* Schemat spoza allowlisty (np. `javascript:`) traktujemy jak brak schematu - bezpieczny
|
|
4
4
|
* fallback zamiast przepuszczenia surowej wartości do `[href]`/`[src]`/`navigator.share()`.
|
|
5
5
|
*/
|
|
6
6
|
export declare function normalizeUrl(value: string): string;
|
package/dist/url.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Normalizacja adresów dla karty POI
|
|
2
|
+
* Normalizacja adresów dla karty POI - pure TS. Wydzielone z `map-poi-card.component.ts`,
|
|
3
3
|
* żeby dało się to testować `node --test` bez ciągnięcia Angulara.
|
|
4
4
|
*/
|
|
5
5
|
const HAS_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
6
6
|
const SAFE_SCHEME = /^(https?:|mailto:|tel:)/i;
|
|
7
7
|
/**
|
|
8
|
-
* Adres bez schematu dostaje `https://`
|
|
9
|
-
* Schemat spoza allowlisty (np. `javascript:`) traktujemy jak brak schematu
|
|
8
|
+
* Adres bez schematu dostaje `https://` - inaczej przeglądarka potraktuje go jak ścieżkę względną.
|
|
9
|
+
* Schemat spoza allowlisty (np. `javascript:`) traktujemy jak brak schematu - bezpieczny
|
|
10
10
|
* fallback zamiast przepuszczenia surowej wartości do `[href]`/`[src]`/`navigator.share()`.
|
|
11
11
|
*/
|
|
12
12
|
export function normalizeUrl(value) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konce-pt/map",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Map core for Koncept UI
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"description": "Map core for Koncept UI - projection and bounds maths, marker clustering, opening hours, marker markup, plus the Leaflet and Google engine adapters. Framework-free TypeScript, zero runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "konce.pt",
|
|
7
7
|
"homepage": "https://ui.konce.pt/",
|