@portabletext/plugin-typeahead-picker 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13,26 +13,30 @@ function defineTypeaheadPicker(config) {
13
13
  _id: /* @__PURE__ */ Symbol("typeahead-picker")
14
14
  };
15
15
  }
16
- function extractKeywordFromPattern(matchedText, pattern, autoCompleteWith) {
17
- if (autoCompleteWith && matchedText.endsWith(autoCompleteWith)) {
18
- const strippedMatch = matchedText.slice(0, -autoCompleteWith.length).match(pattern), strippedKeyword = strippedMatch ? strippedMatch[1] ?? strippedMatch[0] : "";
19
- if (strippedKeyword.length > 0)
20
- return strippedKeyword;
21
- }
22
- const match = matchedText.match(pattern);
23
- return match ? match[1] ?? match[0] : matchedText;
16
+ function extractKeyword(patternText, triggerPattern, delimiter, completePattern) {
17
+ const triggerMatch = patternText.match(triggerPattern);
18
+ if (!triggerMatch || triggerMatch.index !== 0)
19
+ return patternText;
20
+ let keyword = patternText.slice(triggerMatch[0].length);
21
+ return delimiter && keyword.endsWith(delimiter) && (completePattern && (() => {
22
+ const completeMatch = patternText.match(completePattern);
23
+ return completeMatch && completeMatch.index === 0 && completeMatch[0] === patternText;
24
+ })() || keyword.length > delimiter.length) && (keyword = keyword.slice(0, -delimiter.length)), keyword;
24
25
  }
25
26
  function escapeRegExp(str) {
26
27
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27
28
  }
28
- function normalizePattern(pattern) {
29
- return new RegExp(pattern.source);
29
+ function buildTriggerPattern(definition) {
30
+ return new RegExp(definition.trigger.source);
31
+ }
32
+ function buildPartialPattern(definition) {
33
+ return new RegExp(definition.trigger.source + definition.keyword.source);
30
34
  }
31
35
  function buildCompletePattern(definition) {
32
- if (!definition.autoCompleteWith)
36
+ if (!definition.delimiter)
33
37
  return;
34
- const autoCompleteWith = escapeRegExp(definition.autoCompleteWith);
35
- return new RegExp(`${definition.pattern.source}${autoCompleteWith}`);
38
+ const escapedDelimiter = escapeRegExp(definition.delimiter);
39
+ return new RegExp(definition.trigger.source + definition.keyword.source + escapedDelimiter);
36
40
  }
37
41
  const arrowUpShortcut = createKeyboardShortcut({
38
42
  default: [{
@@ -158,79 +162,47 @@ function createKeywordFoundEvent(payload) {
158
162
  ...payload
159
163
  };
160
164
  }
161
- function extractFullMatchFromFocusSpan(focusSpan) {
165
+ function extractPatternTextFromFocusSpan(focusSpan) {
162
166
  return focusSpan.textBefore.length > 0 && focusSpan.textAfter.length > 0 ? focusSpan.node.text.slice(focusSpan.textBefore.length, -focusSpan.textAfter.length) : focusSpan.textBefore.length > 0 ? focusSpan.node.text.slice(focusSpan.textBefore.length) : focusSpan.textAfter.length > 0 ? focusSpan.node.text.slice(0, -focusSpan.textAfter.length) : focusSpan.node.text;
163
167
  }
164
- function validateFocusSpan(currentFocusSpan, editor, partialPattern, completePattern) {
165
- const snapshot = editor.getSnapshot(), focusSpan = getFocusSpan(snapshot);
166
- if (!snapshot.context.selection || !focusSpan)
167
- return;
168
- const nextSpan = getNextSpan({
169
- ...snapshot,
170
- context: {
171
- ...snapshot.context,
172
- selection: {
173
- anchor: {
174
- path: currentFocusSpan.path,
175
- offset: 0
176
- },
177
- focus: {
178
- path: currentFocusSpan.path,
179
- offset: 0
180
- }
181
- }
182
- }
183
- });
184
- if (!isEqualPaths(focusSpan.path, currentFocusSpan.path))
185
- return nextSpan && currentFocusSpan.textAfter.length === 0 && snapshot.context.selection.focus.offset === 0 && isSelectionCollapsed(snapshot) ? currentFocusSpan : void 0;
186
- if (!focusSpan.node.text.startsWith(currentFocusSpan.textBefore) || !focusSpan.node.text.endsWith(currentFocusSpan.textAfter))
187
- return;
188
- const keywordAnchor = {
189
- path: focusSpan.path,
190
- offset: currentFocusSpan.textBefore.length
191
- }, keywordFocus = {
192
- path: focusSpan.path,
193
- offset: focusSpan.node.text.length - currentFocusSpan.textAfter.length
194
- }, selectionIsBeforeKeyword = isPointAfterSelection(keywordAnchor)(snapshot), selectionIsAfterKeyword = isPointBeforeSelection(keywordFocus)(snapshot);
195
- if (selectionIsBeforeKeyword || selectionIsAfterKeyword)
196
- return;
197
- const keywordText = focusSpan.node.text.slice(currentFocusSpan.textBefore.length, currentFocusSpan.textAfter.length > 0 ? -currentFocusSpan.textAfter.length : void 0), patternMatch = keywordText.match(partialPattern);
198
- if (!patternMatch || patternMatch.index !== 0)
199
- return;
200
- let matchEnd = currentFocusSpan.textBefore.length + patternMatch[0].length;
201
- if (completePattern) {
202
- const completeMatch = keywordText.match(completePattern);
203
- completeMatch && completeMatch.index === 0 && completeMatch[0] === keywordText && (matchEnd = currentFocusSpan.textBefore.length + completeMatch[0].length);
204
- }
205
- if (!(snapshot.context.selection.focus.offset > matchEnd))
206
- return {
207
- node: focusSpan.node,
208
- path: focusSpan.path,
209
- textBefore: currentFocusSpan.textBefore,
210
- textAfter: currentFocusSpan.textAfter
211
- };
212
- }
213
- function extractKeywordFromMatch(match, pattern, autoCompleteWith) {
214
- const firstGroupMatch = match.groupMatches[0];
215
- return firstGroupMatch && firstGroupMatch.text.length > 0 ? firstGroupMatch.text : extractKeywordFromPattern(match.text, pattern, autoCompleteWith);
216
- }
217
168
  function createInputRules(definition) {
218
- const rules = [], partialPattern = definition.pattern, completePattern = buildCompletePattern(definition);
169
+ const rules = [], triggerPattern = buildTriggerPattern(definition), partialPattern = buildPartialPattern(definition), completePattern = buildCompletePattern(definition);
219
170
  if (completePattern) {
220
- const completeTriggerRule = defineInputRule({
171
+ const completeRule = defineInputRule({
221
172
  on: completePattern,
222
173
  guard: ({
223
174
  snapshot,
224
175
  event
225
176
  }) => {
226
177
  const lastMatch = event.matches.at(-1);
227
- if (!lastMatch || lastMatch.targetOffsets.anchor.offset < event.textBefore.length)
178
+ if (lastMatch === void 0)
228
179
  return !1;
180
+ if (lastMatch.targetOffsets.anchor.offset < event.textBefore.length) {
181
+ const insertedMatch = event.textInserted.match(completePattern);
182
+ if (insertedMatch === null || insertedMatch.index !== 0)
183
+ return !1;
184
+ const triggerState2 = getTriggerState(snapshot);
185
+ return triggerState2 ? {
186
+ ...triggerState2,
187
+ lastMatch: {
188
+ ...lastMatch,
189
+ text: event.textInserted,
190
+ targetOffsets: {
191
+ ...lastMatch.targetOffsets,
192
+ anchor: {
193
+ ...lastMatch.targetOffsets.anchor,
194
+ offset: event.textBefore.length
195
+ }
196
+ }
197
+ },
198
+ extractedKeyword: extractKeyword(event.textInserted, triggerPattern, definition.delimiter, completePattern)
199
+ } : !1;
200
+ }
229
201
  const triggerState = getTriggerState(snapshot);
230
202
  return triggerState ? {
231
203
  ...triggerState,
232
204
  lastMatch,
233
- extractedKeyword: extractKeywordFromMatch(lastMatch, partialPattern, definition.autoCompleteWith)
205
+ extractedKeyword: extractKeyword(lastMatch.text, triggerPattern, definition.delimiter, completePattern)
234
206
  } : !1;
235
207
  },
236
208
  actions: [({
@@ -242,30 +214,75 @@ function createInputRules(definition) {
242
214
  pickerId: definition._id
243
215
  })]
244
216
  });
245
- rules.push(completeTriggerRule);
217
+ rules.push(completeRule);
246
218
  }
247
- const partialTriggerRule = defineInputRule({
219
+ const partialRule = defineInputRule({
248
220
  on: partialPattern,
249
221
  guard: ({
250
222
  snapshot,
251
223
  event
252
224
  }) => {
253
225
  const lastMatch = event.matches.at(-1);
254
- if (!lastMatch || lastMatch.targetOffsets.anchor.offset < event.textBefore.length)
226
+ if (lastMatch === void 0)
255
227
  return !1;
256
- const triggerState = getTriggerState(snapshot);
257
- if (!triggerState)
258
- return !1;
259
- if (completePattern) {
260
- const completeMatch = triggerState.focusSpan.node.text.slice(lastMatch.targetOffsets.anchor.offset).match(completePattern);
261
- if (completeMatch && completeMatch.index === 0)
228
+ if (lastMatch.targetOffsets.anchor.offset < event.textBefore.length) {
229
+ const insertedMatch = event.textInserted.match(partialPattern);
230
+ if (insertedMatch === null || insertedMatch.index !== 0)
262
231
  return !1;
232
+ if (completePattern) {
233
+ const completeMatch = event.textInserted.match(completePattern);
234
+ if (completeMatch !== null && completeMatch.index === 0 && completeMatch[0] === event.textInserted)
235
+ return !1;
236
+ }
237
+ const triggerState2 = getTriggerState(snapshot);
238
+ return triggerState2 ? {
239
+ ...triggerState2,
240
+ lastMatch: {
241
+ ...lastMatch,
242
+ text: event.textInserted,
243
+ targetOffsets: {
244
+ ...lastMatch.targetOffsets,
245
+ anchor: {
246
+ ...lastMatch.targetOffsets.anchor,
247
+ offset: event.textBefore.length
248
+ }
249
+ }
250
+ },
251
+ extractedKeyword: extractKeyword(event.textInserted, triggerPattern, definition.delimiter)
252
+ } : !1;
263
253
  }
264
- return {
254
+ const triggerState = getTriggerState(snapshot);
255
+ return triggerState ? {
256
+ ...triggerState,
257
+ lastMatch,
258
+ extractedKeyword: extractKeyword(lastMatch.text, triggerPattern, definition.delimiter)
259
+ } : !1;
260
+ },
261
+ actions: [({
262
+ snapshot
263
+ }, payload) => createTriggerActions({
264
+ snapshot,
265
+ payload,
266
+ keywordState: "partial",
267
+ pickerId: definition._id
268
+ })]
269
+ });
270
+ rules.push(partialRule);
271
+ const triggerRule = defineInputRule({
272
+ on: triggerPattern,
273
+ guard: ({
274
+ snapshot,
275
+ event
276
+ }) => {
277
+ const lastMatch = event.matches.at(-1);
278
+ if (lastMatch === void 0 || event.textInserted !== lastMatch.text)
279
+ return !1;
280
+ const triggerState = getTriggerState(snapshot);
281
+ return triggerState ? {
265
282
  ...triggerState,
266
283
  lastMatch,
267
- extractedKeyword: extractKeywordFromMatch(lastMatch, partialPattern, definition.autoCompleteWith)
268
- };
284
+ extractedKeyword: ""
285
+ } : !1;
269
286
  },
270
287
  actions: [({
271
288
  snapshot
@@ -276,7 +293,7 @@ function createInputRules(definition) {
276
293
  pickerId: definition._id
277
294
  })]
278
295
  });
279
- return rules.push(partialTriggerRule), rules;
296
+ return rules.push(triggerRule), rules;
280
297
  }
281
298
  const triggerListenerCallback = () => ({
282
299
  sendBack,
@@ -404,7 +421,7 @@ const triggerListenerCallback = () => ({
404
421
  on: "keyboard.keydown",
405
422
  guard: ({
406
423
  event
407
- }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.fullMatch.length === 1,
424
+ }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.patternText.length === 1,
408
425
  actions: [({
409
426
  event
410
427
  }) => [forward(event), effect(() => {
@@ -418,7 +435,7 @@ const triggerListenerCallback = () => ({
418
435
  on: "keyboard.keydown",
419
436
  guard: ({
420
437
  event
421
- }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.fullMatch.length > 1 && context.matches.length === 0,
438
+ }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.patternText.length > 1 && context.matches.length === 0,
422
439
  actions: [() => [effect(() => {
423
440
  sendBack({
424
441
  type: "dismiss"
@@ -554,11 +571,11 @@ function createTypeaheadPickerMachine() {
554
571
  }) => {
555
572
  if (event.type !== "custom.typeahead trigger found" && event.type !== "custom.typeahead keyword found")
556
573
  return {};
557
- const focusSpan = event.focusSpan, fullMatch = extractFullMatchFromFocusSpan(focusSpan), keyword = event.extractedKeyword;
574
+ const focusSpan = event.focusSpan, patternText = extractPatternTextFromFocusSpan(focusSpan), keyword = event.extractedKeyword;
558
575
  if (context.definition.mode === "async" || context.definition.debounceMs)
559
576
  return {
560
577
  focusSpan,
561
- fullMatch,
578
+ patternText,
562
579
  keyword,
563
580
  isLoading: !0,
564
581
  selectedIndex: 0
@@ -568,82 +585,110 @@ function createTypeaheadPickerMachine() {
568
585
  });
569
586
  return {
570
587
  focusSpan,
571
- fullMatch,
588
+ patternText,
572
589
  keyword,
573
590
  matches,
574
- loadingFullMatch: fullMatch,
591
+ requestedKeyword: keyword,
575
592
  isLoading: !1,
576
593
  selectedIndex: 0
577
594
  };
578
595
  }),
579
- "handle selection changed": assign(({
596
+ "update focus span": assign({
597
+ focusSpan: ({
598
+ context
599
+ }) => {
600
+ if (!context.focusSpan)
601
+ return context.focusSpan;
602
+ const snapshot = context.editor.getSnapshot(), focusSpan = getFocusSpan(snapshot);
603
+ if (!snapshot.context.selection || !focusSpan)
604
+ return;
605
+ const nextSpan = getNextSpan({
606
+ ...snapshot,
607
+ context: {
608
+ ...snapshot.context,
609
+ selection: {
610
+ anchor: {
611
+ path: context.focusSpan.path,
612
+ offset: 0
613
+ },
614
+ focus: {
615
+ path: context.focusSpan.path,
616
+ offset: 0
617
+ }
618
+ }
619
+ }
620
+ });
621
+ if (!isEqualPaths(focusSpan.path, context.focusSpan.path))
622
+ return nextSpan && context.focusSpan.textAfter.length === 0 && snapshot.context.selection.focus.offset === 0 && isSelectionCollapsed(snapshot) ? context.focusSpan : void 0;
623
+ if (!focusSpan.node.text.startsWith(context.focusSpan.textBefore) || !focusSpan.node.text.endsWith(context.focusSpan.textAfter))
624
+ return;
625
+ const keywordAnchor = {
626
+ path: focusSpan.path,
627
+ offset: context.focusSpan.textBefore.length
628
+ }, keywordFocus = {
629
+ path: focusSpan.path,
630
+ offset: focusSpan.node.text.length - context.focusSpan.textAfter.length
631
+ }, selectionIsBeforeKeyword = isPointAfterSelection(keywordAnchor)(snapshot), selectionIsAfterKeyword = isPointBeforeSelection(keywordFocus)(snapshot);
632
+ if (!(selectionIsBeforeKeyword || selectionIsAfterKeyword))
633
+ return {
634
+ node: focusSpan.node,
635
+ path: focusSpan.path,
636
+ textBefore: context.focusSpan.textBefore,
637
+ textAfter: context.focusSpan.textAfter
638
+ };
639
+ }
640
+ }),
641
+ "update pattern text": assign(({
580
642
  context
581
643
  }) => {
582
644
  if (!context.focusSpan)
583
- return {
584
- focusSpan: void 0,
585
- fullMatch: "",
586
- keyword: "",
587
- matches: [],
588
- selectedIndex: 0,
589
- isLoading: !1
590
- };
591
- const updatedFocusSpan = validateFocusSpan(context.focusSpan, context.editor, context.partialPattern, context.completePattern);
592
- if (!updatedFocusSpan)
593
- return {
594
- focusSpan: void 0,
595
- fullMatch: "",
596
- keyword: "",
597
- matches: [],
598
- selectedIndex: 0,
599
- isLoading: !1
600
- };
601
- const fullMatch = extractFullMatchFromFocusSpan(updatedFocusSpan);
602
- if (fullMatch === context.fullMatch)
603
- return {
604
- focusSpan: updatedFocusSpan
605
- };
606
- const keyword = extractKeywordFromPattern(fullMatch, context.definition.pattern, context.definition.autoCompleteWith);
607
- if (context.definition.mode === "async" || context.definition.debounceMs)
608
- return {
609
- focusSpan: updatedFocusSpan,
610
- fullMatch,
611
- keyword,
612
- selectedIndex: 0,
613
- isLoading: context.isLoading || context.loadingFullMatch !== fullMatch
614
- };
615
- const matches = context.definition.getMatches({
645
+ return {};
646
+ const patternText = extractPatternTextFromFocusSpan(context.focusSpan);
647
+ return patternText === context.patternText ? {} : {
648
+ patternText,
649
+ selectedIndex: 0
650
+ };
651
+ }),
652
+ "update keyword": assign(({
653
+ context
654
+ }) => {
655
+ if (!context.focusSpan || !context.patternText)
656
+ return {};
657
+ const keyword = extractKeyword(context.patternText, context.triggerPattern, context.definition.delimiter, context.completePattern);
658
+ return keyword === context.keyword ? {} : {
616
659
  keyword
617
- });
618
- return {
619
- focusSpan: updatedFocusSpan,
620
- fullMatch,
621
- keyword,
622
- matches,
623
- loadingFullMatch: fullMatch,
624
- selectedIndex: 0,
625
- isLoading: !1
626
660
  };
627
661
  }),
662
+ "update matches": assign(({
663
+ context
664
+ }) => !context.focusSpan || !context.patternText ? {} : context.definition.mode === "async" || context.definition.debounceMs ? {
665
+ isLoading: context.isLoading || context.requestedKeyword !== context.keyword
666
+ } : {
667
+ matches: context.definition.getMatches({
668
+ keyword: context.keyword
669
+ }),
670
+ requestedKeyword: context.keyword,
671
+ isLoading: !1
672
+ }),
628
673
  "handle async load complete": assign(({
629
674
  context,
630
675
  event
631
676
  }) => {
632
677
  const output = event.output;
633
678
  return output.keyword !== context.keyword ? {
634
- isLoading: context.fullMatch !== context.loadingFullMatch
679
+ isLoading: context.keyword !== context.requestedKeyword
635
680
  } : {
636
681
  matches: output.matches,
637
- isLoading: context.fullMatch !== context.loadingFullMatch
682
+ isLoading: context.keyword !== context.requestedKeyword
638
683
  };
639
684
  }),
640
685
  reset: assign({
641
- fullMatch: "",
686
+ patternText: "",
642
687
  keyword: "",
643
688
  matches: [],
644
689
  selectedIndex: 0,
645
690
  isLoading: !1,
646
- loadingFullMatch: "",
691
+ requestedKeyword: "",
647
692
  focusSpan: void 0,
648
693
  error: void 0
649
694
  }),
@@ -664,7 +709,7 @@ function createTypeaheadPickerMachine() {
664
709
  }, params) => {
665
710
  if (!context.focusSpan)
666
711
  return;
667
- const match = params.exact ? getExactMatch(context.matches) : context.matches[context.selectedIndex];
712
+ const match = params.exact ? getFirstExactMatch(context.matches) : context.matches[context.selectedIndex];
668
713
  match && context.editor.send({
669
714
  type: "custom.typeahead insert match",
670
715
  match,
@@ -696,6 +741,24 @@ function createTypeaheadPickerMachine() {
696
741
  "no focus span": ({
697
742
  context
698
743
  }) => !context.focusSpan,
744
+ "invalid pattern": ({
745
+ context
746
+ }) => {
747
+ if (!context.patternText)
748
+ return !0;
749
+ const triggerMatch = context.patternText.match(context.triggerPattern);
750
+ if (triggerMatch && triggerMatch.index === 0 && triggerMatch[0] === context.patternText)
751
+ return !1;
752
+ const partialMatch = context.patternText.match(context.partialPattern);
753
+ if (partialMatch && partialMatch.index === 0 && partialMatch[0] === context.patternText)
754
+ return !1;
755
+ if (context.completePattern) {
756
+ const completeMatch = context.patternText.match(context.completePattern);
757
+ if (completeMatch && completeMatch.index === 0 && completeMatch[0] === context.patternText)
758
+ return !1;
759
+ }
760
+ return !0;
761
+ },
699
762
  "no debounce": ({
700
763
  context
701
764
  }) => !context.definition.debounceMs || context.definition.debounceMs === 0,
@@ -705,7 +768,7 @@ function createTypeaheadPickerMachine() {
705
768
  if (!context.completePattern || !context.focusSpan)
706
769
  return !1;
707
770
  const fullKeywordText = context.focusSpan.node.text.slice(context.focusSpan.textBefore.length, context.focusSpan.textAfter.length > 0 ? -context.focusSpan.textAfter.length : void 0), completeMatch = fullKeywordText.match(context.completePattern);
708
- return !completeMatch || completeMatch.index !== 0 || completeMatch[0] !== fullKeywordText ? !1 : hasExactlyOneExactMatch(context.matches);
771
+ return !completeMatch || completeMatch.index !== 0 || completeMatch[0] !== fullKeywordText ? !1 : hasAtLeastOneExactMatch(context.matches);
709
772
  },
710
773
  "has matches": ({
711
774
  context
@@ -724,14 +787,15 @@ function createTypeaheadPickerMachine() {
724
787
  }) => ({
725
788
  editor: input.editor,
726
789
  definition: input.definition,
727
- partialPattern: normalizePattern(input.definition.pattern),
790
+ triggerPattern: buildTriggerPattern(input.definition),
791
+ partialPattern: buildPartialPattern(input.definition),
728
792
  completePattern: buildCompletePattern(input.definition),
729
793
  matches: [],
730
794
  selectedIndex: 0,
731
795
  focusSpan: void 0,
732
- fullMatch: "",
796
+ patternText: "",
733
797
  keyword: "",
734
- loadingFullMatch: "",
798
+ requestedKeyword: "",
735
799
  error: void 0,
736
800
  isLoading: !1
737
801
  }),
@@ -778,7 +842,7 @@ function createTypeaheadPickerMachine() {
778
842
  onDone: [{
779
843
  guard: ({
780
844
  event
781
- }) => hasExactlyOneExactMatch(event.output.matches),
845
+ }) => hasAtLeastOneExactMatch(event.output.matches),
782
846
  target: "idle",
783
847
  actions: [assign({
784
848
  matches: ({
@@ -848,12 +912,15 @@ function createTypeaheadPickerMachine() {
848
912
  target: "idle"
849
913
  },
850
914
  "selection changed": {
851
- actions: ["handle selection changed", "update submit listener context", "update text insertion listener context"]
915
+ actions: ["update focus span", "update pattern text", "update keyword", "update matches", "update submit listener context", "update text insertion listener context"]
852
916
  }
853
917
  },
854
918
  always: [{
855
919
  guard: "no focus span",
856
920
  target: "idle"
921
+ }, {
922
+ guard: "invalid pattern",
923
+ target: "idle"
857
924
  }, {
858
925
  guard: "is complete keyword",
859
926
  actions: [{
@@ -879,9 +946,9 @@ function createTypeaheadPickerMachine() {
879
946
  },
880
947
  loading: {
881
948
  entry: [assign({
882
- loadingFullMatch: ({
949
+ requestedKeyword: ({
883
950
  context
884
- }) => context.fullMatch
951
+ }) => context.keyword
885
952
  })],
886
953
  initial: "debouncing",
887
954
  states: {
@@ -909,10 +976,10 @@ function createTypeaheadPickerMachine() {
909
976
  context,
910
977
  event
911
978
  }) => event.output.keyword !== context.keyword ? {
912
- isLoading: context.fullMatch !== context.loadingFullMatch
979
+ isLoading: context.patternText !== context.requestedKeyword
913
980
  } : {
914
981
  matches: event.output.matches,
915
- isLoading: context.fullMatch !== context.loadingFullMatch
982
+ isLoading: context.keyword !== context.requestedKeyword
916
983
  })]
917
984
  },
918
985
  onError: {
@@ -941,9 +1008,9 @@ function createTypeaheadPickerMachine() {
941
1008
  },
942
1009
  loading: {
943
1010
  entry: [assign({
944
- loadingFullMatch: ({
1011
+ requestedKeyword: ({
945
1012
  context
946
- }) => context.fullMatch
1013
+ }) => context.keyword
947
1014
  })],
948
1015
  initial: "debouncing",
949
1016
  states: {
@@ -1023,9 +1090,9 @@ function createTypeaheadPickerMachine() {
1023
1090
  },
1024
1091
  loading: {
1025
1092
  entry: [assign({
1026
- loadingFullMatch: ({
1093
+ requestedKeyword: ({
1027
1094
  context
1028
- }) => context.fullMatch
1095
+ }) => context.keyword
1029
1096
  })],
1030
1097
  initial: "debouncing",
1031
1098
  states: {
@@ -1066,12 +1133,11 @@ function createTypeaheadPickerMachine() {
1066
1133
  }
1067
1134
  });
1068
1135
  }
1069
- function hasExactlyOneExactMatch(matches) {
1070
- return matches.filter((match) => match?.type === "exact").length === 1;
1136
+ function hasAtLeastOneExactMatch(matches) {
1137
+ return matches.some((match) => match?.type === "exact");
1071
1138
  }
1072
- function getExactMatch(matches) {
1073
- const exactMatches = matches.filter((match) => match?.type === "exact");
1074
- return exactMatches.length === 1 ? exactMatches[0] : void 0;
1139
+ function getFirstExactMatch(matches) {
1140
+ return matches.find((match) => match?.type === "exact");
1075
1141
  }
1076
1142
  function useTypeaheadPicker(definition) {
1077
1143
  const $ = c(19), editor = useEditor();