@fieldia/map 0.18.0 → 0.19.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 +53 -0
- package/package.json +4 -4
- package/src/index.d.mts +5 -0
- package/src/index.d.ts +5 -0
- package/src/index.js +4 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +2 -0
- package/src/index.mjs.map +1 -1
- package/src/lib/address-labels.d.mts +21 -0
- package/src/lib/address-labels.d.ts +21 -0
- package/src/lib/address-labels.js +16 -0
- package/src/lib/address-labels.js.map +1 -0
- package/src/lib/address-labels.mjs +12 -0
- package/src/lib/address-labels.mjs.map +1 -0
- package/src/lib/address-line.d.mts +2 -0
- package/src/lib/address-line.d.ts +2 -0
- package/src/lib/address-line.js +34 -0
- package/src/lib/address-line.js.map +1 -0
- package/src/lib/address-line.mjs +31 -0
- package/src/lib/address-line.mjs.map +1 -0
- package/src/lib/address-parts.d.mts +28 -0
- package/src/lib/address-parts.d.ts +28 -0
- package/src/lib/address-parts.js +47 -0
- package/src/lib/address-parts.js.map +1 -0
- package/src/lib/address-parts.mjs +38 -0
- package/src/lib/address-parts.mjs.map +1 -0
- package/src/lib/address-search.d.mts +21 -0
- package/src/lib/address-search.d.ts +21 -0
- package/src/lib/address-search.js +52 -0
- package/src/lib/address-search.js.map +1 -0
- package/src/lib/address-search.mjs +49 -0
- package/src/lib/address-search.mjs.map +1 -0
- package/src/lib/address-suggest.d.mts +18 -0
- package/src/lib/address-suggest.d.ts +18 -0
- package/src/lib/address-suggest.js +140 -0
- package/src/lib/address-suggest.js.map +1 -0
- package/src/lib/address-suggest.mjs +137 -0
- package/src/lib/address-suggest.mjs.map +1 -0
- package/src/lib/area-count.d.mts +47 -0
- package/src/lib/area-count.d.ts +47 -0
- package/src/lib/area-count.js +123 -0
- package/src/lib/area-count.js.map +1 -0
- package/src/lib/area-count.mjs +116 -0
- package/src/lib/area-count.mjs.map +1 -0
- package/src/lib/card.d.mts +14 -5
- package/src/lib/card.d.ts +14 -5
- package/src/lib/card.js +67 -12
- package/src/lib/card.js.map +1 -1
- package/src/lib/card.mjs +66 -12
- package/src/lib/card.mjs.map +1 -1
- package/src/lib/embed.d.mts +86 -0
- package/src/lib/embed.d.ts +86 -0
- package/src/lib/embed.js +96 -0
- package/src/lib/embed.js.map +1 -0
- package/src/lib/embed.mjs +93 -0
- package/src/lib/embed.mjs.map +1 -0
- package/src/lib/kinds.d.mts +6 -4
- package/src/lib/kinds.d.ts +6 -4
- package/src/lib/kinds.js +49 -2
- package/src/lib/kinds.js.map +1 -1
- package/src/lib/kinds.mjs +49 -2
- package/src/lib/kinds.mjs.map +1 -1
- package/src/lib/labels.d.mts +2 -0
- package/src/lib/labels.d.ts +2 -0
- package/src/lib/labels.js +4 -0
- package/src/lib/labels.js.map +1 -1
- package/src/lib/labels.mjs +4 -0
- package/src/lib/labels.mjs.map +1 -1
- package/src/lib/list-view.d.mts +37 -1
- package/src/lib/list-view.d.ts +37 -1
- package/src/lib/list-view.js +97 -37
- package/src/lib/list-view.js.map +1 -1
- package/src/lib/list-view.mjs +97 -38
- package/src/lib/list-view.mjs.map +1 -1
- package/src/lib/map-core.d.mts +6 -1
- package/src/lib/map-core.d.ts +6 -1
- package/src/lib/map-core.js +13 -5
- package/src/lib/map-core.js.map +1 -1
- package/src/lib/map-core.mjs +13 -5
- package/src/lib/map-core.mjs.map +1 -1
- package/src/lib/pins.d.mts +20 -8
- package/src/lib/pins.d.ts +20 -8
- package/src/lib/pins.js +60 -15
- package/src/lib/pins.js.map +1 -1
- package/src/lib/pins.mjs +60 -17
- package/src/lib/pins.mjs.map +1 -1
- package/src/lib/place.js +2 -1
- package/src/lib/place.js.map +1 -1
- package/src/lib/place.mjs +2 -1
- package/src/lib/place.mjs.map +1 -1
- package/src/lib/providers.d.mts +10 -0
- package/src/lib/providers.d.ts +10 -0
- package/src/lib/providers.js +50 -40
- package/src/lib/providers.js.map +1 -1
- package/src/lib/providers.mjs +50 -40
- package/src/lib/providers.mjs.map +1 -1
- package/src/lib/regions.js +18 -1
- package/src/lib/regions.js.map +1 -1
- package/src/lib/regions.mjs +18 -1
- package/src/lib/regions.mjs.map +1 -1
- package/src/lib/search-box.d.mts +7 -1
- package/src/lib/search-box.d.ts +7 -1
- package/src/lib/search-box.js +15 -10
- package/src/lib/search-box.js.map +1 -1
- package/src/lib/search-box.mjs +15 -10
- package/src/lib/search-box.mjs.map +1 -1
- package/src/lib/settings.d.mts +19 -2
- package/src/lib/settings.d.ts +19 -2
- package/src/lib/styles.d.mts +12 -1
- package/src/lib/styles.d.ts +12 -1
- package/src/lib/styles.js +61 -3
- package/src/lib/styles.js.map +1 -1
- package/src/lib/styles.mjs +59 -2
- package/src/lib/styles.mjs.map +1 -1
- package/src/lib/view-box.d.mts +18 -0
- package/src/lib/view-box.d.ts +18 -0
- package/src/lib/view-box.js +31 -0
- package/src/lib/view-box.js.map +1 -0
- package/src/lib/view-box.mjs +25 -0
- package/src/lib/view-box.mjs.map +1 -0
- package/src/lib/widgets.d.mts +3 -0
- package/src/lib/widgets.d.ts +3 -0
- package/src/lib/widgets.js +10 -1
- package/src/lib/widgets.js.map +1 -1
- package/src/lib/widgets.mjs +10 -1
- package/src/lib/widgets.mjs.map +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createWidget } from '@fieldia/widgets';
|
|
2
|
+
import { addressLine } from './address-line.mjs';
|
|
3
|
+
import { searchBox } from './search-box.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* An address typed on one line (`"widget": "address-search"` on a char
|
|
6
|
+
* field), the addresses the map's provider suggests as people type in a list
|
|
7
|
+
* under it, and no map: the place search's own combobox, its pauses, its
|
|
8
|
+
* credits and its language. What is typed is the answer when nothing is
|
|
9
|
+
* picked. A suggestion picked writes its whole address, and where the page
|
|
10
|
+
* says, its place into a place field (`placeField`: a json field's GeoJSON
|
|
11
|
+
* Point, or a float field's latitude with `longitudeField` for its
|
|
12
|
+
* longitude) and its parts into an address field (`partsField`, a
|
|
13
|
+
* `json.address`: street, city, region, postcode and country, the line under
|
|
14
|
+
* the street kept as typed). `countries` (ISO codes) keeps the suggestions to
|
|
15
|
+
* those countries; `near` (`{ "lat", "lng" }`) puts those near it first, as
|
|
16
|
+
* where the app's maps open does unless it says.
|
|
17
|
+
*
|
|
18
|
+
* The box is the app's own text box, there at once; the suggestions come
|
|
19
|
+
* with their code, loaded the first time an address field is shown. With no
|
|
20
|
+
* address search to ask, the text box is all there is.
|
|
21
|
+
*/
|
|
22
|
+
export function addressSearchWidget(settings) {
|
|
23
|
+
return (context) => {
|
|
24
|
+
const plain = createWidget({ ...context, node: { ...context.node, widget: undefined } });
|
|
25
|
+
const search = settings.search ?? settings.provider.search;
|
|
26
|
+
const input = (plain.element.tagName === 'INPUT' ? plain.element : plain.element.querySelector('input'));
|
|
27
|
+
if (!search || !input)
|
|
28
|
+
return plain;
|
|
29
|
+
const element = context.document.createElement('div');
|
|
30
|
+
element.className = 'fd-address-search';
|
|
31
|
+
element.append(plain.element);
|
|
32
|
+
let stop;
|
|
33
|
+
// The place search's own combobox and line are handed over, as the page has them already: the suggestions' chunk carries neither.
|
|
34
|
+
import('./address-suggest.mjs').then(({ suggest }) => {
|
|
35
|
+
if (stop === undefined)
|
|
36
|
+
stop = suggest({ settings, context, search, element, input, searchBox, addressLine });
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
...plain,
|
|
40
|
+
element,
|
|
41
|
+
destroy() {
|
|
42
|
+
stop?.();
|
|
43
|
+
stop = null;
|
|
44
|
+
plain.destroy?.();
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=address-search.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-search.mjs","sourceRoot":"","sources":["../../../../../libs/map/src/lib/address-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAqB;IACvD,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,KAAK,GAAG,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QACzF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC3D,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAA4B,CAAC;QACpI,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACpC,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACtD,OAAO,CAAC,SAAS,GAAG,mBAAmB,CAAC;QACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,IAAqC,CAAC;QAC1C,kIAAkI;QAClI,MAAM,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;YAC/C,IAAI,IAAI,KAAK,SAAS;gBAAE,IAAI,GAAG,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAChH,CAAC,CAAC,CAAC;QACH,OAAO;YACL,GAAG,KAAK;YACR,OAAO;YACP,OAAO;gBACL,IAAI,EAAE,EAAE,CAAC;gBACT,IAAI,GAAG,IAAI,CAAC;gBACZ,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YACpB,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WidgetContext } from '@fieldia/widgets';
|
|
2
|
+
import type { addressLine } from './address-line.mjs';
|
|
3
|
+
import type { AddressSearch } from './providers.mjs';
|
|
4
|
+
import type { searchBox } from './search-box.mjs';
|
|
5
|
+
import type { MapSettings } from './settings.mjs';
|
|
6
|
+
export interface SuggestOptions {
|
|
7
|
+
settings: MapSettings;
|
|
8
|
+
context: WidgetContext;
|
|
9
|
+
search: AddressSearch;
|
|
10
|
+
/** The field's box, and what holds it (and the list under it). */
|
|
11
|
+
element: HTMLElement;
|
|
12
|
+
input: HTMLInputElement;
|
|
13
|
+
/** The place search's combobox, and the line it writes. */
|
|
14
|
+
searchBox: typeof searchBox;
|
|
15
|
+
addressLine: typeof addressLine;
|
|
16
|
+
}
|
|
17
|
+
/** The suggestions on the field's box; what it gives back takes them off. */
|
|
18
|
+
export declare function suggest({ settings, context, search, element, input, searchBox, addressLine }: SuggestOptions): () => void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WidgetContext } from '@fieldia/widgets';
|
|
2
|
+
import type { addressLine } from './address-line';
|
|
3
|
+
import type { AddressSearch } from './providers';
|
|
4
|
+
import type { searchBox } from './search-box';
|
|
5
|
+
import type { MapSettings } from './settings';
|
|
6
|
+
export interface SuggestOptions {
|
|
7
|
+
settings: MapSettings;
|
|
8
|
+
context: WidgetContext;
|
|
9
|
+
search: AddressSearch;
|
|
10
|
+
/** The field's box, and what holds it (and the list under it). */
|
|
11
|
+
element: HTMLElement;
|
|
12
|
+
input: HTMLInputElement;
|
|
13
|
+
/** The place search's combobox, and the line it writes. */
|
|
14
|
+
searchBox: typeof searchBox;
|
|
15
|
+
addressLine: typeof addressLine;
|
|
16
|
+
}
|
|
17
|
+
/** The suggestions on the field's box; what it gives back takes them off. */
|
|
18
|
+
export declare function suggest({ settings, context, search, element, input, searchBox, addressLine }: SuggestOptions): () => void;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.suggest = suggest;
|
|
4
|
+
const geo_1 = require("./geo");
|
|
5
|
+
const address_labels_1 = require("./address-labels");
|
|
6
|
+
const labels_1 = require("./labels");
|
|
7
|
+
const map_core_1 = require("./map-core");
|
|
8
|
+
const styles_1 = require("./styles");
|
|
9
|
+
/**
|
|
10
|
+
* An address field's suggestions (address-search.ts), loaded the first time
|
|
11
|
+
* one is shown: the place search's combobox on the field's own box, its list
|
|
12
|
+
* in a box of its own under it with the provider's credit, and what a
|
|
13
|
+
* suggestion picked writes.
|
|
14
|
+
*/
|
|
15
|
+
/** The list as the place search draws it, in a box of its own with the credit under it; hidden with the list. */
|
|
16
|
+
const ADDRESS_CSS = `
|
|
17
|
+
.fd-address-search { position: relative; min-width: 0; }
|
|
18
|
+
.fd-address-popup {
|
|
19
|
+
position: absolute; inset-inline: 0; top: calc(100% + 4px); z-index: 1100; overflow: hidden;
|
|
20
|
+
background: var(--fd-surface); color: var(--fd-text); border: 1px solid var(--fd-border); border-radius: var(--fd-control-radius); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
|
|
21
|
+
}
|
|
22
|
+
.fd-address-popup:has(> .fd-map-results[hidden]) { display: none; }
|
|
23
|
+
.fd-address-popup .fd-map-results { position: static; max-height: 264px; border: 0; border-radius: 0; box-shadow: none; background: none; }
|
|
24
|
+
.fd-address-credit { display: block; padding: 3px 10px 5px; border-top: 1px solid var(--fd-border); font-size: 11px; line-height: 1.4; color: var(--fd-muted); text-align: end; text-decoration: none; }
|
|
25
|
+
a.fd-address-credit:hover { text-decoration: underline; }
|
|
26
|
+
.fd-address-credit img { height: 14px; vertical-align: middle; }
|
|
27
|
+
`;
|
|
28
|
+
const round6 = (n) => Math.round(n * 1e6) / 1e6;
|
|
29
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
30
|
+
/** The suggestions on the field's box; what it gives back takes them off. */
|
|
31
|
+
function suggest({ settings, context, search, element, input, searchBox, addressLine }) {
|
|
32
|
+
const { form, name, node, id, document: doc } = context;
|
|
33
|
+
const locale = context.locale ?? 'en';
|
|
34
|
+
const options = node.options ?? {};
|
|
35
|
+
const words = (0, address_labels_1.addressLabels)(locale, settings.labels?.[locale]);
|
|
36
|
+
const named = (key) => (typeof options[key] === 'string' ? options[key] : null);
|
|
37
|
+
const [placeField, longitudeField, partsField] = [named('placeField'), named('longitudeField'), named('partsField')];
|
|
38
|
+
const listed = options['countries'];
|
|
39
|
+
const countries = (Array.isArray(listed) ? listed.map(String) : typeof listed === 'string' ? listed.split(',') : []).map((code) => code.trim().toUpperCase()).filter(Boolean);
|
|
40
|
+
const near = isObject(options['near']) && (0, geo_1.isLatLng)(options['near']['lat'], options['near']['lng']) ? options['near'] : settings.center;
|
|
41
|
+
(0, styles_1.installMapStyles)(doc);
|
|
42
|
+
if (!doc.getElementById('fieldia-address-styles')) {
|
|
43
|
+
const style = doc.createElement('style');
|
|
44
|
+
style.id = 'fieldia-address-styles';
|
|
45
|
+
style.textContent = ADDRESS_CSS;
|
|
46
|
+
doc.head.prepend(style);
|
|
47
|
+
}
|
|
48
|
+
const status = doc.createElement('span');
|
|
49
|
+
status.className = 'fd-sr-only';
|
|
50
|
+
status.setAttribute('role', 'status');
|
|
51
|
+
/** Said to screen readers alone: how many suggestions came, or why none did. A typed address needs none to be an answer. */
|
|
52
|
+
const say = (said) => {
|
|
53
|
+
status.textContent = said ?? (box.list.hidden ? '' : (0, labels_1.fill)(words.suggested, { n: box.list.children.length }));
|
|
54
|
+
// Suggestions come: all of them in sight, as near the bottom of a phone's screen as the box is.
|
|
55
|
+
if (!box.list.hidden)
|
|
56
|
+
popup.scrollIntoView?.({ block: 'nearest' });
|
|
57
|
+
};
|
|
58
|
+
const write = (text) => {
|
|
59
|
+
input.value = text;
|
|
60
|
+
form.setValue(name, text || null);
|
|
61
|
+
};
|
|
62
|
+
let picking = 0;
|
|
63
|
+
const box = searchBox({
|
|
64
|
+
document: doc,
|
|
65
|
+
id,
|
|
66
|
+
input,
|
|
67
|
+
labels: words,
|
|
68
|
+
search: (request) => search({ ...request, ...(countries.length ? { countries } : {}), ...(partsField ? { parts: true } : {}) }),
|
|
69
|
+
asYouType: settings.search ? (settings.searchAsYouType ?? true) : !!settings.provider.searchAsYouType,
|
|
70
|
+
locale,
|
|
71
|
+
near: () => near,
|
|
72
|
+
zoom: () => settings.zoom,
|
|
73
|
+
freeText: true,
|
|
74
|
+
say,
|
|
75
|
+
// Its whole address at once; its place and parts as soon as they are known (looked up now, for a provider whose suggestions carry none).
|
|
76
|
+
async pick(place) {
|
|
77
|
+
const mine = ++picking;
|
|
78
|
+
const whole = addressLine([place.label, place.detail], locale);
|
|
79
|
+
write(whole);
|
|
80
|
+
let at = place.lat !== undefined && place.lng !== undefined ? { lat: place.lat, lng: place.lng } : null;
|
|
81
|
+
let parts = place.parts;
|
|
82
|
+
if (!at && place.locate && (placeField || (partsField && !parts))) {
|
|
83
|
+
const located = await place.locate().catch((error) => ((0, map_core_1.warnOnce)(`Finding the address picked failed: ${error.message}`), null));
|
|
84
|
+
if (mine !== picking || !located)
|
|
85
|
+
return;
|
|
86
|
+
at = located;
|
|
87
|
+
parts = located.parts ?? parts;
|
|
88
|
+
// The provider's own way of writing it, unless a person has typed since.
|
|
89
|
+
if (located.address && input.value === whole)
|
|
90
|
+
write(addressLine([located.address], locale));
|
|
91
|
+
}
|
|
92
|
+
if (at && placeField) {
|
|
93
|
+
if (form.page.fields[placeField]?.type !== 'float')
|
|
94
|
+
form.setValue(placeField, (0, geo_1.asJson)((0, geo_1.writePoint)(at)));
|
|
95
|
+
else if (longitudeField) {
|
|
96
|
+
form.setValue(placeField, round6(at.lat));
|
|
97
|
+
form.setValue(longitudeField, round6(at.lng));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (parts && partsField) {
|
|
101
|
+
const now = form.getState().values[partsField];
|
|
102
|
+
const line2 = isObject(now) && typeof now['line2'] === 'string' ? { line2: now['line2'] } : {};
|
|
103
|
+
form.setValue(partsField, { ...parts, ...line2 });
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
// The list with what the provider's terms ask to be said under it: its mark, or its credit.
|
|
108
|
+
const popup = doc.createElement('div');
|
|
109
|
+
popup.className = 'fd-address-popup';
|
|
110
|
+
popup.append(box.list);
|
|
111
|
+
const mark = settings.search ? undefined : (settings.provider.logo ?? settings.provider.searchCredit);
|
|
112
|
+
if (mark) {
|
|
113
|
+
const credit = doc.createElement(mark.href ? 'a' : 'span');
|
|
114
|
+
credit.className = 'fd-address-credit';
|
|
115
|
+
if (mark.href)
|
|
116
|
+
Object.assign(credit, { href: mark.href, target: '_blank', rel: 'noopener', tabIndex: -1 });
|
|
117
|
+
const image = 'image' in mark ? mark.image : undefined;
|
|
118
|
+
if (image) {
|
|
119
|
+
const img = doc.createElement('img');
|
|
120
|
+
Object.assign(img, { src: image, alt: mark.text });
|
|
121
|
+
credit.append(img);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// Its own words in their own direction: "© OpenStreetMap contributors" in an Arabic page too.
|
|
125
|
+
const text = doc.createElement('bdi');
|
|
126
|
+
text.textContent = mark.text;
|
|
127
|
+
credit.append(text);
|
|
128
|
+
}
|
|
129
|
+
popup.append(credit);
|
|
130
|
+
}
|
|
131
|
+
// A press on it keeps the box's focus, so the list stays open and a credit's link opens.
|
|
132
|
+
popup.addEventListener('mousedown', (event) => event.preventDefault());
|
|
133
|
+
element.append(popup, status);
|
|
134
|
+
return () => {
|
|
135
|
+
box.destroy();
|
|
136
|
+
popup.remove();
|
|
137
|
+
status.remove();
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=address-suggest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-suggest.js","sourceRoot":"","sources":["../../../../../libs/map/src/lib/address-suggest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { asJson, isLatLng, writePoint } from './geo.mjs';
|
|
2
|
+
import { addressLabels } from './address-labels.mjs';
|
|
3
|
+
import { fill } from './labels.mjs';
|
|
4
|
+
import { warnOnce } from './map-core.mjs';
|
|
5
|
+
import { installMapStyles } from './styles.mjs';
|
|
6
|
+
/**
|
|
7
|
+
* An address field's suggestions (address-search.ts), loaded the first time
|
|
8
|
+
* one is shown: the place search's combobox on the field's own box, its list
|
|
9
|
+
* in a box of its own under it with the provider's credit, and what a
|
|
10
|
+
* suggestion picked writes.
|
|
11
|
+
*/
|
|
12
|
+
/** The list as the place search draws it, in a box of its own with the credit under it; hidden with the list. */
|
|
13
|
+
const ADDRESS_CSS = `
|
|
14
|
+
.fd-address-search { position: relative; min-width: 0; }
|
|
15
|
+
.fd-address-popup {
|
|
16
|
+
position: absolute; inset-inline: 0; top: calc(100% + 4px); z-index: 1100; overflow: hidden;
|
|
17
|
+
background: var(--fd-surface); color: var(--fd-text); border: 1px solid var(--fd-border); border-radius: var(--fd-control-radius); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
|
|
18
|
+
}
|
|
19
|
+
.fd-address-popup:has(> .fd-map-results[hidden]) { display: none; }
|
|
20
|
+
.fd-address-popup .fd-map-results { position: static; max-height: 264px; border: 0; border-radius: 0; box-shadow: none; background: none; }
|
|
21
|
+
.fd-address-credit { display: block; padding: 3px 10px 5px; border-top: 1px solid var(--fd-border); font-size: 11px; line-height: 1.4; color: var(--fd-muted); text-align: end; text-decoration: none; }
|
|
22
|
+
a.fd-address-credit:hover { text-decoration: underline; }
|
|
23
|
+
.fd-address-credit img { height: 14px; vertical-align: middle; }
|
|
24
|
+
`;
|
|
25
|
+
const round6 = (n) => Math.round(n * 1e6) / 1e6;
|
|
26
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
+
/** The suggestions on the field's box; what it gives back takes them off. */
|
|
28
|
+
export function suggest({ settings, context, search, element, input, searchBox, addressLine }) {
|
|
29
|
+
const { form, name, node, id, document: doc } = context;
|
|
30
|
+
const locale = context.locale ?? 'en';
|
|
31
|
+
const options = node.options ?? {};
|
|
32
|
+
const words = addressLabels(locale, settings.labels?.[locale]);
|
|
33
|
+
const named = (key) => (typeof options[key] === 'string' ? options[key] : null);
|
|
34
|
+
const [placeField, longitudeField, partsField] = [named('placeField'), named('longitudeField'), named('partsField')];
|
|
35
|
+
const listed = options['countries'];
|
|
36
|
+
const countries = (Array.isArray(listed) ? listed.map(String) : typeof listed === 'string' ? listed.split(',') : []).map((code) => code.trim().toUpperCase()).filter(Boolean);
|
|
37
|
+
const near = isObject(options['near']) && isLatLng(options['near']['lat'], options['near']['lng']) ? options['near'] : settings.center;
|
|
38
|
+
installMapStyles(doc);
|
|
39
|
+
if (!doc.getElementById('fieldia-address-styles')) {
|
|
40
|
+
const style = doc.createElement('style');
|
|
41
|
+
style.id = 'fieldia-address-styles';
|
|
42
|
+
style.textContent = ADDRESS_CSS;
|
|
43
|
+
doc.head.prepend(style);
|
|
44
|
+
}
|
|
45
|
+
const status = doc.createElement('span');
|
|
46
|
+
status.className = 'fd-sr-only';
|
|
47
|
+
status.setAttribute('role', 'status');
|
|
48
|
+
/** Said to screen readers alone: how many suggestions came, or why none did. A typed address needs none to be an answer. */
|
|
49
|
+
const say = (said) => {
|
|
50
|
+
status.textContent = said ?? (box.list.hidden ? '' : fill(words.suggested, { n: box.list.children.length }));
|
|
51
|
+
// Suggestions come: all of them in sight, as near the bottom of a phone's screen as the box is.
|
|
52
|
+
if (!box.list.hidden)
|
|
53
|
+
popup.scrollIntoView?.({ block: 'nearest' });
|
|
54
|
+
};
|
|
55
|
+
const write = (text) => {
|
|
56
|
+
input.value = text;
|
|
57
|
+
form.setValue(name, text || null);
|
|
58
|
+
};
|
|
59
|
+
let picking = 0;
|
|
60
|
+
const box = searchBox({
|
|
61
|
+
document: doc,
|
|
62
|
+
id,
|
|
63
|
+
input,
|
|
64
|
+
labels: words,
|
|
65
|
+
search: (request) => search({ ...request, ...(countries.length ? { countries } : {}), ...(partsField ? { parts: true } : {}) }),
|
|
66
|
+
asYouType: settings.search ? (settings.searchAsYouType ?? true) : !!settings.provider.searchAsYouType,
|
|
67
|
+
locale,
|
|
68
|
+
near: () => near,
|
|
69
|
+
zoom: () => settings.zoom,
|
|
70
|
+
freeText: true,
|
|
71
|
+
say,
|
|
72
|
+
// Its whole address at once; its place and parts as soon as they are known (looked up now, for a provider whose suggestions carry none).
|
|
73
|
+
async pick(place) {
|
|
74
|
+
const mine = ++picking;
|
|
75
|
+
const whole = addressLine([place.label, place.detail], locale);
|
|
76
|
+
write(whole);
|
|
77
|
+
let at = place.lat !== undefined && place.lng !== undefined ? { lat: place.lat, lng: place.lng } : null;
|
|
78
|
+
let parts = place.parts;
|
|
79
|
+
if (!at && place.locate && (placeField || (partsField && !parts))) {
|
|
80
|
+
const located = await place.locate().catch((error) => (warnOnce(`Finding the address picked failed: ${error.message}`), null));
|
|
81
|
+
if (mine !== picking || !located)
|
|
82
|
+
return;
|
|
83
|
+
at = located;
|
|
84
|
+
parts = located.parts ?? parts;
|
|
85
|
+
// The provider's own way of writing it, unless a person has typed since.
|
|
86
|
+
if (located.address && input.value === whole)
|
|
87
|
+
write(addressLine([located.address], locale));
|
|
88
|
+
}
|
|
89
|
+
if (at && placeField) {
|
|
90
|
+
if (form.page.fields[placeField]?.type !== 'float')
|
|
91
|
+
form.setValue(placeField, asJson(writePoint(at)));
|
|
92
|
+
else if (longitudeField) {
|
|
93
|
+
form.setValue(placeField, round6(at.lat));
|
|
94
|
+
form.setValue(longitudeField, round6(at.lng));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (parts && partsField) {
|
|
98
|
+
const now = form.getState().values[partsField];
|
|
99
|
+
const line2 = isObject(now) && typeof now['line2'] === 'string' ? { line2: now['line2'] } : {};
|
|
100
|
+
form.setValue(partsField, { ...parts, ...line2 });
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
// The list with what the provider's terms ask to be said under it: its mark, or its credit.
|
|
105
|
+
const popup = doc.createElement('div');
|
|
106
|
+
popup.className = 'fd-address-popup';
|
|
107
|
+
popup.append(box.list);
|
|
108
|
+
const mark = settings.search ? undefined : (settings.provider.logo ?? settings.provider.searchCredit);
|
|
109
|
+
if (mark) {
|
|
110
|
+
const credit = doc.createElement(mark.href ? 'a' : 'span');
|
|
111
|
+
credit.className = 'fd-address-credit';
|
|
112
|
+
if (mark.href)
|
|
113
|
+
Object.assign(credit, { href: mark.href, target: '_blank', rel: 'noopener', tabIndex: -1 });
|
|
114
|
+
const image = 'image' in mark ? mark.image : undefined;
|
|
115
|
+
if (image) {
|
|
116
|
+
const img = doc.createElement('img');
|
|
117
|
+
Object.assign(img, { src: image, alt: mark.text });
|
|
118
|
+
credit.append(img);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Its own words in their own direction: "© OpenStreetMap contributors" in an Arabic page too.
|
|
122
|
+
const text = doc.createElement('bdi');
|
|
123
|
+
text.textContent = mark.text;
|
|
124
|
+
credit.append(text);
|
|
125
|
+
}
|
|
126
|
+
popup.append(credit);
|
|
127
|
+
}
|
|
128
|
+
// A press on it keeps the box's focus, so the list stays open and a credit's link opens.
|
|
129
|
+
popup.addEventListener('mousedown', (event) => event.preventDefault());
|
|
130
|
+
element.append(popup, status);
|
|
131
|
+
return () => {
|
|
132
|
+
box.destroy();
|
|
133
|
+
popup.remove();
|
|
134
|
+
status.remove();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=address-suggest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-suggest.mjs","sourceRoot":"","sources":["../../../../../libs/map/src/lib/address-suggest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ListMap, ResolvedFilter } from '@fieldia/core';
|
|
2
|
+
import type { LatLng } from './geo.mjs';
|
|
3
|
+
import { type Box } from './view-box.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* How many records a list map's view holds, said on "Search this area"
|
|
6
|
+
* before it is pressed: counted among the records on hand when they can
|
|
7
|
+
* tell, asked otherwise. Its code comes the first time the button shows.
|
|
8
|
+
*/
|
|
9
|
+
/** Whether a place lies in a box, its edges included, as `between` takes them. */
|
|
10
|
+
export declare function inBox(at: LatLng, box: Box): boolean;
|
|
11
|
+
/** Whether one box lies within another. */
|
|
12
|
+
export declare function boxWithin(inner: Box, outer: Box): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The view a search already holds, as the map's "Search this area" puts it
|
|
15
|
+
* (its very conditions, found among the search's), and the search's other
|
|
16
|
+
* conditions: a new view takes its place, as a search bar keeps one.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ownBox(spec: ListMap, filter: readonly ResolvedFilter[]): {
|
|
19
|
+
box: Box;
|
|
20
|
+
rest: ResolvedFilter[];
|
|
21
|
+
} | null;
|
|
22
|
+
/**
|
|
23
|
+
* One question at a time, the last one winning: each ask waits `wait`
|
|
24
|
+
* milliseconds (a run of moves asks once), aborts the one before it, and an
|
|
25
|
+
* answer that comes after a newer ask is dropped. A refusal says nothing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function lastAsk<T>(wait: number): {
|
|
28
|
+
ask(question: (signal: AbortSignal) => Promise<T>, answer: (value: T) => void): void;
|
|
29
|
+
stop: () => void;
|
|
30
|
+
};
|
|
31
|
+
/** What the map has on hand: the search its records were found by, whether they are all it finds, and where each is. */
|
|
32
|
+
export interface OnHand {
|
|
33
|
+
filter: readonly ResolvedFilter[];
|
|
34
|
+
all: boolean;
|
|
35
|
+
places: readonly (LatLng | null)[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The count for a view: the records on hand say it when they are all the
|
|
39
|
+
* search finds and the view lies within any area it already holds;
|
|
40
|
+
* otherwise `ask` (the app's count, or its source's) is asked, for the
|
|
41
|
+
* search with the view in the area's place, a moment after the map stops;
|
|
42
|
+
* otherwise there is no number.
|
|
43
|
+
*/
|
|
44
|
+
export declare function areaCounter(spec: ListMap, ask: ((filter: ResolvedFilter[], signal: AbortSignal) => Promise<number>) | null, say: (n: number) => void): {
|
|
45
|
+
count(view: Box, shown: OnHand): void;
|
|
46
|
+
stop: () => void;
|
|
47
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ListMap, ResolvedFilter } from '@fieldia/core';
|
|
2
|
+
import type { LatLng } from './geo';
|
|
3
|
+
import { type Box } from './view-box';
|
|
4
|
+
/**
|
|
5
|
+
* How many records a list map's view holds, said on "Search this area"
|
|
6
|
+
* before it is pressed: counted among the records on hand when they can
|
|
7
|
+
* tell, asked otherwise. Its code comes the first time the button shows.
|
|
8
|
+
*/
|
|
9
|
+
/** Whether a place lies in a box, its edges included, as `between` takes them. */
|
|
10
|
+
export declare function inBox(at: LatLng, box: Box): boolean;
|
|
11
|
+
/** Whether one box lies within another. */
|
|
12
|
+
export declare function boxWithin(inner: Box, outer: Box): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The view a search already holds, as the map's "Search this area" puts it
|
|
15
|
+
* (its very conditions, found among the search's), and the search's other
|
|
16
|
+
* conditions: a new view takes its place, as a search bar keeps one.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ownBox(spec: ListMap, filter: readonly ResolvedFilter[]): {
|
|
19
|
+
box: Box;
|
|
20
|
+
rest: ResolvedFilter[];
|
|
21
|
+
} | null;
|
|
22
|
+
/**
|
|
23
|
+
* One question at a time, the last one winning: each ask waits `wait`
|
|
24
|
+
* milliseconds (a run of moves asks once), aborts the one before it, and an
|
|
25
|
+
* answer that comes after a newer ask is dropped. A refusal says nothing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function lastAsk<T>(wait: number): {
|
|
28
|
+
ask(question: (signal: AbortSignal) => Promise<T>, answer: (value: T) => void): void;
|
|
29
|
+
stop: () => void;
|
|
30
|
+
};
|
|
31
|
+
/** What the map has on hand: the search its records were found by, whether they are all it finds, and where each is. */
|
|
32
|
+
export interface OnHand {
|
|
33
|
+
filter: readonly ResolvedFilter[];
|
|
34
|
+
all: boolean;
|
|
35
|
+
places: readonly (LatLng | null)[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The count for a view: the records on hand say it when they are all the
|
|
39
|
+
* search finds and the view lies within any area it already holds;
|
|
40
|
+
* otherwise `ask` (the app's count, or its source's) is asked, for the
|
|
41
|
+
* search with the view in the area's place, a moment after the map stops;
|
|
42
|
+
* otherwise there is no number.
|
|
43
|
+
*/
|
|
44
|
+
export declare function areaCounter(spec: ListMap, ask: ((filter: ResolvedFilter[], signal: AbortSignal) => Promise<number>) | null, say: (n: number) => void): {
|
|
45
|
+
count(view: Box, shown: OnHand): void;
|
|
46
|
+
stop: () => void;
|
|
47
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inBox = inBox;
|
|
4
|
+
exports.boxWithin = boxWithin;
|
|
5
|
+
exports.ownBox = ownBox;
|
|
6
|
+
exports.lastAsk = lastAsk;
|
|
7
|
+
exports.areaCounter = areaCounter;
|
|
8
|
+
const view_box_1 = require("./view-box");
|
|
9
|
+
/**
|
|
10
|
+
* How many records a list map's view holds, said on "Search this area"
|
|
11
|
+
* before it is pressed: counted among the records on hand when they can
|
|
12
|
+
* tell, asked otherwise. Its code comes the first time the button shows.
|
|
13
|
+
*/
|
|
14
|
+
/** Whether a place lies in a box, its edges included, as `between` takes them. */
|
|
15
|
+
function inBox(at, box) {
|
|
16
|
+
if (at.lat < box.south || at.lat > box.north)
|
|
17
|
+
return false;
|
|
18
|
+
return box.west <= box.east ? at.lng >= box.west && at.lng <= box.east : at.lng >= box.west || at.lng <= box.east;
|
|
19
|
+
}
|
|
20
|
+
/** Whether one box lies within another. */
|
|
21
|
+
function boxWithin(inner, outer) {
|
|
22
|
+
if (inner.south < outer.south || inner.north > outer.north)
|
|
23
|
+
return false;
|
|
24
|
+
if ((0, view_box_1.whole)(outer))
|
|
25
|
+
return true;
|
|
26
|
+
// Each as a stretch eastwards from its west edge; the inner one a world further round, if that is where it fits.
|
|
27
|
+
const span = (box) => [box.west, box.east < box.west ? box.east + 360 : box.east];
|
|
28
|
+
const [w, e] = span(outer);
|
|
29
|
+
const [iw, ie] = span(inner);
|
|
30
|
+
return [0, 360].some((by) => iw + by >= w && ie + by <= e);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The view a search already holds, as the map's "Search this area" puts it
|
|
34
|
+
* (its very conditions, found among the search's), and the search's other
|
|
35
|
+
* conditions: a new view takes its place, as a search bar keeps one.
|
|
36
|
+
*/
|
|
37
|
+
function ownBox(spec, filter) {
|
|
38
|
+
const said = filter.map((item) => JSON.stringify(item));
|
|
39
|
+
const numbers = (value) => (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === 'number') ? value : null);
|
|
40
|
+
const boxes = [];
|
|
41
|
+
for (const item of filter) {
|
|
42
|
+
if ('any' in item || 'all' in item)
|
|
43
|
+
continue;
|
|
44
|
+
const v = item.value;
|
|
45
|
+
if (spec.point) {
|
|
46
|
+
if (item.field === spec.point && item.op === 'within' && v && ['south', 'west', 'north', 'east'].every((k) => typeof v[k] === 'number'))
|
|
47
|
+
boxes.push(v);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const lat = item.field === spec.latitude && item.op === 'between' ? numbers(v) : null;
|
|
51
|
+
if (!lat)
|
|
52
|
+
continue;
|
|
53
|
+
// Its longitudes: a range, either side of the antimeridian, or (tried last) the world round.
|
|
54
|
+
const sides = [];
|
|
55
|
+
for (const other of filter) {
|
|
56
|
+
if ('any' in other && other.any.length === 2) {
|
|
57
|
+
const [a, b] = other.any;
|
|
58
|
+
if (a.field === spec.longitude && b.field === spec.longitude && typeof a.value === 'number' && typeof b.value === 'number')
|
|
59
|
+
sides.push([a.value, b.value]);
|
|
60
|
+
}
|
|
61
|
+
else if (!('all' in other) && !('any' in other) && other.field === spec.longitude && other.op === 'between' && numbers(other.value))
|
|
62
|
+
sides.push(numbers(other.value));
|
|
63
|
+
}
|
|
64
|
+
for (const [west, east] of [...sides, [-180, 180]])
|
|
65
|
+
boxes.push({ south: lat[0], west, north: lat[1], east });
|
|
66
|
+
}
|
|
67
|
+
for (const box of boxes) {
|
|
68
|
+
const own = (0, view_box_1.boxFilter)(spec, box).map((item) => JSON.stringify(item));
|
|
69
|
+
if (!own.every((item) => said.includes(item)))
|
|
70
|
+
continue;
|
|
71
|
+
return { box: { ...box }, rest: filter.filter((_, i) => !own.includes(said[i])) };
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* One question at a time, the last one winning: each ask waits `wait`
|
|
77
|
+
* milliseconds (a run of moves asks once), aborts the one before it, and an
|
|
78
|
+
* answer that comes after a newer ask is dropped. A refusal says nothing.
|
|
79
|
+
*/
|
|
80
|
+
function lastAsk(wait) {
|
|
81
|
+
let timer;
|
|
82
|
+
let asking = null;
|
|
83
|
+
let asked = 0;
|
|
84
|
+
const stop = () => {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
asking?.abort();
|
|
87
|
+
asking = null;
|
|
88
|
+
asked++;
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
ask(question, answer) {
|
|
92
|
+
stop();
|
|
93
|
+
const mine = asked;
|
|
94
|
+
timer = setTimeout(() => {
|
|
95
|
+
const controller = (asking = new AbortController());
|
|
96
|
+
question(controller.signal).then((value) => mine === asked && !controller.signal.aborted && answer(value), () => undefined);
|
|
97
|
+
}, wait);
|
|
98
|
+
},
|
|
99
|
+
stop,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The count for a view: the records on hand say it when they are all the
|
|
104
|
+
* search finds and the view lies within any area it already holds;
|
|
105
|
+
* otherwise `ask` (the app's count, or its source's) is asked, for the
|
|
106
|
+
* search with the view in the area's place, a moment after the map stops;
|
|
107
|
+
* otherwise there is no number.
|
|
108
|
+
*/
|
|
109
|
+
function areaCounter(spec, ask, say) {
|
|
110
|
+
const asker = lastAsk(400);
|
|
111
|
+
return {
|
|
112
|
+
count(view, shown) {
|
|
113
|
+
asker.stop();
|
|
114
|
+
const own = ownBox(spec, shown.filter);
|
|
115
|
+
if (shown.all && (!own || boxWithin(view, own.box)))
|
|
116
|
+
return say(shown.places.filter((at) => at && inBox(at, view)).length);
|
|
117
|
+
if (ask)
|
|
118
|
+
asker.ask((signal) => ask([...(own ? own.rest : shown.filter), ...(0, view_box_1.boxFilter)(spec, view)], signal), say);
|
|
119
|
+
},
|
|
120
|
+
stop: asker.stop,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=area-count.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"area-count.js","sourceRoot":"","sources":["../../../../../libs/map/src/lib/area-count.ts"],"names":[],"mappings":"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"}
|