@pro6pp/infer-react 0.0.2-beta.8 → 0.1.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/index.cjs +650 -203
- package/dist/index.d.cts +31 -9
- package/dist/index.d.ts +31 -9
- package/dist/index.js +658 -204
- package/dist/styles.css +203 -0
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -3,17 +3,120 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
|
|
5
5
|
// src/index.tsx
|
|
6
|
-
import React, {
|
|
6
|
+
import React, {
|
|
7
|
+
useState,
|
|
8
|
+
useMemo,
|
|
9
|
+
useEffect,
|
|
10
|
+
useRef,
|
|
11
|
+
forwardRef,
|
|
12
|
+
useImperativeHandle
|
|
13
|
+
} from "react";
|
|
14
|
+
|
|
15
|
+
// ../core/src/label-formatter.ts
|
|
16
|
+
function normalize(str) {
|
|
17
|
+
return str.toLowerCase().trim();
|
|
18
|
+
}
|
|
19
|
+
function escapeRegex(str) {
|
|
20
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
21
|
+
}
|
|
22
|
+
function findWordPosition(query, value) {
|
|
23
|
+
const normalizedQuery = normalize(query);
|
|
24
|
+
const normalizedValue = normalize(value);
|
|
25
|
+
if (normalizedValue.includes(" ")) {
|
|
26
|
+
return normalizedQuery.indexOf(normalizedValue);
|
|
27
|
+
}
|
|
28
|
+
const pattern = new RegExp(`(?:^|[,\\s])${escapeRegex(normalizedValue)}(?:$|[,\\s])`, "g");
|
|
29
|
+
const match = pattern.exec(normalizedQuery);
|
|
30
|
+
if (match) {
|
|
31
|
+
const matchStart = match.index;
|
|
32
|
+
const firstChar = normalizedQuery[matchStart];
|
|
33
|
+
if (firstChar === "," || firstChar === " ") {
|
|
34
|
+
return matchStart + 1;
|
|
35
|
+
}
|
|
36
|
+
return matchStart;
|
|
37
|
+
}
|
|
38
|
+
return -1;
|
|
39
|
+
}
|
|
40
|
+
function detectComponentOrder(query, value) {
|
|
41
|
+
const detected = [];
|
|
42
|
+
const componentMap = [];
|
|
43
|
+
if (value.street) {
|
|
44
|
+
componentMap.push({ value: value.street, type: "street" });
|
|
45
|
+
}
|
|
46
|
+
if (value.city) {
|
|
47
|
+
componentMap.push({ value: value.city, type: "city" });
|
|
48
|
+
}
|
|
49
|
+
if (value.postcode) {
|
|
50
|
+
componentMap.push({ value: value.postcode, type: "postcode" });
|
|
51
|
+
}
|
|
52
|
+
if (value.street_number !== void 0 && value.street_number !== null) {
|
|
53
|
+
componentMap.push({ value: String(value.street_number), type: "street_number" });
|
|
54
|
+
}
|
|
55
|
+
if (value.addition) {
|
|
56
|
+
componentMap.push({ value: value.addition, type: "addition" });
|
|
57
|
+
}
|
|
58
|
+
for (const comp of componentMap) {
|
|
59
|
+
const position = findWordPosition(query, comp.value);
|
|
60
|
+
if (position !== -1) {
|
|
61
|
+
detected.push({
|
|
62
|
+
type: comp.type,
|
|
63
|
+
value: comp.value,
|
|
64
|
+
position
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
detected.sort((a, b) => a.position - b.position);
|
|
69
|
+
return detected;
|
|
70
|
+
}
|
|
71
|
+
function formatLabelByInputOrder(query, value) {
|
|
72
|
+
if (!value || !query) {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
const detectedOrder = detectComponentOrder(query, value);
|
|
76
|
+
const detectedTypes = new Set(detectedOrder.map((d) => d.type));
|
|
77
|
+
const parts = [];
|
|
78
|
+
for (const detected of detectedOrder) {
|
|
79
|
+
parts.push(detected.value);
|
|
80
|
+
}
|
|
81
|
+
const defaultOrder = ["street", "street_number", "addition", "postcode", "city"];
|
|
82
|
+
for (const type of defaultOrder) {
|
|
83
|
+
if (detectedTypes.has(type)) continue;
|
|
84
|
+
let val;
|
|
85
|
+
switch (type) {
|
|
86
|
+
case "street":
|
|
87
|
+
val = value.street;
|
|
88
|
+
break;
|
|
89
|
+
case "city":
|
|
90
|
+
val = value.city;
|
|
91
|
+
break;
|
|
92
|
+
case "street_number":
|
|
93
|
+
val = value.street_number !== void 0 ? String(value.street_number) : void 0;
|
|
94
|
+
break;
|
|
95
|
+
case "postcode":
|
|
96
|
+
val = value.postcode;
|
|
97
|
+
break;
|
|
98
|
+
case "addition":
|
|
99
|
+
val = value.addition;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
if (val) {
|
|
103
|
+
parts.push(val);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return parts.join(", ");
|
|
107
|
+
}
|
|
7
108
|
|
|
8
109
|
// ../core/src/core.ts
|
|
9
110
|
var DEFAULTS = {
|
|
10
111
|
API_URL: "https://api.pro6pp.nl/v2",
|
|
11
|
-
LIMIT:
|
|
112
|
+
LIMIT: 20,
|
|
12
113
|
DEBOUNCE_MS: 150,
|
|
13
|
-
MIN_DEBOUNCE_MS: 50
|
|
114
|
+
MIN_DEBOUNCE_MS: 50,
|
|
115
|
+
MAX_RETRIES: 0
|
|
14
116
|
};
|
|
15
117
|
var PATTERNS = {
|
|
16
|
-
DIGITS_1_3: /^[0-9]{1,3}
|
|
118
|
+
DIGITS_1_3: /^[0-9]{1,3}$/,
|
|
119
|
+
STREET_NUMBER_PREFIX: /^(\d+)\s*,\s*$/
|
|
17
120
|
};
|
|
18
121
|
var INITIAL_STATE = {
|
|
19
122
|
query: "",
|
|
@@ -22,8 +125,10 @@ var INITIAL_STATE = {
|
|
|
22
125
|
streets: [],
|
|
23
126
|
suggestions: [],
|
|
24
127
|
isValid: false,
|
|
128
|
+
value: null,
|
|
25
129
|
isError: false,
|
|
26
130
|
isLoading: false,
|
|
131
|
+
hasMore: false,
|
|
27
132
|
selectedSuggestionIndex: -1
|
|
28
133
|
};
|
|
29
134
|
var InferCore = class {
|
|
@@ -34,8 +139,11 @@ var InferCore = class {
|
|
|
34
139
|
constructor(config) {
|
|
35
140
|
__publicField(this, "country");
|
|
36
141
|
__publicField(this, "authKey");
|
|
37
|
-
__publicField(this, "
|
|
38
|
-
__publicField(this, "
|
|
142
|
+
__publicField(this, "explicitApiUrl");
|
|
143
|
+
__publicField(this, "baseLimit");
|
|
144
|
+
__publicField(this, "currentLimit");
|
|
145
|
+
__publicField(this, "maxRetries");
|
|
146
|
+
__publicField(this, "language");
|
|
39
147
|
__publicField(this, "fetcher");
|
|
40
148
|
__publicField(this, "onStateChange");
|
|
41
149
|
__publicField(this, "onSelect");
|
|
@@ -46,11 +154,14 @@ var InferCore = class {
|
|
|
46
154
|
__publicField(this, "state");
|
|
47
155
|
__publicField(this, "abortController", null);
|
|
48
156
|
__publicField(this, "debouncedFetch");
|
|
49
|
-
__publicField(this, "isSelecting", false);
|
|
50
157
|
this.country = config.country;
|
|
51
158
|
this.authKey = config.authKey;
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
159
|
+
this.explicitApiUrl = config.apiUrl;
|
|
160
|
+
this.baseLimit = config.limit || DEFAULTS.LIMIT;
|
|
161
|
+
this.currentLimit = this.baseLimit;
|
|
162
|
+
this.language = config.language;
|
|
163
|
+
const configRetries = config.maxRetries !== void 0 ? config.maxRetries : DEFAULTS.MAX_RETRIES;
|
|
164
|
+
this.maxRetries = Math.max(0, Math.min(configRetries, 10));
|
|
54
165
|
this.fetcher = config.fetcher || ((url, init) => fetch(url, init));
|
|
55
166
|
this.onStateChange = config.onStateChange || (() => {
|
|
56
167
|
});
|
|
@@ -67,28 +178,37 @@ var InferCore = class {
|
|
|
67
178
|
* @param value The raw string from the input field.
|
|
68
179
|
*/
|
|
69
180
|
handleInput(value) {
|
|
70
|
-
|
|
71
|
-
this.isSelecting = false;
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
181
|
+
this.currentLimit = this.baseLimit;
|
|
74
182
|
const isEditingFinal = this.state.stage === "final" && value !== this.state.query;
|
|
75
183
|
this.updateState({
|
|
76
184
|
query: value,
|
|
77
185
|
isValid: false,
|
|
186
|
+
value: null,
|
|
78
187
|
isLoading: !!value.trim(),
|
|
79
|
-
selectedSuggestionIndex: -1
|
|
188
|
+
selectedSuggestionIndex: -1,
|
|
189
|
+
hasMore: false,
|
|
190
|
+
stage: isEditingFinal ? null : this.state.stage
|
|
80
191
|
});
|
|
81
192
|
if (isEditingFinal) {
|
|
82
193
|
this.onSelect(null);
|
|
83
194
|
}
|
|
84
195
|
this.debouncedFetch(value);
|
|
85
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Increases the current limit and re-fetches the query to show more results.
|
|
199
|
+
*/
|
|
200
|
+
loadMore() {
|
|
201
|
+
if (this.state.isLoading) return;
|
|
202
|
+
this.currentLimit += this.baseLimit;
|
|
203
|
+
this.updateState({ isLoading: true });
|
|
204
|
+
this.executeFetch(this.state.query);
|
|
205
|
+
}
|
|
86
206
|
/**
|
|
87
207
|
* Handles keyboard events for the input field.
|
|
88
208
|
* Supports:
|
|
89
209
|
* - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list.
|
|
90
210
|
* - `Enter`: Select the currently highlighted suggestion.
|
|
91
|
-
* - `Space`: Automatically inserts a comma if a numeric
|
|
211
|
+
* - `Space`: Automatically inserts a comma if a numeric street number is detected.
|
|
92
212
|
* @param event The keyboard event from the input element.
|
|
93
213
|
*/
|
|
94
214
|
handleKeyDown(event) {
|
|
@@ -136,6 +256,7 @@ var InferCore = class {
|
|
|
136
256
|
* Manually selects a suggestion or a string value.
|
|
137
257
|
* This is typically called when a user clicks a suggestion in the UI.
|
|
138
258
|
* @param item The suggestion object or string to select.
|
|
259
|
+
* @returns boolean True if the selection is a final address.
|
|
139
260
|
*/
|
|
140
261
|
selectItem(item) {
|
|
141
262
|
this.debouncedFetch.cancel();
|
|
@@ -149,26 +270,27 @@ var InferCore = class {
|
|
|
149
270
|
}
|
|
150
271
|
const valueObj = typeof item !== "string" && typeof item.value === "object" ? item.value : void 0;
|
|
151
272
|
const isFullResult = !!valueObj && Object.keys(valueObj).length > 0;
|
|
152
|
-
this.isSelecting = true;
|
|
153
273
|
if (this.state.stage === "final" || isFullResult) {
|
|
154
274
|
let finalQuery = label;
|
|
155
275
|
if (valueObj && Object.keys(valueObj).length > 0) {
|
|
156
|
-
const { street, street_number,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
276
|
+
const { street, street_number, postcode, city, addition } = valueObj;
|
|
277
|
+
if (street && street_number && city) {
|
|
278
|
+
const suffix = addition ? ` ${addition}` : "";
|
|
279
|
+
const postcodeStr = postcode ? `${postcode}, ` : "";
|
|
280
|
+
finalQuery = `${street}, ${street_number}${suffix}, ${postcodeStr}${city}`;
|
|
160
281
|
}
|
|
161
282
|
}
|
|
162
283
|
this.finishSelection(finalQuery, valueObj);
|
|
163
|
-
return;
|
|
284
|
+
return true;
|
|
164
285
|
}
|
|
165
286
|
const subtitle = typeof item !== "string" ? item.subtitle : null;
|
|
166
287
|
this.processSelection(logicValue, subtitle);
|
|
288
|
+
return false;
|
|
167
289
|
}
|
|
168
290
|
shouldAutoInsertComma(currentVal) {
|
|
169
291
|
const isStartOfSegmentAndNumeric = !currentVal.includes(",") && PATTERNS.DIGITS_1_3.test(currentVal.trim());
|
|
170
292
|
if (isStartOfSegmentAndNumeric) return true;
|
|
171
|
-
if (this.state.stage === "
|
|
293
|
+
if (this.state.stage === "street_number") {
|
|
172
294
|
const currentFragment = this.getCurrentFragment(currentVal);
|
|
173
295
|
return PATTERNS.DIGITS_1_3.test(currentFragment);
|
|
174
296
|
}
|
|
@@ -181,12 +303,11 @@ var InferCore = class {
|
|
|
181
303
|
cities: [],
|
|
182
304
|
streets: [],
|
|
183
305
|
isValid: true,
|
|
184
|
-
|
|
306
|
+
value: value || null,
|
|
307
|
+
stage: "final",
|
|
308
|
+
hasMore: false
|
|
185
309
|
});
|
|
186
310
|
this.onSelect(value || label);
|
|
187
|
-
setTimeout(() => {
|
|
188
|
-
this.isSelecting = false;
|
|
189
|
-
}, 0);
|
|
190
311
|
}
|
|
191
312
|
processSelection(text, subtitle) {
|
|
192
313
|
const { stage, query } = this.state;
|
|
@@ -197,7 +318,22 @@ var InferCore = class {
|
|
|
197
318
|
nextQuery = `${subtitle}, ${text}, `;
|
|
198
319
|
} else {
|
|
199
320
|
const prefix = this.getQueryPrefix(query);
|
|
200
|
-
|
|
321
|
+
const shouldAddSubtitle = !prefix || !prefix.includes(subtitle);
|
|
322
|
+
let effectivePrefix = prefix;
|
|
323
|
+
if (prefix && subtitle) {
|
|
324
|
+
const prefixNumMatch = prefix.match(PATTERNS.STREET_NUMBER_PREFIX);
|
|
325
|
+
if (prefixNumMatch) {
|
|
326
|
+
const num = prefixNumMatch[1];
|
|
327
|
+
if (subtitle.startsWith(num)) {
|
|
328
|
+
effectivePrefix = "";
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (shouldAddSubtitle) {
|
|
333
|
+
nextQuery = effectivePrefix ? `${effectivePrefix} ${text}, ${subtitle}, ` : `${text}, ${subtitle}, `;
|
|
334
|
+
} else {
|
|
335
|
+
nextQuery = effectivePrefix ? `${effectivePrefix} ${text}, ` : `${text}, `;
|
|
336
|
+
}
|
|
201
337
|
}
|
|
202
338
|
this.updateQueryAndFetch(nextQuery);
|
|
203
339
|
return;
|
|
@@ -207,50 +343,78 @@ var InferCore = class {
|
|
|
207
343
|
return;
|
|
208
344
|
}
|
|
209
345
|
const hasComma = query.includes(",");
|
|
210
|
-
const isFirstSegment = !hasComma && (stage === "city" || stage === "street" || stage === "
|
|
346
|
+
const isFirstSegment = !hasComma && (stage === "city" || stage === "street" || stage === "street_number_first");
|
|
211
347
|
if (isFirstSegment) {
|
|
212
348
|
nextQuery = `${text}, `;
|
|
213
349
|
} else {
|
|
214
350
|
nextQuery = this.replaceLastSegment(query, text);
|
|
215
|
-
if (stage !== "
|
|
351
|
+
if (stage !== "street_number") {
|
|
216
352
|
nextQuery += ", ";
|
|
217
353
|
}
|
|
218
354
|
}
|
|
219
355
|
this.updateQueryAndFetch(nextQuery);
|
|
220
356
|
}
|
|
221
|
-
executeFetch(val) {
|
|
357
|
+
executeFetch(val, attempt = 0) {
|
|
222
358
|
const text = (val || "").toString();
|
|
223
359
|
if (!text.trim()) {
|
|
224
360
|
this.abortController?.abort();
|
|
225
361
|
this.resetState();
|
|
226
362
|
return;
|
|
227
363
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
364
|
+
if (attempt === 0) {
|
|
365
|
+
this.updateState({ isError: false });
|
|
366
|
+
if (this.abortController) this.abortController.abort();
|
|
367
|
+
this.abortController = new AbortController();
|
|
368
|
+
}
|
|
369
|
+
const currentSignal = this.abortController?.signal;
|
|
370
|
+
const baseUrl = this.explicitApiUrl ? this.explicitApiUrl : `${DEFAULTS.API_URL}/infer/${this.country.toLowerCase()}`;
|
|
371
|
+
const params = new URLSearchParams({
|
|
234
372
|
query: text,
|
|
235
|
-
limit: this.
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
373
|
+
limit: this.currentLimit.toString()
|
|
374
|
+
});
|
|
375
|
+
if (this.explicitApiUrl) {
|
|
376
|
+
params.append("country", this.country.toLowerCase());
|
|
377
|
+
}
|
|
378
|
+
if (this.authKey) {
|
|
379
|
+
params.set("authKey", this.authKey);
|
|
380
|
+
}
|
|
381
|
+
if (this.language) {
|
|
382
|
+
params.set("language", this.language);
|
|
383
|
+
}
|
|
384
|
+
const separator = baseUrl.includes("?") ? "&" : "?";
|
|
385
|
+
const finalUrl = `${baseUrl}${separator}${params.toString()}`;
|
|
386
|
+
this.fetcher(finalUrl, { signal: currentSignal }).then((res) => {
|
|
387
|
+
if (!res.ok) {
|
|
388
|
+
if (attempt < this.maxRetries && (res.status >= 500 || res.status === 429)) {
|
|
389
|
+
return this.retry(val, attempt, currentSignal);
|
|
390
|
+
}
|
|
391
|
+
throw new Error("Network error");
|
|
392
|
+
}
|
|
240
393
|
return res.json();
|
|
241
|
-
}).then((data) =>
|
|
242
|
-
if (
|
|
243
|
-
|
|
394
|
+
}).then((data) => {
|
|
395
|
+
if (data) this.mapResponseToState(data);
|
|
396
|
+
}).catch((e) => {
|
|
397
|
+
if (e.name === "AbortError") return;
|
|
398
|
+
if (attempt < this.maxRetries) {
|
|
399
|
+
return this.retry(val, attempt, currentSignal);
|
|
244
400
|
}
|
|
401
|
+
this.updateState({ isError: true, isLoading: false });
|
|
245
402
|
});
|
|
246
403
|
}
|
|
404
|
+
retry(val, attempt, signal) {
|
|
405
|
+
if (signal?.aborted) return;
|
|
406
|
+
const delay = Math.pow(2, attempt) * 200;
|
|
407
|
+
setTimeout(() => {
|
|
408
|
+
if (!signal?.aborted) {
|
|
409
|
+
this.executeFetch(val, attempt + 1);
|
|
410
|
+
}
|
|
411
|
+
}, delay);
|
|
412
|
+
}
|
|
247
413
|
mapResponseToState(data) {
|
|
248
414
|
const newState = {
|
|
249
415
|
stage: data.stage,
|
|
250
416
|
isLoading: false
|
|
251
417
|
};
|
|
252
|
-
let autoSelect = false;
|
|
253
|
-
let autoSelectItem = null;
|
|
254
418
|
const rawSuggestions = data.suggestions || [];
|
|
255
419
|
const uniqueSuggestions = [];
|
|
256
420
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -258,43 +422,62 @@ var InferCore = class {
|
|
|
258
422
|
const key = `${item.label}|${item.subtitle || ""}|${JSON.stringify(item.value || {})}`;
|
|
259
423
|
if (!seen.has(key)) {
|
|
260
424
|
seen.add(key);
|
|
261
|
-
|
|
425
|
+
const reformattedItem = this.reformatSuggestionLabel(item);
|
|
426
|
+
uniqueSuggestions.push(reformattedItem);
|
|
262
427
|
}
|
|
263
428
|
}
|
|
429
|
+
const totalCount = uniqueSuggestions.length + (data.cities?.length || 0) + (data.streets?.length || 0);
|
|
430
|
+
newState.hasMore = totalCount >= this.currentLimit;
|
|
264
431
|
if (data.stage === "mixed") {
|
|
265
432
|
newState.cities = data.cities || [];
|
|
266
433
|
newState.streets = data.streets || [];
|
|
267
|
-
newState.
|
|
434
|
+
if (newState.cities?.length === 0 && newState.streets?.length === 0) {
|
|
435
|
+
newState.suggestions = uniqueSuggestions;
|
|
436
|
+
} else {
|
|
437
|
+
newState.suggestions = [];
|
|
438
|
+
}
|
|
268
439
|
} else {
|
|
269
440
|
newState.suggestions = uniqueSuggestions;
|
|
270
441
|
newState.cities = [];
|
|
271
442
|
newState.streets = [];
|
|
272
|
-
if (data.stage === "final" && uniqueSuggestions.length === 1) {
|
|
273
|
-
autoSelect = true;
|
|
274
|
-
autoSelectItem = uniqueSuggestions[0];
|
|
275
|
-
}
|
|
276
443
|
}
|
|
277
444
|
newState.isValid = data.stage === "final";
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
newState.cities = [];
|
|
282
|
-
newState.streets = [];
|
|
283
|
-
newState.isValid = true;
|
|
284
|
-
this.updateState(newState);
|
|
285
|
-
const val = typeof autoSelectItem.value === "object" ? autoSelectItem.value : autoSelectItem.label;
|
|
286
|
-
this.onSelect(val);
|
|
287
|
-
} else {
|
|
288
|
-
this.updateState(newState);
|
|
445
|
+
this.updateState(newState);
|
|
446
|
+
if (newState.isValid && uniqueSuggestions.length === 1) {
|
|
447
|
+
this.selectItem(uniqueSuggestions[0]);
|
|
289
448
|
}
|
|
290
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Reformats a suggestion's label based on the user's input order.
|
|
452
|
+
* If the suggestion has a structured value object, we reorder the label
|
|
453
|
+
* to match how the user typed the components.
|
|
454
|
+
*/
|
|
455
|
+
reformatSuggestionLabel(item) {
|
|
456
|
+
if (!item.value || typeof item.value === "string") {
|
|
457
|
+
return item;
|
|
458
|
+
}
|
|
459
|
+
const addressValue = item.value;
|
|
460
|
+
if (!addressValue.street || !addressValue.city) {
|
|
461
|
+
return item;
|
|
462
|
+
}
|
|
463
|
+
const reformattedLabel = formatLabelByInputOrder(this.state.query, addressValue);
|
|
464
|
+
if (reformattedLabel) {
|
|
465
|
+
return { ...item, label: reformattedLabel };
|
|
466
|
+
}
|
|
467
|
+
return item;
|
|
468
|
+
}
|
|
291
469
|
updateQueryAndFetch(nextQuery) {
|
|
292
|
-
this.updateState({
|
|
293
|
-
|
|
470
|
+
this.updateState({
|
|
471
|
+
query: nextQuery,
|
|
472
|
+
suggestions: [],
|
|
473
|
+
cities: [],
|
|
474
|
+
streets: [],
|
|
475
|
+
isValid: false,
|
|
476
|
+
value: null,
|
|
477
|
+
isLoading: true,
|
|
478
|
+
hasMore: false
|
|
479
|
+
});
|
|
294
480
|
this.debouncedFetch(nextQuery);
|
|
295
|
-
setTimeout(() => {
|
|
296
|
-
this.isSelecting = false;
|
|
297
|
-
}, 0);
|
|
298
481
|
}
|
|
299
482
|
replaceLastSegment(fullText, newSegment) {
|
|
300
483
|
const lastCommaIndex = fullText.lastIndexOf(",");
|
|
@@ -331,6 +514,49 @@ var InferCore = class {
|
|
|
331
514
|
}
|
|
332
515
|
};
|
|
333
516
|
|
|
517
|
+
// ../core/src/highlight.ts
|
|
518
|
+
function mergeSegments(segments) {
|
|
519
|
+
if (segments.length === 0) return segments;
|
|
520
|
+
const merged = [];
|
|
521
|
+
for (const seg of segments) {
|
|
522
|
+
const last = merged[merged.length - 1];
|
|
523
|
+
if (last && last.match === seg.match) {
|
|
524
|
+
last.text += seg.text;
|
|
525
|
+
} else {
|
|
526
|
+
merged.push({ text: seg.text, match: seg.match });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return merged;
|
|
530
|
+
}
|
|
531
|
+
function getHighlightSegments(text, query) {
|
|
532
|
+
if (!query || !text) return [{ text, match: false }];
|
|
533
|
+
const segments = [];
|
|
534
|
+
const normalizedText = text.toLowerCase();
|
|
535
|
+
const normalizedQuery = query.toLowerCase();
|
|
536
|
+
let queryCursor = 0;
|
|
537
|
+
let unmatchedCursor = 0;
|
|
538
|
+
for (let textCursor = 0; textCursor < text.length; textCursor++) {
|
|
539
|
+
const isMatch = queryCursor < query.length && normalizedText[textCursor] === normalizedQuery[queryCursor];
|
|
540
|
+
if (!isMatch) continue;
|
|
541
|
+
const hasPrecedingUnmatched = textCursor > unmatchedCursor;
|
|
542
|
+
if (hasPrecedingUnmatched) {
|
|
543
|
+
segments.push({ text: text.slice(unmatchedCursor, textCursor), match: false });
|
|
544
|
+
}
|
|
545
|
+
segments.push({ text: text[textCursor], match: true });
|
|
546
|
+
queryCursor++;
|
|
547
|
+
unmatchedCursor = textCursor + 1;
|
|
548
|
+
}
|
|
549
|
+
const hasRemainingText = unmatchedCursor < text.length;
|
|
550
|
+
if (hasRemainingText) {
|
|
551
|
+
segments.push({ text: text.slice(unmatchedCursor), match: false });
|
|
552
|
+
}
|
|
553
|
+
const isFullMatch = queryCursor === query.length;
|
|
554
|
+
if (!isFullMatch) {
|
|
555
|
+
return [{ text, match: false }];
|
|
556
|
+
}
|
|
557
|
+
return mergeSegments(segments);
|
|
558
|
+
}
|
|
559
|
+
|
|
334
560
|
// ../core/src/default-styles.ts
|
|
335
561
|
var DEFAULT_STYLES = `
|
|
336
562
|
.pro6pp-wrapper {
|
|
@@ -338,116 +564,276 @@ var DEFAULT_STYLES = `
|
|
|
338
564
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
339
565
|
box-sizing: border-box;
|
|
340
566
|
width: 100%;
|
|
567
|
+
-webkit-tap-highlight-color: transparent;
|
|
341
568
|
}
|
|
342
569
|
.pro6pp-wrapper * {
|
|
343
570
|
box-sizing: border-box;
|
|
344
571
|
}
|
|
345
572
|
.pro6pp-input {
|
|
346
573
|
width: 100%;
|
|
347
|
-
padding:
|
|
574
|
+
padding: 12px 14px;
|
|
575
|
+
padding-right: 48px;
|
|
348
576
|
border: 1px solid #e0e0e0;
|
|
349
|
-
border-radius:
|
|
577
|
+
border-radius: 8px;
|
|
350
578
|
font-size: 16px;
|
|
351
579
|
line-height: 1.5;
|
|
580
|
+
appearance: none;
|
|
352
581
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
353
582
|
}
|
|
583
|
+
|
|
584
|
+
.pro6pp-input::placeholder {
|
|
585
|
+
font-size: 16px;
|
|
586
|
+
color: #a3a3a3;
|
|
587
|
+
}
|
|
588
|
+
|
|
354
589
|
.pro6pp-input:focus {
|
|
355
590
|
outline: none;
|
|
356
591
|
border-color: #3b82f6;
|
|
357
592
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
358
593
|
}
|
|
594
|
+
|
|
595
|
+
.pro6pp-input-addons {
|
|
596
|
+
position: absolute;
|
|
597
|
+
right: 4px;
|
|
598
|
+
top: 0;
|
|
599
|
+
bottom: 0;
|
|
600
|
+
display: flex;
|
|
601
|
+
align-items: center;
|
|
602
|
+
pointer-events: none;
|
|
603
|
+
}
|
|
604
|
+
.pro6pp-input-addons > * {
|
|
605
|
+
pointer-events: auto;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.pro6pp-clear-button {
|
|
609
|
+
background: none;
|
|
610
|
+
border: none;
|
|
611
|
+
width: 32px;
|
|
612
|
+
height: 32px;
|
|
613
|
+
cursor: pointer;
|
|
614
|
+
color: #a3a3a3;
|
|
615
|
+
display: flex;
|
|
616
|
+
align-items: center;
|
|
617
|
+
justify-content: center;
|
|
618
|
+
border-radius: 50%;
|
|
619
|
+
transition: color 0.2s, background-color 0.2s;
|
|
620
|
+
touch-action: manipulation;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
@media (hover: hover) {
|
|
624
|
+
.pro6pp-clear-button:hover {
|
|
625
|
+
color: #1f2937;
|
|
626
|
+
background-color: #f3f4f6;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.pro6pp-clear-button:active {
|
|
631
|
+
background-color: #f3f4f6;
|
|
632
|
+
}
|
|
633
|
+
|
|
359
634
|
.pro6pp-dropdown {
|
|
360
635
|
position: absolute;
|
|
361
636
|
top: 100%;
|
|
362
637
|
left: 0;
|
|
363
638
|
right: 0;
|
|
364
|
-
z-index: 9999;
|
|
365
639
|
margin-top: 4px;
|
|
366
|
-
background:
|
|
367
|
-
border: 1px solid #
|
|
368
|
-
border-radius:
|
|
369
|
-
box-shadow: 0 4px
|
|
370
|
-
|
|
640
|
+
background: #ffffff;
|
|
641
|
+
border: 1px solid #e5e7eb;
|
|
642
|
+
border-radius: 6px;
|
|
643
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
644
|
+
z-index: 9999;
|
|
645
|
+
padding: 0;
|
|
646
|
+
max-height: 280px;
|
|
371
647
|
overflow-y: auto;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
648
|
+
display: flex;
|
|
649
|
+
flex-direction: column;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
@media (max-height: 500px) {
|
|
653
|
+
.pro6pp-dropdown {
|
|
654
|
+
max-height: 180px;
|
|
655
|
+
}
|
|
375
656
|
}
|
|
657
|
+
|
|
658
|
+
.pro6pp-list {
|
|
659
|
+
list-style: none;
|
|
660
|
+
margin: 0;
|
|
661
|
+
padding: 0;
|
|
662
|
+
width: 100%;
|
|
663
|
+
}
|
|
664
|
+
|
|
376
665
|
.pro6pp-item {
|
|
377
|
-
padding:
|
|
666
|
+
padding: 12px 14px;
|
|
378
667
|
cursor: pointer;
|
|
379
668
|
display: flex;
|
|
380
|
-
flex-direction: row;
|
|
381
669
|
align-items: center;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
670
|
+
font-size: 15px;
|
|
671
|
+
line-height: 1.4;
|
|
672
|
+
color: #374151;
|
|
673
|
+
border-bottom: 1px solid #f3f4f6;
|
|
674
|
+
transition: background-color 0.1s;
|
|
675
|
+
flex-shrink: 0;
|
|
387
676
|
}
|
|
388
|
-
|
|
389
|
-
|
|
677
|
+
|
|
678
|
+
.pro6pp-item:last-child {
|
|
679
|
+
border-bottom: none;
|
|
390
680
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
681
|
+
|
|
682
|
+
@media (hover: hover) {
|
|
683
|
+
.pro6pp-item:hover, .pro6pp-item--active {
|
|
684
|
+
background-color: #f9fafb;
|
|
685
|
+
}
|
|
394
686
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
color: #
|
|
687
|
+
|
|
688
|
+
.pro6pp-item:active {
|
|
689
|
+
background-color: #f3f4f6;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.pro6pp-item__label {
|
|
693
|
+
font-weight: 400;
|
|
694
|
+
flex-shrink: 1;
|
|
398
695
|
overflow: hidden;
|
|
399
696
|
text-overflow: ellipsis;
|
|
400
|
-
|
|
697
|
+
white-space: nowrap;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.pro6pp-item__label--match {
|
|
701
|
+
font-weight: 520;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.pro6pp-item__label--unmatched {
|
|
705
|
+
font-weight: 400;
|
|
706
|
+
color: #4b5563;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.pro6pp-item__subtitle {
|
|
710
|
+
color: #6b7280;
|
|
711
|
+
flex-shrink: 0;
|
|
401
712
|
}
|
|
713
|
+
|
|
402
714
|
.pro6pp-item__chevron {
|
|
403
|
-
|
|
715
|
+
color: #d1d5db;
|
|
404
716
|
display: flex;
|
|
405
717
|
align-items: center;
|
|
406
|
-
|
|
718
|
+
margin-left: auto;
|
|
407
719
|
padding-left: 8px;
|
|
408
720
|
}
|
|
721
|
+
|
|
409
722
|
.pro6pp-no-results {
|
|
410
|
-
padding:
|
|
411
|
-
color: #
|
|
412
|
-
font-size:
|
|
723
|
+
padding: 24px 16px;
|
|
724
|
+
color: #6b7280;
|
|
725
|
+
font-size: 15px;
|
|
413
726
|
text-align: center;
|
|
414
|
-
user-select: none;
|
|
415
|
-
pointer-events: none;
|
|
416
727
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
728
|
+
|
|
729
|
+
.pro6pp-loader-item {
|
|
730
|
+
padding: 10px 12px;
|
|
731
|
+
color: #6b7280;
|
|
732
|
+
font-size: 0.875rem;
|
|
733
|
+
display: flex;
|
|
734
|
+
align-items: center;
|
|
735
|
+
justify-content: center;
|
|
736
|
+
gap: 8px;
|
|
737
|
+
background-color: #f9fafb;
|
|
738
|
+
border-top: 1px solid #f3f4f6;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.pro6pp-mini-spinner {
|
|
742
|
+
width: 14px;
|
|
743
|
+
height: 14px;
|
|
744
|
+
border: 2px solid #e5e7eb;
|
|
745
|
+
border-top-color: #6b7280;
|
|
426
746
|
border-radius: 50%;
|
|
427
747
|
animation: pro6pp-spin 0.6s linear infinite;
|
|
428
|
-
pointer-events: none;
|
|
429
748
|
}
|
|
749
|
+
|
|
750
|
+
@media (max-width: 640px) {
|
|
751
|
+
.pro6pp-input {
|
|
752
|
+
font-size: 16px;
|
|
753
|
+
padding: 10px 12px;
|
|
754
|
+
}
|
|
755
|
+
.pro6pp-item {
|
|
756
|
+
padding: 10px 12px;
|
|
757
|
+
font-size: 14px;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
430
761
|
@keyframes pro6pp-spin {
|
|
431
|
-
to { transform:
|
|
762
|
+
to { transform: rotate(360deg); }
|
|
432
763
|
}
|
|
433
764
|
`;
|
|
434
765
|
|
|
435
766
|
// src/index.tsx
|
|
767
|
+
var HighlightedText = ({ text, query }) => {
|
|
768
|
+
const segments = useMemo(() => getHighlightSegments(text, query), [text, query]);
|
|
769
|
+
return /* @__PURE__ */ React.createElement("span", { className: "pro6pp-item__label" }, segments.map(
|
|
770
|
+
(seg, i) => seg.match ? /* @__PURE__ */ React.createElement("span", { key: i, className: "pro6pp-item__label--match" }, seg.text) : /* @__PURE__ */ React.createElement("span", { key: i, className: "pro6pp-item__label--unmatched" }, seg.text)
|
|
771
|
+
));
|
|
772
|
+
};
|
|
436
773
|
function useInfer(config) {
|
|
437
|
-
const [state, setState] = useState(
|
|
774
|
+
const [state, setState] = useState(() => {
|
|
775
|
+
if (config.initialValue) {
|
|
776
|
+
const suffix = config.initialValue.addition ? ` ${config.initialValue.addition}` : "";
|
|
777
|
+
const postcodeStr = config.initialValue.postcode ? `${config.initialValue.postcode}, ` : "";
|
|
778
|
+
return {
|
|
779
|
+
...INITIAL_STATE,
|
|
780
|
+
value: config.initialValue,
|
|
781
|
+
query: `${config.initialValue.street}, ${config.initialValue.street_number}${suffix}, ${postcodeStr}${config.initialValue.city}`,
|
|
782
|
+
isValid: true,
|
|
783
|
+
stage: "final"
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
return INITIAL_STATE;
|
|
787
|
+
});
|
|
788
|
+
const callbacksRef = useRef({
|
|
789
|
+
onStateChange: config.onStateChange,
|
|
790
|
+
onSelect: config.onSelect
|
|
791
|
+
});
|
|
792
|
+
useEffect(() => {
|
|
793
|
+
callbacksRef.current = {
|
|
794
|
+
onStateChange: config.onStateChange,
|
|
795
|
+
onSelect: config.onSelect
|
|
796
|
+
};
|
|
797
|
+
}, [config.onStateChange, config.onSelect]);
|
|
438
798
|
const core = useMemo(() => {
|
|
439
|
-
|
|
799
|
+
const instance = new InferCore({
|
|
440
800
|
...config,
|
|
441
801
|
onStateChange: (newState) => {
|
|
442
802
|
setState({ ...newState });
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
803
|
+
callbacksRef.current.onStateChange?.(newState);
|
|
804
|
+
},
|
|
805
|
+
onSelect: (selection) => {
|
|
806
|
+
callbacksRef.current.onSelect?.(selection);
|
|
446
807
|
}
|
|
447
808
|
});
|
|
448
|
-
|
|
809
|
+
if (config.initialValue) {
|
|
810
|
+
const address = config.initialValue;
|
|
811
|
+
const suffix = address.addition ? ` ${address.addition}` : "";
|
|
812
|
+
const postcodeStr = address.postcode ? `${address.postcode}, ` : "";
|
|
813
|
+
const label = `${address.street}, ${address.street_number}${suffix}, ${postcodeStr}${address.city}`;
|
|
814
|
+
instance.selectItem({ label, value: address });
|
|
815
|
+
}
|
|
816
|
+
return instance;
|
|
817
|
+
}, [
|
|
818
|
+
config.country,
|
|
819
|
+
config.authKey,
|
|
820
|
+
config.apiUrl,
|
|
821
|
+
config.fetcher,
|
|
822
|
+
config.limit,
|
|
823
|
+
config.debounceMs,
|
|
824
|
+
config.maxRetries,
|
|
825
|
+
config.initialValue,
|
|
826
|
+
config.language
|
|
827
|
+
]);
|
|
828
|
+
const setValue = (address) => {
|
|
829
|
+
if (!address) return;
|
|
830
|
+
const suffix = address.addition ? ` ${address.addition}` : "";
|
|
831
|
+
const postcodeStr = address.postcode ? `${address.postcode}, ` : "";
|
|
832
|
+
const label = `${address.street}, ${address.street_number}${suffix}, ${postcodeStr}${address.city}`;
|
|
833
|
+
core.selectItem({ label, value: address });
|
|
834
|
+
};
|
|
449
835
|
return {
|
|
450
|
-
/** The current UI state (suggestions, loading status, query, etc.). */
|
|
836
|
+
/** The current UI state (suggestions, loading status, query, value, etc.). */
|
|
451
837
|
state,
|
|
452
838
|
/** The raw InferCore instance for manual control. */
|
|
453
839
|
core,
|
|
@@ -457,95 +843,125 @@ function useInfer(config) {
|
|
|
457
843
|
onChange: (e) => core.handleInput(e.target.value),
|
|
458
844
|
onKeyDown: (e) => core.handleKeyDown(e)
|
|
459
845
|
},
|
|
460
|
-
/**
|
|
461
|
-
selectItem: (item) => core.selectItem(item)
|
|
846
|
+
/** Manually select a specific suggestion. */
|
|
847
|
+
selectItem: (item) => core.selectItem(item),
|
|
848
|
+
/** Programmatically set the address value. */
|
|
849
|
+
setValue,
|
|
850
|
+
/** Load more results. */
|
|
851
|
+
loadMore: () => core.loadMore()
|
|
462
852
|
};
|
|
463
853
|
}
|
|
464
|
-
var Pro6PPInfer = (
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
854
|
+
var Pro6PPInfer = forwardRef(
|
|
855
|
+
({
|
|
856
|
+
className,
|
|
857
|
+
style,
|
|
858
|
+
inputProps,
|
|
859
|
+
placeholder = "Start typing an address...",
|
|
860
|
+
renderItem,
|
|
861
|
+
disableDefaultStyles = false,
|
|
862
|
+
noResultsText = "No results found",
|
|
863
|
+
loadingText = "Loading more...",
|
|
864
|
+
renderNoResults,
|
|
865
|
+
showClearButton = true,
|
|
866
|
+
...config
|
|
867
|
+
}, ref) => {
|
|
868
|
+
const { state, selectItem, loadMore, inputProps: coreInputProps, core } = useInfer(config);
|
|
869
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
870
|
+
const internalInputRef = useRef(null);
|
|
871
|
+
const wrapperRef = useRef(null);
|
|
872
|
+
const observerTarget = useRef(null);
|
|
873
|
+
useImperativeHandle(ref, () => internalInputRef.current);
|
|
874
|
+
useEffect(() => {
|
|
875
|
+
if (disableDefaultStyles) return;
|
|
876
|
+
const styleId = "pro6pp-styles";
|
|
877
|
+
if (!document.getElementById(styleId)) {
|
|
878
|
+
const styleEl = document.createElement("style");
|
|
879
|
+
styleEl.id = styleId;
|
|
880
|
+
styleEl.textContent = DEFAULT_STYLES;
|
|
881
|
+
document.head.appendChild(styleEl);
|
|
882
|
+
}
|
|
883
|
+
}, [disableDefaultStyles]);
|
|
884
|
+
useEffect(() => {
|
|
885
|
+
const handleClickOutside = (event) => {
|
|
886
|
+
if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
|
|
887
|
+
setIsOpen(false);
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
891
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
892
|
+
}, []);
|
|
893
|
+
useEffect(() => {
|
|
894
|
+
const currentTarget = observerTarget.current;
|
|
895
|
+
if (!currentTarget) return;
|
|
896
|
+
const observer = new IntersectionObserver(
|
|
897
|
+
(entries) => {
|
|
898
|
+
if (entries[0].isIntersecting && state.hasMore && !state.isLoading) {
|
|
899
|
+
loadMore();
|
|
900
|
+
}
|
|
901
|
+
},
|
|
902
|
+
{ threshold: 0.1 }
|
|
903
|
+
);
|
|
904
|
+
observer.observe(currentTarget);
|
|
905
|
+
return () => {
|
|
906
|
+
if (currentTarget) observer.unobserve(currentTarget);
|
|
907
|
+
};
|
|
908
|
+
}, [state.hasMore, state.isLoading, loadMore, isOpen]);
|
|
909
|
+
const items = useMemo(() => {
|
|
910
|
+
return [
|
|
911
|
+
...state.cities.map((c) => ({ ...c, type: "city" })),
|
|
912
|
+
...state.streets.map((s) => ({ ...s, type: "street" })),
|
|
913
|
+
...state.suggestions.map((s) => ({ ...s, type: "suggestion" }))
|
|
914
|
+
];
|
|
915
|
+
}, [state.cities, state.streets, state.suggestions]);
|
|
916
|
+
const handleSelect = (item) => {
|
|
917
|
+
const isFinal = selectItem(item);
|
|
918
|
+
if (!isFinal) {
|
|
919
|
+
setTimeout(() => internalInputRef.current?.focus(), 0);
|
|
920
|
+
} else {
|
|
492
921
|
setIsOpen(false);
|
|
493
922
|
}
|
|
494
923
|
};
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
autoComplete: "off",
|
|
523
|
-
...inputProps,
|
|
524
|
-
...coreInputProps,
|
|
525
|
-
onFocus: (e) => {
|
|
526
|
-
setIsOpen(true);
|
|
527
|
-
inputProps?.onFocus?.(e);
|
|
924
|
+
const handleClear = () => {
|
|
925
|
+
core.handleInput("");
|
|
926
|
+
internalInputRef.current?.focus();
|
|
927
|
+
};
|
|
928
|
+
const hasResults = items.length > 0;
|
|
929
|
+
const showNoResults = !state.isLoading && !state.isError && state.query.length > 0 && !hasResults && !state.isValid;
|
|
930
|
+
const showDropdown = isOpen && (hasResults || state.isLoading || showNoResults);
|
|
931
|
+
const isInfiniteLoading = state.isLoading && items.length > 0;
|
|
932
|
+
return /* @__PURE__ */ React.createElement("div", { ref: wrapperRef, className: `pro6pp-wrapper ${className || ""}`, style }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(
|
|
933
|
+
"input",
|
|
934
|
+
{
|
|
935
|
+
ref: internalInputRef,
|
|
936
|
+
type: "text",
|
|
937
|
+
className: "pro6pp-input",
|
|
938
|
+
placeholder,
|
|
939
|
+
autoComplete: "off",
|
|
940
|
+
autoCorrect: "off",
|
|
941
|
+
autoCapitalize: "none",
|
|
942
|
+
spellCheck: "false",
|
|
943
|
+
inputMode: "search",
|
|
944
|
+
enterKeyHint: "search",
|
|
945
|
+
...inputProps,
|
|
946
|
+
...coreInputProps,
|
|
947
|
+
onFocus: (e) => {
|
|
948
|
+
setIsOpen(true);
|
|
949
|
+
inputProps?.onFocus?.(e);
|
|
950
|
+
}
|
|
528
951
|
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
const isActive = index === state.selectedSuggestionIndex;
|
|
532
|
-
const secondaryText = item.subtitle || (item.count !== void 0 ? item.count : "");
|
|
533
|
-
const showChevron = item.value === void 0 || item.value === null;
|
|
534
|
-
return /* @__PURE__ */ React.createElement(
|
|
535
|
-
"li",
|
|
952
|
+
), /* @__PURE__ */ React.createElement("div", { className: "pro6pp-input-addons" }, showClearButton && state.query.length > 0 && /* @__PURE__ */ React.createElement(
|
|
953
|
+
"button",
|
|
536
954
|
{
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
542
|
-
onClick: () => handleSelect(item)
|
|
955
|
+
type: "button",
|
|
956
|
+
className: "pro6pp-clear-button",
|
|
957
|
+
onClick: handleClear,
|
|
958
|
+
"aria-label": "Clear input"
|
|
543
959
|
},
|
|
544
|
-
|
|
960
|
+
/* @__PURE__ */ React.createElement(
|
|
545
961
|
"svg",
|
|
546
962
|
{
|
|
547
|
-
width: "
|
|
548
|
-
height: "
|
|
963
|
+
width: "14",
|
|
964
|
+
height: "14",
|
|
549
965
|
viewBox: "0 0 24 24",
|
|
550
966
|
fill: "none",
|
|
551
967
|
stroke: "currentColor",
|
|
@@ -553,11 +969,49 @@ var Pro6PPInfer = ({
|
|
|
553
969
|
strokeLinecap: "round",
|
|
554
970
|
strokeLinejoin: "round"
|
|
555
971
|
},
|
|
556
|
-
/* @__PURE__ */ React.createElement("
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
972
|
+
/* @__PURE__ */ React.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
973
|
+
/* @__PURE__ */ React.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
974
|
+
)
|
|
975
|
+
))), showDropdown && /* @__PURE__ */ React.createElement(
|
|
976
|
+
"div",
|
|
977
|
+
{
|
|
978
|
+
className: "pro6pp-dropdown",
|
|
979
|
+
onWheel: (e) => e.stopPropagation(),
|
|
980
|
+
onMouseDown: (e) => e.stopPropagation()
|
|
981
|
+
},
|
|
982
|
+
/* @__PURE__ */ React.createElement("ul", { className: "pro6pp-list", role: "listbox" }, hasResults ? /* @__PURE__ */ React.createElement(React.Fragment, null, items.map((item, index) => {
|
|
983
|
+
const isActive = index === state.selectedSuggestionIndex;
|
|
984
|
+
const secondaryText = item.subtitle || (item.count !== void 0 ? item.count : "");
|
|
985
|
+
const showChevron = item.value === void 0 || item.value === null;
|
|
986
|
+
return /* @__PURE__ */ React.createElement(
|
|
987
|
+
"li",
|
|
988
|
+
{
|
|
989
|
+
key: `${item.label}-${index}`,
|
|
990
|
+
role: "option",
|
|
991
|
+
"aria-selected": isActive,
|
|
992
|
+
className: `pro6pp-item ${isActive ? "pro6pp-item--active" : ""}`,
|
|
993
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
994
|
+
onClick: () => handleSelect(item)
|
|
995
|
+
},
|
|
996
|
+
renderItem ? renderItem(item, isActive) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(HighlightedText, { text: item.label, query: state.query }), secondaryText && /* @__PURE__ */ React.createElement("span", { className: "pro6pp-item__subtitle" }, ", ", secondaryText), showChevron && /* @__PURE__ */ React.createElement("div", { className: "pro6pp-item__chevron" }, /* @__PURE__ */ React.createElement(
|
|
997
|
+
"svg",
|
|
998
|
+
{
|
|
999
|
+
width: "16",
|
|
1000
|
+
height: "16",
|
|
1001
|
+
viewBox: "0 0 24 24",
|
|
1002
|
+
fill: "none",
|
|
1003
|
+
stroke: "currentColor",
|
|
1004
|
+
strokeWidth: "2",
|
|
1005
|
+
strokeLinecap: "round",
|
|
1006
|
+
strokeLinejoin: "round"
|
|
1007
|
+
},
|
|
1008
|
+
/* @__PURE__ */ React.createElement("polyline", { points: "9 18 15 12 9 6" })
|
|
1009
|
+
)))
|
|
1010
|
+
);
|
|
1011
|
+
}), state.hasMore && !state.isLoading && /* @__PURE__ */ React.createElement("li", { key: "sentinel", ref: observerTarget, style: { height: "1px", opacity: 0 } }), isInfiniteLoading && /* @__PURE__ */ React.createElement("li", { key: "loader", className: "pro6pp-loader-item" }, /* @__PURE__ */ React.createElement("div", { className: "pro6pp-mini-spinner" }), /* @__PURE__ */ React.createElement("span", null, loadingText))) : state.isLoading ? /* @__PURE__ */ React.createElement("li", { className: "pro6pp-no-results" }, "Searching...") : /* @__PURE__ */ React.createElement("li", { className: "pro6pp-no-results" }, renderNoResults ? renderNoResults(state) : noResultsText))
|
|
1012
|
+
));
|
|
1013
|
+
}
|
|
1014
|
+
);
|
|
561
1015
|
export {
|
|
562
1016
|
Pro6PPInfer,
|
|
563
1017
|
useInfer
|