@portabletext/plugin-emoji-picker 1.0.8 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-emoji-picker",
3
- "version": "1.0.8",
3
+ "version": "2.0.0",
4
4
  "description": "Easily configure an Emoji Picker for the Portable Text Editor",
5
5
  "keywords": [
6
6
  "portabletext",
@@ -23,14 +23,11 @@
23
23
  "exports": {
24
24
  ".": {
25
25
  "source": "./src/index.ts",
26
- "import": "./dist/index.js",
27
- "require": "./dist/index.cjs",
28
26
  "default": "./dist/index.js"
29
27
  },
30
28
  "./package.json": "./package.json"
31
29
  },
32
- "main": "./dist/index.cjs",
33
- "module": "./dist/index.js",
30
+ "main": "./dist/index.js",
34
31
  "types": "./dist/index.d.ts",
35
32
  "files": [
36
33
  "dist",
@@ -40,11 +37,11 @@
40
37
  "@xstate/react": "^6.0.0",
41
38
  "react-compiler-runtime": "1.0.0",
42
39
  "xstate": "^5.23.0",
43
- "@portabletext/keyboard-shortcuts": "^1.1.1",
44
- "@portabletext/plugin-input-rule": "~0.4.4"
40
+ "@portabletext/keyboard-shortcuts": "^2.0.0",
41
+ "@portabletext/plugin-input-rule": "~0.5.0"
45
42
  },
46
43
  "devDependencies": {
47
- "@sanity/pkg-utils": "^8.1.4",
44
+ "@sanity/pkg-utils": "^9.0.0",
48
45
  "@types/react": "^19.2.0",
49
46
  "@vitejs/plugin-react": "^5.0.4",
50
47
  "@vitest/browser": "^4.0.5",
@@ -57,14 +54,17 @@
57
54
  "typescript": "5.9.3",
58
55
  "typescript-eslint": "^8.46.1",
59
56
  "vitest": "^4.0.5",
60
- "@portabletext/editor": "2.17.2",
61
- "@portabletext/schema": "1.2.0",
62
- "racejar": "1.3.2"
57
+ "@portabletext/editor": "2.18.0",
58
+ "@portabletext/schema": "2.0.0",
59
+ "racejar": "2.0.0"
63
60
  },
64
61
  "peerDependencies": {
65
- "@portabletext/editor": "^2.17.2",
62
+ "@portabletext/editor": "^2.18.0",
66
63
  "react": "^19.1.1"
67
64
  },
65
+ "engines": {
66
+ "node": ">=20.19 <22 || >=22.12"
67
+ },
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
package/dist/index.cjs DELETED
@@ -1,774 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var compilerRuntime = require("react/compiler-runtime"), editor = require("@portabletext/editor"), react = require("@xstate/react"), behaviors = require("@portabletext/editor/behaviors"), selectors = require("@portabletext/editor/selectors"), utils = require("@portabletext/editor/utils"), keyboardShortcuts = require("@portabletext/keyboard-shortcuts"), pluginInputRule = require("@portabletext/plugin-input-rule"), xstate = require("xstate");
4
- function createMatchEmojis(config) {
5
- return ({
6
- keyword
7
- }) => {
8
- const foundEmojis = [];
9
- if (keyword.length < 1)
10
- return foundEmojis;
11
- for (const emoji in config.emojis) {
12
- const emojiKeywords = config.emojis[emoji] ?? [];
13
- for (const emojiKeyword of emojiKeywords) {
14
- const keywordIndex = emojiKeyword.indexOf(keyword);
15
- if (keywordIndex !== -1)
16
- if (emojiKeyword === keyword)
17
- foundEmojis.push({
18
- type: "exact",
19
- key: `${emoji}-${keyword}`,
20
- emoji,
21
- keyword
22
- });
23
- else {
24
- const start = emojiKeyword.slice(0, keywordIndex), end = emojiKeyword.slice(keywordIndex + keyword.length);
25
- foundEmojis.push({
26
- type: "partial",
27
- key: `${emoji}-${start}${keyword}${end}`,
28
- emoji,
29
- keyword,
30
- startSlice: start,
31
- endSlice: end
32
- });
33
- }
34
- }
35
- }
36
- return foundEmojis;
37
- };
38
- }
39
- const arrowUpShortcut = keyboardShortcuts.createKeyboardShortcut({
40
- default: [{
41
- key: "ArrowUp"
42
- }]
43
- }), arrowDownShortcut = keyboardShortcuts.createKeyboardShortcut({
44
- default: [{
45
- key: "ArrowDown"
46
- }]
47
- }), enterShortcut = keyboardShortcuts.createKeyboardShortcut({
48
- default: [{
49
- key: "Enter"
50
- }]
51
- }), tabShortcut = keyboardShortcuts.createKeyboardShortcut({
52
- default: [{
53
- key: "Tab"
54
- }]
55
- }), escapeShortcut = keyboardShortcuts.createKeyboardShortcut({
56
- default: [{
57
- key: "Escape"
58
- }]
59
- }), getTriggerState = (snapshot) => {
60
- const focusSpan = selectors.getFocusSpan(snapshot), markState = selectors.getMarkState(snapshot);
61
- if (!focusSpan || !markState || !snapshot.context.selection)
62
- return;
63
- const focusSpanTextBefore = focusSpan.node.text.slice(0, snapshot.context.selection.focus.offset), focusSpanTextAfter = focusSpan.node.text.slice(snapshot.context.selection.focus.offset), previousSpan = selectors.getPreviousSpan(snapshot), nextSpan = selectors.getNextSpan(snapshot);
64
- return {
65
- focusSpan,
66
- markState,
67
- focusSpanTextBefore,
68
- focusSpanTextAfter,
69
- previousSpan,
70
- nextSpan
71
- };
72
- };
73
- function createTriggerActions({
74
- snapshot,
75
- payload,
76
- keywordState
77
- }) {
78
- if (payload.markState.state === "unchanged") {
79
- const focusSpan2 = {
80
- node: {
81
- _key: payload.focusSpan.node._key,
82
- _type: payload.focusSpan.node._type,
83
- text: `${payload.focusSpanTextBefore}${payload.lastMatch.text}${payload.focusSpanTextAfter}`,
84
- marks: payload.markState.marks
85
- },
86
- path: payload.focusSpan.path,
87
- textBefore: payload.focusSpanTextBefore,
88
- textAfter: payload.focusSpanTextAfter
89
- };
90
- return keywordState === "complete" ? [behaviors.raise(createKeywordFoundEvent({
91
- focusSpan: focusSpan2
92
- }))] : [behaviors.raise(createTriggerFoundEvent({
93
- focusSpan: focusSpan2
94
- }))];
95
- }
96
- const newSpan = {
97
- _key: snapshot.context.keyGenerator(),
98
- _type: payload.focusSpan.node._type,
99
- text: payload.lastMatch.text,
100
- marks: payload.markState.marks
101
- };
102
- let focusSpan = {
103
- node: {
104
- _key: newSpan._key,
105
- _type: newSpan._type,
106
- text: `${newSpan.text}${payload.nextSpan?.node.text ?? payload.focusSpanTextAfter}`,
107
- marks: payload.markState.marks
108
- },
109
- path: [{
110
- _key: payload.focusSpan.path[0]._key
111
- }, "children", {
112
- _key: newSpan._key
113
- }],
114
- textBefore: "",
115
- textAfter: payload.nextSpan?.node.text ?? payload.focusSpanTextAfter
116
- };
117
- return payload.previousSpan && payload.focusSpanTextBefore.length === 0 && JSON.stringify(payload.previousSpan.node.marks ?? []) === JSON.stringify(payload.markState.marks) && (focusSpan = {
118
- node: {
119
- _key: payload.previousSpan.node._key,
120
- _type: newSpan._type,
121
- text: `${payload.previousSpan.node.text}${newSpan.text}`,
122
- marks: newSpan.marks
123
- },
124
- path: payload.previousSpan.path,
125
- textBefore: payload.previousSpan.node.text,
126
- textAfter: ""
127
- }), [behaviors.raise({
128
- type: "select",
129
- at: payload.lastMatch.targetOffsets
130
- }), behaviors.raise({
131
- type: "delete",
132
- at: payload.lastMatch.targetOffsets
133
- }), behaviors.raise({
134
- type: "insert.child",
135
- child: newSpan
136
- }), ...keywordState === "complete" ? [behaviors.raise(createKeywordFoundEvent({
137
- focusSpan
138
- }))] : [behaviors.raise(createTriggerFoundEvent({
139
- focusSpan
140
- }))]];
141
- }
142
- const triggerRule = pluginInputRule.defineInputRule({
143
- on: /:/,
144
- guard: ({
145
- snapshot,
146
- event
147
- }) => {
148
- const lastMatch = event.matches.at(-1);
149
- if (lastMatch === void 0)
150
- return !1;
151
- const triggerState = getTriggerState(snapshot);
152
- return triggerState ? {
153
- lastMatch,
154
- ...triggerState
155
- } : !1;
156
- },
157
- actions: [({
158
- snapshot
159
- }, payload) => createTriggerActions({
160
- snapshot,
161
- payload,
162
- keywordState: "partial"
163
- })]
164
- });
165
- function createTriggerFoundEvent(payload) {
166
- return {
167
- type: "custom.trigger found",
168
- ...payload
169
- };
170
- }
171
- const partialKeywordRule = pluginInputRule.defineInputRule({
172
- on: /:[\S]+/,
173
- guard: ({
174
- snapshot,
175
- event
176
- }) => {
177
- const lastMatch = event.matches.at(-1);
178
- if (lastMatch === void 0 || lastMatch.targetOffsets.anchor.offset < event.textBefore.length)
179
- return !1;
180
- const triggerState = getTriggerState(snapshot);
181
- return triggerState ? {
182
- ...triggerState,
183
- lastMatch
184
- } : !1;
185
- },
186
- actions: [({
187
- snapshot
188
- }, payload) => createTriggerActions({
189
- snapshot,
190
- payload,
191
- keywordState: "partial"
192
- })]
193
- }), keywordRule = pluginInputRule.defineInputRule({
194
- on: /:[\S]+:/,
195
- guard: ({
196
- snapshot,
197
- event
198
- }) => {
199
- const lastMatch = event.matches.at(-1);
200
- if (lastMatch === void 0 || lastMatch.targetOffsets.anchor.offset < event.textBefore.length)
201
- return !1;
202
- const triggerState = getTriggerState(snapshot);
203
- return triggerState ? {
204
- ...triggerState,
205
- lastMatch
206
- } : !1;
207
- },
208
- actions: [({
209
- snapshot
210
- }, payload) => createTriggerActions({
211
- snapshot,
212
- payload,
213
- keywordState: "complete"
214
- })]
215
- });
216
- function createKeywordFoundEvent(payload) {
217
- return {
218
- type: "custom.keyword found",
219
- ...payload
220
- };
221
- }
222
- const triggerListenerCallback = ({
223
- sendBack,
224
- input
225
- }) => {
226
- const unregisterBehaviors = [input.editor.registerBehavior({
227
- behavior: pluginInputRule.defineInputRuleBehavior({
228
- rules: [keywordRule, partialKeywordRule, triggerRule]
229
- })
230
- }), input.editor.registerBehavior({
231
- behavior: behaviors.defineBehavior({
232
- on: "custom.keyword found",
233
- actions: [({
234
- event
235
- }) => [behaviors.effect(() => {
236
- sendBack(event);
237
- })]]
238
- })
239
- }), input.editor.registerBehavior({
240
- behavior: behaviors.defineBehavior({
241
- on: "custom.trigger found",
242
- actions: [({
243
- event
244
- }) => [behaviors.effect(() => {
245
- sendBack(event);
246
- })]]
247
- })
248
- })];
249
- return () => {
250
- for (const unregister of unregisterBehaviors)
251
- unregister();
252
- };
253
- }, escapeListenerCallback = ({
254
- sendBack,
255
- input
256
- }) => input.editor.registerBehavior({
257
- behavior: behaviors.defineBehavior({
258
- on: "keyboard.keydown",
259
- guard: ({
260
- event
261
- }) => escapeShortcut.guard(event.originEvent),
262
- actions: [() => [behaviors.effect(() => {
263
- sendBack({
264
- type: "dismiss"
265
- });
266
- })]]
267
- })
268
- }), arrowListenerCallback = ({
269
- sendBack,
270
- input
271
- }) => {
272
- const unregisterBehaviors = [input.editor.registerBehavior({
273
- behavior: behaviors.defineBehavior({
274
- on: "keyboard.keydown",
275
- guard: ({
276
- event
277
- }) => arrowDownShortcut.guard(event.originEvent),
278
- actions: [() => [behaviors.effect(() => {
279
- sendBack({
280
- type: "navigate down"
281
- });
282
- })]]
283
- })
284
- }), input.editor.registerBehavior({
285
- behavior: behaviors.defineBehavior({
286
- on: "keyboard.keydown",
287
- guard: ({
288
- event
289
- }) => arrowUpShortcut.guard(event.originEvent),
290
- actions: [() => [behaviors.effect(() => {
291
- sendBack({
292
- type: "navigate up"
293
- });
294
- })]]
295
- })
296
- })];
297
- return () => {
298
- for (const unregister of unregisterBehaviors)
299
- unregister();
300
- };
301
- }, emojiInsertListener = ({
302
- sendBack,
303
- input
304
- }) => input.context.editor.registerBehavior({
305
- behavior: behaviors.defineBehavior({
306
- on: "custom.insert emoji",
307
- actions: [({
308
- event
309
- }) => [behaviors.effect(() => {
310
- sendBack({
311
- type: "dismiss"
312
- });
313
- }), behaviors.raise({
314
- type: "delete",
315
- at: {
316
- anchor: {
317
- path: event.focusSpan.path,
318
- offset: event.focusSpan.textBefore.length
319
- },
320
- focus: {
321
- path: event.focusSpan.path,
322
- offset: event.focusSpan.node.text.length - event.focusSpan.textAfter.length
323
- }
324
- }
325
- }), behaviors.raise({
326
- type: "insert.text",
327
- text: event.emoji
328
- })]]
329
- })
330
- }), submitListenerCallback = ({
331
- sendBack,
332
- input,
333
- receive
334
- }) => {
335
- let context = input.context;
336
- receive((event) => {
337
- context = event.context;
338
- });
339
- const unregisterBehaviors = [input.context.editor.registerBehavior({
340
- behavior: behaviors.defineBehavior({
341
- on: "keyboard.keydown",
342
- guard: ({
343
- event
344
- }) => {
345
- if (!enterShortcut.guard(event.originEvent) && !tabShortcut.guard(event.originEvent))
346
- return !1;
347
- const focusSpan = context.focusSpan, match = context.matches[context.selectedIndex];
348
- return match && focusSpan ? {
349
- focusSpan,
350
- emoji: match.emoji
351
- } : !1;
352
- },
353
- actions: [(_, {
354
- focusSpan,
355
- emoji
356
- }) => [behaviors.raise({
357
- type: "custom.insert emoji",
358
- emoji,
359
- focusSpan
360
- })]]
361
- })
362
- }), input.context.editor.registerBehavior({
363
- behavior: behaviors.defineBehavior({
364
- on: "keyboard.keydown",
365
- guard: ({
366
- event
367
- }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.keyword.length === 1,
368
- actions: [({
369
- event
370
- }) => [behaviors.forward(event), behaviors.effect(() => {
371
- sendBack({
372
- type: "dismiss"
373
- });
374
- })]]
375
- })
376
- }), input.context.editor.registerBehavior({
377
- behavior: behaviors.defineBehavior({
378
- on: "keyboard.keydown",
379
- guard: ({
380
- event
381
- }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.keyword.length > 1,
382
- actions: [() => [behaviors.effect(() => {
383
- sendBack({
384
- type: "dismiss"
385
- });
386
- })]]
387
- })
388
- })];
389
- return () => {
390
- for (const unregister of unregisterBehaviors)
391
- unregister();
392
- };
393
- }, selectionListenerCallback = ({
394
- sendBack,
395
- input
396
- }) => input.editor.on("selection", () => {
397
- sendBack({
398
- type: "selection changed"
399
- });
400
- }).unsubscribe, textInsertionListenerCallback = ({
401
- sendBack,
402
- input,
403
- receive
404
- }) => {
405
- let context = input.context;
406
- return receive((event) => {
407
- context = event.context;
408
- }), input.context.editor.registerBehavior({
409
- behavior: behaviors.defineBehavior({
410
- on: "insert.text",
411
- guard: ({
412
- snapshot
413
- }) => {
414
- if (!context.focusSpan || !snapshot.context.selection)
415
- return !1;
416
- const keywordAnchor = {
417
- path: context.focusSpan.path,
418
- offset: context.focusSpan.textBefore.length
419
- };
420
- return utils.isEqualSelectionPoints(snapshot.context.selection.focus, keywordAnchor);
421
- },
422
- actions: [({
423
- event
424
- }) => [behaviors.forward(event), behaviors.effect(() => {
425
- sendBack({
426
- type: "dismiss"
427
- });
428
- })]]
429
- })
430
- });
431
- }, emojiPickerMachine = xstate.setup({
432
- types: {
433
- context: {},
434
- input: {},
435
- events: {}
436
- },
437
- actors: {
438
- "emoji insert listener": xstate.fromCallback(emojiInsertListener),
439
- "submit listener": xstate.fromCallback(submitListenerCallback),
440
- "arrow listener": xstate.fromCallback(arrowListenerCallback),
441
- "trigger listener": xstate.fromCallback(triggerListenerCallback),
442
- "escape listener": xstate.fromCallback(escapeListenerCallback),
443
- "selection listener": xstate.fromCallback(selectionListenerCallback),
444
- "text insertion listener": xstate.fromCallback(textInsertionListenerCallback)
445
- },
446
- actions: {
447
- "set focus span": xstate.assign({
448
- focusSpan: ({
449
- context,
450
- event
451
- }) => event.type !== "custom.trigger found" && event.type !== "custom.keyword found" ? context.focusSpan : event.focusSpan
452
- }),
453
- "update focus span": xstate.assign({
454
- focusSpan: ({
455
- context
456
- }) => {
457
- if (!context.focusSpan)
458
- return;
459
- const snapshot = context.editor.getSnapshot(), focusSpan = selectors.getFocusSpan(snapshot);
460
- if (!snapshot.context.selection || !focusSpan)
461
- return;
462
- const nextSpan = selectors.getNextSpan({
463
- ...snapshot,
464
- context: {
465
- ...snapshot.context,
466
- selection: {
467
- anchor: {
468
- path: context.focusSpan.path,
469
- offset: 0
470
- },
471
- focus: {
472
- path: context.focusSpan.path,
473
- offset: 0
474
- }
475
- }
476
- }
477
- });
478
- if (JSON.stringify(focusSpan.path) !== JSON.stringify(context.focusSpan.path))
479
- return nextSpan && context.focusSpan.textAfter.length === 0 && snapshot.context.selection.focus.offset === 0 && utils.isSelectionCollapsed(snapshot.context.selection) ? context.focusSpan : void 0;
480
- if (!focusSpan.node.text.startsWith(context.focusSpan.textBefore) || !focusSpan.node.text.endsWith(context.focusSpan.textAfter))
481
- return;
482
- const keywordAnchor = {
483
- path: focusSpan.path,
484
- offset: context.focusSpan.textBefore.length
485
- }, keywordFocus = {
486
- path: focusSpan.path,
487
- offset: focusSpan.node.text.length - context.focusSpan.textAfter.length
488
- }, selectionIsBeforeKeyword = selectors.isPointAfterSelection(keywordAnchor)(snapshot), selectionIsAfterKeyword = selectors.isPointBeforeSelection(keywordFocus)(snapshot);
489
- if (!(selectionIsBeforeKeyword || selectionIsAfterKeyword))
490
- return {
491
- node: focusSpan.node,
492
- path: focusSpan.path,
493
- textBefore: context.focusSpan.textBefore,
494
- textAfter: context.focusSpan.textAfter
495
- };
496
- }
497
- }),
498
- "update keyword": xstate.assign({
499
- keyword: ({
500
- context
501
- }) => 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 : ""
502
- }),
503
- "update matches": xstate.assign({
504
- matches: ({
505
- context
506
- }) => {
507
- let rawKeyword = context.keyword.startsWith(":") ? context.keyword.slice(1) : context.keyword;
508
- return rawKeyword = rawKeyword.length > 1 && rawKeyword.endsWith(":") ? rawKeyword.slice(0, -1) : rawKeyword, rawKeyword === void 0 ? [] : context.matchEmojis({
509
- keyword: rawKeyword
510
- });
511
- }
512
- }),
513
- "reset selected index": xstate.assign({
514
- selectedIndex: 0
515
- }),
516
- "increment selected index": xstate.assign({
517
- selectedIndex: ({
518
- context
519
- }) => context.selectedIndex === context.matches.length - 1 ? 0 : context.selectedIndex + 1
520
- }),
521
- "decrement selected index": xstate.assign({
522
- selectedIndex: ({
523
- context
524
- }) => context.selectedIndex === 0 ? context.matches.length - 1 : context.selectedIndex - 1
525
- }),
526
- "set selected index": xstate.assign({
527
- selectedIndex: ({
528
- event
529
- }) => (xstate.assertEvent(event, "navigate to"), event.index)
530
- }),
531
- "update submit listener context": xstate.sendTo("submit listener", ({
532
- context
533
- }) => ({
534
- type: "context changed",
535
- context
536
- })),
537
- "update text insertion listener context": xstate.sendTo("text insertion listener", ({
538
- context
539
- }) => ({
540
- type: "context changed",
541
- context
542
- })),
543
- "insert selected match": ({
544
- context
545
- }) => {
546
- const match = context.matches[context.selectedIndex];
547
- !match || !context.focusSpan || context.editor.send({
548
- type: "custom.insert emoji",
549
- emoji: match.emoji,
550
- focusSpan: context.focusSpan
551
- });
552
- },
553
- reset: xstate.assign({
554
- focusSpan: void 0,
555
- keyword: "",
556
- matches: [],
557
- selectedIndex: 0
558
- })
559
- },
560
- guards: {
561
- "no focus span": ({
562
- context
563
- }) => !context.focusSpan,
564
- "has matches": ({
565
- context
566
- }) => context.matches.length > 0,
567
- "no matches": xstate.not("has matches"),
568
- "keyword is malformed": ({
569
- context
570
- }) => !context.incompleteKeywordRegex.test(context.keyword),
571
- "keyword is direct match": ({
572
- context
573
- }) => {
574
- if (!/^:[\S]+:$/.test(context.keyword))
575
- return !1;
576
- const match = context.matches.at(context.selectedIndex);
577
- return !(!match || match.type !== "exact");
578
- }
579
- }
580
- }).createMachine({
581
- id: "emoji picker",
582
- context: ({
583
- input
584
- }) => ({
585
- editor: input.editor,
586
- keyword: "",
587
- focusSpan: void 0,
588
- matchEmojis: input.matchEmojis,
589
- incompleteKeywordRegex: /^:[\S]*$/,
590
- matches: [],
591
- selectedIndex: 0
592
- }),
593
- initial: "idle",
594
- invoke: [{
595
- src: "emoji insert listener",
596
- id: "emoji insert listener",
597
- input: ({
598
- context
599
- }) => ({
600
- context
601
- })
602
- }],
603
- states: {
604
- idle: {
605
- entry: ["reset"],
606
- invoke: {
607
- src: "trigger listener",
608
- input: ({
609
- context
610
- }) => ({
611
- editor: context.editor
612
- })
613
- },
614
- on: {
615
- "custom.trigger found": {
616
- target: "searching",
617
- actions: ["set focus span", "update keyword"]
618
- },
619
- "custom.keyword found": {
620
- actions: ["set focus span", "update keyword", "update matches", "insert selected match"],
621
- target: "idle",
622
- reenter: !0
623
- }
624
- }
625
- },
626
- searching: {
627
- invoke: [{
628
- src: "submit listener",
629
- id: "submit listener",
630
- input: ({
631
- context
632
- }) => ({
633
- context
634
- })
635
- }, {
636
- src: "escape listener",
637
- input: ({
638
- context
639
- }) => ({
640
- editor: context.editor
641
- })
642
- }, {
643
- src: "selection listener",
644
- input: ({
645
- context
646
- }) => ({
647
- editor: context.editor
648
- })
649
- }, {
650
- src: "text insertion listener",
651
- id: "text insertion listener",
652
- input: ({
653
- context
654
- }) => ({
655
- context
656
- })
657
- }],
658
- on: {
659
- dismiss: {
660
- target: "idle"
661
- },
662
- "selection changed": [{
663
- actions: ["update focus span", "update keyword", "update matches", "reset selected index", "update submit listener context", "update text insertion listener context"]
664
- }]
665
- },
666
- always: [{
667
- guard: "no focus span",
668
- target: "idle"
669
- }, {
670
- guard: "keyword is malformed",
671
- target: "idle"
672
- }, {
673
- guard: "keyword is direct match",
674
- actions: ["insert selected match"],
675
- target: "idle"
676
- }],
677
- initial: "no matches showing",
678
- states: {
679
- "no matches showing": {
680
- entry: ["reset selected index"],
681
- always: {
682
- guard: "has matches",
683
- target: "showing matches"
684
- }
685
- },
686
- "showing matches": {
687
- invoke: {
688
- src: "arrow listener",
689
- input: ({
690
- context
691
- }) => ({
692
- editor: context.editor
693
- })
694
- },
695
- always: [{
696
- guard: "no matches",
697
- target: "no matches showing"
698
- }],
699
- on: {
700
- "navigate down": {
701
- actions: ["increment selected index", "update submit listener context"]
702
- },
703
- "navigate up": {
704
- actions: ["decrement selected index", "update submit listener context"]
705
- },
706
- "navigate to": {
707
- actions: ["set selected index", "update submit listener context"]
708
- },
709
- "insert selected match": {
710
- actions: ["insert selected match"]
711
- }
712
- }
713
- }
714
- }
715
- }
716
- }
717
- });
718
- function useEmojiPicker(props) {
719
- const $ = compilerRuntime.c(17), editor$1 = editor.useEditor();
720
- let t0;
721
- $[0] !== editor$1 || $[1] !== props.matchEmojis ? (t0 = {
722
- input: {
723
- editor: editor$1,
724
- matchEmojis: props.matchEmojis
725
- }
726
- }, $[0] = editor$1, $[1] = props.matchEmojis, $[2] = t0) : t0 = $[2];
727
- const emojiPickerActor = react.useActorRef(emojiPickerMachine, t0), keyword = react.useSelector(emojiPickerActor, _temp), matches = react.useSelector(emojiPickerActor, _temp2), selectedIndex = react.useSelector(emojiPickerActor, _temp3);
728
- let t1;
729
- $[3] !== emojiPickerActor ? (t1 = () => {
730
- emojiPickerActor.send({
731
- type: "dismiss"
732
- });
733
- }, $[3] = emojiPickerActor, $[4] = t1) : t1 = $[4];
734
- const onDismiss = t1;
735
- let t2;
736
- $[5] !== emojiPickerActor ? (t2 = (index) => {
737
- emojiPickerActor.send({
738
- type: "navigate to",
739
- index
740
- });
741
- }, $[5] = emojiPickerActor, $[6] = t2) : t2 = $[6];
742
- const onNavigateTo = t2;
743
- let t3;
744
- $[7] !== editor$1 || $[8] !== emojiPickerActor ? (t3 = () => {
745
- emojiPickerActor.send({
746
- type: "insert selected match"
747
- }), editor$1.send({
748
- type: "focus"
749
- });
750
- }, $[7] = editor$1, $[8] = emojiPickerActor, $[9] = t3) : t3 = $[9];
751
- const onSelect = t3;
752
- let t4;
753
- return $[10] !== keyword || $[11] !== matches || $[12] !== onDismiss || $[13] !== onNavigateTo || $[14] !== onSelect || $[15] !== selectedIndex ? (t4 = {
754
- keyword,
755
- matches,
756
- selectedIndex,
757
- onDismiss,
758
- onNavigateTo,
759
- onSelect
760
- }, $[10] = keyword, $[11] = matches, $[12] = onDismiss, $[13] = onNavigateTo, $[14] = onSelect, $[15] = selectedIndex, $[16] = t4) : t4 = $[16], t4;
761
- }
762
- function _temp3(snapshot_1) {
763
- return snapshot_1.context.selectedIndex;
764
- }
765
- function _temp2(snapshot_0) {
766
- return snapshot_0.context.matches;
767
- }
768
- function _temp(snapshot) {
769
- const rawKeyword = snapshot.context.keyword.startsWith(":") ? snapshot.context.keyword.slice(1) : snapshot.context.keyword;
770
- return rawKeyword.length > 1 && rawKeyword.endsWith(":") ? rawKeyword.slice(0, -1) : rawKeyword;
771
- }
772
- exports.createMatchEmojis = createMatchEmojis;
773
- exports.useEmojiPicker = useEmojiPicker;
774
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/create-match-emojis.ts","../src/emoji-picker-machine.tsx","../src/use-emoji-picker.ts"],"sourcesContent":["import type {MatchEmojis} from './match-emojis'\n\n/**\n * Proposed, but not required type, to represent an emoji match.\n *\n * @example\n * ```tsx\n * {\n * type: 'exact',\n * key: '😂-joy',\n * emoji: '😂',\n * keyword: 'joy',\n * }\n * ```\n * @example\n * ```tsx\n * {\n * type: 'partial',\n * key: '😹-joy-_cat',\n * emoji: '😹',\n * keyword: 'joy',\n * startSlice: '',\n * endSlice: '_cat',\n * }\n * ```\n *\n * @beta\n */\nexport type EmojiMatch =\n | {\n type: 'exact'\n key: string\n emoji: string\n keyword: string\n }\n | {\n type: 'partial'\n key: string\n emoji: string\n keyword: string\n startSlice: string\n endSlice: string\n }\n\n/**\n * Proposed, but not required, function to create a `MatchEmojis` function.\n *\n * @example\n * ```ts\n * const matchEmojis = createMatchEmojis({\n * emojis: {\n * '😂': ['joy'],\n * '😹': ['joy_cat'],\n * },\n * })\n * ```\n *\n * @beta\n */\nexport function createMatchEmojis(config: {\n emojis: Record<string, ReadonlyArray<string>>\n}): MatchEmojis<EmojiMatch> {\n return ({keyword}: {keyword: string}) => {\n const foundEmojis: Array<EmojiMatch> = []\n\n if (keyword.length < 1) {\n return foundEmojis\n }\n\n for (const emoji in config.emojis) {\n const emojiKeywords = config.emojis[emoji] ?? []\n\n for (const emojiKeyword of emojiKeywords) {\n const keywordIndex = emojiKeyword.indexOf(keyword)\n\n if (keywordIndex === -1) {\n continue\n }\n\n if (emojiKeyword === keyword) {\n foundEmojis.push({\n type: 'exact',\n key: `${emoji}-${keyword}`,\n emoji,\n keyword,\n })\n } else {\n const start = emojiKeyword.slice(0, keywordIndex)\n const end = emojiKeyword.slice(keywordIndex + keyword.length)\n\n foundEmojis.push({\n type: 'partial',\n key: `${emoji}-${start}${keyword}${end}`,\n emoji,\n keyword,\n startSlice: start,\n endSlice: end,\n })\n }\n }\n }\n\n return foundEmojis\n }\n}\n","import type {\n ChildPath,\n Editor,\n EditorSelector,\n EditorSnapshot,\n PortableTextSpan,\n} from '@portabletext/editor'\nimport {\n defineBehavior,\n effect,\n forward,\n raise,\n} from '@portabletext/editor/behaviors'\nimport {\n getFocusSpan,\n getMarkState,\n getNextSpan,\n getPreviousSpan,\n isPointAfterSelection,\n isPointBeforeSelection,\n type MarkState,\n} from '@portabletext/editor/selectors'\nimport {\n isEqualSelectionPoints,\n isSelectionCollapsed,\n} from '@portabletext/editor/utils'\nimport {createKeyboardShortcut} from '@portabletext/keyboard-shortcuts'\nimport {\n defineInputRule,\n defineInputRuleBehavior,\n type InputRuleMatch,\n} from '@portabletext/plugin-input-rule'\nimport {\n assertEvent,\n assign,\n fromCallback,\n not,\n sendTo,\n setup,\n type AnyEventObject,\n type CallbackLogicFunction,\n} from 'xstate'\nimport type {BaseEmojiMatch, MatchEmojis} from './match-emojis'\n\n/*******************\n * Keyboard shortcuts\n *******************/\nconst arrowUpShortcut = createKeyboardShortcut({\n default: [{key: 'ArrowUp'}],\n})\nconst arrowDownShortcut = createKeyboardShortcut({\n default: [{key: 'ArrowDown'}],\n})\nconst enterShortcut = createKeyboardShortcut({\n default: [{key: 'Enter'}],\n})\nconst tabShortcut = createKeyboardShortcut({\n default: [{key: 'Tab'}],\n})\nconst escapeShortcut = createKeyboardShortcut({\n default: [{key: 'Escape'}],\n})\n\nconst getTriggerState: EditorSelector<\n | {\n focusSpan: {\n node: PortableTextSpan\n path: ChildPath\n }\n markState: MarkState\n focusSpanTextBefore: string\n focusSpanTextAfter: string\n previousSpan:\n | {\n node: PortableTextSpan\n path: ChildPath\n }\n | undefined\n nextSpan:\n | {\n node: PortableTextSpan\n path: ChildPath\n }\n | undefined\n }\n | undefined\n> = (snapshot) => {\n const focusSpan = getFocusSpan(snapshot)\n const markState = getMarkState(snapshot)\n\n if (!focusSpan || !markState || !snapshot.context.selection) {\n return undefined\n }\n\n const focusSpanTextBefore = focusSpan.node.text.slice(\n 0,\n snapshot.context.selection.focus.offset,\n )\n const focusSpanTextAfter = focusSpan.node.text.slice(\n snapshot.context.selection.focus.offset,\n )\n const previousSpan = getPreviousSpan(snapshot)\n const nextSpan = getNextSpan(snapshot)\n\n return {\n focusSpan,\n markState,\n focusSpanTextBefore,\n focusSpanTextAfter,\n previousSpan,\n nextSpan,\n }\n}\n\nfunction createTriggerActions({\n snapshot,\n payload,\n keywordState,\n}: {\n snapshot: EditorSnapshot\n payload: ReturnType<typeof getTriggerState> & {lastMatch: InputRuleMatch}\n keywordState: 'partial' | 'complete'\n}) {\n if (payload.markState.state === 'unchanged') {\n const focusSpan = {\n node: {\n _key: payload.focusSpan.node._key,\n _type: payload.focusSpan.node._type,\n text: `${payload.focusSpanTextBefore}${payload.lastMatch.text}${payload.focusSpanTextAfter}`,\n marks: payload.markState.marks,\n },\n path: payload.focusSpan.path,\n textBefore: payload.focusSpanTextBefore,\n textAfter: payload.focusSpanTextAfter,\n }\n\n if (keywordState === 'complete') {\n return [\n raise(\n createKeywordFoundEvent({\n focusSpan,\n }),\n ),\n ]\n }\n\n return [\n raise(\n createTriggerFoundEvent({\n focusSpan,\n }),\n ),\n ]\n }\n\n const newSpan = {\n _key: snapshot.context.keyGenerator(),\n _type: payload.focusSpan.node._type,\n text: payload.lastMatch.text,\n marks: payload.markState.marks,\n }\n\n let focusSpan = {\n node: {\n _key: newSpan._key,\n _type: newSpan._type,\n text: `${newSpan.text}${payload.nextSpan?.node.text ?? payload.focusSpanTextAfter}`,\n marks: payload.markState.marks,\n },\n path: [\n {_key: payload.focusSpan.path[0]._key},\n 'children',\n {_key: newSpan._key},\n ] satisfies ChildPath,\n textBefore: '',\n textAfter: payload.nextSpan?.node.text ?? payload.focusSpanTextAfter,\n }\n\n if (\n payload.previousSpan &&\n payload.focusSpanTextBefore.length === 0 &&\n JSON.stringify(payload.previousSpan.node.marks ?? []) ===\n JSON.stringify(payload.markState.marks)\n ) {\n // The text will be inserted into the previous span, so we'll treat that\n // as the focus span\n\n focusSpan = {\n node: {\n _key: payload.previousSpan.node._key,\n _type: newSpan._type,\n text: `${payload.previousSpan.node.text}${newSpan.text}`,\n marks: newSpan.marks,\n },\n path: payload.previousSpan.path,\n textBefore: payload.previousSpan.node.text,\n textAfter: '',\n }\n }\n\n return [\n raise({type: 'select', at: payload.lastMatch.targetOffsets}),\n raise({type: 'delete', at: payload.lastMatch.targetOffsets}),\n raise({type: 'insert.child', child: newSpan}),\n ...(keywordState === 'complete'\n ? [\n raise(\n createKeywordFoundEvent({\n focusSpan,\n }),\n ),\n ]\n : [\n raise(\n createTriggerFoundEvent({\n focusSpan,\n }),\n ),\n ]),\n ]\n}\n\n/*******************\n * Input Rules\n *******************/\n\n/**\n * Listen for a single colon insertion\n */\nconst triggerRule = defineInputRule({\n on: /:/,\n guard: ({snapshot, event}) => {\n const lastMatch = event.matches.at(-1)\n\n if (lastMatch === undefined) {\n return false\n }\n\n const triggerState = getTriggerState(snapshot)\n\n if (!triggerState) {\n return false\n }\n\n return {\n lastMatch,\n ...triggerState,\n }\n },\n actions: [\n ({snapshot}, payload) =>\n createTriggerActions({snapshot, payload, keywordState: 'partial'}),\n ],\n})\n\ntype TriggerFoundEvent = ReturnType<typeof createTriggerFoundEvent>\n\nfunction createTriggerFoundEvent(payload: {\n focusSpan: {\n node: PortableTextSpan\n path: ChildPath\n textBefore: string\n textAfter: string\n }\n}) {\n return {\n type: 'custom.trigger found',\n ...payload,\n } as const\n}\n\n/**\n * Listen for a partial keyword like \":joy\"\n */\nconst partialKeywordRule = defineInputRule({\n on: /:[\\S]+/,\n guard: ({snapshot, event}) => {\n const lastMatch = event.matches.at(-1)\n\n if (lastMatch === undefined) {\n return false\n }\n\n if (lastMatch.targetOffsets.anchor.offset < event.textBefore.length) {\n return false\n }\n\n const triggerState = getTriggerState(snapshot)\n\n if (!triggerState) {\n return false\n }\n\n return {\n ...triggerState,\n lastMatch,\n }\n },\n actions: [\n ({snapshot}, payload) =>\n createTriggerActions({snapshot, payload, keywordState: 'partial'}),\n ],\n})\n\n/**\n * Listen for a complete keyword like \":joy:\"\n */\nconst keywordRule = defineInputRule({\n on: /:[\\S]+:/,\n guard: ({snapshot, event}) => {\n const lastMatch = event.matches.at(-1)\n\n if (lastMatch === undefined) {\n return false\n }\n\n if (lastMatch.targetOffsets.anchor.offset < event.textBefore.length) {\n return false\n }\n\n const triggerState = getTriggerState(snapshot)\n\n if (!triggerState) {\n return false\n }\n\n return {\n ...triggerState,\n lastMatch,\n }\n },\n actions: [\n ({snapshot}, payload) =>\n createTriggerActions({snapshot, payload, keywordState: 'complete'}),\n ],\n})\n\ntype KeywordFoundEvent = ReturnType<typeof createKeywordFoundEvent>\n\nfunction createKeywordFoundEvent(payload: {\n focusSpan: {\n node: PortableTextSpan\n path: ChildPath\n textBefore: string\n textAfter: string\n }\n}) {\n return {\n type: 'custom.keyword found',\n ...payload,\n } as const\n}\n\ntype EmojiPickerContext = {\n editor: Editor\n matches: ReadonlyArray<BaseEmojiMatch>\n matchEmojis: MatchEmojis<BaseEmojiMatch>\n selectedIndex: number\n focusSpan:\n | {\n node: PortableTextSpan\n path: ChildPath\n textBefore: string\n textAfter: string\n }\n | undefined\n incompleteKeywordRegex: RegExp\n keyword: string\n}\n\ntype EmojiPickerEvent =\n | TriggerFoundEvent\n | KeywordFoundEvent\n | {\n type: 'selection changed'\n }\n | {\n type: 'dismiss'\n }\n | {\n type: 'navigate down'\n }\n | {\n type: 'navigate up'\n }\n | {\n type: 'navigate to'\n index: number\n }\n | {\n type: 'insert selected match'\n }\n\nconst triggerListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n EmojiPickerEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const unregisterBehaviors = [\n input.editor.registerBehavior({\n behavior: defineInputRuleBehavior({\n rules: [keywordRule, partialKeywordRule, triggerRule],\n }),\n }),\n input.editor.registerBehavior({\n behavior: defineBehavior<KeywordFoundEvent, KeywordFoundEvent['type']>({\n on: 'custom.keyword found',\n actions: [\n ({event}) => [\n effect(() => {\n sendBack(event)\n }),\n ],\n ],\n }),\n }),\n input.editor.registerBehavior({\n behavior: defineBehavior<TriggerFoundEvent, TriggerFoundEvent['type']>({\n on: 'custom.trigger found',\n actions: [\n ({event}) => [\n effect(() => {\n sendBack(event)\n }),\n ],\n ],\n }),\n }),\n ]\n\n return () => {\n for (const unregister of unregisterBehaviors) {\n unregister()\n }\n }\n}\n\nconst escapeListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n EmojiPickerEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n return input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'keyboard.keydown',\n guard: ({event}) => escapeShortcut.guard(event.originEvent),\n actions: [\n () => [\n effect(() => {\n sendBack({type: 'dismiss'})\n }),\n ],\n ],\n }),\n })\n}\n\nconst arrowListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n EmojiPickerEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const unregisterBehaviors = [\n input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'keyboard.keydown',\n guard: ({event}) => arrowDownShortcut.guard(event.originEvent),\n actions: [\n () => [\n effect(() => {\n sendBack({type: 'navigate down'})\n }),\n ],\n ],\n }),\n }),\n input.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'keyboard.keydown',\n guard: ({event}) => arrowUpShortcut.guard(event.originEvent),\n actions: [\n () => [\n effect(() => {\n sendBack({type: 'navigate up'})\n }),\n ],\n ],\n }),\n }),\n ]\n\n return () => {\n for (const unregister of unregisterBehaviors) {\n unregister()\n }\n }\n}\n\nconst emojiInsertListener: CallbackLogicFunction<\n AnyEventObject,\n EmojiPickerEvent,\n {context: EmojiPickerContext}\n> = ({sendBack, input}) => {\n return input.context.editor.registerBehavior({\n behavior: defineBehavior<{\n emoji: string\n focusSpan: {\n node: PortableTextSpan\n path: ChildPath\n textBefore: string\n textAfter: string\n }\n }>({\n on: 'custom.insert emoji',\n actions: [\n ({event}) => [\n effect(() => {\n sendBack({type: 'dismiss'})\n }),\n raise({\n type: 'delete',\n at: {\n anchor: {\n path: event.focusSpan.path,\n offset: event.focusSpan.textBefore.length,\n },\n focus: {\n path: event.focusSpan.path,\n offset:\n event.focusSpan.node.text.length -\n event.focusSpan.textAfter.length,\n },\n },\n }),\n raise({\n type: 'insert.text',\n text: event.emoji,\n }),\n ],\n ],\n }),\n })\n}\n\nconst submitListenerCallback: CallbackLogicFunction<\n {type: 'context changed'; context: EmojiPickerContext},\n EmojiPickerEvent,\n {context: EmojiPickerContext}\n> = ({sendBack, input, receive}) => {\n let context = input.context\n\n receive((event) => {\n context = event.context\n })\n\n const unregisterBehaviors = [\n input.context.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'keyboard.keydown',\n guard: ({event}) => {\n if (\n !enterShortcut.guard(event.originEvent) &&\n !tabShortcut.guard(event.originEvent)\n ) {\n return false\n }\n\n const focusSpan = context.focusSpan\n const match = context.matches[context.selectedIndex]\n\n return match && focusSpan ? {focusSpan, emoji: match.emoji} : false\n },\n actions: [\n (_, {focusSpan, emoji}) => [\n raise({\n type: 'custom.insert emoji',\n emoji,\n focusSpan,\n }),\n ],\n ],\n }),\n }),\n input.context.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'keyboard.keydown',\n guard: ({event}) =>\n (enterShortcut.guard(event.originEvent) ||\n tabShortcut.guard(event.originEvent)) &&\n context.keyword.length === 1,\n actions: [\n ({event}) => [\n forward(event),\n effect(() => {\n sendBack({type: 'dismiss'})\n }),\n ],\n ],\n }),\n }),\n input.context.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'keyboard.keydown',\n guard: ({event}) =>\n (enterShortcut.guard(event.originEvent) ||\n tabShortcut.guard(event.originEvent)) &&\n context.keyword.length > 1,\n actions: [\n () => [\n effect(() => {\n sendBack({type: 'dismiss'})\n }),\n ],\n ],\n }),\n }),\n ]\n\n return () => {\n for (const unregister of unregisterBehaviors) {\n unregister()\n }\n }\n}\n\nconst selectionListenerCallback: CallbackLogicFunction<\n AnyEventObject,\n EmojiPickerEvent,\n {editor: Editor}\n> = ({sendBack, input}) => {\n const subscription = input.editor.on('selection', () => {\n sendBack({type: 'selection changed'})\n })\n\n return subscription.unsubscribe\n}\n\nconst textInsertionListenerCallback: CallbackLogicFunction<\n {type: 'context changed'; context: EmojiPickerContext},\n EmojiPickerEvent,\n {context: EmojiPickerContext}\n> = ({sendBack, input, receive}) => {\n let context = input.context\n\n receive((event) => {\n context = event.context\n })\n\n return input.context.editor.registerBehavior({\n behavior: defineBehavior({\n on: 'insert.text',\n guard: ({snapshot}) => {\n if (!context.focusSpan) {\n return false\n }\n\n if (!snapshot.context.selection) {\n return false\n }\n\n const keywordAnchor = {\n path: context.focusSpan.path,\n offset: context.focusSpan.textBefore.length,\n }\n\n return isEqualSelectionPoints(\n snapshot.context.selection.focus,\n keywordAnchor,\n )\n },\n actions: [\n ({event}) => [\n forward(event),\n effect(() => {\n sendBack({type: 'dismiss'})\n }),\n ],\n ],\n }),\n })\n}\n\nexport const emojiPickerMachine = setup({\n types: {\n context: {} as EmojiPickerContext,\n input: {} as {\n editor: Editor\n matchEmojis: MatchEmojis\n },\n events: {} as EmojiPickerEvent,\n },\n actors: {\n 'emoji insert listener': fromCallback(emojiInsertListener),\n 'submit listener': fromCallback(submitListenerCallback),\n 'arrow listener': fromCallback(arrowListenerCallback),\n 'trigger listener': fromCallback(triggerListenerCallback),\n 'escape listener': fromCallback(escapeListenerCallback),\n 'selection listener': fromCallback(selectionListenerCallback),\n 'text insertion listener': fromCallback(textInsertionListenerCallback),\n },\n actions: {\n 'set focus span': assign({\n focusSpan: ({context, event}) => {\n if (\n event.type !== 'custom.trigger found' &&\n event.type !== 'custom.keyword found'\n ) {\n return context.focusSpan\n }\n\n return event.focusSpan\n },\n }),\n 'update focus span': assign({\n focusSpan: ({context}) => {\n if (!context.focusSpan) {\n return undefined\n }\n\n const snapshot = context.editor.getSnapshot()\n const focusSpan = getFocusSpan(snapshot)\n\n if (!snapshot.context.selection) {\n return undefined\n }\n\n if (!focusSpan) {\n return undefined\n }\n\n const nextSpan = getNextSpan({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: {\n anchor: {\n path: context.focusSpan.path,\n offset: 0,\n },\n focus: {\n path: context.focusSpan.path,\n offset: 0,\n },\n },\n },\n })\n\n if (\n JSON.stringify(focusSpan.path) !==\n JSON.stringify(context.focusSpan.path)\n ) {\n if (\n nextSpan &&\n context.focusSpan.textAfter.length === 0 &&\n snapshot.context.selection.focus.offset === 0 &&\n isSelectionCollapsed(snapshot.context.selection)\n ) {\n // This is an edge case where the caret is moved from the end of\n // the focus span to the start of the next span.\n return context.focusSpan\n }\n\n return undefined\n }\n\n if (!focusSpan.node.text.startsWith(context.focusSpan.textBefore)) {\n return undefined\n }\n\n if (!focusSpan.node.text.endsWith(context.focusSpan.textAfter)) {\n return undefined\n }\n\n const keywordAnchor = {\n path: focusSpan.path,\n offset: context.focusSpan.textBefore.length,\n }\n const keywordFocus = {\n path: focusSpan.path,\n offset:\n focusSpan.node.text.length - context.focusSpan.textAfter.length,\n }\n\n const selectionIsBeforeKeyword =\n isPointAfterSelection(keywordAnchor)(snapshot)\n\n const selectionIsAfterKeyword =\n isPointBeforeSelection(keywordFocus)(snapshot)\n\n if (selectionIsBeforeKeyword || selectionIsAfterKeyword) {\n return undefined\n }\n\n return {\n node: focusSpan.node,\n path: focusSpan.path,\n textBefore: context.focusSpan.textBefore,\n textAfter: context.focusSpan.textAfter,\n }\n },\n }),\n 'update keyword': assign({\n keyword: ({context}) => {\n if (!context.focusSpan) {\n return ''\n }\n\n if (\n context.focusSpan.textBefore.length > 0 &&\n context.focusSpan.textAfter.length > 0\n ) {\n return context.focusSpan.node.text.slice(\n context.focusSpan.textBefore.length,\n -context.focusSpan.textAfter.length,\n )\n }\n\n if (context.focusSpan.textBefore.length > 0) {\n return context.focusSpan.node.text.slice(\n context.focusSpan.textBefore.length,\n )\n }\n\n if (context.focusSpan.textAfter.length > 0) {\n return context.focusSpan.node.text.slice(\n 0,\n -context.focusSpan.textAfter.length,\n )\n }\n\n return context.focusSpan.node.text\n },\n }),\n 'update matches': assign({\n matches: ({context}) => {\n // Strip leading colon\n let rawKeyword = context.keyword.startsWith(':')\n ? context.keyword.slice(1)\n : context.keyword\n // Strip trailing colon\n rawKeyword =\n rawKeyword.length > 1 && rawKeyword.endsWith(':')\n ? rawKeyword.slice(0, -1)\n : rawKeyword\n\n if (rawKeyword === undefined) {\n return []\n }\n\n return context.matchEmojis({keyword: rawKeyword})\n },\n }),\n 'reset selected index': assign({\n selectedIndex: 0,\n }),\n 'increment selected index': assign({\n selectedIndex: ({context}) => {\n if (context.selectedIndex === context.matches.length - 1) {\n return 0\n }\n return context.selectedIndex + 1\n },\n }),\n 'decrement selected index': assign({\n selectedIndex: ({context}) => {\n if (context.selectedIndex === 0) {\n return context.matches.length - 1\n }\n return context.selectedIndex - 1\n },\n }),\n 'set selected index': assign({\n selectedIndex: ({event}) => {\n assertEvent(event, 'navigate to')\n\n return event.index\n },\n }),\n 'update submit listener context': sendTo(\n 'submit listener',\n ({context}) => ({\n type: 'context changed',\n context,\n }),\n ),\n 'update text insertion listener context': sendTo(\n 'text insertion listener',\n ({context}) => ({\n type: 'context changed',\n context,\n }),\n ),\n 'insert selected match': ({context}) => {\n const match = context.matches[context.selectedIndex]\n\n if (!match || !context.focusSpan) {\n return\n }\n\n context.editor.send({\n type: 'custom.insert emoji',\n emoji: match.emoji,\n focusSpan: context.focusSpan,\n })\n },\n 'reset': assign({\n focusSpan: undefined,\n keyword: '',\n matches: [],\n selectedIndex: 0,\n }),\n },\n guards: {\n 'no focus span': ({context}) => {\n return !context.focusSpan\n },\n 'has matches': ({context}) => {\n return context.matches.length > 0\n },\n 'no matches': not('has matches'),\n 'keyword is malformed': ({context}) => {\n return !context.incompleteKeywordRegex.test(context.keyword)\n },\n 'keyword is direct match': ({context}) => {\n const fullKeywordRegex = /^:[\\S]+:$/\n\n if (!fullKeywordRegex.test(context.keyword)) {\n return false\n }\n\n const match = context.matches.at(context.selectedIndex)\n\n if (!match || match.type !== 'exact') {\n return false\n }\n\n return true\n },\n },\n}).createMachine({\n id: 'emoji picker',\n context: ({input}) => ({\n editor: input.editor,\n keyword: '',\n focusSpan: undefined,\n matchEmojis: input.matchEmojis,\n incompleteKeywordRegex: /^:[\\S]*$/,\n matches: [],\n selectedIndex: 0,\n }),\n initial: 'idle',\n invoke: [\n {\n src: 'emoji insert listener',\n id: 'emoji insert listener',\n input: ({context}) => ({context}),\n },\n ],\n states: {\n idle: {\n entry: ['reset'],\n invoke: {\n src: 'trigger listener',\n input: ({context}) => ({editor: context.editor}),\n },\n on: {\n 'custom.trigger found': {\n target: 'searching',\n actions: ['set focus span', 'update keyword'],\n },\n 'custom.keyword found': {\n actions: [\n 'set focus span',\n 'update keyword',\n 'update matches',\n 'insert selected match',\n ],\n target: 'idle',\n reenter: true,\n },\n },\n },\n searching: {\n invoke: [\n {\n src: 'submit listener',\n id: 'submit listener',\n input: ({context}) => ({context}),\n },\n {\n src: 'escape listener',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'selection listener',\n input: ({context}) => ({editor: context.editor}),\n },\n {\n src: 'text insertion listener',\n id: 'text insertion listener',\n input: ({context}) => ({context}),\n },\n ],\n on: {\n 'dismiss': {\n target: 'idle',\n },\n 'selection changed': [\n {\n actions: [\n 'update focus span',\n 'update keyword',\n 'update matches',\n 'reset selected index',\n 'update submit listener context',\n 'update text insertion listener context',\n ],\n },\n ],\n },\n always: [\n {\n guard: 'no focus span',\n target: 'idle',\n },\n {\n guard: 'keyword is malformed',\n target: 'idle',\n },\n {\n guard: 'keyword is direct match',\n actions: ['insert selected match'],\n target: 'idle',\n },\n ],\n initial: 'no matches showing',\n states: {\n 'no matches showing': {\n entry: ['reset selected index'],\n always: {\n guard: 'has matches',\n target: 'showing matches',\n },\n },\n 'showing matches': {\n invoke: {\n src: 'arrow listener',\n input: ({context}) => ({editor: context.editor}),\n },\n always: [\n {\n guard: 'no matches',\n target: 'no matches showing',\n },\n ],\n on: {\n 'navigate down': {\n actions: [\n 'increment selected index',\n 'update submit listener context',\n ],\n },\n 'navigate up': {\n actions: [\n 'decrement selected index',\n 'update submit listener context',\n ],\n },\n 'navigate to': {\n actions: ['set selected index', 'update submit listener context'],\n },\n 'insert selected match': {\n actions: ['insert selected match'],\n },\n },\n },\n },\n },\n },\n})\n","import {useEditor} from '@portabletext/editor'\nimport {useActorRef, useSelector} from '@xstate/react'\nimport {useCallback} from 'react'\nimport {emojiPickerMachine} from './emoji-picker-machine'\nimport type {BaseEmojiMatch, MatchEmojis} from './match-emojis'\n\n/**\n * @beta\n */\nexport type EmojiPicker<TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch> = {\n /**\n * The matched keyword.\n *\n * Can be used to display the keyword in the UI or conditionally render the\n * list of matches.\n *\n * @example\n * ```tsx\n * if (keyword.length < 1) {\n * return null\n * }\n * ```\n */\n keyword: string\n\n /**\n * Emoji matches found for the current keyword.\n *\n * Can be used to display the matches in a list.\n */\n matches: ReadonlyArray<TEmojiMatch>\n\n /**\n * The index of the selected match.\n *\n * Can be used to highlight the selected match in the list.\n *\n * @example\n * ```tsx\n * <EmojiListItem\n * key={match.key}\n * match={match}\n * selected={selectedIndex === index}\n * />\n * ```\n */\n selectedIndex: number\n\n /**\n * Navigate to a specific match by index.\n *\n * Can be used to control the `selectedIndex`. For example, using\n * `onMouseEnter`.\n *\n * @example\n * ```tsx\n * <EmojiListItem\n * key={match.key}\n * match={match}\n * selected={selectedIndex === index}\n * onMouseEnter={() => {onNavigateTo(index)}}\n * />\n * ```\n */\n onNavigateTo: (index: number) => void\n\n /**\n * Select the current match.\n *\n * Can be used to insert the currently selected match.\n *\n *\n * @example\n * ```tsx\n * <EmojiListItem\n * key={match.key}\n * match={match}\n * selected={selectedIndex === index}\n * onMouseEnter={() => {onNavigateTo(index)}}\n * onSelect={() => {onSelect()}}\n * />\n * ```\n *\n * Note: The currently selected match is automatically inserted on Enter or\n * Tab.\n */\n onSelect: () => void\n\n /**\n * Dismiss the emoji picker. Can be used to let the user dismiss the picker\n * by clicking a button.\n *\n * @example\n * ```tsx\n * {matches.length === 0 ? (\n * <Button onPress={onDismiss}>Dismiss</Button>\n * ) : <EmojiListBox {...props} />}\n * ```\n *\n * Note: The emoji picker is automatically dismissed on Escape.\n */\n onDismiss: () => void\n}\n\n/**\n * @beta\n */\nexport type EmojiPickerProps<\n TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,\n> = {\n matchEmojis: MatchEmojis<TEmojiMatch>\n}\n\n/**\n * Handles the state and logic needed to create an emoji picker.\n *\n * The `matchEmojis` function is generic and can return any shape of emoji\n * match required for the emoji picker.\n *\n * However, the default implementation of `matchEmojis` returns an array of\n * `EmojiMatch` objects and can be created using the `createMatchEmojis`\n * function.\n *\n * @example\n *\n * ```tsx\n * const matchEmojis = createMatchEmojis({emojis: {\n * '😂': ['joy'],\n * '😹': ['joy_cat'],\n * }})\n *\n * const {keyword, matches, selectedIndex, onDismiss, onNavigateTo, onSelect} =\n * useEmojiPicker({matchEmojis})\n * ```\n *\n * Note: This hook is not concerned with the UI, how the emoji picker is\n * rendered or positioned in the document.\n *\n * @beta\n */\nexport function useEmojiPicker<\n TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,\n>(props: EmojiPickerProps<TEmojiMatch>): EmojiPicker<TEmojiMatch> {\n const editor = useEditor()\n const emojiPickerActor = useActorRef(emojiPickerMachine, {\n input: {editor, matchEmojis: props.matchEmojis},\n })\n const keyword = useSelector(emojiPickerActor, (snapshot) => {\n const rawKeyword = snapshot.context.keyword.startsWith(':')\n ? snapshot.context.keyword.slice(1)\n : snapshot.context.keyword\n return rawKeyword.length > 1 && rawKeyword.endsWith(':')\n ? rawKeyword.slice(0, -1)\n : rawKeyword\n })\n const matches = useSelector(\n emojiPickerActor,\n (snapshot) => snapshot.context.matches as ReadonlyArray<TEmojiMatch>,\n )\n const selectedIndex = useSelector(\n emojiPickerActor,\n (snapshot) => snapshot.context.selectedIndex,\n )\n\n const onDismiss = useCallback(() => {\n emojiPickerActor.send({type: 'dismiss'})\n }, [emojiPickerActor])\n const onNavigateTo = useCallback(\n (index: number) => {\n emojiPickerActor.send({type: 'navigate to', index})\n },\n [emojiPickerActor],\n )\n const onSelect = useCallback(() => {\n emojiPickerActor.send({type: 'insert selected match'})\n editor.send({type: 'focus'})\n }, [emojiPickerActor, editor])\n\n return {\n keyword,\n matches,\n selectedIndex,\n onDismiss,\n onNavigateTo,\n onSelect,\n }\n}\n"],"names":["createMatchEmojis","config","keyword","foundEmojis","length","emoji","emojis","emojiKeywords","emojiKeyword","keywordIndex","indexOf","push","type","key","start","slice","end","startSlice","endSlice","arrowUpShortcut","createKeyboardShortcut","default","arrowDownShortcut","enterShortcut","tabShortcut","escapeShortcut","getTriggerState","snapshot","focusSpan","getFocusSpan","markState","getMarkState","context","selection","focusSpanTextBefore","node","text","focus","offset","focusSpanTextAfter","previousSpan","getPreviousSpan","nextSpan","getNextSpan","createTriggerActions","payload","keywordState","state","_key","_type","lastMatch","marks","path","textBefore","textAfter","raise","createKeywordFoundEvent","createTriggerFoundEvent","newSpan","keyGenerator","JSON","stringify","at","targetOffsets","child","triggerRule","defineInputRule","on","guard","event","matches","undefined","triggerState","actions","partialKeywordRule","anchor","keywordRule","triggerListenerCallback","sendBack","input","unregisterBehaviors","editor","registerBehavior","behavior","defineInputRuleBehavior","rules","defineBehavior","effect","unregister","escapeListenerCallback","originEvent","arrowListenerCallback","emojiInsertListener","submitListenerCallback","receive","match","selectedIndex","_","forward","selectionListenerCallback","unsubscribe","textInsertionListenerCallback","keywordAnchor","isEqualSelectionPoints","emojiPickerMachine","setup","types","events","actors","fromCallback","assign","getSnapshot","isSelectionCollapsed","startsWith","endsWith","keywordFocus","selectionIsBeforeKeyword","isPointAfterSelection","selectionIsAfterKeyword","isPointBeforeSelection","rawKeyword","matchEmojis","assertEvent","index","sendTo","insert selected match","send","guards","no focus span","has matches","not","keyword is malformed","incompleteKeywordRegex","test","keyword is direct match","createMachine","id","initial","invoke","src","states","idle","entry","target","reenter","searching","always","useEmojiPicker","props","$","_c","useEditor","t0","emojiPickerActor","useActorRef","useSelector","_temp","_temp2","_temp3","t1","onDismiss","t2","onNavigateTo","t3","onSelect","t4","snapshot_1","snapshot_0"],"mappings":"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"}
package/dist/index.d.cts DELETED
@@ -1,221 +0,0 @@
1
- /**
2
- * The base type representing an emoji match.
3
- *
4
- * @beta
5
- */
6
- export declare type BaseEmojiMatch =
7
- | {
8
- type: 'exact'
9
- emoji: string
10
- }
11
- | {
12
- type: 'partial'
13
- emoji: string
14
- }
15
-
16
- /**
17
- * Proposed, but not required, function to create a `MatchEmojis` function.
18
- *
19
- * @example
20
- * ```ts
21
- * const matchEmojis = createMatchEmojis({
22
- * emojis: {
23
- * '😂': ['joy'],
24
- * '😹': ['joy_cat'],
25
- * },
26
- * })
27
- * ```
28
- *
29
- * @beta
30
- */
31
- export declare function createMatchEmojis(config: {
32
- emojis: Record<string, ReadonlyArray<string>>
33
- }): MatchEmojis<EmojiMatch>
34
-
35
- /**
36
- * Proposed, but not required type, to represent an emoji match.
37
- *
38
- * @example
39
- * ```tsx
40
- * {
41
- * type: 'exact',
42
- * key: '😂-joy',
43
- * emoji: '😂',
44
- * keyword: 'joy',
45
- * }
46
- * ```
47
- * @example
48
- * ```tsx
49
- * {
50
- * type: 'partial',
51
- * key: '😹-joy-_cat',
52
- * emoji: '😹',
53
- * keyword: 'joy',
54
- * startSlice: '',
55
- * endSlice: '_cat',
56
- * }
57
- * ```
58
- *
59
- * @beta
60
- */
61
- export declare type EmojiMatch =
62
- | {
63
- type: 'exact'
64
- key: string
65
- emoji: string
66
- keyword: string
67
- }
68
- | {
69
- type: 'partial'
70
- key: string
71
- emoji: string
72
- keyword: string
73
- startSlice: string
74
- endSlice: string
75
- }
76
-
77
- /**
78
- * @beta
79
- */
80
- export declare type EmojiPicker<
81
- TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,
82
- > = {
83
- /**
84
- * The matched keyword.
85
- *
86
- * Can be used to display the keyword in the UI or conditionally render the
87
- * list of matches.
88
- *
89
- * @example
90
- * ```tsx
91
- * if (keyword.length < 1) {
92
- * return null
93
- * }
94
- * ```
95
- */
96
- keyword: string
97
- /**
98
- * Emoji matches found for the current keyword.
99
- *
100
- * Can be used to display the matches in a list.
101
- */
102
- matches: ReadonlyArray<TEmojiMatch>
103
- /**
104
- * The index of the selected match.
105
- *
106
- * Can be used to highlight the selected match in the list.
107
- *
108
- * @example
109
- * ```tsx
110
- * <EmojiListItem
111
- * key={match.key}
112
- * match={match}
113
- * selected={selectedIndex === index}
114
- * />
115
- * ```
116
- */
117
- selectedIndex: number
118
- /**
119
- * Navigate to a specific match by index.
120
- *
121
- * Can be used to control the `selectedIndex`. For example, using
122
- * `onMouseEnter`.
123
- *
124
- * @example
125
- * ```tsx
126
- * <EmojiListItem
127
- * key={match.key}
128
- * match={match}
129
- * selected={selectedIndex === index}
130
- * onMouseEnter={() => {onNavigateTo(index)}}
131
- * />
132
- * ```
133
- */
134
- onNavigateTo: (index: number) => void
135
- /**
136
- * Select the current match.
137
- *
138
- * Can be used to insert the currently selected match.
139
- *
140
- *
141
- * @example
142
- * ```tsx
143
- * <EmojiListItem
144
- * key={match.key}
145
- * match={match}
146
- * selected={selectedIndex === index}
147
- * onMouseEnter={() => {onNavigateTo(index)}}
148
- * onSelect={() => {onSelect()}}
149
- * />
150
- * ```
151
- *
152
- * Note: The currently selected match is automatically inserted on Enter or
153
- * Tab.
154
- */
155
- onSelect: () => void
156
- /**
157
- * Dismiss the emoji picker. Can be used to let the user dismiss the picker
158
- * by clicking a button.
159
- *
160
- * @example
161
- * ```tsx
162
- * {matches.length === 0 ? (
163
- * <Button onPress={onDismiss}>Dismiss</Button>
164
- * ) : <EmojiListBox {...props} />}
165
- * ```
166
- *
167
- * Note: The emoji picker is automatically dismissed on Escape.
168
- */
169
- onDismiss: () => void
170
- }
171
-
172
- /**
173
- * @beta
174
- */
175
- export declare type EmojiPickerProps<
176
- TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,
177
- > = {
178
- matchEmojis: MatchEmojis<TEmojiMatch>
179
- }
180
-
181
- /**
182
- * A function that returns an array of emoji matches for a given keyword.
183
- *
184
- * @beta
185
- */
186
- export declare type MatchEmojis<
187
- TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,
188
- > = (query: {keyword: string}) => ReadonlyArray<TEmojiMatch>
189
-
190
- /**
191
- * Handles the state and logic needed to create an emoji picker.
192
- *
193
- * The `matchEmojis` function is generic and can return any shape of emoji
194
- * match required for the emoji picker.
195
- *
196
- * However, the default implementation of `matchEmojis` returns an array of
197
- * `EmojiMatch` objects and can be created using the `createMatchEmojis`
198
- * function.
199
- *
200
- * @example
201
- *
202
- * ```tsx
203
- * const matchEmojis = createMatchEmojis({emojis: {
204
- * '😂': ['joy'],
205
- * '😹': ['joy_cat'],
206
- * }})
207
- *
208
- * const {keyword, matches, selectedIndex, onDismiss, onNavigateTo, onSelect} =
209
- * useEmojiPicker({matchEmojis})
210
- * ```
211
- *
212
- * Note: This hook is not concerned with the UI, how the emoji picker is
213
- * rendered or positioned in the document.
214
- *
215
- * @beta
216
- */
217
- export declare function useEmojiPicker<
218
- TEmojiMatch extends BaseEmojiMatch = BaseEmojiMatch,
219
- >(props: EmojiPickerProps<TEmojiMatch>): EmojiPicker<TEmojiMatch>
220
-
221
- export {}