@portabletext/plugin-typeahead-picker 2.1.0 → 3.0.1

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
@@ -1,5 +1,5 @@
1
+ import { keyGenerator, useEditor } from "@portabletext/editor";
1
2
  import { c } from "react/compiler-runtime";
2
- import { useEditor } from "@portabletext/editor";
3
3
  import { useActor } from "@xstate/react";
4
4
  import { defineBehavior, effect, forward, raise } from "@portabletext/editor/behaviors";
5
5
  import { getFocusSpan, getNextSpan, isSelectionCollapsed, isPointAfterSelection, isPointBeforeSelection, getMarkState, getPreviousSpan } from "@portabletext/editor/selectors";
@@ -10,7 +10,7 @@ import { setup, assign, sendTo, fromPromise, fromCallback } from "xstate";
10
10
  function defineTypeaheadPicker(config) {
11
11
  return {
12
12
  ...config,
13
- _id: /* @__PURE__ */ Symbol("typeahead-picker")
13
+ _id: keyGenerator()
14
14
  };
15
15
  }
16
16
  function extractKeyword(patternText, triggerPattern, delimiter, completePattern) {
@@ -343,20 +343,50 @@ const triggerListenerCallback = () => ({
343
343
  };
344
344
  }, escapeListenerCallback = () => ({
345
345
  sendBack,
346
- input
347
- }) => input.editor.registerBehavior({
348
- behavior: defineBehavior({
349
- on: "keyboard.keydown",
350
- guard: ({
351
- event
352
- }) => escapeShortcut.guard(event.originEvent),
353
- actions: [() => [effect(() => {
354
- sendBack({
355
- type: "dismiss"
356
- });
357
- })]]
358
- })
359
- }), arrowListenerCallback = () => ({
346
+ input,
347
+ receive
348
+ }) => {
349
+ let context = input.context;
350
+ return receive((event) => {
351
+ context = event.context;
352
+ }), input.context.editor.registerBehavior({
353
+ behavior: defineBehavior({
354
+ on: "keyboard.keydown",
355
+ guard: ({
356
+ event
357
+ }) => escapeShortcut.guard(event.originEvent),
358
+ actions: [({
359
+ snapshot,
360
+ dom
361
+ }) => {
362
+ if (!context.focusSpan || !context.definition.onDismiss)
363
+ return [effect(() => sendBack({
364
+ type: "close"
365
+ }))];
366
+ const patternSelection = {
367
+ anchor: {
368
+ path: context.focusSpan.path,
369
+ offset: context.focusSpan.textBefore.length
370
+ },
371
+ focus: {
372
+ path: context.focusSpan.path,
373
+ offset: context.focusSpan.node.text.length - context.focusSpan.textAfter.length
374
+ }
375
+ };
376
+ return [...context.definition.onDismiss.flatMap((actionSet) => actionSet({
377
+ snapshot,
378
+ dom,
379
+ event: {
380
+ type: "custom.typeahead dismiss",
381
+ patternSelection
382
+ }
383
+ }, !0)), effect(() => sendBack({
384
+ type: "close"
385
+ }))];
386
+ }]
387
+ })
388
+ });
389
+ }, arrowListenerCallback = () => ({
360
390
  sendBack,
361
391
  input
362
392
  }) => {
@@ -396,7 +426,52 @@ const triggerListenerCallback = () => ({
396
426
  sendBack({
397
427
  type: "selection changed"
398
428
  });
399
- }).unsubscribe, submitListenerCallback = () => ({
429
+ }).unsubscribe, dismissListenerCallback = () => ({
430
+ sendBack,
431
+ input,
432
+ receive
433
+ }) => {
434
+ let context = input.context;
435
+ return receive((event) => {
436
+ context = event.context;
437
+ }), input.context.editor.registerBehavior({
438
+ behavior: defineBehavior({
439
+ on: "custom.typeahead dismiss",
440
+ guard: ({
441
+ event
442
+ }) => event.pickerId === context.definition._id,
443
+ actions: [({
444
+ snapshot,
445
+ dom
446
+ }) => {
447
+ if (!context.focusSpan || !context.definition.onDismiss)
448
+ return [effect(() => sendBack({
449
+ type: "close"
450
+ }))];
451
+ const patternSelection = {
452
+ anchor: {
453
+ path: context.focusSpan.path,
454
+ offset: context.focusSpan.textBefore.length
455
+ },
456
+ focus: {
457
+ path: context.focusSpan.path,
458
+ offset: context.focusSpan.node.text.length - context.focusSpan.textAfter.length
459
+ }
460
+ };
461
+ return [...context.definition.onDismiss.flatMap((actionSet) => actionSet({
462
+ snapshot,
463
+ dom,
464
+ event: {
465
+ type: "custom.typeahead dismiss",
466
+ patternSelection
467
+ }
468
+ }, !0)), effect(() => sendBack({
469
+ type: "close"
470
+ }))];
471
+ }]
472
+ })
473
+ });
474
+ }, submitListenerCallback = () => ({
400
475
  sendBack,
401
476
  input,
402
477
  receive
@@ -435,7 +510,7 @@ const triggerListenerCallback = () => ({
435
510
  event
436
511
  }) => [forward(event), effect(() => {
437
512
  sendBack({
438
- type: "dismiss"
513
+ type: "close"
439
514
  });
440
515
  })]]
441
516
  })
@@ -445,11 +520,35 @@ const triggerListenerCallback = () => ({
445
520
  guard: ({
446
521
  event
447
522
  }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.patternText.length > 1 && context.matches.length === 0,
448
- actions: [() => [effect(() => {
449
- sendBack({
450
- type: "dismiss"
451
- });
452
- })]]
523
+ actions: [({
524
+ snapshot,
525
+ dom
526
+ }) => {
527
+ if (!context.focusSpan || !context.definition.onDismiss)
528
+ return [effect(() => sendBack({
529
+ type: "close"
530
+ }))];
531
+ const patternSelection = {
532
+ anchor: {
533
+ path: context.focusSpan.path,
534
+ offset: context.focusSpan.textBefore.length
535
+ },
536
+ focus: {
537
+ path: context.focusSpan.path,
538
+ offset: context.focusSpan.node.text.length - context.focusSpan.textAfter.length
539
+ }
540
+ };
541
+ return [...context.definition.onDismiss.flatMap((actionSet) => actionSet({
542
+ snapshot,
543
+ dom,
544
+ event: {
545
+ type: "custom.typeahead dismiss",
546
+ patternSelection
547
+ }
548
+ }, !0)), effect(() => sendBack({
549
+ type: "close"
550
+ }))];
551
+ }]
453
552
  })
454
553
  })];
455
554
  return () => {
@@ -482,7 +581,7 @@ const triggerListenerCallback = () => ({
482
581
  event
483
582
  }) => [forward(event), effect(() => {
484
583
  sendBack({
485
- type: "dismiss"
584
+ type: "close"
486
585
  });
487
586
  })]]
488
587
  })
@@ -510,26 +609,21 @@ const triggerListenerCallback = () => ({
510
609
  path: event.focusSpan.path,
511
610
  offset: event.focusSpan.node.text.length - event.focusSpan.textAfter.length
512
611
  }
513
- }, allActions = [effect(() => {
612
+ }, selectActions = input.context.definition.onSelect.flatMap((actionSet) => actionSet({
613
+ snapshot,
614
+ dom,
615
+ event: {
616
+ type: "custom.typeahead select",
617
+ match: event.match,
618
+ keyword: event.keyword,
619
+ patternSelection
620
+ }
621
+ }, !0));
622
+ return [effect(() => {
514
623
  sendBack({
515
- type: "dismiss"
624
+ type: "close"
516
625
  });
517
- })];
518
- for (const actionSet of input.context.definition.actions) {
519
- const actions = actionSet({
520
- snapshot,
521
- dom,
522
- event: {
523
- type: "custom.typeahead select",
524
- match: event.match,
525
- keyword: event.keyword,
526
- patternSelection
527
- }
528
- }, !0);
529
- for (const action of actions)
530
- allActions.push(action);
531
- }
532
- return allActions;
626
+ }), ...selectActions];
533
627
  }]
534
628
  })
535
629
  });
@@ -553,6 +647,7 @@ function createTypeaheadPickerMachine() {
553
647
  "submit listener": fromCallback(submitListenerCallback()),
554
648
  "text insertion listener": fromCallback(textInsertionListenerCallback()),
555
649
  "select match listener": fromCallback(selectMatchListenerCallback()),
650
+ "dismiss listener": fromCallback(dismissListenerCallback()),
556
651
  "get matches": fromPromise(async ({
557
652
  input
558
653
  }) => {
@@ -594,83 +689,84 @@ function createTypeaheadPickerMachine() {
594
689
  selectedIndex: 0
595
690
  };
596
691
  }),
597
- "update focus span": assign({
598
- focusSpan: ({
599
- context
600
- }) => {
601
- if (!context.focusSpan)
602
- return context.focusSpan;
603
- const snapshot = context.editor.getSnapshot(), focusSpan = getFocusSpan(snapshot);
604
- if (!snapshot.context.selection || !focusSpan)
605
- return;
606
- const nextSpan = getNextSpan({
607
- ...snapshot,
608
- context: {
609
- ...snapshot.context,
610
- selection: {
611
- anchor: {
612
- path: context.focusSpan.path,
613
- offset: 0
614
- },
615
- focus: {
616
- path: context.focusSpan.path,
617
- offset: 0
618
- }
619
- }
620
- }
621
- });
622
- if (!isEqualPaths(focusSpan.path, context.focusSpan.path))
623
- return nextSpan && context.focusSpan.textAfter.length === 0 && snapshot.context.selection.focus.offset === 0 && isSelectionCollapsed(snapshot) ? context.focusSpan : void 0;
624
- if (!focusSpan.node.text.startsWith(context.focusSpan.textBefore) || !focusSpan.node.text.endsWith(context.focusSpan.textAfter))
625
- return;
626
- const keywordAnchor = {
627
- path: focusSpan.path,
628
- offset: context.focusSpan.textBefore.length
629
- }, keywordFocus = {
630
- path: focusSpan.path,
631
- offset: focusSpan.node.text.length - context.focusSpan.textAfter.length
632
- }, selectionIsBeforeKeyword = isPointAfterSelection(keywordAnchor)(snapshot), selectionIsAfterKeyword = isPointBeforeSelection(keywordFocus)(snapshot);
633
- if (!(selectionIsBeforeKeyword || selectionIsAfterKeyword))
634
- return {
635
- node: focusSpan.node,
636
- path: focusSpan.path,
637
- textBefore: context.focusSpan.textBefore,
638
- textAfter: context.focusSpan.textAfter
639
- };
640
- }
641
- }),
642
- "update pattern text": assign(({
692
+ "handle selection changed": assign(({
643
693
  context
644
694
  }) => {
645
695
  if (!context.focusSpan)
646
- return {};
647
- const patternText = extractPatternTextFromFocusSpan(context.focusSpan);
648
- return patternText === context.patternText ? {} : {
649
- patternText,
650
- selectedIndex: 0
651
- };
652
- }),
653
- "update keyword": assign(({
654
- context
655
- }) => {
656
- if (!context.focusSpan || !context.patternText)
657
- return {};
658
- const keyword = extractKeyword(context.patternText, context.triggerPattern, context.definition.delimiter, context.completePattern);
659
- return keyword === context.keyword ? {} : {
696
+ return {
697
+ focusSpan: void 0
698
+ };
699
+ const snapshot = context.editor.getSnapshot(), currentFocusSpan = getFocusSpan(snapshot);
700
+ if (!snapshot.context.selection || !currentFocusSpan)
701
+ return {
702
+ focusSpan: void 0
703
+ };
704
+ const nextSpan = getNextSpan({
705
+ ...snapshot,
706
+ context: {
707
+ ...snapshot.context,
708
+ selection: {
709
+ anchor: {
710
+ path: context.focusSpan.path,
711
+ offset: 0
712
+ },
713
+ focus: {
714
+ path: context.focusSpan.path,
715
+ offset: 0
716
+ }
717
+ }
718
+ }
719
+ });
720
+ if (!isEqualPaths(currentFocusSpan.path, context.focusSpan.path))
721
+ return nextSpan && context.focusSpan.textAfter.length === 0 && snapshot.context.selection.focus.offset === 0 && isSelectionCollapsed(snapshot) ? {} : {
722
+ focusSpan: void 0
723
+ };
724
+ if (!currentFocusSpan.node.text.startsWith(context.focusSpan.textBefore))
725
+ return {
726
+ focusSpan: void 0
727
+ };
728
+ if (!currentFocusSpan.node.text.endsWith(context.focusSpan.textAfter))
729
+ return {
730
+ focusSpan: void 0
731
+ };
732
+ const keywordAnchor = {
733
+ path: currentFocusSpan.path,
734
+ offset: context.focusSpan.textBefore.length
735
+ }, keywordFocus = {
736
+ path: currentFocusSpan.path,
737
+ offset: currentFocusSpan.node.text.length - context.focusSpan.textAfter.length
738
+ }, selectionIsBeforeKeyword = isPointAfterSelection(keywordAnchor)(snapshot), selectionIsAfterKeyword = isPointBeforeSelection(keywordFocus)(snapshot);
739
+ if (selectionIsBeforeKeyword || selectionIsAfterKeyword)
740
+ return {
741
+ focusSpan: void 0
742
+ };
743
+ const focusSpan = {
744
+ node: currentFocusSpan.node,
745
+ path: currentFocusSpan.path,
746
+ textBefore: context.focusSpan.textBefore,
747
+ textAfter: context.focusSpan.textAfter
748
+ }, patternText = extractPatternTextFromFocusSpan(focusSpan), keyword = extractKeyword(patternText, context.triggerPattern, context.definition.delimiter, context.completePattern);
749
+ if (context.definition.mode === "async" || context.definition.debounceMs)
750
+ return {
751
+ focusSpan,
752
+ patternText,
753
+ keyword,
754
+ selectedIndex: patternText !== context.patternText ? 0 : context.selectedIndex,
755
+ isLoading: context.isLoading || context.requestedKeyword !== keyword
756
+ };
757
+ const matches = context.definition.getMatches({
660
758
  keyword
759
+ });
760
+ return {
761
+ focusSpan,
762
+ patternText,
763
+ keyword,
764
+ matches,
765
+ requestedKeyword: keyword,
766
+ selectedIndex: patternText !== context.patternText ? 0 : context.selectedIndex,
767
+ isLoading: !1
661
768
  };
662
769
  }),
663
- "update matches": assign(({
664
- context
665
- }) => !context.focusSpan || !context.patternText ? {} : context.definition.mode === "async" || context.definition.debounceMs ? {
666
- isLoading: context.isLoading || context.requestedKeyword !== context.keyword
667
- } : {
668
- matches: context.definition.getMatches({
669
- keyword: context.keyword
670
- }),
671
- requestedKeyword: context.keyword,
672
- isLoading: !1
673
- }),
674
770
  "handle async load complete": assign(({
675
771
  context,
676
772
  event
@@ -731,6 +827,18 @@ function createTypeaheadPickerMachine() {
731
827
  type: "context changed",
732
828
  context
733
829
  })),
830
+ "update escape listener context": sendTo("escape listener", ({
831
+ context
832
+ }) => ({
833
+ type: "context changed",
834
+ context
835
+ })),
836
+ "update request dismiss listener context": sendTo("dismiss listener", ({
837
+ context
838
+ }) => ({
839
+ type: "context changed",
840
+ context
841
+ })),
734
842
  "handle error": assign({
735
843
  isLoading: !1,
736
844
  error: ({
@@ -879,10 +987,11 @@ function createTypeaheadPickerMachine() {
879
987
  })
880
988
  }, {
881
989
  src: "escape listener",
990
+ id: "escape listener",
882
991
  input: ({
883
992
  context
884
993
  }) => ({
885
- editor: context.editor
994
+ context
886
995
  })
887
996
  }, {
888
997
  src: "selection listener",
@@ -907,13 +1016,21 @@ function createTypeaheadPickerMachine() {
907
1016
  }) => ({
908
1017
  context
909
1018
  })
1019
+ }, {
1020
+ src: "dismiss listener",
1021
+ id: "dismiss listener",
1022
+ input: ({
1023
+ context
1024
+ }) => ({
1025
+ context
1026
+ })
910
1027
  }],
911
1028
  on: {
912
- dismiss: {
1029
+ close: {
913
1030
  target: "idle"
914
1031
  },
915
1032
  "selection changed": {
916
- actions: ["update focus span", "update pattern text", "update keyword", "update matches", "update submit listener context", "update text insertion listener context"]
1033
+ actions: ["handle selection changed", "update submit listener context", "update text insertion listener context", "update escape listener context", "update request dismiss listener context"]
917
1034
  }
918
1035
  },
919
1036
  always: [{
@@ -1141,7 +1258,7 @@ function getFirstExactMatch(matches) {
1141
1258
  return matches.find((match) => match?.type === "exact");
1142
1259
  }
1143
1260
  function useTypeaheadPicker(definition) {
1144
- const $ = c(19), editor = useEditor();
1261
+ const $ = c(21), editor = useEditor();
1145
1262
  let t0;
1146
1263
  $[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (t0 = createTypeaheadPickerMachine(), $[0] = t0) : t0 = $[0];
1147
1264
  let t1;
@@ -1168,14 +1285,17 @@ function useTypeaheadPicker(definition) {
1168
1285
  context: t4
1169
1286
  }, $[11] = t2, $[12] = t4, $[13] = t5) : t5 = $[13];
1170
1287
  let t6;
1171
- $[14] !== send ? (t6 = (event) => {
1172
- send(event);
1173
- }, $[14] = send, $[15] = t6) : t6 = $[15];
1288
+ $[14] !== definition || $[15] !== editor || $[16] !== send ? (t6 = (event) => {
1289
+ event.type === "dismiss" ? editor.send({
1290
+ type: "custom.typeahead dismiss",
1291
+ pickerId: definition._id
1292
+ }) : send(event);
1293
+ }, $[14] = definition, $[15] = editor, $[16] = send, $[17] = t6) : t6 = $[17];
1174
1294
  let t7;
1175
- return $[16] !== t5 || $[17] !== t6 ? (t7 = {
1295
+ return $[18] !== t5 || $[19] !== t6 ? (t7 = {
1176
1296
  snapshot: t5,
1177
1297
  send: t6
1178
- }, $[16] = t5, $[17] = t6, $[18] = t7) : t7 = $[18], t7;
1298
+ }, $[18] = t5, $[19] = t6, $[20] = t7) : t7 = $[20], t7;
1179
1299
  }
1180
1300
  export {
1181
1301
  defineTypeaheadPicker,