@portabletext/plugin-emoji-picker 1.0.3 → 1.0.5

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,12 +1,12 @@
1
1
  import { c } from "react/compiler-runtime";
2
2
  import { useEditor } from "@portabletext/editor";
3
3
  import { useActorRef, useSelector } from "@xstate/react";
4
- import { raise, defineBehavior, effect, forward } from "@portabletext/editor/behaviors";
5
- import * as selectors from "@portabletext/editor/selectors";
6
- import * as utils from "@portabletext/editor/utils";
4
+ import { raise, defineBehavior, forward, effect } from "@portabletext/editor/behaviors";
5
+ import { getFocusSpan, getNextSpan, isPointAfterSelection, isPointBeforeSelection, getMarkState, getPreviousSpan } from "@portabletext/editor/selectors";
6
+ import { isSelectionCollapsed, isEqualSelectionPoints } from "@portabletext/editor/utils";
7
7
  import { createKeyboardShortcut } from "@portabletext/keyboard-shortcuts";
8
8
  import { defineInputRule, defineInputRuleBehavior } from "@portabletext/plugin-input-rule";
9
- import { setup, or, not, assign, sendTo, fromCallback, assertEvent } from "xstate";
9
+ import { setup, not, assign, sendTo, fromCallback, assertEvent } from "xstate";
10
10
  function createMatchEmojis(config) {
11
11
  return ({
12
12
  keyword
@@ -62,22 +62,111 @@ const arrowUpShortcut = createKeyboardShortcut({
62
62
  default: [{
63
63
  key: "Escape"
64
64
  }]
65
- }), triggerRule = defineInputRule({
65
+ }), getTriggerState = (snapshot) => {
66
+ const focusSpan = getFocusSpan(snapshot), markState = getMarkState(snapshot);
67
+ if (!focusSpan || !markState || !snapshot.context.selection)
68
+ return;
69
+ const focusSpanTextBefore = focusSpan.node.text.slice(0, snapshot.context.selection.focus.offset), focusSpanTextAfter = focusSpan.node.text.slice(snapshot.context.selection.focus.offset), previousSpan = getPreviousSpan(snapshot), nextSpan = getNextSpan(snapshot);
70
+ return {
71
+ focusSpan,
72
+ markState,
73
+ focusSpanTextBefore,
74
+ focusSpanTextAfter,
75
+ previousSpan,
76
+ nextSpan
77
+ };
78
+ };
79
+ function createTriggerActions({
80
+ snapshot,
81
+ payload,
82
+ keywordState
83
+ }) {
84
+ if (payload.markState.state === "unchanged") {
85
+ const focusSpan2 = {
86
+ node: {
87
+ _key: payload.focusSpan.node._key,
88
+ _type: payload.focusSpan.node._type,
89
+ text: `${payload.focusSpanTextBefore}${payload.lastMatch.text}${payload.focusSpanTextAfter}`,
90
+ marks: payload.markState.marks
91
+ },
92
+ path: payload.focusSpan.path,
93
+ textBefore: payload.focusSpanTextBefore,
94
+ textAfter: payload.focusSpanTextAfter
95
+ };
96
+ return keywordState === "complete" ? [raise(createKeywordFoundEvent({
97
+ focusSpan: focusSpan2
98
+ }))] : [raise(createTriggerFoundEvent({
99
+ focusSpan: focusSpan2
100
+ }))];
101
+ }
102
+ const newSpan = {
103
+ _key: snapshot.context.keyGenerator(),
104
+ _type: payload.focusSpan.node._type,
105
+ text: payload.lastMatch.text,
106
+ marks: payload.markState.marks
107
+ };
108
+ let focusSpan = {
109
+ node: {
110
+ _key: newSpan._key,
111
+ _type: newSpan._type,
112
+ text: `${newSpan.text}${payload.nextSpan?.node.text ?? payload.focusSpanTextAfter}`,
113
+ marks: payload.markState.marks
114
+ },
115
+ path: [{
116
+ _key: payload.focusSpan.path[0]._key
117
+ }, "children", {
118
+ _key: newSpan._key
119
+ }],
120
+ textBefore: "",
121
+ textAfter: payload.nextSpan?.node.text ?? payload.focusSpanTextAfter
122
+ };
123
+ return payload.previousSpan && payload.focusSpanTextBefore.length === 0 && JSON.stringify(payload.previousSpan.node.marks ?? []) === JSON.stringify(payload.markState.marks) && (focusSpan = {
124
+ node: {
125
+ _key: payload.previousSpan.node._key,
126
+ _type: newSpan._type,
127
+ text: `${payload.previousSpan.node.text}${newSpan.text}`,
128
+ marks: newSpan.marks
129
+ },
130
+ path: payload.previousSpan.path,
131
+ textBefore: payload.previousSpan.node.text,
132
+ textAfter: ""
133
+ }), [raise({
134
+ type: "select",
135
+ at: payload.lastMatch.targetOffsets
136
+ }), raise({
137
+ type: "delete",
138
+ at: payload.lastMatch.targetOffsets
139
+ }), raise({
140
+ type: "insert.child",
141
+ child: newSpan
142
+ }), ...keywordState === "complete" ? [raise(createKeywordFoundEvent({
143
+ focusSpan
144
+ }))] : [raise(createTriggerFoundEvent({
145
+ focusSpan
146
+ }))]];
147
+ }
148
+ const triggerRule = defineInputRule({
66
149
  on: /:/,
67
150
  guard: ({
151
+ snapshot,
68
152
  event
69
153
  }) => {
70
154
  const lastMatch = event.matches.at(-1);
71
- return lastMatch === void 0 ? !1 : {
72
- keyword: lastMatch.text,
73
- keywordAnchor: {
74
- point: lastMatch.selection.anchor,
75
- blockOffset: lastMatch.targetOffsets.anchor
76
- },
77
- keywordFocus: lastMatch.targetOffsets.focus
78
- };
155
+ if (lastMatch === void 0)
156
+ return !1;
157
+ const triggerState = getTriggerState(snapshot);
158
+ return triggerState ? {
159
+ lastMatch,
160
+ ...triggerState
161
+ } : !1;
79
162
  },
80
- actions: [(_, payload) => [raise(createTriggerFoundEvent(payload))]]
163
+ actions: [({
164
+ snapshot
165
+ }, payload) => createTriggerActions({
166
+ snapshot,
167
+ payload,
168
+ keywordState: "partial"
169
+ })]
81
170
  });
82
171
  function createTriggerFoundEvent(payload) {
83
172
  return {
@@ -88,48 +177,47 @@ function createTriggerFoundEvent(payload) {
88
177
  const partialKeywordRule = defineInputRule({
89
178
  on: /:[\S]+/,
90
179
  guard: ({
180
+ snapshot,
91
181
  event
92
182
  }) => {
93
183
  const lastMatch = event.matches.at(-1);
94
- if (lastMatch === void 0)
184
+ if (lastMatch === void 0 || lastMatch.targetOffsets.anchor.offset < event.textBefore.length)
95
185
  return !1;
96
- const keyword = lastMatch.text, keywordAnchor = {
97
- point: lastMatch.selection.anchor,
98
- blockOffset: lastMatch.targetOffsets.anchor
99
- }, keywordFocus = lastMatch.targetOffsets.focus;
100
- return {
101
- keyword,
102
- keywordAnchor,
103
- keywordFocus
104
- };
186
+ const triggerState = getTriggerState(snapshot);
187
+ return triggerState ? {
188
+ ...triggerState,
189
+ lastMatch
190
+ } : !1;
105
191
  },
106
- actions: [(_, payload) => [raise(createPartialKeywordFoundEvent(payload))]]
107
- });
108
- function createPartialKeywordFoundEvent(payload) {
109
- return {
110
- type: "custom.partial keyword found",
111
- ...payload
112
- };
113
- }
114
- const keywordRule = defineInputRule({
192
+ actions: [({
193
+ snapshot
194
+ }, payload) => createTriggerActions({
195
+ snapshot,
196
+ payload,
197
+ keywordState: "partial"
198
+ })]
199
+ }), keywordRule = defineInputRule({
115
200
  on: /:[\S]+:/,
116
201
  guard: ({
202
+ snapshot,
117
203
  event
118
204
  }) => {
119
205
  const lastMatch = event.matches.at(-1);
120
- if (lastMatch === void 0)
206
+ if (lastMatch === void 0 || lastMatch.targetOffsets.anchor.offset < event.textBefore.length)
121
207
  return !1;
122
- const keyword = lastMatch.text, keywordAnchor = {
123
- point: lastMatch.selection.anchor,
124
- blockOffset: lastMatch.targetOffsets.anchor
125
- }, keywordFocus = lastMatch.targetOffsets.focus;
126
- return {
127
- keyword,
128
- keywordAnchor,
129
- keywordFocus
130
- };
208
+ const triggerState = getTriggerState(snapshot);
209
+ return triggerState ? {
210
+ ...triggerState,
211
+ lastMatch
212
+ } : !1;
131
213
  },
132
- actions: [(_, payload) => [raise(createKeywordFoundEvent(payload))]]
214
+ actions: [({
215
+ snapshot
216
+ }, payload) => createTriggerActions({
217
+ snapshot,
218
+ payload,
219
+ keywordState: "complete"
220
+ })]
133
221
  });
134
222
  function createKeywordFoundEvent(payload) {
135
223
  return {
@@ -154,15 +242,6 @@ const triggerListenerCallback = ({
154
242
  sendBack(event);
155
243
  })]]
156
244
  })
157
- }), input.editor.registerBehavior({
158
- behavior: defineBehavior({
159
- on: "custom.partial keyword found",
160
- actions: [({
161
- event
162
- }) => [effect(() => {
163
- sendBack(event);
164
- })]]
165
- })
166
245
  }), input.editor.registerBehavior({
167
246
  behavior: defineBehavior({
168
247
  on: "custom.trigger found",
@@ -238,10 +317,16 @@ const triggerListenerCallback = ({
238
317
  type: "dismiss"
239
318
  });
240
319
  }), raise({
241
- type: "delete.text",
320
+ type: "delete",
242
321
  at: {
243
- anchor: event.anchor,
244
- focus: event.focus
322
+ anchor: {
323
+ path: event.focusSpan.path,
324
+ offset: event.focusSpan.textBefore.length
325
+ },
326
+ focus: {
327
+ path: event.focusSpan.path,
328
+ offset: event.focusSpan.node.text.length - event.focusSpan.textAfter.length
329
+ }
245
330
  }
246
331
  }), raise({
247
332
  type: "insert.text",
@@ -265,22 +350,19 @@ const triggerListenerCallback = ({
265
350
  }) => {
266
351
  if (!enterShortcut.guard(event.originEvent) && !tabShortcut.guard(event.originEvent))
267
352
  return !1;
268
- const anchor = context.keywordAnchor?.blockOffset, focus = context.keywordFocus, match = context.matches[context.selectedIndex];
269
- return match && anchor && focus ? {
270
- anchor,
271
- focus,
353
+ const focusSpan = context.focusSpan, match = context.matches[context.selectedIndex];
354
+ return match && focusSpan ? {
355
+ focusSpan,
272
356
  emoji: match.emoji
273
357
  } : !1;
274
358
  },
275
359
  actions: [(_, {
276
- anchor,
277
- focus,
360
+ focusSpan,
278
361
  emoji
279
362
  }) => [raise({
280
363
  type: "custom.insert emoji",
281
364
  emoji,
282
- anchor,
283
- focus
365
+ focusSpan
284
366
  })]]
285
367
  })
286
368
  }), input.context.editor.registerBehavior({
@@ -295,19 +377,6 @@ const triggerListenerCallback = ({
295
377
  });
296
378
  })]]
297
379
  })
298
- }), input.context.editor.registerBehavior({
299
- behavior: defineInputRuleBehavior({
300
- rules: [keywordRule]
301
- })
302
- }), input.context.editor.registerBehavior({
303
- behavior: defineBehavior({
304
- on: "custom.keyword found",
305
- actions: [({
306
- event
307
- }) => [effect(() => {
308
- sendBack(event);
309
- })]]
310
- })
311
380
  })];
312
381
  return () => {
313
382
  for (const unregister of unregisterBehaviors)
@@ -317,79 +386,40 @@ const triggerListenerCallback = ({
317
386
  sendBack,
318
387
  input
319
388
  }) => input.editor.on("selection", () => {
320
- const snapshot = input.editor.getSnapshot();
321
389
  sendBack({
322
- type: "selection changed",
323
- snapshot
390
+ type: "selection changed"
324
391
  });
325
- }).unsubscribe, textChangeListener = ({
392
+ }).unsubscribe, textInsertionListenerCallback = ({
326
393
  sendBack,
327
- input
394
+ input,
395
+ receive
328
396
  }) => {
329
- const unregisterBehaviors = [input.editor.registerBehavior({
397
+ let context = input.context;
398
+ return receive((event) => {
399
+ context = event.context;
400
+ }), input.context.editor.registerBehavior({
330
401
  behavior: defineBehavior({
331
402
  on: "insert.text",
332
403
  guard: ({
333
404
  snapshot
334
- }) => snapshot.context.selection ? {
335
- focus: snapshot.context.selection.focus
336
- } : !1,
337
- actions: [({
338
- event
339
- }, {
340
- focus
341
- }) => [effect(() => {
342
- sendBack({
343
- ...event,
344
- focus
345
- });
346
- }), forward(event)]]
347
- })
348
- }), input.editor.registerBehavior({
349
- behavior: defineBehavior({
350
- on: "delete.backward",
351
- guard: ({
352
- snapshot,
353
- event
354
- }) => event.unit === "character" && snapshot.context.selection ? {
355
- focus: snapshot.context.selection.focus
356
- } : !1,
357
- actions: [({
358
- event
359
- }, {
360
- focus
361
- }) => [effect(() => {
362
- sendBack({
363
- type: "delete.backward",
364
- focus
365
- });
366
- }), forward(event)]]
367
- })
368
- }), input.editor.registerBehavior({
369
- behavior: defineBehavior({
370
- on: "delete.forward",
371
- guard: ({
372
- snapshot,
373
- event
374
- }) => event.unit === "character" && snapshot.context.selection ? {
375
- focus: snapshot.context.selection.focus
376
- } : !1,
405
+ }) => {
406
+ if (!context.focusSpan || !snapshot.context.selection)
407
+ return !1;
408
+ const keywordAnchor = {
409
+ path: context.focusSpan.path,
410
+ offset: context.focusSpan.textBefore.length
411
+ };
412
+ return isEqualSelectionPoints(snapshot.context.selection.focus, keywordAnchor);
413
+ },
377
414
  actions: [({
378
415
  event
379
- }, {
380
- focus
381
- }) => [effect(() => {
416
+ }) => [forward(event), effect(() => {
382
417
  sendBack({
383
- type: "delete.forward",
384
- focus
418
+ type: "dismiss"
385
419
  });
386
- }), forward(event)]]
420
+ })]]
387
421
  })
388
- })];
389
- return () => {
390
- for (const unregister of unregisterBehaviors)
391
- unregister();
392
- };
422
+ });
393
423
  }, emojiPickerMachine = setup({
394
424
  types: {
395
425
  context: {},
@@ -403,60 +433,65 @@ const triggerListenerCallback = ({
403
433
  "trigger listener": fromCallback(triggerListenerCallback),
404
434
  "escape listener": fromCallback(escapeListenerCallback),
405
435
  "selection listener": fromCallback(selectionListenerCallback),
406
- "text change listener": fromCallback(textChangeListener)
436
+ "text insertion listener": fromCallback(textInsertionListenerCallback)
407
437
  },
408
438
  actions: {
409
- "init keyword": assign({
410
- keyword: ({
411
- context,
412
- event
413
- }) => event.type !== "custom.trigger found" && event.type !== "custom.partial keyword found" && event.type !== "custom.keyword found" ? context.keyword : event.keyword
414
- }),
415
- "set keyword anchor": assign({
416
- keywordAnchor: ({
417
- context,
418
- event
419
- }) => event.type !== "custom.trigger found" && event.type !== "custom.partial keyword found" && event.type !== "custom.keyword found" ? context.keywordAnchor : event.keywordAnchor
420
- }),
421
- "set keyword focus": assign({
422
- keywordFocus: ({
439
+ "set focus span": assign({
440
+ focusSpan: ({
423
441
  context,
424
442
  event
425
- }) => event.type !== "custom.trigger found" && event.type !== "custom.partial keyword found" && event.type !== "custom.keyword found" ? context.keywordFocus : event.keywordFocus
443
+ }) => event.type !== "custom.trigger found" && event.type !== "custom.keyword found" ? context.focusSpan : event.focusSpan
426
444
  }),
427
- "update keyword focus": assign({
428
- keywordFocus: ({
429
- context,
430
- event
431
- }) => (assertEvent(event, ["insert.text", "delete.backward", "delete.forward"]), context.keywordFocus ? {
432
- path: context.keywordFocus.path,
433
- offset: event.type === "insert.text" ? context.keywordFocus.offset + event.text.length : event.type === "delete.backward" || event.type === "delete.forward" ? context.keywordFocus.offset - 1 : event.focus.offset
434
- } : context.keywordFocus)
435
- }),
436
- "update keyword": assign({
437
- keyword: ({
438
- context,
439
- event
445
+ "update focus span": assign({
446
+ focusSpan: ({
447
+ context
440
448
  }) => {
441
- if (assertEvent(event, "selection changed"), !context.keywordAnchor || !context.keywordFocus)
442
- return "";
443
- const keywordFocusPoint = utils.blockOffsetToSpanSelectionPoint({
444
- context: event.snapshot.context,
445
- blockOffset: context.keywordFocus,
446
- direction: "forward"
447
- });
448
- return keywordFocusPoint ? selectors.getSelectionText({
449
- ...event.snapshot,
449
+ if (!context.focusSpan)
450
+ return;
451
+ const snapshot = context.editor.getSnapshot(), focusSpan = getFocusSpan(snapshot);
452
+ if (!snapshot.context.selection || !focusSpan)
453
+ return;
454
+ const nextSpan = getNextSpan({
455
+ ...snapshot,
450
456
  context: {
451
- ...event.snapshot.context,
457
+ ...snapshot.context,
452
458
  selection: {
453
- anchor: context.keywordAnchor.point,
454
- focus: keywordFocusPoint
459
+ anchor: {
460
+ path: context.focusSpan.path,
461
+ offset: 0
462
+ },
463
+ focus: {
464
+ path: context.focusSpan.path,
465
+ offset: 0
466
+ }
455
467
  }
456
468
  }
457
- }) : "";
469
+ });
470
+ if (JSON.stringify(focusSpan.path) !== JSON.stringify(context.focusSpan.path))
471
+ return nextSpan && context.focusSpan.textAfter.length === 0 && snapshot.context.selection.focus.offset === 0 && isSelectionCollapsed(snapshot.context.selection) ? context.focusSpan : void 0;
472
+ if (!focusSpan.node.text.startsWith(context.focusSpan.textBefore) || !focusSpan.node.text.endsWith(context.focusSpan.textAfter))
473
+ return;
474
+ const keywordAnchor = {
475
+ path: focusSpan.path,
476
+ offset: context.focusSpan.textBefore.length
477
+ }, keywordFocus = {
478
+ path: focusSpan.path,
479
+ offset: focusSpan.node.text.length - context.focusSpan.textAfter.length
480
+ }, selectionIsBeforeKeyword = isPointAfterSelection(keywordAnchor)(snapshot), selectionIsAfterKeyword = isPointBeforeSelection(keywordFocus)(snapshot);
481
+ if (!(selectionIsBeforeKeyword || selectionIsAfterKeyword))
482
+ return {
483
+ node: focusSpan.node,
484
+ path: focusSpan.path,
485
+ textBefore: context.focusSpan.textBefore,
486
+ textAfter: context.focusSpan.textAfter
487
+ };
458
488
  }
459
489
  }),
490
+ "update keyword": assign({
491
+ keyword: ({
492
+ context
493
+ }) => context.focusSpan ? context.focusSpan.textBefore.length > 0 && context.focusSpan.textAfter.length > 0 ? context.focusSpan.node.text.slice(context.focusSpan.textBefore.length, -context.focusSpan.textAfter.length) : context.focusSpan.textBefore.length > 0 ? context.focusSpan.node.text.slice(context.focusSpan.textBefore.length) : context.focusSpan.textAfter.length > 0 ? context.focusSpan.node.text.slice(0, -context.focusSpan.textAfter.length) : context.focusSpan.node.text : ""
494
+ }),
460
495
  "update matches": assign({
461
496
  matches: ({
462
497
  context
@@ -485,99 +520,65 @@ const triggerListenerCallback = ({
485
520
  event
486
521
  }) => (assertEvent(event, "navigate to"), event.index)
487
522
  }),
488
- "update emoji insert listener context": sendTo("emoji insert listener", ({
523
+ "update submit listener context": sendTo("submit listener", ({
489
524
  context
490
525
  }) => ({
491
526
  type: "context changed",
492
527
  context
493
528
  })),
494
- "update submit listener context": sendTo("submit listener", ({
529
+ "update text insertion listener context": sendTo("text insertion listener", ({
495
530
  context
496
531
  }) => ({
497
532
  type: "context changed",
498
533
  context
499
534
  })),
500
535
  "insert selected match": ({
501
- context,
502
- event
536
+ context
503
537
  }) => {
504
538
  const match = context.matches[context.selectedIndex];
505
- !match || !context.keywordAnchor || !context.keywordFocus || event.type === "custom.keyword found" && match.type !== "exact" || context.editor.send({
539
+ !match || !context.focusSpan || context.editor.send({
506
540
  type: "custom.insert emoji",
507
541
  emoji: match.emoji,
508
- anchor: context.keywordAnchor.blockOffset,
509
- focus: context.keywordFocus
542
+ focusSpan: context.focusSpan
510
543
  });
511
544
  },
512
545
  reset: assign({
513
- keywordAnchor: void 0,
514
- keywordFocus: void 0,
546
+ focusSpan: void 0,
515
547
  keyword: "",
516
548
  matches: [],
517
549
  selectedIndex: 0
518
550
  })
519
551
  },
520
552
  guards: {
553
+ "no focus span": ({
554
+ context
555
+ }) => !context.focusSpan,
521
556
  "has matches": ({
522
557
  context
523
558
  }) => context.matches.length > 0,
524
559
  "no matches": not("has matches"),
525
- "keyword is wel-formed": ({
560
+ "keyword is malformed": ({
561
+ context
562
+ }) => !context.incompleteKeywordRegex.test(context.keyword),
563
+ "keyword is direct match": ({
526
564
  context
527
- }) => context.incompleteKeywordRegex.test(context.keyword),
528
- "keyword is malformed": not("keyword is wel-formed"),
529
- "selection is before keyword": ({
530
- context,
531
- event
532
- }) => (assertEvent(event, "selection changed"), context.keywordAnchor ? selectors.isPointAfterSelection(context.keywordAnchor.point)(event.snapshot) : !0),
533
- "selection is after keyword": ({
534
- context,
535
- event
536
- }) => {
537
- if (assertEvent(event, "selection changed"), context.keywordFocus === void 0)
538
- return !0;
539
- const keywordFocusPoint = utils.blockOffsetToSpanSelectionPoint({
540
- context: event.snapshot.context,
541
- blockOffset: context.keywordFocus,
542
- direction: "forward"
543
- });
544
- return keywordFocusPoint ? selectors.isPointBeforeSelection(keywordFocusPoint)(event.snapshot) : !0;
545
- },
546
- "selection is expanded": ({
547
- event
548
- }) => (assertEvent(event, "selection changed"), selectors.isSelectionExpanded(event.snapshot)),
549
- "selection moved unexpectedly": or(["selection is before keyword", "selection is after keyword", "selection is expanded"]),
550
- "unexpected text insertion": ({
551
- context,
552
- event
553
565
  }) => {
554
- if (event.type !== "insert.text" || !context.keywordAnchor)
566
+ if (!/^:[\S]+:$/.test(context.keyword))
555
567
  return !1;
556
- const snapshot = context.editor.getSnapshot();
557
- return selectors.isPointBeforeSelection(event.focus)({
558
- ...snapshot,
559
- context: {
560
- ...snapshot.context,
561
- selection: {
562
- anchor: context.keywordAnchor.point,
563
- focus: context.keywordAnchor.point
564
- }
565
- }
566
- }) || utils.isEqualSelectionPoints(event.focus, context.keywordAnchor.point);
568
+ const match = context.matches.at(context.selectedIndex);
569
+ return !(!match || match.type !== "exact");
567
570
  }
568
571
  }
569
572
  }).createMachine({
570
- /** @xstate-layout N4IgpgJg5mDOIC5RgLYHsBWBLABABywGMBrMAJwDosIAbMAYkLRrQDsctXZyAXSAbQAMAXUSg8aWFh5Y2YkAA9EARmUB2AJwUAzADYNADgCs65YO1q1ygDQgAnojUG1O5c+W7tAJmdfdRgF8A21RMXAIScgpuAEMyQgALTih6Tm4yHgo+BR4hUSQQCSkZOQKlBABaZW0DHSMAFksNQUF6oyNzL1sHBC9vCnrGtS8GtQaNNt0gkPRsfCJSSlj4pNYUiDA6PgoAMzQyAHc4iDz5IulZVnlyr3MKE30LA31DZoNuxD6vAaHdP29vOo1NNwLNwgsostEsl6BstmAKAAjGIkI5kE4iM6SC6lUDlerabT3arDfS3eoaPQfXr9QaWEaNcaTEGhOYRRbRMBxaFrWFYWAofmwU4Fc4lK5lFTqWqDAwUjReeoGbwaNTU1TPCh-QxNNS6XQGHwssHzSJLLkrGHcOiEcU4RIxNYCTGi7Hi66IfxE1oeZX1EbeZXUhUUZSKjxOOV6bTmY1hU0cqGrFLWsC2y72hKOmAnZT5cRuy4ehDVXQUVXDIyWGpmAzveyfLRKwQaVRVwR1ixeONsiHm7nJ+gigvFIuSkvKVuhgwaEwKmMKwbqkaCAaverqVuvKbBUHx9mQi08qAUVhoHAoGI8RJwHCwBJoA4w4eFQu4xSfaoDA3KOmCVSTn06r-i4-hGH0ZiEoI4H1D24JmpyA7JNED5PmsF5XjesD0KwMQAG5YFAV5gDgECPqwL5imOeKIIM9ShsoRh1i2erPB41L6C40GqISUb6n8cEJoeSFrChj7JBh14JHAOH4YRxE4AArnglFvhKNEIGoq4+E0yraPULa6PUHGqhQ3HVDUBL8dogkHv2lqife4noZeUkybhBFEXwOA8Ggqmju+5RGPqFBqP6ujDD4v4tjYDYIMqLjVKo5gdM4TGBLurLwYmR7JmJaFQJJWGpFwvB3psaZ8BARUJP5OLqR+CD1Loq5ymYfyeP4spGOqv70fpv7NBSBKtBlMz7n2iEOSeTkFTVMl1e645eB49wGP+K0UpBbjaMBzQUF4IzBYq7TNa2QS7meGzwAUWVCWQWIBQ15RVJq2ijJoLRtB03jUhUVYUHKtz-gqeoxkYGi2ZN1B0I99XFqobTlh4-5-Ot4H1j0ZirbcENhR4RmKrBmUmnZU3HnDS0aVUjHlh2cqtkqfTBdSSr0RMGieBS7htASUMIUmyFnvNsB3qhySU9RjUVBFdN1ltTPvbowZOGZEWHe9xgTHo-M5SJM3iy5mHSTdI7w+OlnlgY6heJzbgUv4ytxaqtSCBFg1eJFM4XQEQA */
571
573
  id: "emoji picker",
572
574
  context: ({
573
575
  input
574
576
  }) => ({
575
577
  editor: input.editor,
576
578
  keyword: "",
577
- keywordAnchor: void 0,
578
- keywordFocus: void 0,
579
+ focusSpan: void 0,
579
580
  matchEmojis: input.matchEmojis,
580
- incompleteKeywordRegex: /:[\S]*$/,
581
+ incompleteKeywordRegex: /^:[\S]*$/,
581
582
  matches: [],
582
583
  selectedIndex: 0
583
584
  }),
@@ -605,14 +606,10 @@ const triggerListenerCallback = ({
605
606
  on: {
606
607
  "custom.trigger found": {
607
608
  target: "searching",
608
- actions: ["set keyword anchor", "set keyword focus", "init keyword"]
609
- },
610
- "custom.partial keyword found": {
611
- target: "searching",
612
- actions: ["set keyword anchor", "set keyword focus", "init keyword"]
609
+ actions: ["set focus span", "update keyword"]
613
610
  },
614
611
  "custom.keyword found": {
615
- actions: ["set keyword anchor", "set keyword focus", "init keyword", "update matches", "insert selected match"],
612
+ actions: ["set focus span", "update keyword", "update matches", "insert selected match"],
616
613
  target: "idle",
617
614
  reenter: !0
618
615
  }
@@ -642,42 +639,32 @@ const triggerListenerCallback = ({
642
639
  editor: context.editor
643
640
  })
644
641
  }, {
645
- src: "text change listener",
642
+ src: "text insertion listener",
643
+ id: "text insertion listener",
646
644
  input: ({
647
645
  context
648
646
  }) => ({
649
- editor: context.editor
647
+ context
650
648
  })
651
649
  }],
652
650
  on: {
653
- "custom.keyword found": {
654
- actions: ["set keyword anchor", "set keyword focus", "init keyword", "update matches", "insert selected match"]
655
- },
656
- "insert.text": [{
657
- guard: "unexpected text insertion",
658
- target: "idle"
659
- }, {
660
- actions: ["update keyword focus"]
661
- }],
662
- "delete.forward": {
663
- actions: ["update keyword focus"]
664
- },
665
- "delete.backward": {
666
- actions: ["update keyword focus"]
667
- },
668
651
  dismiss: {
669
652
  target: "idle"
670
653
  },
671
654
  "selection changed": [{
672
- guard: "selection moved unexpectedly",
673
- target: "idle"
674
- }, {
675
- actions: ["update keyword", "update matches", "reset selected index", "update submit listener context"]
655
+ actions: ["update focus span", "update keyword", "update matches", "reset selected index", "update submit listener context", "update text insertion listener context"]
676
656
  }]
677
657
  },
678
658
  always: [{
659
+ guard: "no focus span",
660
+ target: "idle"
661
+ }, {
679
662
  guard: "keyword is malformed",
680
663
  target: "idle"
664
+ }, {
665
+ guard: "keyword is direct match",
666
+ actions: ["insert selected match"],
667
+ target: "idle"
681
668
  }],
682
669
  initial: "no matches showing",
683
670
  states: {