@konce-pt/map 0.8.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.
@@ -0,0 +1,238 @@
1
+ /** Punkt geograficzny w stopniach (WGS 84). */
2
+ export interface KptLatLng {
3
+ lat: number;
4
+ lng: number;
5
+ }
6
+ /** Prostokąt geograficzny. `south < north`; `west`/`east` w stopniach. */
7
+ export interface KptMapBounds {
8
+ south: number;
9
+ west: number;
10
+ north: number;
11
+ east: number;
12
+ }
13
+ /** Punkt w pikselach — kontenera mapy albo świata, zależnie od kontekstu. */
14
+ export interface KptMapPoint {
15
+ x: number;
16
+ y: number;
17
+ }
18
+ /**
19
+ * Kolor znacznika: rola z warstwy semantycznej tokenów albo gotowe `var(--kpt-*)`.
20
+ * Literały (`#c00`, `rgb(...)`) są celowo niedozwolone — kontrakt repo wymaga tokenów.
21
+ */
22
+ export type KptMapColorRole = 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'muted';
23
+ export type KptMapColor = KptMapColorRole | `var(--kpt-${string})`;
24
+ /** Kształt znacznika. */
25
+ export type KptMapMarkerShape = 'pin' | 'dot' | 'square';
26
+ /** Typ pola w schemie formularza „dodaj lokalizację". */
27
+ export type KptMapFieldType = 'text' | 'textarea' | 'number' | 'date' | 'select' | 'multiselect' | 'checkbox' | 'tel' | 'email' | 'url'
28
+ /** Zdjęcie: wybór pliku (wartość jako `data:`) albo wklejony adres. */
29
+ | 'image'
30
+ /** Tygodniowy rozkład godzin otwarcia; wartość to `KptMapOpeningHours`. */
31
+ | 'hours';
32
+ /** Opcja listy wyboru w polu `select` / `multiselect`. */
33
+ export interface KptMapFieldOption {
34
+ value: unknown;
35
+ label: string;
36
+ }
37
+ /** Definicja jednego pola formularza dodawania lokalizacji. */
38
+ export interface KptMapFieldDef {
39
+ /** Klucz w obiekcie wartości formularza. */
40
+ key: string;
41
+ type: KptMapFieldType;
42
+ label: string;
43
+ required?: boolean;
44
+ placeholder?: string;
45
+ /** Tekst pomocniczy pod polem. */
46
+ hint?: string;
47
+ /** Wymagane dla `select` i `multiselect`. */
48
+ options?: KptMapFieldOption[];
49
+ /** Dolna granica dla `number`; dla `date` data w formacie ISO. */
50
+ min?: number | string;
51
+ /** Górna granica dla `number`; dla `date` data w formacie ISO. */
52
+ max?: number | string;
53
+ /** Wartość początkowa; bez niej pole startuje puste (`''`, `null`, `[]`, `false`). */
54
+ initial?: unknown;
55
+ /**
56
+ * Część adresu, którą wypełnić z geokodowania wstecznego współrzędnych kliknięcia.
57
+ * Działa tylko dla pól tekstowych i tylko gdy aplikacja poda port `KPT_MAP_GEOCODER`.
58
+ * Nigdy nie nadpisuje tego, co użytkownik już wpisał.
59
+ */
60
+ autofill?: KptMapAddressPart;
61
+ }
62
+ /** Rozbiór adresu zwracany przez geokoder. Wszystko opcjonalne — źródła bywają dziurawe. */
63
+ export interface KptMapAddress {
64
+ /** Nazwa ulicy bez numeru. */
65
+ street?: string;
66
+ houseNumber?: string;
67
+ /** Ulica z numerem, gotowa do jednego pola „Adres". */
68
+ address?: string;
69
+ city?: string;
70
+ postcode?: string;
71
+ /** Województwo / region. */
72
+ state?: string;
73
+ /** Powiat / hrabstwo. */
74
+ county?: string;
75
+ country?: string;
76
+ /** Kod ISO 3166-1 alpha-2, wielkimi literami. */
77
+ countryCode?: string;
78
+ /** Pełny adres jednym ciągiem, tak jak podał go geokoder. */
79
+ label?: string;
80
+ }
81
+ /** Pole `KptMapAddress`, którym można wypełnić kontrolkę formularza. */
82
+ export type KptMapAddressPart = keyof KptMapAddress;
83
+ /**
84
+ * Podpowiedź z listy wyszukiwarki adresów (geokodowanie wprost: tekst → miejsce).
85
+ *
86
+ * Pochodzi z portu `KPT_MAP_GEOCODER`, więc kształt jest wspólny dla dowolnego
87
+ * dostawcy — Nominatim, instancji własnej i komercyjnej.
88
+ */
89
+ export interface KptMapSuggestion {
90
+ /** Stabilny w obrębie jednej odpowiedzi — służy za klucz `track` listy podpowiedzi. */
91
+ id: string;
92
+ /** Pełna nazwa jednym ciągiem; to widzi użytkownik na liście. */
93
+ label: string;
94
+ /**
95
+ * Współrzędne, **gdy dostawca podaje je już na liście** — tak robi każdy geokoder
96
+ * pytany wprost (Nominatim, Photon, Google Geocoding API).
97
+ *
98
+ * Brak wartości znaczy, że podpowiedź trzeba jeszcze uzupełnić metodą `resolve()`
99
+ * portu. Tak działa Google Places Autocomplete: zwraca `placeId` i tekst, a `lat`/`lng`
100
+ * dociąga się osobnym Place Details — jedno wywołanie na *wybór* użytkownika, a nie
101
+ * na każdy naciśnięty klawisz. Na tym stoi jego rozliczanie sesyjne, więc to nie jest
102
+ * dziwactwo jednego dostawcy, tylko wzorzec, który port musi umieć wyrazić.
103
+ */
104
+ at?: KptLatLng;
105
+ /**
106
+ * Zasięg obiektu, gdy dostawca go podaje. Miasto trafia wtedy w kadr w całości,
107
+ * zamiast dostać punktowy zoom w środek rynku.
108
+ */
109
+ bounds?: KptMapBounds;
110
+ /** Rozbiór adresu, gdy dostawca go podaje — ten sam typ, co przy geokodowaniu wstecznym. */
111
+ address?: KptMapAddress;
112
+ }
113
+ /**
114
+ * Wybrane miejsce — podpowiedź z **pewnymi** współrzędnymi.
115
+ *
116
+ * To ten typ wychodzi z `selected` pola wyszukiwania i z `searchSelect` mapy: zanim
117
+ * zdarzenie poleci, brakujące `at` zostaje dociągnięte przez `resolve()`. Aplikacja
118
+ * nigdy nie musi sprawdzać, czy współrzędne są.
119
+ */
120
+ export interface KptMapSearchResult extends KptMapSuggestion {
121
+ at: KptLatLng;
122
+ }
123
+ /** Kategoria obiektu — kolor, ikona i schema formularza. */
124
+ export interface KptMapCategory {
125
+ id: string;
126
+ /** Etykieta w legendzie i domyślnym popupie. */
127
+ label: string;
128
+ color: KptMapColor;
129
+ /** Nazwa ikony z `KptIconRegistry`; bez niej znacznik nie ma glifu. */
130
+ icon?: string;
131
+ /** Domyślnie `'pin'`. */
132
+ shape?: KptMapMarkerShape;
133
+ /** Schema formularza „dodaj lokalizację" dla tej kategorii. */
134
+ fields?: KptMapFieldDef[];
135
+ /** Pokazać w legendzie. Domyślnie `true`. */
136
+ legend?: boolean;
137
+ }
138
+ /** Pojedyncza lokalizacja na mapie. */
139
+ export interface KptMapLocation<T = unknown> {
140
+ id: string;
141
+ at: KptLatLng;
142
+ categoryId: string;
143
+ /** Tytuł w popupie oraz `aria-label` znacznika — bez niego znacznik jest niedostępny. */
144
+ title: string;
145
+ /** Dowolne dane aplikacji; trafiają do szablonu popupu jako `data`. */
146
+ data?: T;
147
+ }
148
+ /** Grupa lokalizacji zwinięta w jeden znacznik z licznikiem. */
149
+ export interface KptMapCluster<T = unknown> {
150
+ /** Stabilny w obrębie zoomu: `c:{zoom}:{cellX}:{cellY}`. */
151
+ id: string;
152
+ /** Środek ciężkości grupy. */
153
+ at: KptLatLng;
154
+ count: number;
155
+ /** Najliczniejsza kategoria w grupie — źródło koloru przy `clusterColorBy: 'dominant'`. */
156
+ dominantCategoryId: string;
157
+ items: KptMapLocation<T>[];
158
+ /** Zasięg grupy — do `fitBounds` po kliknięciu. */
159
+ bounds: KptMapBounds;
160
+ }
161
+ /** Element wyniku grupowania: albo grupa, albo pojedyncza lokalizacja. */
162
+ export type KptMapClusterEntry<T = unknown> = KptMapCluster<T> | KptMapLocation<T>;
163
+ /** Zawęża wynik `clusterLocations` do grupy. */
164
+ export declare function isCluster<T>(entry: KptMapClusterEntry<T>): entry is KptMapCluster<T>;
165
+ /** Pozycja użytkownika z geolokalizacji. */
166
+ export interface KptMapUserPosition {
167
+ at: KptLatLng;
168
+ /** Promień niepewności w metrach (`GeolocationCoordinates.accuracy`). */
169
+ accuracy: number;
170
+ /** Znacznik czasu odczytu. */
171
+ timestamp: number;
172
+ }
173
+ /** Powód niepowodzenia geolokalizacji. */
174
+ export type KptMapGeolocationErrorKind = 'denied' | 'unavailable' | 'timeout' | 'unsupported';
175
+ export interface KptMapGeolocationError {
176
+ kind: KptMapGeolocationErrorKind;
177
+ message: string;
178
+ }
179
+ /** Zdarzenie utworzenia lokalizacji przez formularz. */
180
+ export interface KptMapCreateEvent {
181
+ at: KptLatLng;
182
+ categoryId: string;
183
+ /** Wartości pól wg schemy kategorii (albo z własnego szablonu formularza). */
184
+ value: Record<string, unknown>;
185
+ }
186
+ /** Widok mapy — środek i zoom. */
187
+ export interface KptMapView {
188
+ center: KptLatLng;
189
+ zoom: number;
190
+ }
191
+ /** Dzień tygodnia wg ISO-8601: 1 = poniedziałek … 7 = niedziela. */
192
+ export type KptMapWeekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;
193
+ /**
194
+ * Przedział otwarcia w dobie, `HH:MM` w czasie lokalnym miejsca.
195
+ *
196
+ * `to` mniejsze albo równe `from` znaczy „przez północ" — przedział kończy się
197
+ * następnego dnia. `24:00` to prawidłowy koniec doby i nie jest przejściem przez północ.
198
+ */
199
+ export interface KptMapHoursRange {
200
+ from: string;
201
+ to: string;
202
+ }
203
+ /** Godziny jednego dnia tygodnia. */
204
+ export interface KptMapDayHours {
205
+ day: KptMapWeekday;
206
+ /** Zamknięte przez całą dobę — `ranges` jest wtedy ignorowane. */
207
+ closed?: boolean;
208
+ ranges: KptMapHoursRange[];
209
+ }
210
+ /** Tygodniowy rozkład godzin. Brakujący dzień znaczy „zamknięte". */
211
+ export type KptMapOpeningHours = KptMapDayHours[];
212
+ /** Wiersz „ikona + etykieta + wartość" w karcie miejsca. */
213
+ export interface KptMapPoiFact {
214
+ /** Nazwa ikony z `KptIconRegistry`; bez niej wiersz jest bez glifu. */
215
+ icon?: string;
216
+ label: string;
217
+ value: string;
218
+ }
219
+ /**
220
+ * Dane karty miejsca (`kpt-map-poi-card`). Aplikacja mapuje na to swój
221
+ * `KptMapLocation.data` — biblioteka nie narzuca kształtu danych domenowych.
222
+ */
223
+ export interface KptMapPoi {
224
+ title: string;
225
+ /** Drugi wiersz nagłówka, obok dystansu. */
226
+ address?: string;
227
+ /** Adres zdjęcia albo `data:`; karta nie rozróżnia. Puste = zastępnik z ikoną. */
228
+ photo?: string;
229
+ photoAlt?: string;
230
+ hours?: KptMapOpeningHours;
231
+ phone?: string;
232
+ email?: string;
233
+ website?: string;
234
+ /** Wiersz współrzędnych; bez tego się nie renderuje. */
235
+ at?: KptLatLng;
236
+ /** Dodatkowe wiersze informacyjne, bez akcji. */
237
+ facts?: KptMapPoiFact[];
238
+ }
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /*
2
+ * Typy rdzenia mapy — pure TS, zero Angulara i zero silnika mapowego.
3
+ * Ten plik przenosi się do portu React bez zmian.
4
+ */
5
+ /** Zawęża wynik `clusterLocations` do grupy. */
6
+ export function isCluster(entry) {
7
+ return entry.count !== undefined;
8
+ }
package/dist/url.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
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
+ * fallback zamiast przepuszczenia surowej wartości do `[href]`/`[src]`/`navigator.share()`.
5
+ */
6
+ export declare function normalizeUrl(value: string): string;
7
+ /** Wersja adresu do pokazania: bez schematu i bez końcowego ukośnika. */
8
+ export declare function prettyUrl(value: string): string;
package/dist/url.js ADDED
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Normalizacja adresów dla karty POI — pure TS. Wydzielone z `map-poi-card.component.ts`,
3
+ * żeby dało się to testować `node --test` bez ciągnięcia Angulara.
4
+ */
5
+ const HAS_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
6
+ const SAFE_SCHEME = /^(https?:|mailto:|tel:)/i;
7
+ /**
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
+ * fallback zamiast przepuszczenia surowej wartości do `[href]`/`[src]`/`navigator.share()`.
11
+ */
12
+ export function normalizeUrl(value) {
13
+ if (!HAS_SCHEME.test(value))
14
+ return `https://${value}`;
15
+ return SAFE_SCHEME.test(value) ? value : `https://${value.replace(HAS_SCHEME, '')}`;
16
+ }
17
+ /** Wersja adresu do pokazania: bez schematu i bez końcowego ukośnika. */
18
+ export function prettyUrl(value) {
19
+ return value.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '').replace(/\/$/, '');
20
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@konce-pt/map",
3
+ "version": "0.8.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
+ "license": "MIT",
6
+ "author": "konce.pt",
7
+ "homepage": "https://ui.konce.pt/",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://gitlab.com/konce-pt/koncept-ui.git",
11
+ "directory": "packages/map"
12
+ },
13
+ "bugs": {
14
+ "url": "https://gitlab.com/konce-pt/koncept-ui/-/issues"
15
+ },
16
+ "keywords": [
17
+ "map",
18
+ "leaflet",
19
+ "markers",
20
+ "design-system",
21
+ "koncept-ui",
22
+ "kpt"
23
+ ],
24
+ "type": "module",
25
+ "sideEffects": false,
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.js"
35
+ }
36
+ },
37
+ "peerDependencies": {
38
+ "leaflet": "^1.9.0"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "leaflet": {
42
+ "optional": true
43
+ }
44
+ },
45
+ "devDependencies": {
46
+ "@types/leaflet": "^1.9.22",
47
+ "leaflet": "^1.9.4",
48
+ "typescript": "~6.0.3"
49
+ },
50
+ "scripts": {
51
+ "build": "tsc -p tsconfig.build.json",
52
+ "test": "node --test \"src/**/*.test.ts\"",
53
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
54
+ }
55
+ }