@portabletext/plugin-emoji-picker 1.0.5 → 1.0.7

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.cjs CHANGED
@@ -364,7 +364,21 @@ const triggerListenerCallback = ({
364
364
  on: "keyboard.keydown",
365
365
  guard: ({
366
366
  event
367
- }) => enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent),
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,
368
382
  actions: [() => [behaviors.effect(() => {
369
383
  sendBack({
370
384
  type: "dismiss"
@@ -1 +1 @@
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 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","_","selectionListenerCallback","unsubscribe","textInsertionListenerCallback","keywordAnchor","isEqualSelectionPoints","forward","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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"}
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.js CHANGED
@@ -370,7 +370,21 @@ const triggerListenerCallback = ({
370
370
  on: "keyboard.keydown",
371
371
  guard: ({
372
372
  event
373
- }) => enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent),
373
+ }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.keyword.length === 1,
374
+ actions: [({
375
+ event
376
+ }) => [forward(event), effect(() => {
377
+ sendBack({
378
+ type: "dismiss"
379
+ });
380
+ })]]
381
+ })
382
+ }), input.context.editor.registerBehavior({
383
+ behavior: defineBehavior({
384
+ on: "keyboard.keydown",
385
+ guard: ({
386
+ event
387
+ }) => (enterShortcut.guard(event.originEvent) || tabShortcut.guard(event.originEvent)) && context.keyword.length > 1,
374
388
  actions: [() => [effect(() => {
375
389
  sendBack({
376
390
  type: "dismiss"
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","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 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","_","selectionListenerCallback","unsubscribe","textInsertionListenerCallback","keywordAnchor","isEqualSelectionPoints","forward","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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"}
1
+ {"version":3,"file":"index.js","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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-emoji-picker",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Easily configure an Emoji Picker for the Portable Text Editor",
5
5
  "keywords": [
6
6
  "portabletext",
@@ -41,7 +41,7 @@
41
41
  "react-compiler-runtime": "1.0.0",
42
42
  "xstate": "^5.23.0",
43
43
  "@portabletext/keyboard-shortcuts": "^1.1.1",
44
- "@portabletext/plugin-input-rule": "~0.4.2"
44
+ "@portabletext/plugin-input-rule": "~0.4.3"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@sanity/pkg-utils": "^8.1.4",
@@ -57,12 +57,12 @@
57
57
  "typescript": "5.9.3",
58
58
  "typescript-eslint": "^8.46.1",
59
59
  "vitest": "^4.0.4",
60
- "@portabletext/editor": "2.17.0",
61
- "@portabletext/schema": "1.2.0",
62
- "racejar": "1.3.2"
60
+ "@portabletext/editor": "2.17.1",
61
+ "racejar": "1.3.2",
62
+ "@portabletext/schema": "1.2.0"
63
63
  },
64
64
  "peerDependencies": {
65
- "@portabletext/editor": "^2.17.0",
65
+ "@portabletext/editor": "^2.17.1",
66
66
  "react": "^19.1.1"
67
67
  },
68
68
  "publishConfig": {
@@ -585,8 +585,26 @@ const submitListenerCallback: CallbackLogicFunction<
585
585
  behavior: defineBehavior({
586
586
  on: 'keyboard.keydown',
587
587
  guard: ({event}) =>
588
- enterShortcut.guard(event.originEvent) ||
589
- tabShortcut.guard(event.originEvent),
588
+ (enterShortcut.guard(event.originEvent) ||
589
+ tabShortcut.guard(event.originEvent)) &&
590
+ context.keyword.length === 1,
591
+ actions: [
592
+ ({event}) => [
593
+ forward(event),
594
+ effect(() => {
595
+ sendBack({type: 'dismiss'})
596
+ }),
597
+ ],
598
+ ],
599
+ }),
600
+ }),
601
+ input.context.editor.registerBehavior({
602
+ behavior: defineBehavior({
603
+ on: 'keyboard.keydown',
604
+ guard: ({event}) =>
605
+ (enterShortcut.guard(event.originEvent) ||
606
+ tabShortcut.guard(event.originEvent)) &&
607
+ context.keyword.length > 1,
590
608
  actions: [
591
609
  () => [
592
610
  effect(() => {
@@ -93,6 +93,17 @@ Feature: Emoji Picker
93
93
  And "{Enter}" is pressed
94
94
  Then the text is ":joy|"
95
95
 
96
+ Scenario: Aborting and forwarding Enter if there is no keyword
97
+ When ":" is typed
98
+ And "{Enter}" is pressed
99
+ Then the text is ":|"
100
+
101
+ Scenario: Aborting Enter if there are no matches
102
+ When ":asdf" is typed
103
+ And "{Enter}" is pressed
104
+ Then the text is ":asdf"
105
+ And the keyword is ""
106
+
96
107
  Scenario: Backspacing to narrow search
97
108
  When ":joy" is typed
98
109
  And "{Backspace}" is pressed