@modernman00/shared-js-lib 1.2.82 → 1.2.87
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/AddressAutocomplete.js +473 -0
- package/CardCanvasGenerator.js +367 -0
- package/OfflineQueue.js +278 -0
- package/OtpInputManager.js +429 -0
- package/PwaInstall.js +165 -0
- package/PwaManager.js +342 -0
- package/ShareManager.js +462 -0
- package/config/version.php +1 -1
- package/index.js +9 -1
- package/package.json +12 -3
- package/release-history.md +37 -0
- package/templates/sw.template.js +239 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Address & Venue Autocomplete Engine
|
|
3
|
+
* Zero-cost hybrid geocoding using OpenStreetMap (Photon) & Postcodes.io
|
|
4
|
+
*
|
|
5
|
+
* Part of @modernman00/shared-js-lib
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const UK_POSTCODE_REGEX = /^[A-Z]{1,2}[0-9][A-Z0-9]?(\s*[0-9][A-Z]{2})?$/i;
|
|
9
|
+
const UK_FULL_POSTCODE_REGEX = /^[A-Z]{1,2}[0-9][A-Z0-9]?\s*[0-9][A-Z]{2}$/i;
|
|
10
|
+
|
|
11
|
+
export class AddressAutocomplete {
|
|
12
|
+
/**
|
|
13
|
+
* @param {Object} options
|
|
14
|
+
* @param {string|HTMLElement} options.input - Target input element or selector
|
|
15
|
+
* @param {string|HTMLElement} [options.dropdown] - Dropdown container element or selector (optional)
|
|
16
|
+
* @param {string|HTMLElement} [options.spinner] - Spinner element or selector (optional)
|
|
17
|
+
* @param {string|HTMLElement} [options.errorElement] - Error container to clear on select (optional)
|
|
18
|
+
* @param {string} [options.themeColor='#7b03fc'] - Primary highlight color
|
|
19
|
+
* @param {string} [options.country='uk'] - Target country ('uk' prioritizes UK PAF/postcode formats)
|
|
20
|
+
* @param {Function} [options.onSelect] - Callback when an address is selected
|
|
21
|
+
* @param {boolean} [options.injectStyles=true] - Auto-inject component CSS
|
|
22
|
+
*/
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.options = Object.assign({
|
|
25
|
+
themeColor: '#7b03fc',
|
|
26
|
+
country: 'uk',
|
|
27
|
+
injectStyles: true,
|
|
28
|
+
onSelect: null,
|
|
29
|
+
errorElement: null,
|
|
30
|
+
}, options);
|
|
31
|
+
|
|
32
|
+
this.input = typeof this.options.input === 'string'
|
|
33
|
+
? document.querySelector(this.options.input)
|
|
34
|
+
: this.options.input;
|
|
35
|
+
|
|
36
|
+
if (!this.input) {
|
|
37
|
+
console.warn('[AddressAutocomplete] Target input element not found.');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.dropdown = this.options.dropdown
|
|
42
|
+
? (typeof this.options.dropdown === 'string' ? document.querySelector(this.options.dropdown) : this.options.dropdown)
|
|
43
|
+
: null;
|
|
44
|
+
|
|
45
|
+
this.spinner = this.options.spinner
|
|
46
|
+
? (typeof this.options.spinner === 'string' ? document.querySelector(this.options.spinner) : this.options.spinner)
|
|
47
|
+
: null;
|
|
48
|
+
|
|
49
|
+
this.errorElement = this.options.errorElement
|
|
50
|
+
? (typeof this.options.errorElement === 'string' ? document.querySelector(this.options.errorElement) : this.options.errorElement)
|
|
51
|
+
: null;
|
|
52
|
+
|
|
53
|
+
this.debounceTimer = null;
|
|
54
|
+
this.abortController = null;
|
|
55
|
+
this.activeIndex = -1;
|
|
56
|
+
this.currentItems = [];
|
|
57
|
+
|
|
58
|
+
this._boundInput = this.handleInput.bind(this);
|
|
59
|
+
this._boundKeydown = this.handleKeydown.bind(this);
|
|
60
|
+
this._boundDocClick = this.handleDocumentClick.bind(this);
|
|
61
|
+
|
|
62
|
+
if (this.options.injectStyles) {
|
|
63
|
+
this.injectStyles();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.setupDropdown();
|
|
67
|
+
this.bindEvents();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
injectStyles() {
|
|
71
|
+
if (typeof document === 'undefined') return;
|
|
72
|
+
if (document.getElementById('shared-address-autocomplete-styles')) return;
|
|
73
|
+
|
|
74
|
+
const style = document.createElement('style');
|
|
75
|
+
style.id = 'shared-address-autocomplete-styles';
|
|
76
|
+
style.textContent = `
|
|
77
|
+
.shared-loc-autocomplete-menu {
|
|
78
|
+
position: absolute;
|
|
79
|
+
top: calc(100% + 6px);
|
|
80
|
+
left: 0;
|
|
81
|
+
right: 0;
|
|
82
|
+
background: rgba(255, 255, 255, 0.98);
|
|
83
|
+
backdrop-filter: blur(16px);
|
|
84
|
+
-webkit-backdrop-filter: blur(16px);
|
|
85
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
86
|
+
border-radius: 18px;
|
|
87
|
+
box-shadow: 0 20px 45px -10px rgba(0, 0, 0, 0.15), 0 4px 16px rgba(0, 0, 0, 0.05);
|
|
88
|
+
z-index: 1050;
|
|
89
|
+
max-height: 290px;
|
|
90
|
+
overflow-y: auto;
|
|
91
|
+
padding: 0.5rem;
|
|
92
|
+
margin: 0;
|
|
93
|
+
list-style: none;
|
|
94
|
+
animation: sharedFadeInLoc 0.2s ease-out;
|
|
95
|
+
}
|
|
96
|
+
@keyframes sharedFadeInLoc {
|
|
97
|
+
from { opacity: 0; transform: translateY(-6px); }
|
|
98
|
+
to { opacity: 1; transform: translateY(0); }
|
|
99
|
+
}
|
|
100
|
+
.shared-loc-item {
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
gap: 0.75rem;
|
|
104
|
+
padding: 0.7rem 0.95rem;
|
|
105
|
+
border-radius: 12px;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
transition: all 0.15s ease;
|
|
108
|
+
border: 1px solid transparent;
|
|
109
|
+
}
|
|
110
|
+
.shared-loc-item:hover,
|
|
111
|
+
.shared-loc-item.active {
|
|
112
|
+
background: rgba(123, 3, 252, 0.08);
|
|
113
|
+
border-color: rgba(123, 3, 252, 0.2);
|
|
114
|
+
}
|
|
115
|
+
.shared-loc-item .shared-loc-icon {
|
|
116
|
+
width: 32px;
|
|
117
|
+
height: 32px;
|
|
118
|
+
border-radius: 10px;
|
|
119
|
+
background: #f1f5f9;
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
font-size: 1.05rem;
|
|
124
|
+
flex-shrink: 0;
|
|
125
|
+
}
|
|
126
|
+
.shared-loc-item .shared-loc-text {
|
|
127
|
+
flex-grow: 1;
|
|
128
|
+
min-width: 0;
|
|
129
|
+
}
|
|
130
|
+
.shared-loc-item .shared-loc-title {
|
|
131
|
+
font-weight: 600;
|
|
132
|
+
font-size: 0.92rem;
|
|
133
|
+
color: #0f172a;
|
|
134
|
+
white-space: nowrap;
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
text-overflow: ellipsis;
|
|
137
|
+
}
|
|
138
|
+
.shared-loc-item .shared-loc-sub {
|
|
139
|
+
font-size: 0.78rem;
|
|
140
|
+
color: #64748b;
|
|
141
|
+
white-space: nowrap;
|
|
142
|
+
overflow: hidden;
|
|
143
|
+
text-overflow: ellipsis;
|
|
144
|
+
}
|
|
145
|
+
.shared-loc-footer {
|
|
146
|
+
display: flex;
|
|
147
|
+
justify-content: space-between;
|
|
148
|
+
align-items: center;
|
|
149
|
+
padding: 0.45rem 0.75rem 0.2rem;
|
|
150
|
+
font-size: 0.72rem;
|
|
151
|
+
color: #94a3b8;
|
|
152
|
+
border-top: 1px solid #f1f5f9;
|
|
153
|
+
margin-top: 0.35rem;
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
156
|
+
document.head.appendChild(style);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
setupDropdown() {
|
|
160
|
+
if (!this.dropdown && this.input.parentElement) {
|
|
161
|
+
const dd = document.createElement('div');
|
|
162
|
+
dd.className = 'shared-loc-autocomplete-menu';
|
|
163
|
+
dd.style.display = 'none';
|
|
164
|
+
dd.setAttribute('role', 'listbox');
|
|
165
|
+
dd.setAttribute('aria-label', 'Address suggestions');
|
|
166
|
+
this.input.parentElement.appendChild(dd);
|
|
167
|
+
this.dropdown = dd;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
bindEvents() {
|
|
172
|
+
this.input.setAttribute('autocomplete', 'off');
|
|
173
|
+
this.input.addEventListener('input', this._boundInput);
|
|
174
|
+
this.input.addEventListener('keydown', this._boundKeydown);
|
|
175
|
+
document.addEventListener('click', this._boundDocClick);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
showSpinner(show) {
|
|
179
|
+
if (this.spinner) {
|
|
180
|
+
this.spinner.style.display = show ? 'block' : 'none';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
close() {
|
|
185
|
+
if (this.dropdown) {
|
|
186
|
+
this.dropdown.style.display = 'none';
|
|
187
|
+
this.dropdown.innerHTML = '';
|
|
188
|
+
}
|
|
189
|
+
this.currentItems = [];
|
|
190
|
+
this.activeIndex = -1;
|
|
191
|
+
this.showSpinner(false);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
handleInput() {
|
|
195
|
+
clearTimeout(this.debounceTimer);
|
|
196
|
+
const query = this.input.value;
|
|
197
|
+
this.debounceTimer = setTimeout(() => {
|
|
198
|
+
this.search(query);
|
|
199
|
+
}, 300);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
handleKeydown(e) {
|
|
203
|
+
if (!this.dropdown || this.dropdown.style.display === 'none' || this.currentItems.length === 0) return;
|
|
204
|
+
|
|
205
|
+
if (e.key === 'ArrowDown') {
|
|
206
|
+
e.preventDefault();
|
|
207
|
+
this.activeIndex = (this.activeIndex + 1) % this.currentItems.length;
|
|
208
|
+
this.updateActiveSelection();
|
|
209
|
+
} else if (e.key === 'ArrowUp') {
|
|
210
|
+
e.preventDefault();
|
|
211
|
+
this.activeIndex = (this.activeIndex - 1 + this.currentItems.length) % this.currentItems.length;
|
|
212
|
+
this.updateActiveSelection();
|
|
213
|
+
} else if (e.key === 'Enter') {
|
|
214
|
+
if (this.activeIndex >= 0 && this.activeIndex < this.currentItems.length) {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
e.stopPropagation();
|
|
217
|
+
this.selectItem(this.currentItems[this.activeIndex]);
|
|
218
|
+
}
|
|
219
|
+
} else if (e.key === 'Escape') {
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
this.close();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
handleDocumentClick(e) {
|
|
226
|
+
if (!this.input.contains(e.target) && this.dropdown && !this.dropdown.contains(e.target)) {
|
|
227
|
+
this.close();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
updateActiveSelection() {
|
|
232
|
+
if (!this.dropdown) return;
|
|
233
|
+
const rows = this.dropdown.querySelectorAll('.shared-loc-item, .location-autocomplete-item');
|
|
234
|
+
rows.forEach((r, idx) => {
|
|
235
|
+
if (idx === this.activeIndex) {
|
|
236
|
+
r.classList.add('active');
|
|
237
|
+
if (typeof r.scrollIntoView === 'function') {
|
|
238
|
+
r.scrollIntoView({ block: 'nearest' });
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
r.classList.remove('active');
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
selectItem(item) {
|
|
247
|
+
if (!item) return;
|
|
248
|
+
this.input.value = item.fullAddress;
|
|
249
|
+
this.close();
|
|
250
|
+
|
|
251
|
+
if (this.errorElement) {
|
|
252
|
+
this.errorElement.style.display = 'none';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (typeof this.options.onSelect === 'function') {
|
|
256
|
+
this.options.onSelect(item);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Dispatch native change & input events for frameworks
|
|
260
|
+
this.input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
render(items) {
|
|
264
|
+
if (!this.dropdown) return;
|
|
265
|
+
this.dropdown.innerHTML = '';
|
|
266
|
+
this.currentItems = items || [];
|
|
267
|
+
this.activeIndex = -1;
|
|
268
|
+
|
|
269
|
+
if (!items || items.length === 0) {
|
|
270
|
+
this.close();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
items.forEach((item, index) => {
|
|
275
|
+
const row = document.createElement('div');
|
|
276
|
+
row.className = 'shared-loc-item location-autocomplete-item';
|
|
277
|
+
row.setAttribute('role', 'option');
|
|
278
|
+
row.dataset.index = String(index);
|
|
279
|
+
|
|
280
|
+
const iconDiv = document.createElement('div');
|
|
281
|
+
iconDiv.className = 'shared-loc-icon loc-icon';
|
|
282
|
+
iconDiv.textContent = item.icon || '📍';
|
|
283
|
+
|
|
284
|
+
const textDiv = document.createElement('div');
|
|
285
|
+
textDiv.className = 'shared-loc-text loc-text';
|
|
286
|
+
|
|
287
|
+
const titleDiv = document.createElement('div');
|
|
288
|
+
titleDiv.className = 'shared-loc-title loc-title';
|
|
289
|
+
titleDiv.textContent = item.title;
|
|
290
|
+
|
|
291
|
+
const subDiv = document.createElement('div');
|
|
292
|
+
subDiv.className = 'shared-loc-sub loc-sub';
|
|
293
|
+
subDiv.textContent = item.subtitle;
|
|
294
|
+
|
|
295
|
+
textDiv.appendChild(titleDiv);
|
|
296
|
+
textDiv.appendChild(subDiv);
|
|
297
|
+
row.appendChild(iconDiv);
|
|
298
|
+
row.appendChild(textDiv);
|
|
299
|
+
|
|
300
|
+
row.addEventListener('click', () => {
|
|
301
|
+
this.selectItem(item);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
this.dropdown.appendChild(row);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const footer = document.createElement('div');
|
|
308
|
+
footer.className = 'shared-loc-footer location-autocomplete-footer';
|
|
309
|
+
|
|
310
|
+
const leftBadge = document.createElement('span');
|
|
311
|
+
leftBadge.textContent = '✨ Address & Venue Finder';
|
|
312
|
+
const rightBadge = document.createElement('span');
|
|
313
|
+
rightBadge.textContent = 'Free · OpenStreetMap & Postcodes.io';
|
|
314
|
+
|
|
315
|
+
footer.appendChild(leftBadge);
|
|
316
|
+
footer.appendChild(rightBadge);
|
|
317
|
+
this.dropdown.appendChild(footer);
|
|
318
|
+
|
|
319
|
+
this.dropdown.style.display = 'block';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async fetchPostcodes(query, signal) {
|
|
323
|
+
const cleanQuery = query.trim().toUpperCase().replace(/\s+/g, '');
|
|
324
|
+
if (UK_FULL_POSTCODE_REGEX.test(query.trim())) {
|
|
325
|
+
try {
|
|
326
|
+
const res = await fetch(`https://api.postcodes.io/postcodes/${encodeURIComponent(cleanQuery)}`, { signal });
|
|
327
|
+
if (res.ok) {
|
|
328
|
+
const data = await res.json();
|
|
329
|
+
if (data.status === 200 && data.result) {
|
|
330
|
+
const r = data.result;
|
|
331
|
+
const subParts = [r.admin_district, r.region, 'United Kingdom'].filter(Boolean);
|
|
332
|
+
const fullParts = [r.postcode, r.admin_ward, r.admin_district, r.region, 'UK'].filter(Boolean);
|
|
333
|
+
return [{
|
|
334
|
+
icon: '📮',
|
|
335
|
+
title: r.postcode,
|
|
336
|
+
subtitle: subParts.join(', '),
|
|
337
|
+
fullAddress: fullParts.join(', '),
|
|
338
|
+
postcode: r.postcode,
|
|
339
|
+
city: r.admin_district || '',
|
|
340
|
+
country: 'UK',
|
|
341
|
+
raw: r
|
|
342
|
+
}];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} catch (e) {
|
|
346
|
+
// Ignored or aborted
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const autoRes = await fetch(`https://api.postcodes.io/postcodes/${encodeURIComponent(cleanQuery)}/autocomplete`, { signal });
|
|
352
|
+
if (autoRes.ok) {
|
|
353
|
+
const autoData = await autoRes.json();
|
|
354
|
+
if (autoData.status === 200 && Array.isArray(autoData.result)) {
|
|
355
|
+
return autoData.result.slice(0, 5).map(pc => ({
|
|
356
|
+
icon: '📮',
|
|
357
|
+
title: pc,
|
|
358
|
+
subtitle: 'UK Postcode',
|
|
359
|
+
fullAddress: pc + ', UK',
|
|
360
|
+
postcode: pc,
|
|
361
|
+
city: '',
|
|
362
|
+
country: 'UK',
|
|
363
|
+
raw: { postcode: pc }
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch (e) {
|
|
368
|
+
// Ignored or aborted
|
|
369
|
+
}
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async fetchPhotonVenues(query, signal) {
|
|
374
|
+
const bbox = '-7.57,49.96,1.68,58.64';
|
|
375
|
+
const url = `https://photon.komoot.io/api/?q=${encodeURIComponent(query)}&limit=6&lang=en&bbox=${bbox}`;
|
|
376
|
+
try {
|
|
377
|
+
const res = await fetch(url, { signal });
|
|
378
|
+
if (!res.ok) return [];
|
|
379
|
+
const data = await res.json();
|
|
380
|
+
if (!data.features || !Array.isArray(data.features)) return [];
|
|
381
|
+
|
|
382
|
+
return data.features.map(f => {
|
|
383
|
+
const p = f.properties || {};
|
|
384
|
+
const name = p.name || p.street || p.city || query;
|
|
385
|
+
const subParts = [
|
|
386
|
+
p.housenumber ? p.housenumber + ' ' + (p.street || '') : p.street,
|
|
387
|
+
p.district,
|
|
388
|
+
p.city || p.town || p.village,
|
|
389
|
+
p.postcode,
|
|
390
|
+
p.country || 'UK'
|
|
391
|
+
].filter(Boolean);
|
|
392
|
+
|
|
393
|
+
const isVenue = !p.housenumber && (p.osm_value === 'amenity' || p.osm_value === 'tourism' || p.osm_value === 'leisure' || p.osm_key === 'amenity');
|
|
394
|
+
const fullParts = [name, ...subParts.filter(s => s !== name)];
|
|
395
|
+
const fullAddress = fullParts.filter(Boolean).join(', ');
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
icon: isVenue ? '🏛️' : '📍',
|
|
399
|
+
title: name,
|
|
400
|
+
subtitle: subParts.join(', '),
|
|
401
|
+
fullAddress: fullAddress,
|
|
402
|
+
postcode: p.postcode || '',
|
|
403
|
+
city: p.city || p.town || '',
|
|
404
|
+
country: p.country || 'UK',
|
|
405
|
+
raw: f
|
|
406
|
+
};
|
|
407
|
+
});
|
|
408
|
+
} catch (e) {
|
|
409
|
+
return [];
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async search(query) {
|
|
414
|
+
const trimmed = (query || '').trim();
|
|
415
|
+
if (trimmed.length < 2) {
|
|
416
|
+
this.close();
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (this.abortController) {
|
|
421
|
+
this.abortController.abort();
|
|
422
|
+
}
|
|
423
|
+
this.abortController = new AbortController();
|
|
424
|
+
const signal = this.abortController.signal;
|
|
425
|
+
|
|
426
|
+
this.showSpinner(true);
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
let results = [];
|
|
430
|
+
if (this.options.country === 'uk' && UK_POSTCODE_REGEX.test(trimmed)) {
|
|
431
|
+
results = await this.fetchPostcodes(trimmed, signal);
|
|
432
|
+
if (results.length < 3) {
|
|
433
|
+
const venueResults = await this.fetchPhotonVenues(trimmed, signal);
|
|
434
|
+
results = [...results, ...venueResults].slice(0, 6);
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
results = await this.fetchPhotonVenues(trimmed, signal);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
this.showSpinner(false);
|
|
441
|
+
this.render(results);
|
|
442
|
+
} catch (err) {
|
|
443
|
+
if (err.name !== 'AbortError') {
|
|
444
|
+
this.showSpinner(false);
|
|
445
|
+
this.close();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
destroy() {
|
|
451
|
+
clearTimeout(this.debounceTimer);
|
|
452
|
+
if (this.abortController) {
|
|
453
|
+
this.abortController.abort();
|
|
454
|
+
}
|
|
455
|
+
if (this.input) {
|
|
456
|
+
this.input.removeEventListener('input', this._boundInput);
|
|
457
|
+
this.input.removeEventListener('keydown', this._boundKeydown);
|
|
458
|
+
}
|
|
459
|
+
document.removeEventListener('click', this._boundDocClick);
|
|
460
|
+
if (this.dropdown && this.dropdown.parentElement) {
|
|
461
|
+
this.dropdown.remove();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Functional helper for easy one-line attachment
|
|
468
|
+
* @param {Object} options
|
|
469
|
+
* @returns {AddressAutocomplete}
|
|
470
|
+
*/
|
|
471
|
+
export function setupAddressAutocomplete(options) {
|
|
472
|
+
return new AddressAutocomplete(options);
|
|
473
|
+
}
|