From 56f0dc573d7207861b7c8e9c25f3f18cc22ce925 Mon Sep 17 00:00:00 2001 From: Maksim Karmatskikh Date: Mon, 2 Oct 2023 13:22:40 +0600 Subject: [PATCH] UBERF-17: Missing smiles auto-conversion in rich texts :) (#3771) Signed-off-by: Maxim Karmatskikh --- .../src/components/ReferenceInput.svelte | 3 +- .../src/components/StyledTextBox.svelte | 53 ++++++++---- .../src/components/extension/emoji.ts | 80 +++++++++++++++++++ 3 files changed, 119 insertions(+), 17 deletions(-) create mode 100644 packages/text-editor/src/components/extension/emoji.ts diff --git a/packages/text-editor/src/components/ReferenceInput.svelte b/packages/text-editor/src/components/ReferenceInput.svelte index 9943d2808d..670fc1b493 100644 --- a/packages/text-editor/src/components/ReferenceInput.svelte +++ b/packages/text-editor/src/components/ReferenceInput.svelte @@ -34,6 +34,7 @@ import { RefAction, RefInputActionItem, TextEditorHandler, TextFormatCategory } from '../types' import TextEditor from './TextEditor.svelte' import { completionConfig } from './extensions' + import { EmojiExtension } from './extension/emoji' import Attach from './icons/Attach.svelte' import RIMention from './icons/RIMention.svelte' import Send from './icons/Send.svelte' @@ -177,7 +178,7 @@ updateFocus() dispatch('focus', focused) }} - extensions={[completionPlugin]} + extensions={[completionPlugin, EmojiExtension.configure()]} on:update placeholder={placeholder ?? textEditorPlugin.string.EditorPlaceholder} textFormatCategories={[ diff --git a/packages/text-editor/src/components/StyledTextBox.svelte b/packages/text-editor/src/components/StyledTextBox.svelte index 21d9c3e7b8..45d2adc31b 100644 --- a/packages/text-editor/src/components/StyledTextBox.svelte +++ b/packages/text-editor/src/components/StyledTextBox.svelte @@ -13,10 +13,15 @@ resizeObserver } from '@hcengineering/ui' import { createEventDispatcher } from 'svelte' + import type { AnyExtension } from '@tiptap/core' + import { Completion } from '../Completion' import textEditorPlugin from '../plugin' import StyledTextEditor from './StyledTextEditor.svelte' + import { completionConfig } from './extensions' + import { EmojiExtension } from './extension/emoji' + import { ImageRef, FileAttachFunction } from './imageExt' import { Node as ProseMirrorNode } from '@tiptap/pm/model' @@ -38,6 +43,7 @@ export let enableFormatting = false export let autofocus = false export let enableBackReferences: boolean = false + export let enableEmojiReplace: boolean = true export let isScrollable: boolean = true export let attachFile: FileAttachFunction | undefined = undefined @@ -136,24 +142,9 @@ focusManager?.setFocus(idx) } } - const completionPlugin = Completion.configure({ - ...completionConfig, - showDoc (event: MouseEvent, _id: string, _class: string) { - dispatch('open-document', { event, _id, _class }) - } - }) const attachments = new Map() - const imagePlugin = ImageRef.configure({ - inline: true, - HTMLAttributes: {}, - attachFile, - reportNode: (id, node) => { - attachments.set(id, node) - } - }) - /** * @public */ @@ -163,6 +154,36 @@ textEditor.removeNode(nde) } } + + function configureExtensions () { + const imagePlugin = ImageRef.configure({ + inline: true, + HTMLAttributes: {}, + attachFile, + reportNode: (id, node) => { + attachments.set(id, node) + } + }) + + const completionPlugin = Completion.configure({ + ...completionConfig, + showDoc (event: MouseEvent, _id: string, _class: string) { + dispatch('open-document', { event, _id, _class }) + } + }) + + const extensions: AnyExtension[] = [imagePlugin] + if (enableBackReferences) { + extensions.unshift(completionPlugin) + } + if (enableEmojiReplace) { + extensions.push(EmojiExtension.configure()) + } + + return extensions + } + + const extensions = configureExtensions() @@ -195,7 +216,7 @@ {enableFormatting} {autofocus} {isScrollable} - extensions={enableBackReferences ? [completionPlugin, imagePlugin] : [imagePlugin]} + {extensions} bind:content={rawValue} bind:this={textEditor} on:attach diff --git a/packages/text-editor/src/components/extension/emoji.ts b/packages/text-editor/src/components/extension/emoji.ts new file mode 100644 index 0000000000..ffb20d3f29 --- /dev/null +++ b/packages/text-editor/src/components/extension/emoji.ts @@ -0,0 +1,80 @@ +import { Extension } from '@tiptap/core' + +const emojiReplaceDict = { + '0:)': '😇', + '0:-)': '😇', + '0:-3': '😇', + '0:3': '😇', + '0;^)': '😇', + 'O:-)': '😇', + '3:)': '😈', + '3:-)': '😈', + '}:)': '😈', + '}:-)': '😈', + '>:)': '😈', + '>:-)': '😈', + '>;)': '😈', + ':-D': '😁', + ":')": '😂', + ":'-)": '😂', + ':)': '😊', + ':-)': '😄', + ':]': '😄', + ':^)': '😄', + ':o)': '😄', + ':}': '😄', + '*-)': '😉', + ':-,': '😉', + ';)': '😉', + ';-)': '😉', + ';-]': '😉', + ';]': '😉', + ';^)': '😉', + ':-|': '😐', + ':|': '😐', + ':(': '😞', + ':-(': '😒', + ':-<': '😒', + ':-[': '😒', + ':-c': '😒', + ':<': '😒', + ':[': '😒', + ':{': '😒', + '%)': '😖', + '%-)': '😖', + ':-P': '😜', + ':-p': '😜', + ';(': '😜', + ':-||': '😠', + ':-.': '😡', + ':-/': '😡', + ':/': '😐', + ":'(": '😢', + ":'-(": '😢', + ':-O': '😲', + ':-o': '😲', + ':-&': '😶', + ':-X': '😶' +} + +function escapeRegExp (text: string): string { + return text.replace(/[[\]{}()*+?.\\^$|#]/g, '\\$&') +} + +export const EmojiExtension = Extension.create({ + addInputRules () { + return Object.keys(emojiReplaceDict).map((pattern) => { + return { + find: new RegExp(escapeRegExp(pattern)), + handler: ({ range, match, commands }) => { + commands.insertContentAt(range, [ + { + type: 'text', + text: emojiReplaceDict[pattern as keyof typeof emojiReplaceDict] + } + ]) + } + } + }) + } +})