@kaufmann-lab/ui 0.1.17 → 0.1.19
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 +19 -2
- package/fesm2022/kaufmann-lab-ui-auth.mjs +322 -0
- package/fesm2022/kaufmann-lab-ui-auth.mjs.map +1 -0
- package/fesm2022/kaufmann-lab-ui-business.mjs +468 -0
- package/fesm2022/kaufmann-lab-ui-business.mjs.map +1 -0
- package/fesm2022/kaufmann-lab-ui-core.mjs +6338 -0
- package/fesm2022/kaufmann-lab-ui-core.mjs.map +1 -0
- package/fesm2022/kaufmann-lab-ui-map.mjs +1637 -0
- package/fesm2022/kaufmann-lab-ui-map.mjs.map +1 -0
- package/fesm2022/kaufmann-lab-ui-mobile.mjs +92 -0
- package/fesm2022/kaufmann-lab-ui-mobile.mjs.map +1 -0
- package/fesm2022/kaufmann-lab-ui-shared.mjs +269 -0
- package/fesm2022/kaufmann-lab-ui-shared.mjs.map +1 -0
- package/fesm2022/kaufmann-lab-ui.mjs +23 -8472
- package/fesm2022/kaufmann-lab-ui.mjs.map +1 -1
- package/package.json +25 -1
- package/types/kaufmann-lab-ui-auth.d.ts +240 -0
- package/types/kaufmann-lab-ui-business.d.ts +313 -0
- package/types/kaufmann-lab-ui-core.d.ts +3459 -0
- package/types/kaufmann-lab-ui-map.d.ts +631 -0
- package/types/kaufmann-lab-ui-mobile.d.ts +53 -0
- package/types/kaufmann-lab-ui-shared.d.ts +154 -0
- package/types/kaufmann-lab-ui.d.ts +5 -4631
- package/fesm2022/mapbox-gl-csp.worker.js +0 -6
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
3
|
+
import { IconName } from '@kaufmann-lab/ui/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Motor de mapas configurado a nivel app, no por uso — mismo criterio que
|
|
7
|
+
* `API_CONFIG` en `packages/api-client` de `route-assistance-monorepo`: el
|
|
8
|
+
* access token de Mapbox es un dato de despliegue (cada app/entorno tiene
|
|
9
|
+
* el propio), no algo que cada `<kf-map>` deba repetir.
|
|
10
|
+
*/
|
|
11
|
+
interface MapConfig {
|
|
12
|
+
/** Access token público de Mapbox (`pk.*`) de la cuenta del proyecto que consume la librería. */
|
|
13
|
+
accessToken: string;
|
|
14
|
+
/**
|
|
15
|
+
* Estilo de Mapbox Studio propio del cliente, si lo tiene. Cuando se
|
|
16
|
+
* define, gana siempre sobre `theme()` del componente — un estilo de
|
|
17
|
+
* marca custom no tiene una variante "light"/"dark" que elegir, ya viene
|
|
18
|
+
* resuelto. Sin esto, `kf-map` usa el estilo `light-v11`/`dark-v11` de
|
|
19
|
+
* Mapbox según `theme()`.
|
|
20
|
+
*/
|
|
21
|
+
styleUrl?: string;
|
|
22
|
+
}
|
|
23
|
+
declare const MAP_CONFIG: InjectionToken<MapConfig>;
|
|
24
|
+
/**
|
|
25
|
+
* `provide*` idiomático (mismo patrón que `provideHttpClient`/`provideRouter`)
|
|
26
|
+
* para inyectar una sola vez en `app.config.ts` de cada app consumidora:
|
|
27
|
+
*
|
|
28
|
+
* providers: [provideMapConfig({ accessToken: 'pk.xxx' })]
|
|
29
|
+
*/
|
|
30
|
+
declare function provideMapConfig(config: MapConfig): Provider;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Vocabulario de temas de `kf-map`, separado de `MapView.ts` para que
|
|
34
|
+
* `MapThemePicker` (el panel flotante del control `mapStyle`) pueda
|
|
35
|
+
* importarlo sin crear un ciclo `MapView → MapControls → MapThemePicker →
|
|
36
|
+
* MapView`.
|
|
37
|
+
*
|
|
38
|
+
* `streets`/`satellite`/`outdoors` se suman a los `light`/`dark`
|
|
39
|
+
* originales — los 3 IDs de estilo (`streets-v12`, `satellite-streets-v12`,
|
|
40
|
+
* `outdoors-v12`) están confirmados contra la cuenta/token real del
|
|
41
|
+
* ecosistema: la API de estilos de Mapbox es por cuenta, no por versión de
|
|
42
|
+
* `mapbox-gl`, así que cualquier duda se resuelve probando contra el token
|
|
43
|
+
* real, no leyendo el número de versión instalado.
|
|
44
|
+
*/
|
|
45
|
+
type MapTheme = 'light' | 'dark' | 'streets' | 'satellite' | 'outdoors';
|
|
46
|
+
declare const STYLE_BY_THEME: Record<MapTheme, string>;
|
|
47
|
+
/** Orden fijo en que `MapThemePicker` lista las 5 opciones — predecible
|
|
48
|
+
* (siempre el mismo orden en toda pantalla que use el picker), no importa
|
|
49
|
+
* cuál esté activa. */
|
|
50
|
+
declare const THEME_ORDER: MapTheme[];
|
|
51
|
+
/** Nombre en español de cada tema, para el `aria-label`/label del control
|
|
52
|
+
* `mapStyle` y las opciones del picker — anuncia qué tema rige HOY o qué
|
|
53
|
+
* tema se está por elegir, no solo "otro tema distinto". */
|
|
54
|
+
declare const THEME_LABEL: Record<MapTheme, string>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Cada pieza es independiente entre sí — agregar/sacar un `MapControlKind`
|
|
58
|
+
* de `controls` en `kf-map` no reacomoda ni afecta a los demás. Array vacío
|
|
59
|
+
* (default de `kf-map`) es "ningún control": opt-in explícito del
|
|
60
|
+
* consumidor, nunca activado por default (ver JSDoc de `controls` en
|
|
61
|
+
* `MapView.ts`).
|
|
62
|
+
*
|
|
63
|
+
* zoom — botones +/- reales (`map.zoomIn()`/`zoomOut()`).
|
|
64
|
+
* fullscreen — expandir/contraer el mapa (Fullscreen API nativa).
|
|
65
|
+
* mapStyle — ciclar entre los temas de `MapTheme`.
|
|
66
|
+
* recenter — re-ejecuta el encuadre a markers/route bajo demanda.
|
|
67
|
+
* cluster — togglear agrupamiento nativo de markers cercanos.
|
|
68
|
+
* follow — la cámara sigue al marker `live` mientras se mueve.
|
|
69
|
+
*/
|
|
70
|
+
type MapControlKind = 'zoom' | 'fullscreen' | 'mapStyle' | 'recenter' | 'cluster' | 'follow';
|
|
71
|
+
/**
|
|
72
|
+
* Barra de controles flotantes de `kf-map` — pieza puramente presentacional,
|
|
73
|
+
* sin estado propio: todo lo que sabe se lo pasa `MapView` por input, todo
|
|
74
|
+
* lo que hace lo reporta por output para que `MapView` (única dueña del
|
|
75
|
+
* `Map` de Mapbox) decida qué hacer. Vive en la misma carpeta que `MapView`
|
|
76
|
+
* a propósito (mismo criterio que `DropdownItem`/`DropdownPanel` con
|
|
77
|
+
* `Dropdown`): el acoplamiento es real, no de nombre — no tiene sentido
|
|
78
|
+
* fuera de un `kf-map` que la controle.
|
|
79
|
+
*
|
|
80
|
+
* Un solo stack vertical, siempre en la esquina superior derecha del mapa
|
|
81
|
+
* (fijado por `MapView.css`, no acá): es donde ya vivía el `NavigationControl`
|
|
82
|
+
* de Mapbox que este componente reemplaza, y la atribución obligatoria de
|
|
83
|
+
* Mapbox (`AttributionControl`, nunca oculta) vive abajo-derecha — un solo
|
|
84
|
+
* stack evita que dos grupos floten en esquinas distintas y compitan por
|
|
85
|
+
* espacio en un mapa `size="sm"` de 200px de alto.
|
|
86
|
+
*
|
|
87
|
+
* El stack no es una lista continua de botones idénticos: cada grupo lógico
|
|
88
|
+
* es su propia "card" flotante (`kf-radius-md`+`kf-shadow-md`+fondo blanco,
|
|
89
|
+
* ver `.kf-map-controls__group` en `MapControls.css`), separadas entre sí
|
|
90
|
+
* por el `gap` del `:host` — mismo lenguaje ya verificado visualmente en
|
|
91
|
+
* `route-assistance-monorepo/design/mockups/operations-final.html`
|
|
92
|
+
* (`.map__ctrl-group`). Orden fijo de arriba a abajo, independiente del
|
|
93
|
+
* orden en el array `controls` del consumidor (para que dos pantallas que
|
|
94
|
+
* piden los mismos controles en distinto orden se vean IGUAL):
|
|
95
|
+
*
|
|
96
|
+
* 1. zoom — magnificación (par +/-, unido por un divisor, sin gap entre sí).
|
|
97
|
+
* 2. cámara — recenter + follow (posición de la cámara).
|
|
98
|
+
* 3. datos — cluster (densidad de los markers, eje distinto de cámara).
|
|
99
|
+
* 4. chrome — mapStyle + fullscreen (nada del mapa en sí).
|
|
100
|
+
*
|
|
101
|
+
* Cada grupo usa `@if` sobre AL MENOS UNO de sus controles (ej. el grupo de
|
|
102
|
+
* cámara aparece con solo `recenter`, solo `follow`, o ambos) — agregar o
|
|
103
|
+
* sacar un control nunca reacomoda ni depende de los demás grupos.
|
|
104
|
+
*
|
|
105
|
+
* Todos los botones siguen siendo `kf-button[variant=surface][shape=circle]`
|
|
106
|
+
* — sin tocar `Button.ts`: el fondo/sombra propios de `surface` se anulan
|
|
107
|
+
* SOLO dentro de un `.kf-map-controls__group` vía una custom property CSS
|
|
108
|
+
* (ver el comentario junto a esa clase en `MapControls.css` sobre por qué
|
|
109
|
+
* eso sí atraviesa la encapsulación de Angular y un selector normal no).
|
|
110
|
+
*/
|
|
111
|
+
declare class MapControls {
|
|
112
|
+
readonly controls: _angular_core.InputSignal<MapControlKind[]>;
|
|
113
|
+
readonly clusterActive: _angular_core.InputSignal<boolean>;
|
|
114
|
+
readonly followActive: _angular_core.InputSignal<boolean>;
|
|
115
|
+
/** Sin ningún marker `live`, togglear "seguir" no tendría ningún efecto
|
|
116
|
+
* visible — se deshabilita en vez de dejar un botón que parece no hacer nada. */
|
|
117
|
+
readonly canFollow: _angular_core.InputSignal<boolean>;
|
|
118
|
+
readonly isFullscreen: _angular_core.InputSignal<boolean>;
|
|
119
|
+
/** Nombre en español del tema actualmente resuelto (ej. "Calles"), para
|
|
120
|
+
* anunciar en el `aria-label` del botón de estilo qué se está por perder,
|
|
121
|
+
* no solo que "algo" va a cambiar. */
|
|
122
|
+
readonly styleLabel: _angular_core.InputSignal<string>;
|
|
123
|
+
/** Tema resuelto HOY (`light|dark|...`) — a diferencia de `styleLabel`
|
|
124
|
+
* (ya traducido a texto), esto es lo que `MapThemePicker` necesita para
|
|
125
|
+
* marcar la opción activa. */
|
|
126
|
+
readonly activeTheme: _angular_core.InputSignal<MapTheme>;
|
|
127
|
+
readonly zoomIn: _angular_core.OutputEmitterRef<void>;
|
|
128
|
+
readonly zoomOut: _angular_core.OutputEmitterRef<void>;
|
|
129
|
+
readonly recenter: _angular_core.OutputEmitterRef<void>;
|
|
130
|
+
readonly followToggle: _angular_core.OutputEmitterRef<void>;
|
|
131
|
+
readonly clusterToggle: _angular_core.OutputEmitterRef<void>;
|
|
132
|
+
/** Reemplaza al viejo `styleCycle` (ciclado de un click): ahora el botón
|
|
133
|
+
* `mapStyle` abre `MapThemePicker` y este output reporta la elección real
|
|
134
|
+
* hecha ahí. */
|
|
135
|
+
readonly themeSelect: _angular_core.OutputEmitterRef<MapTheme>;
|
|
136
|
+
readonly fullscreenToggle: _angular_core.OutputEmitterRef<void>;
|
|
137
|
+
/** Visibilidad del panel de temas — estado puramente de UI (abierto o no),
|
|
138
|
+
* no de negocio: `MapView` no necesita saberlo, así que vive acá y no
|
|
139
|
+
* rompe el criterio de "sin estado propio" del resto de la clase (ver
|
|
140
|
+
* JSDoc). Se cierra solo (click afuera/Escape) vía `kf-dropdown`, y acá
|
|
141
|
+
* además al elegir un tema. */
|
|
142
|
+
protected readonly mapStyleOpen: _angular_core.WritableSignal<boolean>;
|
|
143
|
+
protected onThemeSelect(theme: MapTheme): void;
|
|
144
|
+
protected readonly hasZoom: _angular_core.Signal<boolean>;
|
|
145
|
+
protected readonly hasRecenter: _angular_core.Signal<boolean>;
|
|
146
|
+
protected readonly hasFollow: _angular_core.Signal<boolean>;
|
|
147
|
+
protected readonly hasCluster: _angular_core.Signal<boolean>;
|
|
148
|
+
protected readonly hasMapStyle: _angular_core.Signal<boolean>;
|
|
149
|
+
protected readonly hasFullscreen: _angular_core.Signal<boolean>;
|
|
150
|
+
/** El grupo "cámara" (card) aparece con solo `recenter`, solo `follow`, o
|
|
151
|
+
* ambos — ver JSDoc de la clase sobre por qué cada `@if` de grupo cubre
|
|
152
|
+
* "al menos uno de sus controles" en vez de un control puntual. */
|
|
153
|
+
protected readonly hasCamera: _angular_core.Signal<boolean>;
|
|
154
|
+
protected readonly hasChrome: _angular_core.Signal<boolean>;
|
|
155
|
+
/**
|
|
156
|
+
* Texto corto que revela cada botón en hover/foco vía `[kfTooltip]` (ver
|
|
157
|
+
* `MapControls.html` — la librería ya resuelve este patrón con
|
|
158
|
+
* `TooltipDirective`, no con CSS a mano) — la misma acción que ya describe
|
|
159
|
+
* `ariaLabel`, condensada para caber en una píldora en vez de leerse como
|
|
160
|
+
* oración completa. No reemplaza `ariaLabel` (que sigue siendo la fuente
|
|
161
|
+
* real para lectores de pantalla): esto es SOLO visual, para quien puede
|
|
162
|
+
* ver el mapa pero no sabe qué hace un ícono suelto.
|
|
163
|
+
*/
|
|
164
|
+
protected readonly followLabel: _angular_core.Signal<"Dejar de seguir" | "Seguir">;
|
|
165
|
+
protected readonly clusterLabel: _angular_core.Signal<"Desagrupar" | "Agrupar">;
|
|
166
|
+
protected readonly mapStyleLabel: _angular_core.Signal<string>;
|
|
167
|
+
protected readonly fullscreenLabel: _angular_core.Signal<"Salir de pantalla completa" | "Pantalla completa">;
|
|
168
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MapControls, never>;
|
|
169
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MapControls, "kf-map-controls", never, { "controls": { "alias": "controls"; "required": false; "isSignal": true; }; "clusterActive": { "alias": "clusterActive"; "required": false; "isSignal": true; }; "followActive": { "alias": "followActive"; "required": false; "isSignal": true; }; "canFollow": { "alias": "canFollow"; "required": false; "isSignal": true; }; "isFullscreen": { "alias": "isFullscreen"; "required": false; "isSignal": true; }; "styleLabel": { "alias": "styleLabel"; "required": false; "isSignal": true; }; "activeTheme": { "alias": "activeTheme"; "required": false; "isSignal": true; }; }, { "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "recenter": "recenter"; "followToggle": "followToggle"; "clusterToggle": "clusterToggle"; "themeSelect": "themeSelect"; "fullscreenToggle": "fullscreenToggle"; }, never, never, true, never>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
type MapSize = 'sm' | 'md' | 'lg' | 'full';
|
|
173
|
+
type MapVariant = 'interactive' | 'preview';
|
|
174
|
+
/** Vocabulario semántico único de tono, compartido con `TextTone`/`StatTileTone`. */
|
|
175
|
+
type MapMarkerTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
|
|
176
|
+
interface MapLatLng {
|
|
177
|
+
lat: number;
|
|
178
|
+
lng: number;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Botón de acción real dentro de `MapMarker.popup` — cada click emite
|
|
182
|
+
* `markerAction` con su `key` (nunca el `label`, ver JSDoc de `popup` más
|
|
183
|
+
* abajo). `icon` es OPCIONAL a propósito: no romper a los consumidores que
|
|
184
|
+
* ya usaban `actions` sin él (era el único campo hasta esta ronda).
|
|
185
|
+
*
|
|
186
|
+
* La jerarquía visual entre acciones NO es un campo aparte (`variant`) sino
|
|
187
|
+
* la POSICIÓN en el array: la primera acción se renderiza con estilo
|
|
188
|
+
* prominente (fill, mismo peso que `kf-button[variant=primary]`, el
|
|
189
|
+
* default del botón); el resto con estilo discreto (mismo peso que
|
|
190
|
+
* `kf-button[variant=ghost]` — ver el ejemplo de `Modal` con
|
|
191
|
+
* `variant="ghost"` para "Cancelar" junto a un primary implícito). Un
|
|
192
|
+
* consumidor con una sola acción no necesita pensar en esto: esa acción ya
|
|
193
|
+
* es "la primera".
|
|
194
|
+
*/
|
|
195
|
+
interface MapPopupAction {
|
|
196
|
+
key: string;
|
|
197
|
+
label: string;
|
|
198
|
+
/** Ícono líder del catálogo de `kf-icon` — sin esto, la acción se ve solo
|
|
199
|
+
* con texto (compatibilidad con `actions` ya declarado sin `icon`). */
|
|
200
|
+
icon?: IconName;
|
|
201
|
+
}
|
|
202
|
+
interface MapMarker {
|
|
203
|
+
position: MapLatLng;
|
|
204
|
+
/** Sin `icon`, cae en `map-pin` — el pin genérico del catálogo. Se
|
|
205
|
+
* ignora cuando el control `cluster` está activo (ver JSDoc de
|
|
206
|
+
* `controls` — el clustering nativo resuelve por capas WebGL, no por
|
|
207
|
+
* DOM, y ahí no hay dónde montar un `kf-icon`). */
|
|
208
|
+
icon?: IconName;
|
|
209
|
+
tone?: MapMarkerTone;
|
|
210
|
+
/** Nombre accesible del marker (`aria-label`/`title`) — no abre un popup. */
|
|
211
|
+
label?: string;
|
|
212
|
+
/**
|
|
213
|
+
* Identificador propio del consumidor (ej. el id del ticket/vehículo que
|
|
214
|
+
* representa) — sin esto el marker no es clickeable: `kf-map` no tiene
|
|
215
|
+
* forma de reportar "se hizo click en ESTE marker" sin algo que
|
|
216
|
+
* identificarlo de vuelta hacia `markerSelect`. Opcional a propósito: un
|
|
217
|
+
* mapa puramente decorativo (ej. la vista previa de `variant="preview"`)
|
|
218
|
+
* no necesita que sus markers respondan a nada.
|
|
219
|
+
*/
|
|
220
|
+
id?: string;
|
|
221
|
+
/**
|
|
222
|
+
* Marca este marker como el punto que sigue la cámara cuando el control
|
|
223
|
+
* `follow` está activo — ej. la posición en tiempo real de un vehículo o
|
|
224
|
+
* técnico. Genérico a propósito (no `isVehicle` ni ningún nombre atado al
|
|
225
|
+
* dominio de un portal): `@kaufmann-lab/ui` no sabe qué es un "vehículo",
|
|
226
|
+
* solo que ALGÚN marker es el que se sigue. Si más de un marker lo marca,
|
|
227
|
+
* se sigue el primero en `markers()`. Distinto de la geolocalización del
|
|
228
|
+
* propio dispositivo (`navigator.geolocation`, fuera de alcance): esto
|
|
229
|
+
* sigue un dato que el consumidor ya tiene, no ubica al usuario.
|
|
230
|
+
*/
|
|
231
|
+
live?: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Mini-card que aparece al hover/foco de este marker — solo si el marker
|
|
234
|
+
* tiene `id` (ver JSDoc de arriba): sin `id` el marker no es clickeable ni
|
|
235
|
+
* enfocable, así que no habría forma de revelarla por teclado.
|
|
236
|
+
*
|
|
237
|
+
* `actions` son botones REALES (no el viejo `ctaLabel` de texto plano sin
|
|
238
|
+
* acción): cada click emite `markerAction` con su `key` — nunca el
|
|
239
|
+
* `label`, que es solo texto de UI y puede repetirse con significados
|
|
240
|
+
* distintos entre consumidores — y detiene la propagación del click para
|
|
241
|
+
* no disparar además `markerSelect` sobre el marker completo (ver
|
|
242
|
+
* `buildMarkerPopup()`).
|
|
243
|
+
*
|
|
244
|
+
* Vocabulario genérico a propósito (`title`/`description`/`details`/
|
|
245
|
+
* `actions`, no `folio`/`cliente`/`verTicket`): esta librería no sabe qué
|
|
246
|
+
* es un "ticket" — el caso de uso real (folio+cliente+estado+SLA+acciones)
|
|
247
|
+
* es responsabilidad de quien arma cada `MapMarker`.
|
|
248
|
+
*/
|
|
249
|
+
popup?: {
|
|
250
|
+
title: string;
|
|
251
|
+
description?: string;
|
|
252
|
+
/** Pares clave/valor extra (ej. estado + SLA) — se listan debajo de `description`. */
|
|
253
|
+
details?: {
|
|
254
|
+
label: string;
|
|
255
|
+
value: string;
|
|
256
|
+
}[];
|
|
257
|
+
/** Botones de acción reales — ver JSDoc de `MapPopupAction`. */
|
|
258
|
+
actions?: MapPopupAction[];
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Mapa con motor real de Mapbox GL JS — decisión de producto ya cerrada
|
|
263
|
+
* (comparación de 6 proveedores). El access token vive a nivel app, no por
|
|
264
|
+
* uso: ver `provideMapConfig` en `map-config.ts` (mismo criterio que
|
|
265
|
+
* `API_CONFIG` de `packages/api-client`), inyectado una vez en
|
|
266
|
+
* `app.config.ts` de cada app consumidora.
|
|
267
|
+
*
|
|
268
|
+
* `size` (`sm|md|lg` alturas fijas, `full` llena el contenedor padre) NO
|
|
269
|
+
* hace step-down por viewport como `kf-page-header`: ese mecanismo existe
|
|
270
|
+
* ahí porque un tamaño de FUENTE fijo se corta en pantallas chicas; una
|
|
271
|
+
* caja de mapa no tiene ese problema — el canvas de Mapbox ya es
|
|
272
|
+
* responsive dentro de cualquier tamaño de contenedor. Lo que sí hace
|
|
273
|
+
* falta (y que el header no necesita) es un `ResizeObserver` real: Mapbox
|
|
274
|
+
* no se entera solo de que su contenedor cambió de tamaño (un layout que
|
|
275
|
+
* colapsa un sidebar, un cambio de orientación) — hay que llamar
|
|
276
|
+
* `map.resize()` a mano cuando el contenedor cambia (mismo motivo por el
|
|
277
|
+
* que `fullscreenchange` también llama `resize()`, ver `onFullscreenChange`).
|
|
278
|
+
*
|
|
279
|
+
* `variant` es un eje ortogonal a `size` (mismo criterio que `variant` +
|
|
280
|
+
* `tone` en `kf-badge`): `interactive` trae la posibilidad de controles
|
|
281
|
+
* flotantes reales (ver `controls`); `preview` (`interactive: false` de
|
|
282
|
+
* Mapbox GL) es para una card de lista, sin chrome y sin foco por teclado —
|
|
283
|
+
* no hace nada al recibir foco, así que no debe formar parte del tab order,
|
|
284
|
+
* y por eso NUNCA renderiza `controls` aunque el consumidor los pida:
|
|
285
|
+
* togglear "seguir" o abrir pantalla completa no tiene sentido en un mapa
|
|
286
|
+
* que no acepta interacción. Ninguna de las dos variantes oculta la
|
|
287
|
+
* atribución: es requisito de licencia de Mapbox, no cosmético.
|
|
288
|
+
*
|
|
289
|
+
* `theme` (`light|dark|streets|satellite|outdoors`) resuelve el estilo real
|
|
290
|
+
* de Mapbox — eje totalmente distinto del `[data-theme]` de
|
|
291
|
+
* `tokens/themes/*` (paleta de marca de la librería): ninguno de los 5
|
|
292
|
+
* temas de `tokens/` es oscuro todavía, así que no hay ninguna señal real
|
|
293
|
+
* de la que derivar un default "según el tema de marca activo" — construir
|
|
294
|
+
* esa detección ahora sería simular una señal que no existe.
|
|
295
|
+
*
|
|
296
|
+
* `controls` (`MapControlKind[]`, default `[]`) es siempre opt-in: un mapa
|
|
297
|
+
* sin `controls` no dibuja ningún botón flotante, ni siquiera en
|
|
298
|
+
* `interactive` (antes de esta versión, `interactive` agregaba el
|
|
299
|
+
* `NavigationControl` default de Mapbox gratis — se sacó a propósito: era
|
|
300
|
+
* el look genérico de Mapbox, no el de esta librería, exactamente la queja
|
|
301
|
+
* que motivó este rediseño. Un consumidor que quería esos botones de zoom
|
|
302
|
+
* ahora los pide con `[controls]="['zoom']"`). Cada kind es una pieza
|
|
303
|
+
* independiente entre sí (ver `MapControls.ts` para el detalle de cada
|
|
304
|
+
* una) — agregar una no reacomoda ni depende de las demás.
|
|
305
|
+
*
|
|
306
|
+
* <kf-map label="Ubicación del vehículo y del destino" [markers]="markers()" />
|
|
307
|
+
* <kf-map size="sm" variant="preview" label="Vista previa de la ruta" [route]="route()" />
|
|
308
|
+
* <kf-map label="Flota completa" [markers]="fleet()" [controls]="['zoom', 'recenter', 'cluster', 'follow']" />
|
|
309
|
+
*/
|
|
310
|
+
declare class MapView {
|
|
311
|
+
readonly size: _angular_core.InputSignal<MapSize>;
|
|
312
|
+
readonly variant: _angular_core.InputSignal<MapVariant>;
|
|
313
|
+
readonly markers: _angular_core.InputSignal<MapMarker[]>;
|
|
314
|
+
readonly route: _angular_core.InputSignal<MapLatLng[] | undefined>;
|
|
315
|
+
readonly fitToMarkers: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
316
|
+
readonly center: _angular_core.InputSignal<MapLatLng | undefined>;
|
|
317
|
+
readonly zoom: _angular_core.InputSignal<number | undefined>;
|
|
318
|
+
/**
|
|
319
|
+
* Default `'streets'`, no `'light'` ni `'dark'`. `'dark'` se probó
|
|
320
|
+
* primero por coherencia con la paleta navy de marca, pero una
|
|
321
|
+
* verificación visual real sobre una ubicación de servicio típica (una
|
|
322
|
+
* faena/zona rural, no un centro urbano denso) mostró el problema real:
|
|
323
|
+
* `dark-v11` casi no tiene datos que pintar fuera de ciudades grandes —
|
|
324
|
+
* el mapa se ve prácticamente todo negro, ilegible, exactamente donde
|
|
325
|
+
* más importa que se lea bien (ubicaciones de terreno, no de oficina).
|
|
326
|
+
* `'streets'` SIEMPRE tiene cartografía base visible (calles, nombres,
|
|
327
|
+
* uso de suelo) sin importar la densidad del lugar — es el único de los
|
|
328
|
+
* 5 estilos sin ese riesgo. Sigue siendo overrideable por el consumidor. */
|
|
329
|
+
readonly theme: _angular_core.InputSignal<MapTheme>;
|
|
330
|
+
/** Controles flotantes a mostrar — ver JSDoc de la clase y `MapControls.ts`. */
|
|
331
|
+
readonly controls: _angular_core.InputSignal<MapControlKind[]>;
|
|
332
|
+
/**
|
|
333
|
+
* Descripción accesible real del mapa — el `<div>` donde Mapbox renderiza
|
|
334
|
+
* su canvas queda vacío de semántica para un lector de pantalla. Requerido
|
|
335
|
+
* a propósito: un default genérico ("Mapa") no dice qué está mostrando
|
|
336
|
+
* ESTE mapa; cada consumidor conoce su propio caso real (ej. "Ubicación
|
|
337
|
+
* del vehículo y del destino").
|
|
338
|
+
*/
|
|
339
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
340
|
+
/** Se emite al hacer click sobre un marker que declaró `id` (ver JSDoc de
|
|
341
|
+
* `MapMarker.id`) — un marker SIN `id` no es clickeable, no emite nada.
|
|
342
|
+
* No dispara en modo `cluster` (los markers pasan a ser capas WebGL sin
|
|
343
|
+
* DOM propio donde colgar un listener) ni en `variant="preview"` (el
|
|
344
|
+
* mapa entero no es interactivo ahí). */
|
|
345
|
+
readonly markerSelect: _angular_core.OutputEmitterRef<string>;
|
|
346
|
+
/** Se emite al clickear un botón de `MapMarker.popup.actions` — ver el
|
|
347
|
+
* JSDoc de `popup` para por qué reporta `actionKey` (nunca el `label`) y
|
|
348
|
+
* por qué el click no dispara además `markerSelect`. */
|
|
349
|
+
readonly markerAction: _angular_core.OutputEmitterRef<{
|
|
350
|
+
markerId: string;
|
|
351
|
+
actionKey: string;
|
|
352
|
+
}>;
|
|
353
|
+
private readonly mapConfig;
|
|
354
|
+
private readonly environmentInjector;
|
|
355
|
+
private readonly document;
|
|
356
|
+
private readonly destroyRef;
|
|
357
|
+
private readonly hostElementRef;
|
|
358
|
+
private readonly containerRef;
|
|
359
|
+
/** `preview` no es foco-navegable ni se anuncia como región interactiva. */
|
|
360
|
+
protected readonly isPreview: _angular_core.Signal<boolean>;
|
|
361
|
+
protected readonly containerRole: _angular_core.Signal<"img" | "region">;
|
|
362
|
+
/** Ver estado de carga/error real, más abajo (`status`). */
|
|
363
|
+
protected readonly status: _angular_core.WritableSignal<"error" | "loading" | "ready">;
|
|
364
|
+
/** Elección manual del consumidor final vía el control `mapStyle` — pisa
|
|
365
|
+
* `theme()` de forma sostenida (no vuelve a `theme()` sola) hasta que se
|
|
366
|
+
* destruya este `kf-map`; ver `resolvedTheme()`. */
|
|
367
|
+
private readonly userTheme;
|
|
368
|
+
protected readonly styleLabel: _angular_core.Signal<string>;
|
|
369
|
+
/** Igual que `resolvedTheme()` pero público para el template — lo necesita
|
|
370
|
+
* `MapThemePicker` (vía `MapControls`) para marcar la opción activa. */
|
|
371
|
+
protected readonly activeTheme: _angular_core.Signal<MapTheme>;
|
|
372
|
+
protected readonly clusterActive: _angular_core.WritableSignal<boolean>;
|
|
373
|
+
protected readonly followActive: _angular_core.WritableSignal<boolean>;
|
|
374
|
+
protected readonly hasLiveMarker: _angular_core.Signal<boolean>;
|
|
375
|
+
protected readonly isFullscreen: _angular_core.WritableSignal<boolean>;
|
|
376
|
+
/** Sin estilo de marca propio (Mapbox Studio) no hay nada que ciclar con
|
|
377
|
+
* `mapStyle` — se filtra en vez de dejar un botón que no cambiaría nada. */
|
|
378
|
+
protected readonly effectiveControls: _angular_core.Signal<MapControlKind[]>;
|
|
379
|
+
protected readonly showControls: _angular_core.Signal<boolean>;
|
|
380
|
+
private map;
|
|
381
|
+
/** `extraIconRefs`: íconos de las acciones del popup (ver `buildMarkerPopup()`)
|
|
382
|
+
* — se crean con `createComponent()` igual que `iconRef` (el pin), así que
|
|
383
|
+
* necesitan el mismo `.destroy()` en `clearMarkers()`. */
|
|
384
|
+
private markerHandles;
|
|
385
|
+
/** Markers DOM reales sobre cada cluster real (punto 1, a11y) — separados
|
|
386
|
+
* de `markerHandles` porque se reconstruyen en su propio ciclo (ver
|
|
387
|
+
* `syncClusterMarkers()`) y no tienen `iconRef` propio (sin `kf-icon`
|
|
388
|
+
* adentro, ver JSDoc de `addClusterMarkerToMap()`). */
|
|
389
|
+
private clusterMarkerHandles;
|
|
390
|
+
private resizeObserver;
|
|
391
|
+
private currentStyle;
|
|
392
|
+
private mapReady;
|
|
393
|
+
/** Popup resumen de un cluster real (ver `renderClusterPopup()`) — a
|
|
394
|
+
* diferencia del popup de marker individual (siempre en el DOM, mostrado
|
|
395
|
+
* por CSS `:hover`/`:focus-within`), este se crea/destruye a demanda: no
|
|
396
|
+
* cuelga de ningún elemento con estado hover propio, lo abre un click
|
|
397
|
+
* sobre una capa WebGL sin DOM. */
|
|
398
|
+
private clusterPopupElement;
|
|
399
|
+
private clusterPopupIconRefs;
|
|
400
|
+
/** Coordenadas del cluster abierto — necesarias para volver a proyectar
|
|
401
|
+
* contra pantalla en cada `'move'` (ver `repositionClusterPopup()`); el
|
|
402
|
+
* cluster en sí no se mueve, pero su posición EN PANTALLA sí cuando se
|
|
403
|
+
* hace pan. */
|
|
404
|
+
private clusterPopupCoordinates;
|
|
405
|
+
private readonly onFullscreenChange;
|
|
406
|
+
constructor();
|
|
407
|
+
private initializeMap;
|
|
408
|
+
private resolvedTheme;
|
|
409
|
+
private resolvedStyle;
|
|
410
|
+
private toLngLat;
|
|
411
|
+
/**
|
|
412
|
+
* `matchMedia` (no una media query CSS): esta preferencia decide la
|
|
413
|
+
* `duration` que se le pasa a la API de cámara de Mapbox, que corre en
|
|
414
|
+
* JS/WebGL — CSS no tiene nada que animar acá. `document.defaultView` (no
|
|
415
|
+
* el `window` global) para que quede mockeable en test igual que el resto
|
|
416
|
+
* de la clase usa `DOCUMENT` inyectado.
|
|
417
|
+
*/
|
|
418
|
+
private prefersReducedMotion;
|
|
419
|
+
/** Colapsa cualquier duración a 0 (instantáneo, sin animar) si el usuario
|
|
420
|
+
* pidió `prefers-reduced-motion: reduce` — un único punto de verdad para
|
|
421
|
+
* que ningún movimiento de cámara se salga de esa preferencia. */
|
|
422
|
+
private cameraDuration;
|
|
423
|
+
private syncCamera;
|
|
424
|
+
/**
|
|
425
|
+
* Encuadra `bounds` con `fitBounds()`, salvo que sea una caja degenerada
|
|
426
|
+
* (ver `DEGENERATE_BOUNDS_THRESHOLD_DEG`) — un solo punto total (un único
|
|
427
|
+
* marker sin ruta, o vehículo+destino prácticamente en el mismo sitio) le
|
|
428
|
+
* da a `fitBounds()` un ancho y alto ~cero, que Mapbox resuelve con un
|
|
429
|
+
* `padding`/zoom que en la práctica deja el mapa mayormente en blanco
|
|
430
|
+
* ("se ve todo blanco", el bug real que esto corrige). Un `easeTo` con
|
|
431
|
+
* zoom fijo sensato muestra contexto real de calle en ese caso, animado
|
|
432
|
+
* igual que el resto (antes era un `jumpTo` seco).
|
|
433
|
+
*/
|
|
434
|
+
private frameBounds;
|
|
435
|
+
private isDegenerateBounds;
|
|
436
|
+
/**
|
|
437
|
+
* Extiende sobre markers Y route: el nombre del input sigue el caso de
|
|
438
|
+
* uso principal (encuadrar los markers), pero un mapa con solo `route` y
|
|
439
|
+
* sin `markers` tampoco debería quedar en el encuadre default inútil.
|
|
440
|
+
*/
|
|
441
|
+
private computeBounds;
|
|
442
|
+
private syncMarkers;
|
|
443
|
+
/**
|
|
444
|
+
* Crea el marker de Mapbox (DOM + `kf-icon` + wiring de click/teclado/
|
|
445
|
+
* popup) para UN `MapMarker` y lo agrega a `markerHandles` — compartido
|
|
446
|
+
* entre el modo normal (`syncMarkers()`, todos los markers) y el modo
|
|
447
|
+
* cluster (`syncClusterMarkers()`, solo los que quedan sueltos): un punto
|
|
448
|
+
* suelto en modo cluster tiene que verse y comportarse EXACTAMENTE igual
|
|
449
|
+
* que en modo normal, así que no puede haber dos implementaciones.
|
|
450
|
+
*/
|
|
451
|
+
private addMarkerToMap;
|
|
452
|
+
/**
|
|
453
|
+
* Mini-card hija del `<div>` del marker — mismo patrón que
|
|
454
|
+
* `.kf-map-controls__label` (ver `MapControls.css`): un elemento SIEMPRE
|
|
455
|
+
* presente en el DOM, oculto por defecto vía `opacity`/`pointer-events` en
|
|
456
|
+
* `MapView.css` y revelado por CSS puro en `:hover`/`:focus-within` del
|
|
457
|
+
* marker padre — sin JS de show/hide, y sin `mapboxgl.Popup` (evita una
|
|
458
|
+
* segunda gestión de overlay/posicionamiento cuando el CSS ya alcanza acá,
|
|
459
|
+
* igual que ya decidió `.kf-map-controls__label`).
|
|
460
|
+
*
|
|
461
|
+
* Todo el subárbol (título, descripción, `details`, `actions`) se arma con
|
|
462
|
+
* `document.createElement` puro, nunca `kf-text`/`kf-button`: este marker
|
|
463
|
+
* entero vive fuera del template de Angular (ver el comentario grande de
|
|
464
|
+
* `ViewEncapsulation.None` más arriba) — meter un componente real acá
|
|
465
|
+
* significaría un `createComponent()` propio por cada línea de texto, el
|
|
466
|
+
* mismo costo que ya paga `kf-icon` en `syncMarkers()` pero sin ninguna
|
|
467
|
+
* necesidad real (no hay nada dinámico que un componente resuelva mejor
|
|
468
|
+
* que texto plano estilado por clase). Es la MISMA decisión que ya regía
|
|
469
|
+
* para `title`/`description` antes de esta ronda — se extiende acá para no
|
|
470
|
+
* mezclar dos convenciones distintas dentro del mismo fragmento de DOM.
|
|
471
|
+
*
|
|
472
|
+
* `actions` sí necesita wiring propio (a diferencia del resto de la card,
|
|
473
|
+
* que burbujea su click al listener ya cableado sobre `element` en
|
|
474
|
+
* `syncMarkers()`): cada botón corta esa burbuja con `stopPropagation()`
|
|
475
|
+
* para reportar `markerAction` en vez de (además) `markerSelect`.
|
|
476
|
+
*
|
|
477
|
+
* Jerarquía visual de `actions` (ver JSDoc de `MapPopupAction`): la
|
|
478
|
+
* PRIMERA acción del array es la principal (`--primary`, fill); el resto
|
|
479
|
+
* es secundaria (`--ghost`, transparente) — mismo peso que ya usan
|
|
480
|
+
* `kf-button[variant=primary]`/`[variant=ghost]` en el resto de la
|
|
481
|
+
* librería, sin inventar un esquema de color nuevo acá.
|
|
482
|
+
*
|
|
483
|
+
* El punto de color junto al título (`kf-map__marker-popup-tone`) reusa
|
|
484
|
+
* el `tone` del propio marker — no el de cada `details` (que son pares
|
|
485
|
+
* label/value genéricos, sin un campo "cuál de estos es el estado"): es
|
|
486
|
+
* la misma señal que YA pinta el círculo del marker en el mapa, mostrada
|
|
487
|
+
* de nuevo acá para que el popup se lea sin tener que mirar el marker.
|
|
488
|
+
*/
|
|
489
|
+
private buildMarkerPopup;
|
|
490
|
+
private clearMarkers;
|
|
491
|
+
/**
|
|
492
|
+
* Recorte de borde para el popup de marker individual (punto 2) —
|
|
493
|
+
* `getBoundingClientRect()` del marker y del propio contenedor del mapa,
|
|
494
|
+
* recalculado en el evento que dispara la apertura (`mouseenter`/
|
|
495
|
+
* `focusin`, ver `addMarkerToMap()`). El mecanismo de mostrar/ocultar
|
|
496
|
+
* sigue siendo 100% CSS; esto solo decide 3 clases modificadoras que
|
|
497
|
+
* `MapView.css` traduce en alineación horizontal/vertical:
|
|
498
|
+
*
|
|
499
|
+
* `--align-start` — el popup se desborda por la izquierda del
|
|
500
|
+
* contenedor si se centra sobre el marker: se alinea su borde
|
|
501
|
+
* izquierdo con el del marker en vez de centrarlo.
|
|
502
|
+
* `--align-end` — mismo caso, contra el borde derecho.
|
|
503
|
+
* `--below` — no entra arriba del marker (cerca del borde superior del
|
|
504
|
+
* contenedor): se muestra debajo en vez de arriba.
|
|
505
|
+
*
|
|
506
|
+
* El popup YA está en el DOM en este punto (oculto por `opacity`, no por
|
|
507
|
+
* `display:none` — ver `MapView.css`), así que `getBoundingClientRect()`
|
|
508
|
+
* devuelve su tamaño real aunque todavía no sea visible.
|
|
509
|
+
*/
|
|
510
|
+
private positionMarkerPopup;
|
|
511
|
+
/**
|
|
512
|
+
* Clustering nativo de Mapbox (`cluster: true` en la fuente GeoJSON, no un
|
|
513
|
+
* algoritmo armado a mano) — 2 capas, solo para clusters REALES (2+
|
|
514
|
+
* puntos agrupados, `has: point_count`): círculo agregado y su label de
|
|
515
|
+
* cantidad. Un punto que Mapbox deja suelto (sin `point_count`) YA NO
|
|
516
|
+
* tiene capa WebGL propia — ver `syncClusterMarkers()`, que lo renderiza
|
|
517
|
+
* como el mismo marker DOM de siempre (con ícono/tono/popup reales, cosa
|
|
518
|
+
* que una capa `circle` no puede mostrar).
|
|
519
|
+
*/
|
|
520
|
+
private syncClusterSource;
|
|
521
|
+
/**
|
|
522
|
+
* Contraparte en DOM de la capa WebGL: mientras esa capa solo dibuja
|
|
523
|
+
* clusters reales, ESTE método decide qué puntos quedan sueltos al zoom
|
|
524
|
+
* ACTUAL (`querySourceFeatures` con el mismo filtro `!has(point_count)`
|
|
525
|
+
* que ya usa Mapbox para separar cluster de leaf) y les arma el mismo
|
|
526
|
+
* marker DOM que tendrían sin clustering — vía `addMarkerToMap()`, la
|
|
527
|
+
* MISMA función que usa el modo normal, no una copia.
|
|
528
|
+
*
|
|
529
|
+
* `querySourceFeatures` puede devolver una feature repetida si cae cerca
|
|
530
|
+
* de un borde interno de tile — de ahí el `Set` sobre `index` (la
|
|
531
|
+
* property que identifica al `MapMarker` de origen, ver
|
|
532
|
+
* `markersToFeatureCollection()`), no una lista con posibles duplicados.
|
|
533
|
+
* Mismo motivo (y mismo `Set`, esta vez sobre `cluster_id`) para los
|
|
534
|
+
* clusters reales que se resuelven acá abajo.
|
|
535
|
+
*
|
|
536
|
+
* Se llama solo cuando `clusterActive()` — ver los listeners `'data'`/
|
|
537
|
+
* `'moveend'` en `initializeMap()` sobre CUÁNDO se llama esto.
|
|
538
|
+
*/
|
|
539
|
+
private syncClusterMarkers;
|
|
540
|
+
/**
|
|
541
|
+
* Marker DOM real sobre un cluster (punto 1, a11y) — MISMO patrón de
|
|
542
|
+
* wiring que `addMarkerToMap()` (`role="button"`, `tabindex="0"`,
|
|
543
|
+
* `click`+`keydown` de Enter/Space), aplicado acá porque el círculo que
|
|
544
|
+
* pinta `CLUSTER_CIRCLE_LAYER_ID` es una capa WebGL sin nodo DOM propio
|
|
545
|
+
* —inalcanzable por teclado (WCAG SC 2.1.1 Keyboard). `mapboxgl.Marker`
|
|
546
|
+
* posiciona y reproyecta este `<div>` solo en cada pan/zoom, igual que ya
|
|
547
|
+
* hace con cualquier marker individual — no hace falta reposicionarlo a
|
|
548
|
+
* mano como sí necesita el popup resumen (ver `repositionClusterPopup()`).
|
|
549
|
+
*
|
|
550
|
+
* Sin `kf-icon`/texto propio adentro a propósito: el círculo+conteo que ya
|
|
551
|
+
* pinta la capa WebGL sigue siendo la representación visual (mismo
|
|
552
|
+
* `'circle-radius'`, ver `clusterMarkerRadiusPx()`); este `<div>` es
|
|
553
|
+
* transparente y se superpone exacto encima, aportando SOLO la
|
|
554
|
+
* interactividad real que la capa WebGL no puede tener.
|
|
555
|
+
*/
|
|
556
|
+
private addClusterMarkerToMap;
|
|
557
|
+
private clearClusterMarkers;
|
|
558
|
+
/**
|
|
559
|
+
* Activación de un cluster real (click O Enter/Space sobre su marker DOM,
|
|
560
|
+
* ver `addClusterMarkerToMap()`): ANTES hacía zoom directo
|
|
561
|
+
* (`getClusterExpansionZoom()` + `easeTo`) — ahora abre el popup resumen
|
|
562
|
+
* (`renderClusterPopup()`) con la lista de tickets agrupados, y ESE zoom
|
|
563
|
+
* pasa a ser la acción explícita "Acercar para separar" dentro del popup
|
|
564
|
+
* (ver `expandCluster()`).
|
|
565
|
+
*/
|
|
566
|
+
private activateCluster;
|
|
567
|
+
/** Dispara el zoom que antes hacía el click directo sobre el círculo —
|
|
568
|
+
* ahora vive detrás del botón "Acercar para separar" del popup resumen. */
|
|
569
|
+
private expandCluster;
|
|
570
|
+
/**
|
|
571
|
+
* `index` identifica de vuelta al `MapMarker` de origen (ver
|
|
572
|
+
* `syncClusterMarkers()`); `title`/`description`/`markerId` son las
|
|
573
|
+
* mismas properties que ya arma `buildMarkerPopup()` (folio/cliente/id
|
|
574
|
+
* del caso real) — se serializan acá TAMBIÉN para que
|
|
575
|
+
* `getClusterLeaves()` pueda listarlas en el popup resumen sin tener que
|
|
576
|
+
* ir a buscar cada `MapMarker` original por separado. Los campos son
|
|
577
|
+
* `string` (no `undefined`): las properties de una `Feature` de GeoJSON
|
|
578
|
+
* no viajan bien como `undefined` — `''` significa "sin dato" en los 3.
|
|
579
|
+
*/
|
|
580
|
+
private markersToFeatureCollection;
|
|
581
|
+
private clearClusterLayers;
|
|
582
|
+
/**
|
|
583
|
+
* Popup resumen de un cluster real (punto 4) — a diferencia del popup de
|
|
584
|
+
* marker individual (siempre en el DOM, `:hover`/`:focus-within` puro),
|
|
585
|
+
* este no cuelga de ningún elemento con estado hover propio: lo abre un
|
|
586
|
+
* click sobre una capa WebGL sin DOM, así que se crea/destruye a demanda
|
|
587
|
+
* y su visibilidad la maneja JS, no CSS. Se agrega como hijo del HOST
|
|
588
|
+
* (`kf-map`, no `.kf-map__container`): ambos comparten exactamente la
|
|
589
|
+
* misma caja (`.kf-map__container` es `width/height:100%` sin padding/
|
|
590
|
+
* borde dentro de un host `position:relative`), así que las coordenadas
|
|
591
|
+
* de `map.project()` (relativas al contenedor) valen también acá — mismo
|
|
592
|
+
* ancestro posicionado que ya usa `.kf-map__overlay`.
|
|
593
|
+
*
|
|
594
|
+
* `title`/`description` de cada leaf son `popup.title`/`popup.description`
|
|
595
|
+
* del `MapMarker` original (folio/cliente en el caso real) — ver
|
|
596
|
+
* `markersToFeatureCollection()`. El punto de color reusa `tone`, MISMO
|
|
597
|
+
* criterio que el punto de color del popup individual (ver JSDoc de
|
|
598
|
+
* `buildMarkerPopup()`): no se inventa un texto de "estado" que la
|
|
599
|
+
* librería no tiene, se reusa la señal de color que sí existe.
|
|
600
|
+
*/
|
|
601
|
+
private renderClusterPopup;
|
|
602
|
+
/** Reproyecta el popup de cluster contra pantalla — se llama en cada
|
|
603
|
+
* `'move'` (ver `initializeMap()`): el punto geográfico no se mueve, pero
|
|
604
|
+
* su posición EN PANTALLA sí cuando se hace pan. Mismo criterio de
|
|
605
|
+
* recorte de borde que `positionMarkerPopup()` (punto 2), pero en
|
|
606
|
+
* píxeles calculados a mano en vez de clases CSS: este popup no cuelga
|
|
607
|
+
* de un ancestro posicionado por CSS relativo al marker, su posición
|
|
608
|
+
* ENTERA viene de `map.project()`. */
|
|
609
|
+
private repositionClusterPopup;
|
|
610
|
+
private closeClusterPopup;
|
|
611
|
+
private syncRoute;
|
|
612
|
+
private resolveColorToken;
|
|
613
|
+
protected handleZoomIn(): void;
|
|
614
|
+
protected handleZoomOut(): void;
|
|
615
|
+
/** Reencuadra a demanda, ignorando `fitToMarkers()` (tiene sentido pedirlo
|
|
616
|
+
* justo cuando `fitToMarkers` está en `false`) y sin apagar `follow`: si
|
|
617
|
+
* está activo, la próxima posición nueva vuelve a pegar la cámara al
|
|
618
|
+
* marker `live` — este reencuadre es una corrección puntual, no un modo. */
|
|
619
|
+
protected handleRecenter(): void;
|
|
620
|
+
protected handleFollowToggle(): void;
|
|
621
|
+
protected handleClusterToggle(): void;
|
|
622
|
+
/** Reemplaza al viejo ciclado de un click (`handleStyleCycle`): la
|
|
623
|
+
* elección real llega desde `MapThemePicker`, vía `MapControls`. */
|
|
624
|
+
protected handleThemeSelect(theme: MapTheme): void;
|
|
625
|
+
protected handleFullscreenToggle(): void;
|
|
626
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MapView, never>;
|
|
627
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MapView, "kf-map", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "markers": { "alias": "markers"; "required": false; "isSignal": true; }; "route": { "alias": "route"; "required": false; "isSignal": true; }; "fitToMarkers": { "alias": "fitToMarkers"; "required": false; "isSignal": true; }; "center": { "alias": "center"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "controls": { "alias": "controls"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; }, { "markerSelect": "markerSelect"; "markerAction": "markerAction"; }, never, never, true, never>;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
export { MAP_CONFIG, MapControls, MapView, STYLE_BY_THEME, THEME_LABEL, THEME_ORDER, provideMapConfig };
|
|
631
|
+
export type { MapConfig, MapControlKind, MapLatLng, MapMarker, MapMarkerTone, MapPopupAction, MapSize, MapTheme, MapVariant };
|