@verbb/formie-browser 1.0.4 → 1.0.6
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/CHANGELOG.md +76 -22
- package/dist/chunks/address-country-Rx6GQTND.js +68 -0
- package/dist/chunks/address-finder-yXU5yMjG.js +30 -0
- package/dist/chunks/address-state-BQ0xGV9n.js +344 -0
- package/dist/chunks/api-CmwLRq_n.js +780 -0
- package/dist/chunks/api-DMK8NSUI.js +407 -0
- package/dist/chunks/api-sPqGbOww.js +125 -0
- package/dist/chunks/async-nPFRNQ06.js +25 -0
- package/dist/chunks/bpoint-DSjqRv63.js +24 -0
- package/dist/chunks/calculations-CeDgB9jt.js +299 -0
- package/dist/chunks/captcha-eu-CWJb64_j.js +31 -0
- package/dist/chunks/checkbox-radio-DQ0H67Tj.js +125 -0
- package/dist/chunks/chunk-3b4jIN3o.js +13 -0
- package/dist/chunks/combobox-D-2b42wn.js +1954 -0
- package/dist/chunks/conditions-DjZOtHfS.js +412 -0
- package/dist/chunks/constants-DVcJAvc5.js +38 -0
- package/dist/chunks/country-from-ip-BwxKaBuJ.js +26 -0
- package/dist/chunks/custom-google-maps-BsaSirEZ.js +194 -0
- package/dist/chunks/custom-link-CJ1-FjEM.js +46 -0
- package/dist/chunks/custom-maps-DYW_pBid.js +161 -0
- package/dist/chunks/date-picker-5CA_-zGb.js +5311 -0
- package/dist/chunks/debug-BV0DvdHx.js +41 -0
- package/dist/chunks/dist-BQ4a9hmy.js +2659 -0
- package/dist/chunks/event-names-BCI2FLD8.js +51 -0
- package/dist/chunks/eway-D3ctTbEk.js +70 -0
- package/dist/chunks/field-references.keys-58ZSTrCW.js +21 -0
- package/dist/chunks/field-references.resolver-CHwn0G0L.js +145 -0
- package/dist/chunks/file-upload-D49m8-DR.js +287 -0
- package/dist/chunks/friendly-captcha-v1-ByXzENYZ.js +31 -0
- package/dist/chunks/friendly-captcha-v2-BPdlmhQu.js +36 -0
- package/dist/chunks/friendly-challenge-8uPatWqk.js +832 -0
- package/dist/chunks/go-cardless-BwASSYSK.js +23 -0
- package/dist/chunks/google-address-OhKilPWE.js +161 -0
- package/dist/chunks/hcaptcha-CdCd9oEi.js +54 -0
- package/dist/chunks/hidden-D7_Ch-QN.js +26 -0
- package/dist/chunks/http-D-JExro7.js +27 -0
- package/dist/chunks/i18n-BY1ds1BL.js +121 -0
- package/dist/chunks/leaflet-src-j-lHNmhR.js +4585 -0
- package/dist/chunks/loqate-Cp2zvwpE.js +76 -0
- package/dist/chunks/mollie-COG-ko2D.js +23 -0
- package/dist/chunks/moneris-CMH8PlRc.js +117 -0
- package/dist/chunks/opayo-D2jsR5vh.js +113 -0
- package/dist/chunks/paddle-DxF88RfC.js +55 -0
- package/dist/chunks/paypal-b5pnwrA2.js +95 -0
- package/dist/chunks/payway-BJYu9DIR.js +55 -0
- package/dist/chunks/phone-country-D3ayr60L.js +2645 -0
- package/dist/chunks/place-kit-BGHklIZ-.js +48 -0
- package/dist/chunks/placekit-autocomplete.esm-DWVabQlq.js +1236 -0
- package/dist/chunks/recaptcha-enterprise-BSwi8IVX.js +65 -0
- package/dist/chunks/recaptcha-shared-B4zAescW.js +15 -0
- package/dist/chunks/recaptcha-v2-checkbox-CnDQmsKa.js +40 -0
- package/dist/chunks/recaptcha-v2-invisible-Cz5uIlp-.js +53 -0
- package/dist/chunks/recaptcha-v3-BtbbP111.js +27 -0
- package/dist/chunks/repeater-B5leVxZU.js +102 -0
- package/dist/chunks/rich-text-D0sb8bmX.js +349 -0
- package/dist/chunks/scripts-6CYSUGKQ.js +24 -0
- package/dist/chunks/sdk-BhHAYgga.js +1397 -0
- package/dist/chunks/shared-Bx9s0i0P.js +100 -0
- package/dist/chunks/signature-xz4QO7CR.js +536 -0
- package/dist/chunks/snaptcha-vz1RnZ15.js +8 -0
- package/dist/chunks/square-BP8RWAKB.js +40 -0
- package/dist/chunks/stripe-MihN63uC.js +184 -0
- package/dist/chunks/styles-BfoIZwJp.js +19 -0
- package/dist/chunks/summary-D3AjxpYN.js +114 -0
- package/dist/chunks/table-C-lkQN6I.js +78 -0
- package/dist/chunks/text-limit-DbgEFHLO.js +125 -0
- package/dist/chunks/theme-classes-Tv7q7ToE.js +44 -0
- package/dist/chunks/turnstile-D_Pi1CKM.js +58 -0
- package/dist/chunks/upload-manager-BpqWSHmU.js +2558 -0
- package/dist/chunks/utils-Vqp8i_RM.js +49401 -0
- package/dist/css/formie-theme.css +2 -0
- package/dist/css/theme/_buttons.css +16 -0
- package/dist/css/theme/_tokens.css +2 -0
- package/dist/css/theme/fields/_address-state.css +94 -0
- package/dist/css/theme/fields/_combobox.css +246 -0
- package/dist/css/theme/fields/_select.css +13 -0
- package/dist/css/theme/fields/_signature.css +19 -0
- package/dist/css/theme/fields/_upload-manager.css +279 -0
- package/dist/index.js +2448 -39
- package/dist/js/core/create-formie-client.d.ts.map +1 -1
- package/dist/js/core/submit-result-ui.d.ts.map +1 -1
- package/dist/js/core/validation-focus.d.ts +4 -0
- package/dist/js/core/validation-focus.d.ts.map +1 -0
- package/dist/js/modules/address/api.d.ts +1 -0
- package/dist/js/modules/address/api.d.ts.map +1 -1
- package/dist/js/modules/address/constants.d.ts +22 -1
- package/dist/js/modules/address/constants.d.ts.map +1 -1
- package/dist/js/modules/address/host.d.ts +2 -2
- package/dist/js/modules/address/host.d.ts.map +1 -1
- package/dist/js/modules/address/loqate.d.ts.map +1 -1
- package/dist/js/modules/fields/address-country.d.ts +3 -0
- package/dist/js/modules/fields/address-country.d.ts.map +1 -0
- package/dist/js/modules/fields/address-state.d.ts +3 -0
- package/dist/js/modules/fields/address-state.d.ts.map +1 -0
- package/dist/js/modules/fields/calculations.d.ts.map +1 -1
- package/dist/js/modules/fields/combobox.d.ts +17 -0
- package/dist/js/modules/fields/combobox.d.ts.map +1 -0
- package/dist/js/modules/fields/custom-google-maps.d.ts +64 -0
- package/dist/js/modules/fields/custom-google-maps.d.ts.map +1 -0
- package/dist/js/modules/fields/custom-link.d.ts +3 -0
- package/dist/js/modules/fields/custom-link.d.ts.map +1 -0
- package/dist/js/modules/fields/custom-maps.d.ts +3 -0
- package/dist/js/modules/fields/custom-maps.d.ts.map +1 -0
- package/dist/js/modules/fields/date-picker.d.ts.map +1 -1
- package/dist/js/modules/fields/index.d.ts.map +1 -1
- package/dist/js/modules/fields/phone-country.d.ts.map +1 -1
- package/dist/js/modules/fields/signature.d.ts.map +1 -1
- package/dist/js/modules/fields/upload-manager.d.ts +3 -0
- package/dist/js/modules/fields/upload-manager.d.ts.map +1 -0
- package/dist/js/submit/pipeline.d.ts.map +1 -1
- package/dist/js/utils/country-from-ip.d.ts +7 -0
- package/dist/js/utils/country-from-ip.d.ts.map +1 -0
- package/dist/js/utils/field-references.d.ts +1 -0
- package/dist/js/utils/field-references.d.ts.map +1 -1
- package/dist/js/utils/field-references.row-scope.d.ts +11 -0
- package/dist/js/utils/field-references.row-scope.d.ts.map +1 -0
- package/package.json +8 -3
- package/src/css/formie-theme.css +2 -0
- package/src/css/theme/_buttons.css +16 -0
- package/src/css/theme/_tokens.css +2 -0
- package/src/css/theme/fields/_address-state.css +94 -0
- package/src/css/theme/fields/_combobox.css +246 -0
- package/src/css/theme/fields/_select.css +13 -0
- package/src/css/theme/fields/_signature.css +19 -0
- package/src/css/theme/fields/_upload-manager.css +279 -0
- package/src/js/core/create-formie-client.ts +27 -0
- package/src/js/core/submit-result-ui.ts +4 -4
- package/src/js/core/validation-focus.ts +117 -0
- package/src/js/modules/address/api.ts +7 -0
- package/src/js/modules/address/constants.ts +82 -2
- package/src/js/modules/address/host.ts +16 -6
- package/src/js/modules/address/loqate.ts +3 -17
- package/src/js/modules/fields/address-country.ts +156 -0
- package/src/js/modules/fields/address-state.ts +946 -0
- package/src/js/modules/fields/calculations.ts +32 -0
- package/src/js/modules/fields/combobox.ts +188 -0
- package/src/js/modules/fields/custom-google-maps.ts +416 -0
- package/src/js/modules/fields/custom-link.ts +95 -0
- package/src/js/modules/fields/custom-maps.ts +339 -0
- package/src/js/modules/fields/date-picker.ts +96 -3
- package/src/js/modules/fields/index.ts +7 -0
- package/src/js/modules/fields/phone-country.ts +6 -0
- package/src/js/modules/fields/signature.ts +199 -15
- package/src/js/modules/fields/upload-manager.ts +1277 -0
- package/src/js/submit/pipeline.ts +11 -1
- package/src/js/utils/country-from-ip.ts +54 -0
- package/src/js/utils/field-references.row-scope.ts +303 -0
- package/src/js/utils/field-references.ts +1 -0
- package/src/vendor.d.ts +1 -1
- package/dist/chunks/address-finder-BU4roxT6.js +0 -37
- package/dist/chunks/bpoint-uNg5j6rt.js +0 -26
- package/dist/chunks/calculations-C9t6Zop9.js +0 -147
- package/dist/chunks/captcha-eu-BAPzHNts.js +0 -34
- package/dist/chunks/checkbox-radio-CHeLNVIK.js +0 -141
- package/dist/chunks/conditions-DKxVCsjD.js +0 -459
- package/dist/chunks/date-picker-B3iQ797Z.js +0 -4994
- package/dist/chunks/eway-DCielIML.js +0 -74
- package/dist/chunks/file-upload-DP2TOUYV.js +0 -316
- package/dist/chunks/friendly-captcha-v1-DdlcGiFY.js +0 -33
- package/dist/chunks/friendly-captcha-v2-76w-YnFm.js +0 -38
- package/dist/chunks/go-cardless-rwCkzbNK.js +0 -24
- package/dist/chunks/google-address-BD8IZ86V.js +0 -181
- package/dist/chunks/hcaptcha-BNNTL6uf.js +0 -56
- package/dist/chunks/hidden-CW6Rcf1N.js +0 -31
- package/dist/chunks/index-BEsfee-D.js +0 -1839
- package/dist/chunks/index-BP7Unm5Y.js +0 -863
- package/dist/chunks/index-D6npYxvr.js +0 -4423
- package/dist/chunks/loqate-B-ZNS65B.js +0 -55
- package/dist/chunks/mollie-BDAZ-1X4.js +0 -24
- package/dist/chunks/moneris-B1b0uEtY.js +0 -101
- package/dist/chunks/opayo-CC5hraHo.js +0 -122
- package/dist/chunks/paddle-B4GmrDsq.js +0 -60
- package/dist/chunks/paypal-BFS6XsIS.js +0 -100
- package/dist/chunks/payway-DekoJead.js +0 -56
- package/dist/chunks/phone-country-Cm5YUeXI.js +0 -3765
- package/dist/chunks/place-kit-DizZWV_e.js +0 -42
- package/dist/chunks/placekit-autocomplete.esm-LAwfbPZl.js +0 -1299
- package/dist/chunks/recaptcha-enterprise-BbOvO7JZ.js +0 -71
- package/dist/chunks/recaptcha-shared-Dwg1eVFS.js +0 -15
- package/dist/chunks/recaptcha-v2-checkbox-SY3ti5iQ.js +0 -41
- package/dist/chunks/recaptcha-v2-invisible-DSmQnn6c.js +0 -59
- package/dist/chunks/recaptcha-v3-DNh7YCnR.js +0 -28
- package/dist/chunks/repeater-DM0vuFNQ.js +0 -124
- package/dist/chunks/rich-text-C8aGYp70.js +0 -304
- package/dist/chunks/scripts-CKTFtxPO.js +0 -36
- package/dist/chunks/sdk-3SicTWby.js +0 -1559
- package/dist/chunks/shared--BU5CFoL.js +0 -80
- package/dist/chunks/signature-pFhZfH7d.js +0 -550
- package/dist/chunks/snaptcha-VuOUOaLj.js +0 -8
- package/dist/chunks/square-BR9pvToy.js +0 -46
- package/dist/chunks/stripe-OHUwoV9g.js +0 -202
- package/dist/chunks/styles-C3aqgtek.js +0 -21
- package/dist/chunks/summary-Da7Ktg3N.js +0 -126
- package/dist/chunks/table-C0PmR9bI.js +0 -95
- package/dist/chunks/text-limit-mLEwZDIa.js +0 -144
- package/dist/chunks/turnstile-9tM7q_LK.js +0 -59
- package/dist/chunks/utils-BntI3W4S.js +0 -6149
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { FormieModuleDefinition } from '#contracts/modules';
|
|
2
|
+
import { getModuleFieldTarget, observeMatchingElements } from '#modules/fields/shared';
|
|
3
|
+
|
|
4
|
+
const MODULE_ID = 'custom-link';
|
|
5
|
+
const LINK_SELECTOR = '[data-formie-custom-link]';
|
|
6
|
+
|
|
7
|
+
type LinkInputAttributes = {
|
|
8
|
+
type: string;
|
|
9
|
+
inputMode: string;
|
|
10
|
+
autocomplete: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function flag(element: HTMLElement, name: string): boolean {
|
|
14
|
+
return element.getAttribute(name) === '1';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getValueInputAttributes(link: HTMLElement, type: string): LinkInputAttributes {
|
|
18
|
+
if (type === 'email') {
|
|
19
|
+
return {
|
|
20
|
+
type: 'email',
|
|
21
|
+
inputMode: 'email',
|
|
22
|
+
autocomplete: 'email',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (type === 'tel' || type === 'sms') {
|
|
27
|
+
return {
|
|
28
|
+
type: 'tel',
|
|
29
|
+
inputMode: 'tel',
|
|
30
|
+
autocomplete: 'tel',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const strictUrl = !flag(link, 'data-formie-custom-link-allow-root-relative') &&
|
|
35
|
+
!flag(link, 'data-formie-custom-link-allow-anchors') &&
|
|
36
|
+
!flag(link, 'data-formie-custom-link-allow-custom-schemes');
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
type: strictUrl ? 'url' : 'text',
|
|
40
|
+
inputMode: 'url',
|
|
41
|
+
autocomplete: 'url',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isCustomLink(element: Element): element is HTMLElement {
|
|
46
|
+
return element instanceof HTMLElement && element.matches(LINK_SELECTOR);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function initCustomLink(link: HTMLElement): () => void {
|
|
50
|
+
const typeInput = link.querySelector('[data-formie-custom-link-type]');
|
|
51
|
+
const valueInput = link.querySelector('[data-formie-custom-link-value]');
|
|
52
|
+
|
|
53
|
+
if (!(typeInput instanceof HTMLSelectElement) || !(valueInput instanceof HTMLInputElement)) {
|
|
54
|
+
return () => {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const syncValueInput = (): void => {
|
|
58
|
+
const attributes = getValueInputAttributes(link, typeInput.value);
|
|
59
|
+
valueInput.type = attributes.type;
|
|
60
|
+
valueInput.inputMode = attributes.inputMode;
|
|
61
|
+
valueInput.setAttribute('autocomplete', attributes.autocomplete);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
syncValueInput();
|
|
65
|
+
typeInput.addEventListener('change', syncValueInput);
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
typeInput.removeEventListener('change', syncValueInput);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const customLinkModule: FormieModuleDefinition = {
|
|
73
|
+
id: MODULE_ID,
|
|
74
|
+
kind: 'field',
|
|
75
|
+
match: ({ target }) => {
|
|
76
|
+
return target instanceof Element && (
|
|
77
|
+
target.matches(LINK_SELECTOR) ||
|
|
78
|
+
!!target.querySelector(LINK_SELECTOR)
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
setup: async(ctx) => {
|
|
82
|
+
const field = getModuleFieldTarget(ctx);
|
|
83
|
+
const root = field || ctx.target;
|
|
84
|
+
|
|
85
|
+
if (!(root instanceof Element)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const cleanup = observeMatchingElements(root, LINK_SELECTOR, isCustomLink, initCustomLink);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
destroy: cleanup,
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
};
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import type { FormieModuleDefinition } from '#contracts/modules';
|
|
2
|
+
import type * as Leaflet from 'leaflet';
|
|
3
|
+
import leafletCss from 'leaflet/dist/leaflet.css?inline';
|
|
4
|
+
import { getModuleFieldTarget, observeMatchingElements } from '#modules/fields/shared';
|
|
5
|
+
import { ensureModuleStyles } from '#modules/styles';
|
|
6
|
+
|
|
7
|
+
const MODULE_ID = 'custom-maps';
|
|
8
|
+
const MAPS_SELECTOR = '[data-formie-custom-maps]';
|
|
9
|
+
|
|
10
|
+
ensureModuleStyles(MODULE_ID, [leafletCss]);
|
|
11
|
+
|
|
12
|
+
type LatLng = {
|
|
13
|
+
lat: number;
|
|
14
|
+
lng: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type MapsSettings = {
|
|
18
|
+
mapTiles?: string;
|
|
19
|
+
mapToken?: string | Record<string, string>;
|
|
20
|
+
geoService?: string;
|
|
21
|
+
geoToken?: string | Record<string, string>;
|
|
22
|
+
defaultLat?: number | null;
|
|
23
|
+
defaultLng?: number | null;
|
|
24
|
+
defaultZoom?: number | null;
|
|
25
|
+
minZoom?: number | null;
|
|
26
|
+
maxZoom?: number | null;
|
|
27
|
+
country?: string | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type MapsElements = {
|
|
31
|
+
root: HTMLElement;
|
|
32
|
+
canvas: HTMLElement | null;
|
|
33
|
+
searchInput: HTMLInputElement | null;
|
|
34
|
+
addressInput: HTMLInputElement | null;
|
|
35
|
+
latInput: HTMLInputElement | null;
|
|
36
|
+
lngInput: HTMLInputElement | null;
|
|
37
|
+
zoomInput: HTMLInputElement | null;
|
|
38
|
+
partsInput: HTMLInputElement | null;
|
|
39
|
+
currentLocationButton: HTMLButtonElement | null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type NominatimResult = {
|
|
43
|
+
display_name?: string;
|
|
44
|
+
lat?: string;
|
|
45
|
+
lon?: string;
|
|
46
|
+
address?: Record<string, string | undefined>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let leafletPromise: Promise<typeof Leaflet> | null = null;
|
|
50
|
+
|
|
51
|
+
function isCustomMaps(element: Element): element is HTMLElement {
|
|
52
|
+
return element instanceof HTMLElement && element.matches(MAPS_SELECTOR);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseSettings(root: HTMLElement): MapsSettings {
|
|
56
|
+
const raw = root.getAttribute('data-formie-custom-maps-settings');
|
|
57
|
+
|
|
58
|
+
if (!raw) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(raw) as MapsSettings;
|
|
64
|
+
} catch {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function findElements(root: HTMLElement): MapsElements {
|
|
70
|
+
return {
|
|
71
|
+
root,
|
|
72
|
+
canvas: root.querySelector<HTMLElement>('[data-formie-custom-maps-canvas]'),
|
|
73
|
+
searchInput: root.querySelector<HTMLInputElement>('[data-formie-custom-maps-search]'),
|
|
74
|
+
addressInput: root.querySelector<HTMLInputElement>('[data-formie-custom-map-address]'),
|
|
75
|
+
latInput: root.querySelector<HTMLInputElement>('[data-formie-custom-map-lat]'),
|
|
76
|
+
lngInput: root.querySelector<HTMLInputElement>('[data-formie-custom-map-lng]'),
|
|
77
|
+
zoomInput: root.querySelector<HTMLInputElement>('[data-formie-custom-map-zoom]'),
|
|
78
|
+
partsInput: root.querySelector<HTMLInputElement>('[data-formie-custom-maps-parts]'),
|
|
79
|
+
currentLocationButton: root.querySelector<HTMLButtonElement>('[data-formie-custom-maps-current-location]'),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function toNumber(value: string | number | null | undefined): number | null {
|
|
84
|
+
if (value === null || value === undefined || value === '') {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const number = Number(value);
|
|
89
|
+
|
|
90
|
+
return Number.isFinite(number) ? number : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getCurrentLatLng(elements: MapsElements, settings: MapsSettings): LatLng {
|
|
94
|
+
return {
|
|
95
|
+
lat: toNumber(elements.latInput?.value) ?? settings.defaultLat ?? 51.272154,
|
|
96
|
+
lng: toNumber(elements.lngInput?.value) ?? settings.defaultLng ?? 0.514951,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getCurrentZoom(elements: MapsElements, settings: MapsSettings): number {
|
|
101
|
+
return toNumber(elements.zoomInput?.value) ?? settings.defaultZoom ?? 15;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function setValue(input: HTMLInputElement | null, value: string | number | null | undefined): void {
|
|
105
|
+
if (!input) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
input.value = value === null || value === undefined ? '' : String(value);
|
|
110
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
111
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function syncCoordinates(elements: MapsElements, latLng: LatLng, zoom?: number): void {
|
|
115
|
+
setValue(elements.latInput, latLng.lat);
|
|
116
|
+
setValue(elements.lngInput, latLng.lng);
|
|
117
|
+
|
|
118
|
+
if (zoom !== undefined) {
|
|
119
|
+
setValue(elements.zoomInput, zoom);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function loadLeaflet(): Promise<typeof Leaflet> {
|
|
124
|
+
if (!leafletPromise) {
|
|
125
|
+
leafletPromise = import('leaflet');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return leafletPromise;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getRasterTileUrl(settings: MapsSettings): string {
|
|
132
|
+
switch (settings.mapTiles) {
|
|
133
|
+
case 'openstreetmap':
|
|
134
|
+
case 'wikimedia':
|
|
135
|
+
return 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png';
|
|
136
|
+
case 'carto.light_all':
|
|
137
|
+
return 'https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
|
|
138
|
+
case 'carto.dark_all':
|
|
139
|
+
return 'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png';
|
|
140
|
+
case 'carto.rastertiles/voyager':
|
|
141
|
+
default:
|
|
142
|
+
return 'https://a.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function mapNominatimParts(address: Record<string, string | undefined> | undefined): Record<string, string> {
|
|
147
|
+
if (!address) {
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
number: address.house_number || '',
|
|
153
|
+
address: address.road || address.pedestrian || address.footway || address.path || address.neighbourhood || '',
|
|
154
|
+
city: address.city || address.town || address.village || address.suburb || '',
|
|
155
|
+
postcode: address.postcode || '',
|
|
156
|
+
county: address.county || '',
|
|
157
|
+
state: address.state || address.state_district || '',
|
|
158
|
+
country: address.country || '',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function searchNominatim(query: string, settings: MapsSettings): Promise<{ latLng: LatLng; address: string; parts: Record<string, string> } | null> {
|
|
163
|
+
const params = new URLSearchParams({
|
|
164
|
+
format: 'jsonv2',
|
|
165
|
+
limit: '1',
|
|
166
|
+
addressdetails: '1',
|
|
167
|
+
q: query,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (settings.country) {
|
|
171
|
+
params.set('countrycodes', settings.country.toLowerCase());
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const response = await fetch(`https://nominatim.openstreetmap.org/search?${params.toString()}`);
|
|
175
|
+
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const results = await response.json() as NominatimResult[];
|
|
181
|
+
const result = results[0];
|
|
182
|
+
const lat = toNumber(result?.lat);
|
|
183
|
+
const lng = toNumber(result?.lon);
|
|
184
|
+
|
|
185
|
+
if (!result || lat === null || lng === null) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
latLng: { lat, lng },
|
|
191
|
+
address: result.display_name || query,
|
|
192
|
+
parts: mapNominatimParts(result.address),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function applySearchResult(elements: MapsElements, result: { latLng: LatLng; address: string; parts: Record<string, string> }, zoom: number): void {
|
|
197
|
+
setValue(elements.addressInput, result.address);
|
|
198
|
+
setValue(elements.searchInput, result.address);
|
|
199
|
+
setValue(elements.partsInput, JSON.stringify(result.parts));
|
|
200
|
+
syncCoordinates(elements, result.latLng, zoom);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function initLeafletMap(elements: MapsElements, settings: MapsSettings): Promise<() => void> {
|
|
204
|
+
if (!elements.canvas) {
|
|
205
|
+
return () => {};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const L = await loadLeaflet();
|
|
209
|
+
const initialLatLng = getCurrentLatLng(elements, settings);
|
|
210
|
+
const initialZoom = getCurrentZoom(elements, settings);
|
|
211
|
+
const map = L.map(elements.canvas, {
|
|
212
|
+
minZoom: settings.minZoom ?? 3,
|
|
213
|
+
maxZoom: settings.maxZoom ?? 18,
|
|
214
|
+
}).setView([initialLatLng.lat, initialLatLng.lng], initialZoom);
|
|
215
|
+
const marker = L.marker([initialLatLng.lat, initialLatLng.lng], {
|
|
216
|
+
draggable: true,
|
|
217
|
+
}).addTo(map);
|
|
218
|
+
|
|
219
|
+
L.tileLayer(getRasterTileUrl(settings), {
|
|
220
|
+
maxZoom: settings.maxZoom ?? 18,
|
|
221
|
+
attribution: '© OpenStreetMap contributors, © CARTO',
|
|
222
|
+
}).addTo(map);
|
|
223
|
+
|
|
224
|
+
map.on('click', (event) => {
|
|
225
|
+
marker.setLatLng([event.latlng.lat, event.latlng.lng]);
|
|
226
|
+
syncCoordinates(elements, event.latlng, map.getZoom());
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
marker.on('dragend', (event) => {
|
|
230
|
+
const latLng = event.target.getLatLng();
|
|
231
|
+
syncCoordinates(elements, latLng, map.getZoom());
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const submitSearch = async(): Promise<void> => {
|
|
235
|
+
const query = elements.searchInput?.value.trim();
|
|
236
|
+
|
|
237
|
+
if (!query) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const result = await searchNominatim(query, settings);
|
|
242
|
+
|
|
243
|
+
if (!result) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const zoom = getCurrentZoom(elements, settings);
|
|
248
|
+
applySearchResult(elements, result, zoom);
|
|
249
|
+
map.setView([result.latLng.lat, result.latLng.lng], zoom);
|
|
250
|
+
marker.setLatLng([result.latLng.lat, result.latLng.lng]);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const onSearchKeydown = (event: KeyboardEvent): void => {
|
|
254
|
+
if (event.key !== 'Enter') {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
void submitSearch();
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const onSearchBlur = (): void => {
|
|
263
|
+
void submitSearch();
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
elements.searchInput?.addEventListener('keydown', onSearchKeydown);
|
|
267
|
+
elements.searchInput?.addEventListener('blur', onSearchBlur);
|
|
268
|
+
|
|
269
|
+
const onCurrentLocationClick = (): void => {
|
|
270
|
+
navigator.geolocation?.getCurrentPosition((position) => {
|
|
271
|
+
const latLng = {
|
|
272
|
+
lat: position.coords.latitude,
|
|
273
|
+
lng: position.coords.longitude,
|
|
274
|
+
};
|
|
275
|
+
const zoom = getCurrentZoom(elements, settings);
|
|
276
|
+
|
|
277
|
+
syncCoordinates(elements, latLng, zoom);
|
|
278
|
+
map.setView([latLng.lat, latLng.lng], zoom);
|
|
279
|
+
marker.setLatLng([latLng.lat, latLng.lng]);
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
elements.currentLocationButton?.addEventListener('click', onCurrentLocationClick);
|
|
284
|
+
|
|
285
|
+
return () => {
|
|
286
|
+
elements.searchInput?.removeEventListener('keydown', onSearchKeydown);
|
|
287
|
+
elements.searchInput?.removeEventListener('blur', onSearchBlur);
|
|
288
|
+
elements.currentLocationButton?.removeEventListener('click', onCurrentLocationClick);
|
|
289
|
+
map.remove();
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function initCustomMaps(root: HTMLElement): () => void {
|
|
294
|
+
const elements = findElements(root);
|
|
295
|
+
const settings = parseSettings(root);
|
|
296
|
+
let cleanup: (() => void) | null = null;
|
|
297
|
+
let destroyed = false;
|
|
298
|
+
|
|
299
|
+
if (root.getAttribute('data-formie-custom-map-hide-map') !== '1') {
|
|
300
|
+
void initLeafletMap(elements, settings).then((destroy) => {
|
|
301
|
+
if (destroyed) {
|
|
302
|
+
destroy();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
cleanup = destroy;
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return () => {
|
|
311
|
+
destroyed = true;
|
|
312
|
+
cleanup?.();
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export const customMapsModule: FormieModuleDefinition = {
|
|
317
|
+
id: MODULE_ID,
|
|
318
|
+
kind: 'field',
|
|
319
|
+
match: ({ target }) => {
|
|
320
|
+
return target instanceof Element && (
|
|
321
|
+
target.matches(MAPS_SELECTOR) ||
|
|
322
|
+
!!target.querySelector(MAPS_SELECTOR)
|
|
323
|
+
);
|
|
324
|
+
},
|
|
325
|
+
setup: async(ctx) => {
|
|
326
|
+
const field = getModuleFieldTarget(ctx);
|
|
327
|
+
const root = field || ctx.target;
|
|
328
|
+
|
|
329
|
+
if (!(root instanceof Element)) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const cleanup = observeMatchingElements(root, MAPS_SELECTOR, isCustomMaps, initCustomMaps);
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
destroy: cleanup,
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
};
|
|
@@ -8,18 +8,20 @@ import { ensureModuleStyles } from '#modules/styles';
|
|
|
8
8
|
import { createDebug } from '#utils/debug';
|
|
9
9
|
|
|
10
10
|
const INPUT_SELECTOR = 'input[data-formie-date-datepicker-input]';
|
|
11
|
+
const RANGE_START_SELECTOR = 'input[data-formie-date-range-start-input]';
|
|
12
|
+
const RANGE_END_SELECTOR = 'input[data-formie-date-range-end-input]';
|
|
11
13
|
const MODULE_ID = 'date-picker';
|
|
12
14
|
const debug = createDebug('fields', 'date-picker');
|
|
13
15
|
|
|
14
|
-
ensureModuleStyles(MODULE_ID, [flatpickrCss]);
|
|
15
|
-
|
|
16
16
|
type DatePickerOptions = {
|
|
17
|
+
includeFlatpickrCss?: boolean;
|
|
17
18
|
datePickerOptions?: Array<{ label?: string; value?: unknown }>;
|
|
18
19
|
dateFormat?: string;
|
|
19
20
|
timeFormat?: string;
|
|
20
21
|
getIsDate?: boolean;
|
|
21
22
|
getIsTime?: boolean;
|
|
22
23
|
getIsDateTime?: boolean;
|
|
24
|
+
collectMode?: string;
|
|
23
25
|
locale?: string;
|
|
24
26
|
minDate?: string | null;
|
|
25
27
|
maxDate?: string | null;
|
|
@@ -34,6 +36,11 @@ type FlatpickrInput = HTMLInputElement & {
|
|
|
34
36
|
_formieFlatpickr?: FlatpickrInstanceLike;
|
|
35
37
|
};
|
|
36
38
|
|
|
39
|
+
type FlatpickrChangeInstance = {
|
|
40
|
+
input: HTMLInputElement;
|
|
41
|
+
altInput?: HTMLInputElement;
|
|
42
|
+
};
|
|
43
|
+
|
|
37
44
|
function attributesPlugin() {
|
|
38
45
|
return (instance: { input: HTMLInputElement; altInput?: HTMLInputElement | undefined; loadedPlugins: string[] }) => {
|
|
39
46
|
return {
|
|
@@ -168,9 +175,61 @@ function getCustomOptions(options: DatePickerOptions): Record<string, unknown> {
|
|
|
168
175
|
return result;
|
|
169
176
|
}
|
|
170
177
|
|
|
178
|
+
function pad(value: number): string {
|
|
179
|
+
return String(value).padStart(2, '0');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function formatSubmissionDate(date: Date, options: DatePickerOptions): string {
|
|
183
|
+
const year = date.getFullYear();
|
|
184
|
+
const month = pad(date.getMonth() + 1);
|
|
185
|
+
const day = pad(date.getDate());
|
|
186
|
+
|
|
187
|
+
if (!options.getIsTime && !options.getIsDateTime) {
|
|
188
|
+
return `${year}-${month}-${day}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const hours = pad(date.getHours());
|
|
192
|
+
const minutes = pad(date.getMinutes());
|
|
193
|
+
const seconds = pad(date.getSeconds());
|
|
194
|
+
|
|
195
|
+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function parseSubmissionDate(value: string): Date | null {
|
|
199
|
+
const parsed = new Date(value);
|
|
200
|
+
|
|
201
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return parsed;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function syncRangeHiddenInputs(
|
|
209
|
+
selectedDates: Date[],
|
|
210
|
+
startInput: HTMLInputElement | null,
|
|
211
|
+
endInput: HTMLInputElement | null,
|
|
212
|
+
options: DatePickerOptions,
|
|
213
|
+
): void {
|
|
214
|
+
if (startInput) {
|
|
215
|
+
startInput.value = selectedDates[0] ? formatSubmissionDate(selectedDates[0], options) : '';
|
|
216
|
+
startInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (endInput) {
|
|
220
|
+
endInput.value = selectedDates[1] ? formatSubmissionDate(selectedDates[1], options) : '';
|
|
221
|
+
endInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
171
225
|
function initDatePicker(input: FlatpickrInput, options: DatePickerOptions): () => void {
|
|
172
226
|
input._formieFlatpickr?.destroy();
|
|
173
227
|
|
|
228
|
+
const fieldContainer = input.closest('[data-formie-field-handle]');
|
|
229
|
+
const isRange = options.collectMode === 'range' || input.hasAttribute('data-formie-date-range-input');
|
|
230
|
+
const startInput = fieldContainer?.querySelector(RANGE_START_SELECTOR);
|
|
231
|
+
const endInput = fieldContainer?.querySelector(RANGE_END_SELECTOR);
|
|
232
|
+
|
|
174
233
|
const defaultOptions: Record<string, unknown> = {
|
|
175
234
|
disableMobile: true,
|
|
176
235
|
allowInput: true,
|
|
@@ -183,7 +242,16 @@ function initDatePicker(input: FlatpickrInput, options: DatePickerOptions): () =
|
|
|
183
242
|
maxDate: normalizeOffsetDate(options.maxDate, 'max'),
|
|
184
243
|
plugins: [attributesPlugin()],
|
|
185
244
|
locale: getLocale(options.locale),
|
|
186
|
-
onChange: (
|
|
245
|
+
onChange: (selectedDates: Date[], _dateStr: string, instance: FlatpickrChangeInstance) => {
|
|
246
|
+
if (isRange) {
|
|
247
|
+
syncRangeHiddenInputs(
|
|
248
|
+
selectedDates,
|
|
249
|
+
startInput instanceof HTMLInputElement ? startInput : null,
|
|
250
|
+
endInput instanceof HTMLInputElement ? endInput : null,
|
|
251
|
+
options,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
187
255
|
// Mirror changes back through bubbling input events so validation,
|
|
188
256
|
// conditions, and calculations respond to picker-driven updates.
|
|
189
257
|
instance.input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
@@ -204,11 +272,30 @@ function initDatePicker(input: FlatpickrInput, options: DatePickerOptions): () =
|
|
|
204
272
|
defaultOptions.noCalendar = true;
|
|
205
273
|
}
|
|
206
274
|
|
|
275
|
+
if (isRange) {
|
|
276
|
+
defaultOptions.mode = 'range';
|
|
277
|
+
|
|
278
|
+
const startDate = startInput instanceof HTMLInputElement && startInput.value
|
|
279
|
+
? parseSubmissionDate(startInput.value)
|
|
280
|
+
: null;
|
|
281
|
+
const endDate = endInput instanceof HTMLInputElement && endInput.value
|
|
282
|
+
? parseSubmissionDate(endInput.value)
|
|
283
|
+
: null;
|
|
284
|
+
|
|
285
|
+
if (startDate) {
|
|
286
|
+
defaultOptions.defaultDate = endDate ? [startDate, endDate] : [startDate];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
207
290
|
const mergedOptions = {
|
|
208
291
|
...defaultOptions,
|
|
209
292
|
...getCustomOptions(options),
|
|
210
293
|
};
|
|
211
294
|
|
|
295
|
+
if (isRange) {
|
|
296
|
+
mergedOptions.mode = 'range';
|
|
297
|
+
}
|
|
298
|
+
|
|
212
299
|
dispatchFieldEvent(input, MODULE_ID, 'before-init', {
|
|
213
300
|
datepicker: input,
|
|
214
301
|
options: mergedOptions,
|
|
@@ -218,6 +305,7 @@ function initDatePicker(input: FlatpickrInput, options: DatePickerOptions): () =
|
|
|
218
305
|
input._formieFlatpickr = instance;
|
|
219
306
|
debug.log('Initialized.', {
|
|
220
307
|
inputName: input.name,
|
|
308
|
+
isRange,
|
|
221
309
|
});
|
|
222
310
|
|
|
223
311
|
dispatchFieldEvent(input, MODULE_ID, 'after-init', {
|
|
@@ -242,6 +330,11 @@ export const datePickerModule: FormieModuleDefinition = {
|
|
|
242
330
|
},
|
|
243
331
|
setup: async (ctx) => {
|
|
244
332
|
const options = (ctx.options || {}) as DatePickerOptions;
|
|
333
|
+
|
|
334
|
+
if (options.includeFlatpickrCss !== false) {
|
|
335
|
+
ensureModuleStyles(MODULE_ID, [flatpickrCss]);
|
|
336
|
+
}
|
|
337
|
+
|
|
245
338
|
const fields = getModuleFieldContainers(ctx);
|
|
246
339
|
const cleanups = fields.map((field) => {
|
|
247
340
|
const input = field.querySelector(INPUT_SELECTOR);
|
|
@@ -5,11 +5,18 @@ export const builtinFieldModuleLoaders: Record<string, () => Promise<FormieModul
|
|
|
5
5
|
// truth for lazy-loading first-party field enhancements.
|
|
6
6
|
'calculations': () => import('#modules/fields/calculations').then((module) => module.calculationsModule),
|
|
7
7
|
'checkbox-radio': () => import('#modules/fields/checkbox-radio').then((module) => module.checkboxRadioModule),
|
|
8
|
+
'combobox': () => import('#modules/fields/combobox').then((module) => module.comboboxModule),
|
|
8
9
|
'conditions': () => import('#modules/fields/conditions').then((module) => module.conditionsModule),
|
|
10
|
+
'custom-google-maps': () => import('#modules/fields/custom-google-maps').then((module) => module.customGoogleMapsModule),
|
|
11
|
+
'custom-link': () => import('#modules/fields/custom-link').then((module) => module.customLinkModule),
|
|
12
|
+
'custom-maps': () => import('#modules/fields/custom-maps').then((module) => module.customMapsModule),
|
|
9
13
|
'date-picker': () => import('#modules/fields/date-picker').then((module) => module.datePickerModule),
|
|
10
14
|
'file-upload': () => import('#modules/fields/file-upload').then((module) => module.fileUploadModule),
|
|
15
|
+
'upload-manager': () => import('#modules/fields/upload-manager').then((module) => module.uploadManagerModule),
|
|
11
16
|
'hidden': () => import('#modules/fields/hidden').then((module) => module.hiddenModule),
|
|
12
17
|
'phone-country': () => import('#modules/fields/phone-country').then((module) => module.phoneCountryModule),
|
|
18
|
+
'address-country': () => import('#modules/fields/address-country').then((module) => module.addressCountryModule),
|
|
19
|
+
'address-state': () => import('#modules/fields/address-state').then((module) => module.addressStateModule),
|
|
13
20
|
'repeater': () => import('#modules/fields/repeater').then((module) => module.repeaterModule),
|
|
14
21
|
'rich-text': () => import('#modules/fields/rich-text').then((module) => module.richTextModule),
|
|
15
22
|
'signature': () => import('#modules/fields/signature').then((module) => module.signatureModule),
|
|
@@ -4,6 +4,7 @@ import intlTelInputCss from 'intl-tel-input/styles?inline';
|
|
|
4
4
|
import type { FormieModuleDefinition } from '#contracts/modules';
|
|
5
5
|
import { dispatchFieldEvent, getModuleFieldContainers, releaseFormValidators, retainFormValidators } from '#modules/fields/shared';
|
|
6
6
|
import { ensureModuleStyles } from '#modules/styles';
|
|
7
|
+
import { createGeoIpLookup } from '#utils/country-from-ip';
|
|
7
8
|
import { createDebug } from '#utils/debug';
|
|
8
9
|
|
|
9
10
|
const PHONE_SELECTOR = 'input[type="tel"][data-formie-phone-input]';
|
|
@@ -18,6 +19,8 @@ ensureModuleStyles(MODULE_ID, [intlTelInputCss]);
|
|
|
18
19
|
type PhoneCountryOptions = {
|
|
19
20
|
countryDefaultValue?: string;
|
|
20
21
|
countryAllowed?: string[];
|
|
22
|
+
countryPreselectFromIp?: boolean;
|
|
23
|
+
countryFromIpAction?: string;
|
|
21
24
|
language?: string;
|
|
22
25
|
};
|
|
23
26
|
|
|
@@ -111,6 +114,9 @@ function buildOptions(options: PhoneCountryOptions): Record<string, unknown> {
|
|
|
111
114
|
|
|
112
115
|
if (options.countryDefaultValue) {
|
|
113
116
|
intlOptions.initialCountry = options.countryDefaultValue.toLowerCase();
|
|
117
|
+
} else if (options.countryPreselectFromIp) {
|
|
118
|
+
intlOptions.initialCountry = 'auto';
|
|
119
|
+
intlOptions.geoIpLookup = createGeoIpLookup(options.countryFromIpAction);
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
if (Array.isArray(intlOptions.onlyCountries) && typeof intlOptions.initialCountry === 'string') {
|