@pro6pp/infer-react 0.0.2-beta.10 → 0.0.2-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.cjs +129 -85
  2. package/dist/index.js +129 -85
  3. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -83,7 +83,6 @@ var InferCore = class {
83
83
  __publicField(this, "state");
84
84
  __publicField(this, "abortController", null);
85
85
  __publicField(this, "debouncedFetch");
86
- __publicField(this, "isSelecting", false);
87
86
  this.country = config.country;
88
87
  this.authKey = config.authKey;
89
88
  this.explicitApiUrl = config.apiUrl;
@@ -107,10 +106,6 @@ var InferCore = class {
107
106
  * @param value The raw string from the input field.
108
107
  */
109
108
  handleInput(value) {
110
- if (this.isSelecting) {
111
- this.isSelecting = false;
112
- return;
113
- }
114
109
  this.currentLimit = this.baseLimit;
115
110
  const isEditingFinal = this.state.stage === "final" && value !== this.state.query;
116
111
  this.updateState({
@@ -139,7 +134,7 @@ var InferCore = class {
139
134
  * Supports:
140
135
  * - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list.
141
136
  * - `Enter`: Select the currently highlighted suggestion.
142
- * - `Space`: Automatically inserts a comma if a numeric house number is detected.
137
+ * - `Space`: Automatically inserts a comma if a numeric street number is detected.
143
138
  * @param event The keyboard event from the input element.
144
139
  */
145
140
  handleKeyDown(event) {
@@ -201,14 +196,13 @@ var InferCore = class {
201
196
  }
202
197
  const valueObj = typeof item !== "string" && typeof item.value === "object" ? item.value : void 0;
203
198
  const isFullResult = !!valueObj && Object.keys(valueObj).length > 0;
204
- this.isSelecting = true;
205
199
  if (this.state.stage === "final" || isFullResult) {
206
200
  let finalQuery = label;
207
201
  if (valueObj && Object.keys(valueObj).length > 0) {
208
- const { street, street_number, house_number, city } = valueObj;
209
- const number = street_number || house_number;
210
- if (street && number && city) {
211
- finalQuery = `${street} ${number}, ${city}`;
202
+ const { street, street_number, city, addition } = valueObj;
203
+ if (street && street_number && city) {
204
+ const suffix = addition ? ` ${addition}` : "";
205
+ finalQuery = `${street} ${street_number}${suffix}, ${city}`;
212
206
  }
213
207
  }
214
208
  this.finishSelection(finalQuery, valueObj);
@@ -221,7 +215,7 @@ var InferCore = class {
221
215
  shouldAutoInsertComma(currentVal) {
222
216
  const isStartOfSegmentAndNumeric = !currentVal.includes(",") && PATTERNS.DIGITS_1_3.test(currentVal.trim());
223
217
  if (isStartOfSegmentAndNumeric) return true;
224
- if (this.state.stage === "house_number") {
218
+ if (this.state.stage === "street_number") {
225
219
  const currentFragment = this.getCurrentFragment(currentVal);
226
220
  return PATTERNS.DIGITS_1_3.test(currentFragment);
227
221
  }
@@ -248,7 +242,12 @@ var InferCore = class {
248
242
  nextQuery = `${subtitle}, ${text}, `;
249
243
  } else {
250
244
  const prefix = this.getQueryPrefix(query);
251
- nextQuery = prefix ? `${prefix} ${text}, ${subtitle}, ` : `${text}, ${subtitle}, `;
245
+ const shouldAddSubtitle = !prefix || !prefix.includes(subtitle);
246
+ if (shouldAddSubtitle) {
247
+ nextQuery = prefix ? `${prefix} ${text}, ${subtitle}, ` : `${text}, ${subtitle}, `;
248
+ } else {
249
+ nextQuery = prefix ? `${prefix} ${text}, ` : `${text}, `;
250
+ }
252
251
  }
253
252
  this.updateQueryAndFetch(nextQuery);
254
253
  return;
@@ -258,12 +257,12 @@ var InferCore = class {
258
257
  return;
259
258
  }
260
259
  const hasComma = query.includes(",");
261
- const isFirstSegment = !hasComma && (stage === "city" || stage === "street" || stage === "house_number_first");
260
+ const isFirstSegment = !hasComma && (stage === "city" || stage === "street" || stage === "street_number_first");
262
261
  if (isFirstSegment) {
263
262
  nextQuery = `${text}, `;
264
263
  } else {
265
264
  nextQuery = this.replaceLastSegment(query, text);
266
- if (stage !== "house_number") {
265
+ if (stage !== "street_number") {
267
266
  nextQuery += ", ";
268
267
  }
269
268
  }
@@ -325,8 +324,6 @@ var InferCore = class {
325
324
  stage: data.stage,
326
325
  isLoading: false
327
326
  };
328
- let autoSelect = false;
329
- let autoSelectItem = null;
330
327
  const rawSuggestions = data.suggestions || [];
331
328
  const uniqueSuggestions = [];
332
329
  const seen = /* @__PURE__ */ new Set();
@@ -347,27 +344,11 @@ var InferCore = class {
347
344
  newState.suggestions = uniqueSuggestions;
348
345
  newState.cities = [];
349
346
  newState.streets = [];
350
- const firstItem = uniqueSuggestions[0];
351
- const hasFullValue = firstItem && typeof firstItem.value === "object" && firstItem.value !== null && Object.keys(firstItem.value).length > 0;
352
- if ((data.stage === "final" || hasFullValue) && uniqueSuggestions.length === 1) {
353
- autoSelect = true;
354
- autoSelectItem = firstItem;
355
- }
356
347
  }
357
348
  newState.isValid = data.stage === "final";
358
- if (autoSelect && autoSelectItem) {
359
- newState.query = autoSelectItem.label;
360
- newState.suggestions = [];
361
- newState.cities = [];
362
- newState.streets = [];
363
- newState.isValid = true;
364
- newState.hasMore = false;
365
- this.isSelecting = true;
366
- this.updateState(newState);
367
- const val = typeof autoSelectItem.value === "object" ? autoSelectItem.value : autoSelectItem.label;
368
- this.onSelect(val);
369
- } else {
370
- this.updateState(newState);
349
+ this.updateState(newState);
350
+ if (newState.isValid && uniqueSuggestions.length === 1) {
351
+ this.selectItem(uniqueSuggestions[0]);
371
352
  }
372
353
  }
373
354
  updateQueryAndFetch(nextQuery) {
@@ -410,6 +391,36 @@ var InferCore = class {
410
391
  }
411
392
  };
412
393
 
394
+ // ../core/src/highlight.ts
395
+ function getHighlightSegments(text, query) {
396
+ if (!query || !text) return [{ text, match: false }];
397
+ const segments = [];
398
+ const normalizedText = text.toLowerCase();
399
+ const normalizedQuery = query.toLowerCase();
400
+ let queryCursor = 0;
401
+ let unmatchedCursor = 0;
402
+ for (let textCursor = 0; textCursor < text.length; textCursor++) {
403
+ const isMatch = queryCursor < query.length && normalizedText[textCursor] === normalizedQuery[queryCursor];
404
+ if (!isMatch) continue;
405
+ const hasPrecedingUnmatched = textCursor > unmatchedCursor;
406
+ if (hasPrecedingUnmatched) {
407
+ segments.push({ text: text.slice(unmatchedCursor, textCursor), match: false });
408
+ }
409
+ segments.push({ text: text[textCursor], match: true });
410
+ queryCursor++;
411
+ unmatchedCursor = textCursor + 1;
412
+ }
413
+ const hasRemainingText = unmatchedCursor < text.length;
414
+ if (hasRemainingText) {
415
+ segments.push({ text: text.slice(unmatchedCursor), match: false });
416
+ }
417
+ const isFullMatch = queryCursor === query.length;
418
+ if (!isFullMatch) {
419
+ return [{ text, match: false }];
420
+ }
421
+ return segments;
422
+ }
423
+
413
424
  // ../core/src/default-styles.ts
414
425
  var DEFAULT_STYLES = `
415
426
  .pro6pp-wrapper {
@@ -417,18 +428,20 @@ var DEFAULT_STYLES = `
417
428
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
418
429
  box-sizing: border-box;
419
430
  width: 100%;
431
+ -webkit-tap-highlight-color: transparent;
420
432
  }
421
433
  .pro6pp-wrapper * {
422
434
  box-sizing: border-box;
423
435
  }
424
436
  .pro6pp-input {
425
437
  width: 100%;
426
- padding: 10px 12px;
438
+ padding: 12px 14px;
427
439
  padding-right: 48px;
428
440
  border: 1px solid #e0e0e0;
429
- border-radius: 4px;
441
+ border-radius: 8px;
430
442
  font-size: 16px;
431
443
  line-height: 1.5;
444
+ appearance: none;
432
445
  transition: border-color 0.2s, box-shadow 0.2s;
433
446
  }
434
447
  .pro6pp-input:focus {
@@ -439,12 +452,11 @@ var DEFAULT_STYLES = `
439
452
 
440
453
  .pro6pp-input-addons {
441
454
  position: absolute;
442
- right: 6px;
455
+ right: 4px;
443
456
  top: 0;
444
457
  bottom: 0;
445
458
  display: flex;
446
459
  align-items: center;
447
- gap: 2px;
448
460
  pointer-events: none;
449
461
  }
450
462
  .pro6pp-input-addons > * {
@@ -454,32 +466,33 @@ var DEFAULT_STYLES = `
454
466
  .pro6pp-clear-button {
455
467
  background: none;
456
468
  border: none;
457
- width: 28px;
458
- height: 28px;
469
+ width: 40px;
470
+ height: 40px;
459
471
  cursor: pointer;
460
472
  color: #a3a3a3;
461
473
  display: flex;
462
474
  align-items: center;
463
475
  justify-content: center;
464
476
  border-radius: 50%;
465
- transition: color 0.2s, background-color 0.2s, transform 0.1s;
477
+ transition: color 0.2s, background-color 0.2s;
478
+ touch-action: manipulation;
466
479
  }
467
- .pro6pp-clear-button:hover {
468
- color: #1f2937;
469
- background-color: #f3f4f6;
480
+
481
+ @media (hover: hover) {
482
+ .pro6pp-clear-button:hover {
483
+ color: #1f2937;
484
+ background-color: #f3f4f6;
485
+ }
470
486
  }
487
+
471
488
  .pro6pp-clear-button:active {
472
- transform: scale(0.92);
473
- }
474
- .pro6pp-clear-button svg {
475
- width: 18px;
476
- height: 18px;
489
+ background-color: #f3f4f6;
477
490
  }
478
491
 
479
492
  .pro6pp-loader {
480
- width: 18px;
481
- height: 18px;
482
- margin: 0 4px;
493
+ width: 20px;
494
+ height: 20px;
495
+ margin: 0 8px;
483
496
  border: 2px solid #e0e0e0;
484
497
  border-top-color: #6b7280;
485
498
  border-radius: 50%;
@@ -492,38 +505,58 @@ var DEFAULT_STYLES = `
492
505
  top: 100%;
493
506
  left: 0;
494
507
  right: 0;
495
- z-index: 9999;
496
508
  margin-top: 4px;
497
- background: white;
498
- border: 1px solid #e0e0e0;
499
- border-radius: 4px;
500
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
501
- max-height: 300px;
509
+ background: #ffffff;
510
+ border: 1px solid #e5e7eb;
511
+ border-radius: 6px;
512
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
513
+ z-index: 9999;
514
+ padding: 0;
515
+ max-height: 260px;
502
516
  overflow-y: auto;
503
517
  display: flex;
504
518
  flex-direction: column;
505
519
  }
520
+
521
+ @media (max-height: 500px) {
522
+ .pro6pp-dropdown {
523
+ max-height: 140px;
524
+ }
525
+ }
526
+
506
527
  .pro6pp-list {
507
- list-style: none !important;
508
- padding: 0 !important;
509
- margin: 0 !important;
510
- flex-grow: 1;
528
+ list-style: none;
529
+ margin: 0;
530
+ padding: 0;
531
+ width: 100%;
511
532
  }
533
+
512
534
  .pro6pp-item {
513
- padding: 12px 16px;
535
+ padding: 12px 14px;
514
536
  cursor: pointer;
515
537
  display: flex;
516
- flex-direction: row;
517
538
  align-items: center;
518
- color: #111827;
519
539
  font-size: 14px;
520
- line-height: 1.2;
521
- white-space: nowrap;
522
- overflow: hidden;
540
+ color: #374151;
541
+ border-bottom: 1px solid #f3f4f6;
542
+ transition: background-color 0.1s;
543
+ flex-shrink: 0;
544
+ }
545
+
546
+ .pro6pp-item:last-child {
547
+ border-bottom: none;
548
+ }
549
+
550
+ @media (hover: hover) {
551
+ .pro6pp-item:hover, .pro6pp-item--active {
552
+ background-color: #f9fafb;
553
+ }
523
554
  }
524
- .pro6pp-item:hover, .pro6pp-item--active {
525
- background-color: #f9fafb;
555
+
556
+ .pro6pp-item:active {
557
+ background-color: #f3f4f6;
526
558
  }
559
+
527
560
  .pro6pp-item__label {
528
561
  font-weight: 500;
529
562
  flex-shrink: 0;
@@ -531,37 +564,37 @@ var DEFAULT_STYLES = `
531
564
  .pro6pp-item__subtitle {
532
565
  font-size: 14px;
533
566
  color: #6b7280;
534
- overflow: hidden;
535
- text-overflow: ellipsis;
536
- flex-shrink: 1;
567
+ flex-grow: 1;
537
568
  }
538
569
  .pro6pp-item__chevron {
539
- margin-left: auto;
570
+ color: #d1d5db;
540
571
  display: flex;
541
572
  align-items: center;
542
- color: #9ca3af;
543
- padding-left: 8px;
573
+ margin-left: auto;
544
574
  }
575
+
545
576
  .pro6pp-no-results {
546
- padding: 16px;
577
+ padding: 24px 16px;
547
578
  color: #6b7280;
548
- font-size: 14px;
579
+ font-size: 15px;
549
580
  text-align: center;
550
581
  }
582
+
551
583
  .pro6pp-load-more {
552
584
  width: 100%;
553
- padding: 10px;
585
+ padding: 14px;
554
586
  background: #f9fafb;
555
587
  border: none;
556
588
  border-top: 1px solid #e0e0e0;
557
589
  color: #3b82f6;
558
- font-size: 13px;
590
+ font-size: 14px;
559
591
  font-weight: 600;
560
592
  cursor: pointer;
561
- transition: background-color 0.2s;
562
593
  flex-shrink: 0;
594
+ touch-action: manipulation;
563
595
  }
564
- .pro6pp-load-more:hover {
596
+
597
+ .pro6pp-load-more:active {
565
598
  background-color: #f3f4f6;
566
599
  }
567
600
 
@@ -571,6 +604,12 @@ var DEFAULT_STYLES = `
571
604
  `;
572
605
 
573
606
  // src/index.tsx
607
+ var HighlightedText = ({ text, query }) => {
608
+ const segments = (0, import_react.useMemo)(() => getHighlightSegments(text, query), [text, query]);
609
+ return /* @__PURE__ */ import_react.default.createElement("span", { className: "pro6pp-item__label" }, segments.map(
610
+ (seg, i) => seg.match ? /* @__PURE__ */ import_react.default.createElement("strong", { key: i, className: "pro6pp-item__label--match" }, seg.text) : seg.text
611
+ ));
612
+ };
574
613
  function useInfer(config) {
575
614
  const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
576
615
  const callbacksRef = (0, import_react.useRef)({
@@ -688,6 +727,11 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
688
727
  className: "pro6pp-input",
689
728
  placeholder,
690
729
  autoComplete: "off",
730
+ autoCorrect: "off",
731
+ autoCapitalize: "none",
732
+ spellCheck: "false",
733
+ inputMode: "search",
734
+ enterKeyHint: "search",
691
735
  ...inputProps,
692
736
  ...coreInputProps,
693
737
  onFocus: (e) => {
@@ -739,7 +783,7 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
739
783
  onMouseDown: (e) => e.preventDefault(),
740
784
  onClick: () => handleSelect(item)
741
785
  },
742
- renderItem ? renderItem(item, isActive) : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("span", { className: "pro6pp-item__label" }, item.label), secondaryText && /* @__PURE__ */ import_react.default.createElement("span", { className: "pro6pp-item__subtitle" }, ", ", secondaryText), showChevron && /* @__PURE__ */ import_react.default.createElement("div", { className: "pro6pp-item__chevron" }, /* @__PURE__ */ import_react.default.createElement(
786
+ renderItem ? renderItem(item, isActive) : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(HighlightedText, { text: item.label, query: state.query }), secondaryText && /* @__PURE__ */ import_react.default.createElement("span", { className: "pro6pp-item__subtitle" }, ", ", secondaryText), showChevron && /* @__PURE__ */ import_react.default.createElement("div", { className: "pro6pp-item__chevron" }, /* @__PURE__ */ import_react.default.createElement(
743
787
  "svg",
744
788
  {
745
789
  width: "16",
package/dist/index.js CHANGED
@@ -57,7 +57,6 @@ var InferCore = class {
57
57
  __publicField(this, "state");
58
58
  __publicField(this, "abortController", null);
59
59
  __publicField(this, "debouncedFetch");
60
- __publicField(this, "isSelecting", false);
61
60
  this.country = config.country;
62
61
  this.authKey = config.authKey;
63
62
  this.explicitApiUrl = config.apiUrl;
@@ -81,10 +80,6 @@ var InferCore = class {
81
80
  * @param value The raw string from the input field.
82
81
  */
83
82
  handleInput(value) {
84
- if (this.isSelecting) {
85
- this.isSelecting = false;
86
- return;
87
- }
88
83
  this.currentLimit = this.baseLimit;
89
84
  const isEditingFinal = this.state.stage === "final" && value !== this.state.query;
90
85
  this.updateState({
@@ -113,7 +108,7 @@ var InferCore = class {
113
108
  * Supports:
114
109
  * - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list.
115
110
  * - `Enter`: Select the currently highlighted suggestion.
116
- * - `Space`: Automatically inserts a comma if a numeric house number is detected.
111
+ * - `Space`: Automatically inserts a comma if a numeric street number is detected.
117
112
  * @param event The keyboard event from the input element.
118
113
  */
119
114
  handleKeyDown(event) {
@@ -175,14 +170,13 @@ var InferCore = class {
175
170
  }
176
171
  const valueObj = typeof item !== "string" && typeof item.value === "object" ? item.value : void 0;
177
172
  const isFullResult = !!valueObj && Object.keys(valueObj).length > 0;
178
- this.isSelecting = true;
179
173
  if (this.state.stage === "final" || isFullResult) {
180
174
  let finalQuery = label;
181
175
  if (valueObj && Object.keys(valueObj).length > 0) {
182
- const { street, street_number, house_number, city } = valueObj;
183
- const number = street_number || house_number;
184
- if (street && number && city) {
185
- finalQuery = `${street} ${number}, ${city}`;
176
+ const { street, street_number, city, addition } = valueObj;
177
+ if (street && street_number && city) {
178
+ const suffix = addition ? ` ${addition}` : "";
179
+ finalQuery = `${street} ${street_number}${suffix}, ${city}`;
186
180
  }
187
181
  }
188
182
  this.finishSelection(finalQuery, valueObj);
@@ -195,7 +189,7 @@ var InferCore = class {
195
189
  shouldAutoInsertComma(currentVal) {
196
190
  const isStartOfSegmentAndNumeric = !currentVal.includes(",") && PATTERNS.DIGITS_1_3.test(currentVal.trim());
197
191
  if (isStartOfSegmentAndNumeric) return true;
198
- if (this.state.stage === "house_number") {
192
+ if (this.state.stage === "street_number") {
199
193
  const currentFragment = this.getCurrentFragment(currentVal);
200
194
  return PATTERNS.DIGITS_1_3.test(currentFragment);
201
195
  }
@@ -222,7 +216,12 @@ var InferCore = class {
222
216
  nextQuery = `${subtitle}, ${text}, `;
223
217
  } else {
224
218
  const prefix = this.getQueryPrefix(query);
225
- nextQuery = prefix ? `${prefix} ${text}, ${subtitle}, ` : `${text}, ${subtitle}, `;
219
+ const shouldAddSubtitle = !prefix || !prefix.includes(subtitle);
220
+ if (shouldAddSubtitle) {
221
+ nextQuery = prefix ? `${prefix} ${text}, ${subtitle}, ` : `${text}, ${subtitle}, `;
222
+ } else {
223
+ nextQuery = prefix ? `${prefix} ${text}, ` : `${text}, `;
224
+ }
226
225
  }
227
226
  this.updateQueryAndFetch(nextQuery);
228
227
  return;
@@ -232,12 +231,12 @@ var InferCore = class {
232
231
  return;
233
232
  }
234
233
  const hasComma = query.includes(",");
235
- const isFirstSegment = !hasComma && (stage === "city" || stage === "street" || stage === "house_number_first");
234
+ const isFirstSegment = !hasComma && (stage === "city" || stage === "street" || stage === "street_number_first");
236
235
  if (isFirstSegment) {
237
236
  nextQuery = `${text}, `;
238
237
  } else {
239
238
  nextQuery = this.replaceLastSegment(query, text);
240
- if (stage !== "house_number") {
239
+ if (stage !== "street_number") {
241
240
  nextQuery += ", ";
242
241
  }
243
242
  }
@@ -299,8 +298,6 @@ var InferCore = class {
299
298
  stage: data.stage,
300
299
  isLoading: false
301
300
  };
302
- let autoSelect = false;
303
- let autoSelectItem = null;
304
301
  const rawSuggestions = data.suggestions || [];
305
302
  const uniqueSuggestions = [];
306
303
  const seen = /* @__PURE__ */ new Set();
@@ -321,27 +318,11 @@ var InferCore = class {
321
318
  newState.suggestions = uniqueSuggestions;
322
319
  newState.cities = [];
323
320
  newState.streets = [];
324
- const firstItem = uniqueSuggestions[0];
325
- const hasFullValue = firstItem && typeof firstItem.value === "object" && firstItem.value !== null && Object.keys(firstItem.value).length > 0;
326
- if ((data.stage === "final" || hasFullValue) && uniqueSuggestions.length === 1) {
327
- autoSelect = true;
328
- autoSelectItem = firstItem;
329
- }
330
321
  }
331
322
  newState.isValid = data.stage === "final";
332
- if (autoSelect && autoSelectItem) {
333
- newState.query = autoSelectItem.label;
334
- newState.suggestions = [];
335
- newState.cities = [];
336
- newState.streets = [];
337
- newState.isValid = true;
338
- newState.hasMore = false;
339
- this.isSelecting = true;
340
- this.updateState(newState);
341
- const val = typeof autoSelectItem.value === "object" ? autoSelectItem.value : autoSelectItem.label;
342
- this.onSelect(val);
343
- } else {
344
- this.updateState(newState);
323
+ this.updateState(newState);
324
+ if (newState.isValid && uniqueSuggestions.length === 1) {
325
+ this.selectItem(uniqueSuggestions[0]);
345
326
  }
346
327
  }
347
328
  updateQueryAndFetch(nextQuery) {
@@ -384,6 +365,36 @@ var InferCore = class {
384
365
  }
385
366
  };
386
367
 
368
+ // ../core/src/highlight.ts
369
+ function getHighlightSegments(text, query) {
370
+ if (!query || !text) return [{ text, match: false }];
371
+ const segments = [];
372
+ const normalizedText = text.toLowerCase();
373
+ const normalizedQuery = query.toLowerCase();
374
+ let queryCursor = 0;
375
+ let unmatchedCursor = 0;
376
+ for (let textCursor = 0; textCursor < text.length; textCursor++) {
377
+ const isMatch = queryCursor < query.length && normalizedText[textCursor] === normalizedQuery[queryCursor];
378
+ if (!isMatch) continue;
379
+ const hasPrecedingUnmatched = textCursor > unmatchedCursor;
380
+ if (hasPrecedingUnmatched) {
381
+ segments.push({ text: text.slice(unmatchedCursor, textCursor), match: false });
382
+ }
383
+ segments.push({ text: text[textCursor], match: true });
384
+ queryCursor++;
385
+ unmatchedCursor = textCursor + 1;
386
+ }
387
+ const hasRemainingText = unmatchedCursor < text.length;
388
+ if (hasRemainingText) {
389
+ segments.push({ text: text.slice(unmatchedCursor), match: false });
390
+ }
391
+ const isFullMatch = queryCursor === query.length;
392
+ if (!isFullMatch) {
393
+ return [{ text, match: false }];
394
+ }
395
+ return segments;
396
+ }
397
+
387
398
  // ../core/src/default-styles.ts
388
399
  var DEFAULT_STYLES = `
389
400
  .pro6pp-wrapper {
@@ -391,18 +402,20 @@ var DEFAULT_STYLES = `
391
402
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
392
403
  box-sizing: border-box;
393
404
  width: 100%;
405
+ -webkit-tap-highlight-color: transparent;
394
406
  }
395
407
  .pro6pp-wrapper * {
396
408
  box-sizing: border-box;
397
409
  }
398
410
  .pro6pp-input {
399
411
  width: 100%;
400
- padding: 10px 12px;
412
+ padding: 12px 14px;
401
413
  padding-right: 48px;
402
414
  border: 1px solid #e0e0e0;
403
- border-radius: 4px;
415
+ border-radius: 8px;
404
416
  font-size: 16px;
405
417
  line-height: 1.5;
418
+ appearance: none;
406
419
  transition: border-color 0.2s, box-shadow 0.2s;
407
420
  }
408
421
  .pro6pp-input:focus {
@@ -413,12 +426,11 @@ var DEFAULT_STYLES = `
413
426
 
414
427
  .pro6pp-input-addons {
415
428
  position: absolute;
416
- right: 6px;
429
+ right: 4px;
417
430
  top: 0;
418
431
  bottom: 0;
419
432
  display: flex;
420
433
  align-items: center;
421
- gap: 2px;
422
434
  pointer-events: none;
423
435
  }
424
436
  .pro6pp-input-addons > * {
@@ -428,32 +440,33 @@ var DEFAULT_STYLES = `
428
440
  .pro6pp-clear-button {
429
441
  background: none;
430
442
  border: none;
431
- width: 28px;
432
- height: 28px;
443
+ width: 40px;
444
+ height: 40px;
433
445
  cursor: pointer;
434
446
  color: #a3a3a3;
435
447
  display: flex;
436
448
  align-items: center;
437
449
  justify-content: center;
438
450
  border-radius: 50%;
439
- transition: color 0.2s, background-color 0.2s, transform 0.1s;
451
+ transition: color 0.2s, background-color 0.2s;
452
+ touch-action: manipulation;
440
453
  }
441
- .pro6pp-clear-button:hover {
442
- color: #1f2937;
443
- background-color: #f3f4f6;
454
+
455
+ @media (hover: hover) {
456
+ .pro6pp-clear-button:hover {
457
+ color: #1f2937;
458
+ background-color: #f3f4f6;
459
+ }
444
460
  }
461
+
445
462
  .pro6pp-clear-button:active {
446
- transform: scale(0.92);
447
- }
448
- .pro6pp-clear-button svg {
449
- width: 18px;
450
- height: 18px;
463
+ background-color: #f3f4f6;
451
464
  }
452
465
 
453
466
  .pro6pp-loader {
454
- width: 18px;
455
- height: 18px;
456
- margin: 0 4px;
467
+ width: 20px;
468
+ height: 20px;
469
+ margin: 0 8px;
457
470
  border: 2px solid #e0e0e0;
458
471
  border-top-color: #6b7280;
459
472
  border-radius: 50%;
@@ -466,38 +479,58 @@ var DEFAULT_STYLES = `
466
479
  top: 100%;
467
480
  left: 0;
468
481
  right: 0;
469
- z-index: 9999;
470
482
  margin-top: 4px;
471
- background: white;
472
- border: 1px solid #e0e0e0;
473
- border-radius: 4px;
474
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
475
- max-height: 300px;
483
+ background: #ffffff;
484
+ border: 1px solid #e5e7eb;
485
+ border-radius: 6px;
486
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
487
+ z-index: 9999;
488
+ padding: 0;
489
+ max-height: 260px;
476
490
  overflow-y: auto;
477
491
  display: flex;
478
492
  flex-direction: column;
479
493
  }
494
+
495
+ @media (max-height: 500px) {
496
+ .pro6pp-dropdown {
497
+ max-height: 140px;
498
+ }
499
+ }
500
+
480
501
  .pro6pp-list {
481
- list-style: none !important;
482
- padding: 0 !important;
483
- margin: 0 !important;
484
- flex-grow: 1;
502
+ list-style: none;
503
+ margin: 0;
504
+ padding: 0;
505
+ width: 100%;
485
506
  }
507
+
486
508
  .pro6pp-item {
487
- padding: 12px 16px;
509
+ padding: 12px 14px;
488
510
  cursor: pointer;
489
511
  display: flex;
490
- flex-direction: row;
491
512
  align-items: center;
492
- color: #111827;
493
513
  font-size: 14px;
494
- line-height: 1.2;
495
- white-space: nowrap;
496
- overflow: hidden;
514
+ color: #374151;
515
+ border-bottom: 1px solid #f3f4f6;
516
+ transition: background-color 0.1s;
517
+ flex-shrink: 0;
518
+ }
519
+
520
+ .pro6pp-item:last-child {
521
+ border-bottom: none;
522
+ }
523
+
524
+ @media (hover: hover) {
525
+ .pro6pp-item:hover, .pro6pp-item--active {
526
+ background-color: #f9fafb;
527
+ }
497
528
  }
498
- .pro6pp-item:hover, .pro6pp-item--active {
499
- background-color: #f9fafb;
529
+
530
+ .pro6pp-item:active {
531
+ background-color: #f3f4f6;
500
532
  }
533
+
501
534
  .pro6pp-item__label {
502
535
  font-weight: 500;
503
536
  flex-shrink: 0;
@@ -505,37 +538,37 @@ var DEFAULT_STYLES = `
505
538
  .pro6pp-item__subtitle {
506
539
  font-size: 14px;
507
540
  color: #6b7280;
508
- overflow: hidden;
509
- text-overflow: ellipsis;
510
- flex-shrink: 1;
541
+ flex-grow: 1;
511
542
  }
512
543
  .pro6pp-item__chevron {
513
- margin-left: auto;
544
+ color: #d1d5db;
514
545
  display: flex;
515
546
  align-items: center;
516
- color: #9ca3af;
517
- padding-left: 8px;
547
+ margin-left: auto;
518
548
  }
549
+
519
550
  .pro6pp-no-results {
520
- padding: 16px;
551
+ padding: 24px 16px;
521
552
  color: #6b7280;
522
- font-size: 14px;
553
+ font-size: 15px;
523
554
  text-align: center;
524
555
  }
556
+
525
557
  .pro6pp-load-more {
526
558
  width: 100%;
527
- padding: 10px;
559
+ padding: 14px;
528
560
  background: #f9fafb;
529
561
  border: none;
530
562
  border-top: 1px solid #e0e0e0;
531
563
  color: #3b82f6;
532
- font-size: 13px;
564
+ font-size: 14px;
533
565
  font-weight: 600;
534
566
  cursor: pointer;
535
- transition: background-color 0.2s;
536
567
  flex-shrink: 0;
568
+ touch-action: manipulation;
537
569
  }
538
- .pro6pp-load-more:hover {
570
+
571
+ .pro6pp-load-more:active {
539
572
  background-color: #f3f4f6;
540
573
  }
541
574
 
@@ -545,6 +578,12 @@ var DEFAULT_STYLES = `
545
578
  `;
546
579
 
547
580
  // src/index.tsx
581
+ var HighlightedText = ({ text, query }) => {
582
+ const segments = useMemo(() => getHighlightSegments(text, query), [text, query]);
583
+ return /* @__PURE__ */ React.createElement("span", { className: "pro6pp-item__label" }, segments.map(
584
+ (seg, i) => seg.match ? /* @__PURE__ */ React.createElement("strong", { key: i, className: "pro6pp-item__label--match" }, seg.text) : seg.text
585
+ ));
586
+ };
548
587
  function useInfer(config) {
549
588
  const [state, setState] = useState(INITIAL_STATE);
550
589
  const callbacksRef = useRef({
@@ -662,6 +701,11 @@ var Pro6PPInfer = forwardRef(
662
701
  className: "pro6pp-input",
663
702
  placeholder,
664
703
  autoComplete: "off",
704
+ autoCorrect: "off",
705
+ autoCapitalize: "none",
706
+ spellCheck: "false",
707
+ inputMode: "search",
708
+ enterKeyHint: "search",
665
709
  ...inputProps,
666
710
  ...coreInputProps,
667
711
  onFocus: (e) => {
@@ -713,7 +757,7 @@ var Pro6PPInfer = forwardRef(
713
757
  onMouseDown: (e) => e.preventDefault(),
714
758
  onClick: () => handleSelect(item)
715
759
  },
716
- renderItem ? renderItem(item, isActive) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "pro6pp-item__label" }, item.label), secondaryText && /* @__PURE__ */ React.createElement("span", { className: "pro6pp-item__subtitle" }, ", ", secondaryText), showChevron && /* @__PURE__ */ React.createElement("div", { className: "pro6pp-item__chevron" }, /* @__PURE__ */ React.createElement(
760
+ 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(
717
761
  "svg",
718
762
  {
719
763
  width: "16",
package/package.json CHANGED
@@ -20,7 +20,7 @@
20
20
  "url": "https://github.com/pro6pp/infer-sdk/issues"
21
21
  },
22
22
  "sideEffects": false,
23
- "version": "0.0.2-beta.10",
23
+ "version": "0.0.2-beta.12",
24
24
  "main": "./dist/index.cjs",
25
25
  "module": "./dist/index.js",
26
26
  "types": "./dist/index.d.ts",
@@ -46,7 +46,7 @@
46
46
  "react": ">=16"
47
47
  },
48
48
  "dependencies": {
49
- "@pro6pp/infer-core": "0.0.2-beta.8"
49
+ "@pro6pp/infer-core": "0.0.2-beta.10"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@testing-library/dom": "^10.4.1",