@fieldia/map 0.0.0-stage → 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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -2
  3. package/package.json +37 -3
  4. package/src/index.d.mts +6 -0
  5. package/src/index.d.ts +6 -0
  6. package/src/index.js +22 -0
  7. package/src/index.js.map +1 -0
  8. package/src/index.mjs +6 -0
  9. package/src/index.mjs.map +1 -0
  10. package/src/lib/area.d.mts +3 -0
  11. package/src/lib/area.d.ts +3 -0
  12. package/src/lib/area.js +591 -0
  13. package/src/lib/area.js.map +1 -0
  14. package/src/lib/area.mjs +588 -0
  15. package/src/lib/area.mjs.map +1 -0
  16. package/src/lib/geo.d.mts +72 -0
  17. package/src/lib/geo.d.ts +72 -0
  18. package/src/lib/geo.js +149 -0
  19. package/src/lib/geo.js.map +1 -0
  20. package/src/lib/geo.mjs +134 -0
  21. package/src/lib/geo.mjs.map +1 -0
  22. package/src/lib/kinds.d.mts +35 -0
  23. package/src/lib/kinds.d.ts +35 -0
  24. package/src/lib/kinds.js +127 -0
  25. package/src/lib/kinds.js.map +1 -0
  26. package/src/lib/kinds.mjs +124 -0
  27. package/src/lib/kinds.mjs.map +1 -0
  28. package/src/lib/labels.d.mts +58 -0
  29. package/src/lib/labels.d.ts +58 -0
  30. package/src/lib/labels.js +214 -0
  31. package/src/lib/labels.js.map +1 -0
  32. package/src/lib/labels.mjs +209 -0
  33. package/src/lib/labels.mjs.map +1 -0
  34. package/src/lib/leaflet-css.d.mts +2 -0
  35. package/src/lib/leaflet-css.d.ts +2 -0
  36. package/src/lib/leaflet-css.js +34 -0
  37. package/src/lib/leaflet-css.js.map +1 -0
  38. package/src/lib/leaflet-css.mjs +31 -0
  39. package/src/lib/leaflet-css.mjs.map +1 -0
  40. package/src/lib/leaflet.d.mts +15 -0
  41. package/src/lib/leaflet.d.ts +15 -0
  42. package/src/lib/leaflet.js +76 -0
  43. package/src/lib/leaflet.js.map +1 -0
  44. package/src/lib/leaflet.mjs +72 -0
  45. package/src/lib/leaflet.mjs.map +1 -0
  46. package/src/lib/map-core.d.mts +47 -0
  47. package/src/lib/map-core.d.ts +47 -0
  48. package/src/lib/map-core.js +300 -0
  49. package/src/lib/map-core.js.map +1 -0
  50. package/src/lib/map-core.mjs +297 -0
  51. package/src/lib/map-core.mjs.map +1 -0
  52. package/src/lib/place.d.mts +3 -0
  53. package/src/lib/place.d.ts +3 -0
  54. package/src/lib/place.js +323 -0
  55. package/src/lib/place.js.map +1 -0
  56. package/src/lib/place.mjs +320 -0
  57. package/src/lib/place.mjs.map +1 -0
  58. package/src/lib/providers.d.mts +131 -0
  59. package/src/lib/providers.d.ts +131 -0
  60. package/src/lib/providers.js +224 -0
  61. package/src/lib/providers.js.map +1 -0
  62. package/src/lib/providers.mjs +217 -0
  63. package/src/lib/providers.mjs.map +1 -0
  64. package/src/lib/search-box.d.mts +28 -0
  65. package/src/lib/search-box.d.ts +28 -0
  66. package/src/lib/search-box.js +141 -0
  67. package/src/lib/search-box.js.map +1 -0
  68. package/src/lib/search-box.mjs +138 -0
  69. package/src/lib/search-box.mjs.map +1 -0
  70. package/src/lib/settings.d.mts +19 -0
  71. package/src/lib/settings.d.ts +19 -0
  72. package/src/lib/settings.js +3 -0
  73. package/src/lib/settings.js.map +1 -0
  74. package/src/lib/settings.mjs +2 -0
  75. package/src/lib/settings.mjs.map +1 -0
  76. package/src/lib/styles.d.mts +11 -0
  77. package/src/lib/styles.d.ts +11 -0
  78. package/src/lib/styles.js +127 -0
  79. package/src/lib/styles.js.map +1 -0
  80. package/src/lib/styles.mjs +122 -0
  81. package/src/lib/styles.mjs.map +1 -0
  82. package/src/lib/widgets.d.mts +14 -0
  83. package/src/lib/widgets.d.ts +14 -0
  84. package/src/lib/widgets.js +21 -0
  85. package/src/lib/widgets.js.map +1 -0
  86. package/src/lib/widgets.mjs +18 -0
  87. package/src/lib/widgets.mjs.map +1 -0
@@ -0,0 +1,297 @@
1
+ import { fill } from './labels.mjs';
2
+ import { loadLeaflet, tileLayer } from './leaflet.mjs';
3
+ import { installLeafletStyles, installMapStyles } from './styles.mjs';
4
+ const warned = new Set();
5
+ const isMac = (doc) => /Mac|iPhone|iPad/.test(doc.defaultView?.navigator.platform ?? '');
6
+ export function createMapBox(options) {
7
+ const { document: doc, provider, locale, labels } = options;
8
+ installMapStyles(doc);
9
+ const element = doc.createElement('div');
10
+ element.className = 'fd-map-box';
11
+ // The map's own layout is left to right in every language; the controls over it follow the page.
12
+ element.dir = 'ltr';
13
+ element.style.setProperty('--fd-map-height', `${options.height ?? 280}px`);
14
+ element.setAttribute('role', 'region');
15
+ element.setAttribute('aria-label', fill(labels.mapOf, { label: options.name }));
16
+ const keysId = `fd-map-keys-${Math.random().toString(36).slice(2, 9)}`;
17
+ const keys = doc.createElement('span');
18
+ keys.id = keysId;
19
+ keys.className = 'fd-sr-only';
20
+ keys.textContent = [labels.mapKeys, options.keys].filter(Boolean).join(' ');
21
+ element.setAttribute('aria-describedby', keysId);
22
+ const zoom = doc.createElement('div');
23
+ zoom.className = 'fd-map-zoom';
24
+ zoom.dataset['side'] = 'right';
25
+ const zoomButton = (words, sign) => {
26
+ const button = doc.createElement('button');
27
+ button.type = 'button';
28
+ button.setAttribute('aria-label', words);
29
+ button.title = words;
30
+ button.textContent = sign;
31
+ return button;
32
+ };
33
+ const zoomIn = zoomButton(labels.zoomIn, '+');
34
+ const zoomOut = zoomButton(labels.zoomOut, '−');
35
+ zoom.append(zoomIn, zoomOut);
36
+ const credits = doc.createElement('div');
37
+ credits.className = 'fd-map-credits';
38
+ const veilElement = doc.createElement('div');
39
+ veilElement.className = 'fd-map-veil';
40
+ veilElement.setAttribute('aria-hidden', 'true');
41
+ const crosshair = doc.createElement('div');
42
+ crosshair.className = 'fd-map-crosshair';
43
+ element.append(keys, veilElement, crosshair);
44
+ // The buttons and credits go in after Leaflet's layers, once the map is made: Tab reaches the map's own pins and grips first.
45
+ const overlays = [zoom, credits];
46
+ if (provider.logo) {
47
+ const mark = doc.createElement(provider.logo.href ? 'a' : 'span');
48
+ mark.className = 'fd-map-logo';
49
+ if (mark instanceof HTMLAnchorElement && provider.logo.href) {
50
+ mark.href = provider.logo.href;
51
+ mark.target = '_blank';
52
+ mark.rel = 'noopener';
53
+ }
54
+ if (provider.logo.image) {
55
+ const img = doc.createElement('img');
56
+ img.src = provider.logo.image;
57
+ img.alt = provider.logo.text;
58
+ mark.append(img);
59
+ }
60
+ else {
61
+ const words = doc.createElement('span');
62
+ words.textContent = provider.logo.text;
63
+ mark.append(words);
64
+ }
65
+ overlays.push(mark);
66
+ }
67
+ if (provider.demoOnly && !warned.has(provider.id)) {
68
+ warned.add(provider.id);
69
+ console.warn(`[Fieldia] The "${provider.id}" map provider is for demos and development only: choose a provider (azureMaps, googleMaps, mapbox, or tiles) for an app in use.`);
70
+ }
71
+ let map = null;
72
+ let L = null;
73
+ let destroyed = false;
74
+ let pannable = true;
75
+ let baseMap = null;
76
+ let baseLayer = null;
77
+ let creditsAsked = 0;
78
+ let creditsTimer;
79
+ let veilTimer;
80
+ const waiting = [];
81
+ const following = [veilElement];
82
+ const cleanups = [];
83
+ function veil(words) {
84
+ veilElement.textContent = words;
85
+ veilElement.dataset['show'] = '';
86
+ clearTimeout(veilTimer);
87
+ veilTimer = setTimeout(() => delete veilElement.dataset['show'], 1400);
88
+ }
89
+ function shield(over) {
90
+ if (!L) {
91
+ waiting.push(() => shield(over));
92
+ return;
93
+ }
94
+ L.DomEvent.disableClickPropagation(over);
95
+ L.DomEvent.disableScrollPropagation(over);
96
+ }
97
+ /** Dark when the form is: its own scheme, or the reader's when it follows theirs. */
98
+ function dark() {
99
+ const view = doc.defaultView;
100
+ const scheme = view?.getComputedStyle(element).colorScheme ?? '';
101
+ if (!scheme.includes('dark'))
102
+ return false;
103
+ if (!scheme.includes('light'))
104
+ return true;
105
+ return !!view?.matchMedia?.('(prefers-color-scheme: dark)').matches;
106
+ }
107
+ function showCredits(list) {
108
+ credits.replaceChildren();
109
+ list.forEach((credit, i) => {
110
+ if (i)
111
+ credits.append(' · ');
112
+ if (credit.href) {
113
+ const link = doc.createElement('a');
114
+ link.href = credit.href;
115
+ link.target = '_blank';
116
+ link.rel = 'noopener';
117
+ link.textContent = credit.text;
118
+ credits.append(link);
119
+ }
120
+ else
121
+ credits.append(credit.text);
122
+ });
123
+ }
124
+ function askCredits() {
125
+ clearTimeout(creditsTimer);
126
+ creditsTimer = setTimeout(() => {
127
+ if (!map || !baseMap)
128
+ return;
129
+ const asked = ++creditsAsked;
130
+ const b = map.getBounds();
131
+ provider
132
+ .credits(baseMap, { zoom: map.getZoom(), bounds: { south: b.getSouth(), west: b.getWest(), north: b.getNorth(), east: b.getEast() } }, locale)
133
+ .then((list) => asked === creditsAsked && showCredits(list))
134
+ .catch(() => undefined);
135
+ }, 300);
136
+ }
137
+ async function setBaseMap(kind) {
138
+ if (!map || !L || kind === baseMap)
139
+ return;
140
+ let spec = await provider.tiles(kind, locale).catch(() => null);
141
+ let drawn = kind;
142
+ if (!spec && kind !== 'road') {
143
+ spec = await provider.tiles('road', locale).catch(() => null);
144
+ drawn = 'road';
145
+ }
146
+ if (!spec || destroyed || !map)
147
+ return;
148
+ if (drawn === baseMap) {
149
+ baseMap = kind;
150
+ return;
151
+ }
152
+ const layer = tileLayer(L, spec).addTo(map);
153
+ baseLayer?.remove();
154
+ baseLayer = layer;
155
+ baseMap = drawn;
156
+ element.dataset['baseMap'] = drawn;
157
+ askCredits();
158
+ }
159
+ function refresh() {
160
+ const page = element.parentElement ? doc.defaultView?.getComputedStyle(element.parentElement).direction : 'ltr';
161
+ zoom.dataset['side'] = page === 'rtl' ? 'left' : 'right';
162
+ element.dataset['side'] = zoom.dataset['side'];
163
+ for (const words of following)
164
+ words.dir = page === 'rtl' ? 'rtl' : 'ltr';
165
+ if (map)
166
+ void setBaseMap(dark() ? 'dark' : 'road');
167
+ }
168
+ function setPannable(on) {
169
+ pannable = on;
170
+ if (!map)
171
+ return;
172
+ if (on)
173
+ map.dragging.enable();
174
+ else
175
+ map.dragging.disable();
176
+ }
177
+ // A scroll passes to the page; Ctrl or ⌘ with it zooms. Caught on the way down, before Leaflet's own.
178
+ const wheel = (event) => {
179
+ if (event.ctrlKey || event.metaKey)
180
+ return;
181
+ event.stopPropagation();
182
+ veil(isMac(doc) ? labels.cmdZoom : labels.ctrlZoom);
183
+ };
184
+ element.addEventListener('wheel', wheel, { capture: true, passive: true });
185
+ // The cross at the centre is the keyboard's pointer: shown once a key is pressed on the map, gone at the next press of a pointer.
186
+ // (Leaflet focuses the map in code on every press, which a browser may show as keyboard focus.)
187
+ // Focus that no press brought (Tab, a shortcut) is the keyboard's too.
188
+ let pressing = false;
189
+ const byKeys = (event) => event.target === element && element.setAttribute('data-keys', '');
190
+ const byPointer = () => {
191
+ element.removeAttribute('data-keys');
192
+ pressing = true;
193
+ setTimeout(() => (pressing = false), 0);
194
+ };
195
+ const byFocus = () => !pressing && element.setAttribute('data-keys', '');
196
+ element.addEventListener('keydown', byKeys);
197
+ element.addEventListener('pointerdown', byPointer, { capture: true });
198
+ element.addEventListener('focus', byFocus);
199
+ cleanups.push(() => {
200
+ element.removeEventListener('keydown', byKeys);
201
+ element.removeEventListener('pointerdown', byPointer, { capture: true });
202
+ element.removeEventListener('focus', byFocus);
203
+ });
204
+ // One finger scrolls the page; two move the map.
205
+ let touchStart = null;
206
+ const touchstart = (event) => {
207
+ if (!map)
208
+ return;
209
+ const one = event.touches.length === 1;
210
+ touchStart = one ? { x: event.touches[0].clientX, y: event.touches[0].clientY } : null;
211
+ if (one || !pannable)
212
+ map.dragging.disable();
213
+ else
214
+ map.dragging.enable();
215
+ };
216
+ const touchmove = (event) => {
217
+ if (!touchStart || event.touches.length !== 1 || !pannable)
218
+ return;
219
+ const moved = Math.hypot(event.touches[0].clientX - touchStart.x, event.touches[0].clientY - touchStart.y);
220
+ if (moved > 12)
221
+ veil(labels.twoFingers);
222
+ };
223
+ const touchend = (event) => {
224
+ if (event.touches.length === 0 && map && pannable)
225
+ map.dragging.enable();
226
+ };
227
+ element.addEventListener('touchstart', touchstart, { capture: true, passive: true });
228
+ element.addEventListener('touchmove', touchmove, { capture: true, passive: true });
229
+ element.addEventListener('touchend', touchend, { capture: true, passive: true });
230
+ cleanups.push(() => {
231
+ element.removeEventListener('wheel', wheel, { capture: true });
232
+ element.removeEventListener('touchstart', touchstart, { capture: true });
233
+ element.removeEventListener('touchmove', touchmove, { capture: true });
234
+ element.removeEventListener('touchend', touchend, { capture: true });
235
+ });
236
+ void loadLeaflet().then(({ L: leaflet, css }) => {
237
+ if (destroyed)
238
+ return;
239
+ installLeafletStyles(doc, css);
240
+ L = leaflet;
241
+ map = leaflet.map(element, {
242
+ zoomControl: false,
243
+ attributionControl: false,
244
+ keyboard: true,
245
+ keyboardPanDelta: 80,
246
+ worldCopyJump: true,
247
+ dragging: pannable,
248
+ });
249
+ if (options.center)
250
+ map.setView([options.center.lat, options.center.lng], options.zoom ?? 15);
251
+ else
252
+ map.setView([20, 0], options.zoom ?? 2);
253
+ element.append(...overlays);
254
+ for (const over of overlays)
255
+ shield(over);
256
+ zoomIn.addEventListener('click', () => map?.zoomIn());
257
+ zoomOut.addEventListener('click', () => map?.zoomOut());
258
+ map.on('moveend', askCredits);
259
+ // Shown at last in a tab or a dialog: the map measures itself again.
260
+ const ResizeWatch = doc.defaultView?.ResizeObserver;
261
+ if (ResizeWatch) {
262
+ const watch = new ResizeWatch(() => map?.invalidateSize({ pan: false }));
263
+ watch.observe(element);
264
+ cleanups.push(() => watch.disconnect());
265
+ }
266
+ refresh();
267
+ for (const use of waiting.splice(0))
268
+ use(map, leaflet);
269
+ });
270
+ return {
271
+ element,
272
+ ready(use) {
273
+ if (map && L)
274
+ use(map, L);
275
+ else
276
+ waiting.push(use);
277
+ },
278
+ veil,
279
+ setPannable,
280
+ refresh,
281
+ shield,
282
+ follow(words) {
283
+ following.push(words);
284
+ refresh();
285
+ },
286
+ destroy() {
287
+ destroyed = true;
288
+ clearTimeout(creditsTimer);
289
+ clearTimeout(veilTimer);
290
+ for (const cleanup of cleanups.splice(0))
291
+ cleanup();
292
+ map?.remove();
293
+ map = null;
294
+ },
295
+ };
296
+ }
297
+ //# sourceMappingURL=map-core.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-core.mjs","sourceRoot":"","sources":["../../../../../libs/map/src/lib/map-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { WidgetFactory } from '@fieldia/widgets';
2
+ import type { MapSettings } from './settings.mjs';
3
+ export declare function placeWidget(settings: MapSettings): WidgetFactory;
@@ -0,0 +1,3 @@
1
+ import type { WidgetFactory } from '@fieldia/widgets';
2
+ import type { MapSettings } from './settings';
3
+ export declare function placeWidget(settings: MapSettings): WidgetFactory;
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.placeWidget = placeWidget;
4
+ const geo_1 = require("./geo");
5
+ const labels_1 = require("./labels");
6
+ const map_core_1 = require("./map-core");
7
+ const search_box_1 = require("./search-box");
8
+ /**
9
+ * A place on a form: a click on the map, a dragged pin, an address found,
10
+ * or "My location". Kept as a GeoJSON Point in a json field, or in two
11
+ * number fields (`latitude` with `options.longitudeField`), the way Odoo
12
+ * keeps a partner's place. The coordinates stay typeable under the map:
13
+ * that is the way in without a mouse, beside Enter on the map (the place
14
+ * at its centre) and the arrow keys on the pin.
15
+ *
16
+ * Options: `longitudeField`, `addressField` (filled from the address
17
+ * found, or named from the point where the provider can), `myLocation`
18
+ * (on unless false), `search` (on unless false), `zoom` (15), `height`.
19
+ */
20
+ const PIN = `<svg viewBox="0 0 28 40" aria-hidden="true"><path class="fd-map-pin-body" d="M14 1C6.8 1 1 6.7 1 13.8 1 23.6 14 39 14 39s13-15.4 13-25.2C27 6.7 21.2 1 14 1z"/><circle class="fd-map-pin-dot" cx="14" cy="14" r="5"/></svg>`;
21
+ const round6 = (n) => Math.round(n * 1e6) / 1e6;
22
+ function placeWidget(settings) {
23
+ return (context) => {
24
+ const { form, name, field, node, id, document: doc } = context;
25
+ const options = node.options ?? {};
26
+ const labels = (0, labels_1.mapLabels)(context.locale, settings.labels?.[context.locale ?? 'en']);
27
+ const longitudeField = typeof options['longitudeField'] === 'string' ? options['longitudeField'] : null;
28
+ const addressField = typeof options['addressField'] === 'string' ? options['addressField'] : null;
29
+ const inFields = field.type === 'float';
30
+ if (inFields && !longitudeField)
31
+ throw new Error(`The place on "${name}" keeps its latitude there and needs options.longitudeField for its longitude`);
32
+ const zoom = typeof options['zoom'] === 'number' ? options['zoom'] : 15;
33
+ const search = options['search'] === false ? undefined : (settings.search ?? settings.provider.search);
34
+ const reverse = settings.reverse ?? settings.provider.reverse;
35
+ const asYouType = settings.search ? (settings.searchAsYouType ?? true) : !!settings.provider.searchAsYouType;
36
+ const geolocation = options['myLocation'] === false ? undefined : doc.defaultView?.navigator.geolocation;
37
+ let readonly = false;
38
+ let point = null;
39
+ /** The address this field last wrote: one a person typed is never written over. */
40
+ let addressWritten;
41
+ let values = form.getState().values;
42
+ const element = doc.createElement('div');
43
+ element.className = 'fd-map-field fd-place';
44
+ element.setAttribute('role', 'group');
45
+ element.setAttribute('aria-labelledby', `${id}-label`);
46
+ const tools = doc.createElement('div');
47
+ tools.className = 'fd-map-tools';
48
+ const note = doc.createElement('p');
49
+ note.className = 'fd-map-note';
50
+ note.setAttribute('aria-live', 'polite');
51
+ note.hidden = true;
52
+ const say = (words, tone) => {
53
+ note.textContent = words ?? '';
54
+ note.hidden = !words;
55
+ if (tone)
56
+ note.dataset['tone'] = tone;
57
+ else
58
+ delete note.dataset['tone'];
59
+ };
60
+ const box = (0, map_core_1.createMapBox)({
61
+ document: doc,
62
+ provider: settings.provider,
63
+ locale: context.locale ?? 'en',
64
+ labels,
65
+ name: field.label,
66
+ keys: labels.placeKeys,
67
+ center: settings.center,
68
+ zoom: settings.center ? (settings.zoom ?? 11) : undefined,
69
+ height: typeof options['height'] === 'number' ? options['height'] : undefined,
70
+ });
71
+ // ---- the coordinates, typeable -------------------------------------------
72
+ const coords = doc.createElement('div');
73
+ coords.className = 'fd-map-coords';
74
+ const coordinate = (words, inputId) => {
75
+ const label = doc.createElement('label');
76
+ const input = doc.createElement('input');
77
+ input.className = 'fd-input';
78
+ input.id = inputId;
79
+ input.inputMode = 'decimal';
80
+ input.autocomplete = 'off';
81
+ input.spellcheck = false;
82
+ label.append(words, input);
83
+ coords.append(label);
84
+ return input;
85
+ };
86
+ const lat = coordinate(labels.latitude, search ? `${id}-lat` : id);
87
+ const lng = coordinate(labels.longitude, `${id}-lng`);
88
+ const typed = () => {
89
+ const a = (0, geo_1.parseCoordinate)(lat.value);
90
+ const b = (0, geo_1.parseCoordinate)(lng.value);
91
+ const empty = !lat.value.trim() && !lng.value.trim();
92
+ const good = empty || (0, geo_1.isLatLng)(a, b);
93
+ lat.setAttribute('aria-invalid', String(!good || (a !== null && Math.abs(a) > 90)));
94
+ lng.setAttribute('aria-invalid', String(!good || (b !== null && Math.abs(b) > 180)));
95
+ if (!good)
96
+ return say(labels.notACoordinate, 'error');
97
+ say(null);
98
+ if (empty)
99
+ return set(null);
100
+ set({ lat: a, lng: b }, { fly: true, name: true });
101
+ };
102
+ for (const input of [lat, lng]) {
103
+ input.addEventListener('change', typed);
104
+ input.addEventListener('keydown', (event) => event.key === 'Enter' && (event.preventDefault(), typed()));
105
+ }
106
+ // ---- writing ---------------------------------------------------------------
107
+ function write(next) {
108
+ if (inFields) {
109
+ form.setValue(name, next ? round6(next.lat) : null);
110
+ form.setValue(longitudeField, next ? round6(next.lng) : null);
111
+ }
112
+ else
113
+ form.setValue(name, next ? (0, geo_1.asJson)((0, geo_1.writePoint)(next)) : null);
114
+ }
115
+ let naming = null;
116
+ /** The address from the point, where the provider can say one, unless a person typed their own. */
117
+ function nameFrom(at) {
118
+ if (!addressField || !reverse)
119
+ return;
120
+ const current = values[addressField];
121
+ if (current !== null && current !== undefined && current !== '' && current !== addressWritten)
122
+ return;
123
+ naming?.abort();
124
+ naming = new AbortController();
125
+ reverse({ ...at, locale: context.locale ?? 'en', signal: naming.signal })
126
+ .then((label) => {
127
+ if (!label)
128
+ return;
129
+ addressWritten = label;
130
+ form.setValue(addressField, label);
131
+ })
132
+ .catch(() => undefined);
133
+ }
134
+ function set(next, how = {}) {
135
+ if (readonly)
136
+ return;
137
+ if (next)
138
+ next = { lat: round6(next.lat), lng: round6(next.lng) };
139
+ point = next;
140
+ show(how.fly);
141
+ write(next);
142
+ if (next && how.address !== undefined && addressField) {
143
+ addressWritten = how.address;
144
+ form.setValue(addressField, how.address);
145
+ }
146
+ else if (next && how.name)
147
+ nameFrom(next);
148
+ }
149
+ // ---- the map and its pin ---------------------------------------------------
150
+ let marker = null;
151
+ function show(fly = false) {
152
+ clear.hidden = readonly || !point;
153
+ box.ready((map, L) => {
154
+ if (!point) {
155
+ marker?.remove();
156
+ marker = null;
157
+ return;
158
+ }
159
+ const at = [point.lat, point.lng];
160
+ if (!marker)
161
+ marker = makeMarker(map, L, at);
162
+ else
163
+ marker.setLatLng(at);
164
+ if (marker.dragging) {
165
+ if (readonly)
166
+ marker.dragging.disable();
167
+ else
168
+ marker.dragging.enable();
169
+ }
170
+ marker.getElement()?.setAttribute('aria-label', `${field.label}: ${point.lat}, ${point.lng}`);
171
+ if (fly)
172
+ map.setView(at, Math.max(map.getZoom(), zoom));
173
+ else if (!map.getBounds().contains(at))
174
+ map.setView(at, map.getZoom() < 5 ? zoom : map.getZoom());
175
+ });
176
+ }
177
+ function makeMarker(map, L, at) {
178
+ const icon = L.divIcon({ className: 'fd-map-pin', html: PIN, iconSize: [28, 40], iconAnchor: [14, 39] });
179
+ const pin = L.marker(at, { icon, draggable: !readonly, keyboard: true, autoPan: true }).addTo(map);
180
+ pin.on('dragend', () => {
181
+ const moved = pin.getLatLng();
182
+ set({ lat: moved.lat, lng: moved.lng }, { name: true });
183
+ });
184
+ // The arrow keys carry the pin a few pixels; Shift, further.
185
+ pin.getElement()?.addEventListener('keydown', (event) => {
186
+ const step = event.shiftKey ? 16 : 4;
187
+ const by = { ArrowUp: [0, -step], ArrowDown: [0, step], ArrowLeft: [-step, 0], ArrowRight: [step, 0] }[event.key];
188
+ if (!by || readonly)
189
+ return;
190
+ event.preventDefault();
191
+ event.stopPropagation();
192
+ const there = map.containerPointToLatLng(map.latLngToContainerPoint(pin.getLatLng()).add(L.point(by[0], by[1])));
193
+ set({ lat: there.lat, lng: there.lng }, { name: true });
194
+ });
195
+ return pin;
196
+ }
197
+ box.ready((map) => {
198
+ map.on('click', (event) => set({ lat: event.latlng.lat, lng: event.latlng.lng }, { name: true }));
199
+ // Enter on the map: the place at its centre, where the cross is.
200
+ box.element.addEventListener('keydown', (event) => {
201
+ if ((event.key !== 'Enter' && event.key !== ' ') || event.target !== box.element || readonly)
202
+ return;
203
+ event.preventDefault();
204
+ const centre = map.getCenter();
205
+ set({ lat: centre.lat, lng: centre.lng }, { name: true });
206
+ });
207
+ show(true);
208
+ });
209
+ // ---- search, my location, clear ---------------------------------------------
210
+ const found = search
211
+ ? (0, search_box_1.searchBox)({
212
+ document: doc,
213
+ id: lat.id === id ? `${id}-search` : id,
214
+ labels,
215
+ search,
216
+ asYouType: asYouType,
217
+ locale: context.locale ?? 'en',
218
+ near: () => point ?? settings.center,
219
+ pick: (place) => set({ lat: place.lat, lng: place.lng }, { fly: true, address: place.label }),
220
+ say,
221
+ })
222
+ : null;
223
+ if (found)
224
+ tools.append(found.element);
225
+ const myLocation = doc.createElement('button');
226
+ myLocation.type = 'button';
227
+ myLocation.className = 'fd-button';
228
+ myLocation.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3"/></svg>';
229
+ myLocation.append(labels.myLocation);
230
+ let blocked = false;
231
+ myLocation.hidden = !geolocation;
232
+ myLocation.addEventListener('click', () => {
233
+ if (!geolocation)
234
+ return;
235
+ say(labels.locating);
236
+ geolocation.getCurrentPosition((position) => {
237
+ say(null);
238
+ set({ lat: position.coords.latitude, lng: position.coords.longitude }, { fly: true, name: true });
239
+ }, (error) => {
240
+ if (error.code === error.PERMISSION_DENIED) {
241
+ blocked = true;
242
+ myLocation.hidden = true;
243
+ say(labels.locationBlocked);
244
+ }
245
+ else
246
+ say(labels.locationFailed, 'error');
247
+ }, { enableHighAccuracy: true, timeout: 10_000, maximumAge: 60_000 });
248
+ });
249
+ // Location blocked for the site: the button goes, with the reason in its place.
250
+ doc.defaultView?.navigator.permissions
251
+ ?.query({ name: 'geolocation' })
252
+ .then((status) => {
253
+ const read = () => {
254
+ blocked = status.state === 'denied';
255
+ myLocation.hidden = !geolocation || blocked || readonly;
256
+ if (blocked)
257
+ say(labels.locationBlocked);
258
+ };
259
+ read();
260
+ status.onchange = read;
261
+ })
262
+ .catch(() => undefined);
263
+ const clear = doc.createElement('button');
264
+ clear.type = 'button';
265
+ clear.className = 'fd-button fd-button-link';
266
+ clear.textContent = labels.clear;
267
+ clear.hidden = true;
268
+ clear.addEventListener('click', () => {
269
+ set(null);
270
+ say(null);
271
+ (found?.input ?? lat).focus();
272
+ });
273
+ tools.append(myLocation, clear);
274
+ element.append(tools, box.element, coords, note);
275
+ const showCoords = () => {
276
+ for (const [input, n] of [[lat, point?.lat], [lng, point?.lng]]) {
277
+ if (doc.activeElement === input)
278
+ continue;
279
+ input.value = n === undefined ? '' : String(n);
280
+ input.setAttribute('aria-invalid', 'false');
281
+ }
282
+ };
283
+ return {
284
+ element,
285
+ focus: () => (found?.input ?? lat).focus(),
286
+ destroy: () => {
287
+ naming?.abort();
288
+ found?.destroy();
289
+ box.destroy();
290
+ },
291
+ update(state) {
292
+ values = state.values;
293
+ readonly = state.readonly;
294
+ const next = inFields
295
+ ? (0, geo_1.isLatLng)(state.value, state.values[longitudeField])
296
+ ? { lat: state.value, lng: state.values[longitudeField] }
297
+ : null
298
+ : (0, geo_1.readPoint)(state.value);
299
+ const changed = next?.lat !== point?.lat || next?.lng !== point?.lng;
300
+ point = next;
301
+ tools.hidden = readonly;
302
+ myLocation.hidden = !geolocation || blocked || readonly;
303
+ for (const input of [lat, lng])
304
+ input.readOnly = readonly;
305
+ const target = found?.input ?? lat;
306
+ target.setAttribute('aria-invalid', String(state.invalid));
307
+ target.setAttribute('aria-required', String(state.required));
308
+ if (state.describedBy)
309
+ target.setAttribute('aria-describedby', state.describedBy);
310
+ else
311
+ target.removeAttribute('aria-describedby');
312
+ lat.setAttribute('aria-required', String(state.required));
313
+ showCoords();
314
+ box.refresh();
315
+ if (changed)
316
+ show();
317
+ else
318
+ clear.hidden = readonly || !point;
319
+ },
320
+ };
321
+ };
322
+ }
323
+ //# sourceMappingURL=place.js.map