diff --git a/packages/picasso/src/LexicalEditor/LexicalEditor.test.tsx b/packages/picasso/src/LexicalEditor/LexicalEditor.test.tsx index eee8d45be5c..01adaa7a9df 100644 --- a/packages/picasso/src/LexicalEditor/LexicalEditor.test.tsx +++ b/packages/picasso/src/LexicalEditor/LexicalEditor.test.tsx @@ -9,6 +9,7 @@ import LexicalTextLengthPlugin from '../LexicalTextLengthPlugin' import LexicalHeadingsReplacementPlugin from '../LexicalHeadingsReplacementPlugin' import ToolbarPlugin from '../LexicalEditorToolbarPlugin' import LexicalListPlugin from '../LexicalListPlugin' +import type { CustomEmojiGroup } from './types' jest.mock('../LexicalEditorToolbarPlugin', () => ({ __esModule: true, @@ -18,6 +19,10 @@ jest.mock('../LexicalListPlugin', () => ({ __esModule: true, default: jest.fn(() =>
LexicalListPlugin
), })) +jest.mock('../LexicalEmojiPlugin', () => ({ + __esModule: true, + default: jest.fn(() =>
LexicalEmojiPlugin
), +})) jest.mock('@lexical/react/LexicalComposerContext', () => ({ __esModule: true, @@ -136,6 +141,8 @@ describe('LexicalEditor', () => { expect(mockedToolbarPlugin).toHaveBeenCalledWith( { disabled: true, + customEmojis: undefined, + plugins: [], toolbarRef: { current: null, }, @@ -152,6 +159,30 @@ describe('LexicalEditor', () => { expect(mockedToolbarPlugin).toHaveBeenCalledWith( { disabled: true, + customEmojis: undefined, + plugins: [], + toolbarRef: { + current: null, + }, + }, + {} + ) + }) + }) + + describe('when customEmojis and plugins prop is passed', () => { + it('renders ToolbarPlugin with correct props', () => { + renderLexicalEditor({ + disabled: true, + customEmojis: ['foo' as unknown as CustomEmojiGroup], + plugins: ['link'], + }) + + expect(mockedToolbarPlugin).toHaveBeenCalledWith( + { + disabled: true, + customEmojis: ['foo'], + plugins: ['link'], toolbarRef: { current: null, }, diff --git a/packages/picasso/src/LexicalEditor/LexicalEditor.tsx b/packages/picasso/src/LexicalEditor/LexicalEditor.tsx index 9b34644248a..a1e9844314a 100644 --- a/packages/picasso/src/LexicalEditor/LexicalEditor.tsx +++ b/packages/picasso/src/LexicalEditor/LexicalEditor.tsx @@ -9,10 +9,10 @@ import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin' import { ContentEditable } from '@lexical/react/LexicalContentEditable' import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary' -import { HeadingNode } from '@lexical/rich-text' import { $generateHtmlFromNodes } from '@lexical/html' -import { ListItemNode, ListNode } from '@lexical/list' import { $isRootTextContentEmpty } from '@lexical/text' +import { ListItemNode, ListNode } from '@lexical/list' +import { HeadingNode } from '@lexical/rich-text' import { createLexicalTheme } from './utils' import noop from '../utils/noop' @@ -20,11 +20,18 @@ import Container from '../Container' import Typography from '../Typography' import { useTypographyClasses, useOnFocus } from './hooks' import styles from './styles' -import type { ChangeHandler, TextLengthChangeHandler } from './types' +import type { + ChangeHandler, + EditorPlugin, + TextLengthChangeHandler, + CustomEmojiGroup, +} from './types' import ToolbarPlugin from '../LexicalEditorToolbarPlugin' import LexicalTextLengthPlugin from '../LexicalTextLengthPlugin' import LexicalListPlugin from '../LexicalListPlugin' import LexicalHeadingsReplacementPlugin from '../LexicalHeadingsReplacementPlugin' +import { CustomEmojiNode } from '../LexicalEmojiPlugin/nodes/CustomEmojiNode' +import LexicalEmojiPlugin from '../LexicalEmojiPlugin' const useStyles = makeStyles(styles, { name: 'LexicalEditor', @@ -65,8 +72,7 @@ export type Props = BaseProps & { onTextLengthChange: TextLengthChangeHandler /** The placeholder attribute specifies a short hint that describes the expected value of a text editor. */ placeholder?: string - /** List of plugins to enable on the editor */ - // plugins?: EditorPlugin[] + testIds?: { editor?: string // headerSelect?: string @@ -75,7 +81,9 @@ export type Props = BaseProps & { // unorderedListButton?: string // orderedListButton?: string } - // customEmojis?: CustomEmojiGroup[] + customEmojis?: CustomEmojiGroup[] + /** List of plugins to enable on the editor */ + plugins?: EditorPlugin[] } const LexicalEditor = forwardRef(function LexicalEditor( @@ -83,7 +91,7 @@ const LexicalEditor = forwardRef(function LexicalEditor( ref ) { const { - // plugins, + plugins = [], autoFocus = false, // defaultValue, disabled = false, @@ -105,7 +113,7 @@ const LexicalEditor = forwardRef(function LexicalEditor( // @todo don't know what to do with NAME prop // name, // highlight, - // customEmojis, + customEmojis, } = props const classes = useStyles() @@ -125,21 +133,22 @@ const LexicalEditor = forwardRef(function LexicalEditor( typographyClassNames, classes, }), - [typographyClassNames, classes.paragraph] + [typographyClassNames, classes] ) - const editorConfig: InitialConfigType = useMemo( - () => ({ + const editorConfig = useMemo(() => { + const config: InitialConfigType = { theme, onError(error: Error) { throw error }, namespace: 'editor', - nodes: [ListNode, ListItemNode, HeadingNode], + nodes: [CustomEmojiNode, ListNode, ListItemNode, HeadingNode], editable: !disabled, - }), - [theme, disabled] - ) + } + + return config + }, [theme, disabled]) const handleChange = useCallback( (editorState, editor) => { @@ -170,6 +179,8 @@ const LexicalEditor = forwardRef(function LexicalEditor( toolbarRef={toolbarRef} // remount Toolbar when disabled key={`${disabled || !isFocused}`} + customEmojis={customEmojis} + plugins={plugins} /> {autoFocus && } @@ -177,6 +188,7 @@ const LexicalEditor = forwardRef(function LexicalEditor( +
{ italic: { fontStyle: 'italic', }, + customEmoji: { + '& > img': { + verticalAlign: 'bottom', + width: '22px', + height: '22px', + }, + }, }) } diff --git a/packages/picasso/src/LexicalEditor/types.ts b/packages/picasso/src/LexicalEditor/types.ts index 935bb66cc3f..9014525a868 100644 --- a/packages/picasso/src/LexicalEditor/types.ts +++ b/packages/picasso/src/LexicalEditor/types.ts @@ -1,3 +1,35 @@ +export type SettingName = 'link' | 'emoji' + export type ChangeHandler = (html: string) => void export type { TextLengthChangeHandler } from '../LexicalTextLengthPlugin' + +export type EditorPlugin = SettingName + +export type CustomEmoji = { + id: string + name: string + keywords: string[] + skins: [ + { + src: string + } + ] +} + +export type CustomEmojiGroup = { + id: string + name: string + emojis: CustomEmoji[] +} + +export type Emoji = { + id: string + name: string + native?: string + unified?: string + keywords: string[] + shortcodes: string + emoticons?: string[] + src?: string +} diff --git a/packages/picasso/src/LexicalEditor/utils/createLexicalTheme.ts b/packages/picasso/src/LexicalEditor/utils/createLexicalTheme.ts index 2087fddc4d0..2c88f25695d 100644 --- a/packages/picasso/src/LexicalEditor/utils/createLexicalTheme.ts +++ b/packages/picasso/src/LexicalEditor/utils/createLexicalTheme.ts @@ -34,6 +34,7 @@ export const createLexicalTheme = ({ ul: classes.ul, ol: classes.ol, }, + customEmoji: classes.customEmoji, } return theme diff --git a/packages/picasso/src/LexicalEditor/utils/synchronizeToolbarState.test.ts b/packages/picasso/src/LexicalEditor/utils/synchronizeToolbarState.test.ts index 4bfa687ac44..df12a6f5c4e 100644 --- a/packages/picasso/src/LexicalEditor/utils/synchronizeToolbarState.test.ts +++ b/packages/picasso/src/LexicalEditor/utils/synchronizeToolbarState.test.ts @@ -14,6 +14,10 @@ import { ToolbarActions } from './toolbarState' import { synchronizeToolbarState } from './synchronizeToolbarState' import { getLexicalNode } from './getLexicalNode' +// jest.mock('../nodes/CustomEmojiNode', () => ({ +// default: jest.fn(), +// })) + jest.mock('lexical', () => ({ $getSelection: jest.fn(), $isRangeSelection: jest.fn(), diff --git a/packages/picasso/src/LexicalEditorToolbarPlugin/LexicalEditorToolbarPlugin.tsx b/packages/picasso/src/LexicalEditorToolbarPlugin/LexicalEditorToolbarPlugin.tsx index 66f800c822c..9c5d4b7b393 100644 --- a/packages/picasso/src/LexicalEditorToolbarPlugin/LexicalEditorToolbarPlugin.tsx +++ b/packages/picasso/src/LexicalEditorToolbarPlugin/LexicalEditorToolbarPlugin.tsx @@ -21,19 +21,32 @@ import { toolbarStateReducer, } from '../LexicalEditor/utils' import { noop } from '../utils' +import type { + CustomEmojiGroup, + EditorPlugin, + Emoji, +} from '../LexicalEditor/types' +import { + INSERT_CUSTOM_EMOJI_COMMAND, + INSERT_EMOJI_COMMAND, +} from '../LexicalEmojiPlugin/commands' import type { HeaderValue } from '../RichTextEditorToolbar' import RichTextEditorToolbar, { ALLOWED_HEADER_TYPE, } from '../RichTextEditorToolbar' type Props = { + customEmojis?: CustomEmojiGroup[] disabled?: boolean toolbarRef: React.RefObject + plugins?: EditorPlugin[] } const LexicalEditorToolbarPlugin = ({ disabled = false, toolbarRef, + customEmojis, + plugins, }: Props) => { const [editor] = useLexicalComposerContext() const [{ bold, italic, list, header }, dispatch] = useReducer( @@ -73,6 +86,24 @@ const LexicalEditorToolbarPlugin = ({ ) } + const handleInsertEmoji = (emoji: Emoji) => { + const isNativeEmoji = emoji.native + const isCustomEmoji = emoji.src + + if (isNativeEmoji) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + editor.dispatchCommand(INSERT_EMOJI_COMMAND, emoji.native!) + } + + if (isCustomEmoji) { + editor.dispatchCommand(INSERT_CUSTOM_EMOJI_COMMAND, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + src: emoji.src!, + id: emoji.id, + }) + } + } + const handleHeaderClick = ({ target: { value }, }: ChangeEvent<{ @@ -108,8 +139,10 @@ const LexicalEditorToolbarPlugin = ({ onLinkClick={noop} onHeaderChange={handleHeaderClick} disabled={disabled} - onInsertEmoji={noop} + onInsertEmoji={handleInsertEmoji} ref={toolbarRef} + customEmojis={customEmojis} + plugins={plugins} /> ) } diff --git a/packages/picasso/src/LexicalEmojiPlugin/LexicalEmojiPlugin.test.ts b/packages/picasso/src/LexicalEmojiPlugin/LexicalEmojiPlugin.test.ts new file mode 100644 index 00000000000..4ef4d2658e0 --- /dev/null +++ b/packages/picasso/src/LexicalEmojiPlugin/LexicalEmojiPlugin.test.ts @@ -0,0 +1,77 @@ +import { renderHook } from '@testing-library/react-hooks' +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { $createTextNode, $insertNodes, COMMAND_PRIORITY_EDITOR } from 'lexical' + +import LexicalEmojiPlugin, { + INSERT_CUSTOM_EMOJI_COMMAND, + INSERT_EMOJI_COMMAND, +} from './index' +import { $createCustomEmojiNode } from './nodes/CustomEmojiNode' + +jest.mock('@lexical/react/LexicalComposerContext', () => ({ + useLexicalComposerContext: jest.fn(() => [{}]), +})) + +jest.mock('@lexical/utils', () => ({ + mergeRegister: jest.fn(), +})) + +jest.mock('lexical', () => ({ + $createTextNode: jest.fn(), + $insertNodes: jest.fn(), + COMMAND_PRIORITY_EDITOR: jest.fn(), + createCommand: jest.fn(), +})) + +jest.mock('./nodes/CustomEmojiNode', () => ({ + $createCustomEmojiNode: jest.fn(), +})) + +describe('LexicalEmojiPlugin', () => { + const mockEditor = { + registerCommand: jest.fn(), + } + + beforeEach(() => { + jest.resetAllMocks() + ;(useLexicalComposerContext as jest.Mock).mockReturnValue([mockEditor]) + }) + + it('registers commands on mount', () => { + renderHook(() => LexicalEmojiPlugin()) + + expect(mockEditor.registerCommand).toHaveBeenCalledTimes(2) + expect(mockEditor.registerCommand).toHaveBeenCalledWith( + INSERT_EMOJI_COMMAND, + expect.any(Function), + COMMAND_PRIORITY_EDITOR + ) + expect(mockEditor.registerCommand).toHaveBeenCalledWith( + INSERT_CUSTOM_EMOJI_COMMAND, + expect.any(Function), + COMMAND_PRIORITY_EDITOR + ) + }) + + it('inserts a text node when the native emoji command is called', () => { + renderHook(() => LexicalEmojiPlugin()) + const nativeEmojiCommand = mockEditor.registerCommand.mock.calls[0][1] + + nativeEmojiCommand('😃') + + expect($createTextNode).toHaveBeenCalledWith('😃') + expect($insertNodes).toHaveBeenCalledWith([$createTextNode()]) + }) + + it('inserts a custom emoji node when the custom emoji command is called', () => { + const payload = { id: 'custom emoji', src: 'https://example.com/emoji.png' } + + renderHook(() => LexicalEmojiPlugin()) + const customEmojiCommand = mockEditor.registerCommand.mock.calls[1][1] + + customEmojiCommand(payload) + + expect($createCustomEmojiNode).toHaveBeenCalledWith(payload) + expect($insertNodes).toHaveBeenCalledWith([$createCustomEmojiNode(payload)]) + }) +}) diff --git a/packages/picasso/src/LexicalEmojiPlugin/LexicalEmojiPlugin.tsx b/packages/picasso/src/LexicalEmojiPlugin/LexicalEmojiPlugin.tsx new file mode 100644 index 00000000000..f7c4392a848 --- /dev/null +++ b/packages/picasso/src/LexicalEmojiPlugin/LexicalEmojiPlugin.tsx @@ -0,0 +1,41 @@ +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { mergeRegister } from '@lexical/utils' +import { useEffect } from 'react' +import { $createTextNode, $insertNodes, COMMAND_PRIORITY_EDITOR } from 'lexical' + +import { INSERT_CUSTOM_EMOJI_COMMAND, INSERT_EMOJI_COMMAND } from './commands' +import { $createCustomEmojiNode } from './nodes/CustomEmojiNode' +import type { CustomEmojiPayload } from './types' + +const LexicalEmojiPlugin = () => { + const [editor] = useLexicalComposerContext() + + useEffect(() => { + return mergeRegister( + editor.registerCommand( + INSERT_EMOJI_COMMAND, + (nativeEmoji: string) => { + $insertNodes([$createTextNode(nativeEmoji)]) + + return true + }, + COMMAND_PRIORITY_EDITOR + ), + editor.registerCommand( + INSERT_CUSTOM_EMOJI_COMMAND, + (customEmojiPayload: CustomEmojiPayload) => { + const emojiNode = $createCustomEmojiNode(customEmojiPayload) + + $insertNodes([emojiNode]) + + return true + }, + COMMAND_PRIORITY_EDITOR + ) + ) + }, [editor]) + + return null +} + +export default LexicalEmojiPlugin diff --git a/packages/picasso/src/LexicalEmojiPlugin/commands/index.ts b/packages/picasso/src/LexicalEmojiPlugin/commands/index.ts new file mode 100644 index 00000000000..1bb6fa4277e --- /dev/null +++ b/packages/picasso/src/LexicalEmojiPlugin/commands/index.ts @@ -0,0 +1,11 @@ +import { createCommand } from 'lexical' +import type { LexicalCommand } from 'lexical' + +import type { CustomEmojiPayload } from '../nodes/CustomEmojiNode' + +export const INSERT_EMOJI_COMMAND: LexicalCommand = createCommand( + 'INSERT_EMOJI_COMMAND' +) + +export const INSERT_CUSTOM_EMOJI_COMMAND: LexicalCommand = + createCommand('INSERT_CUSTOM_EMOJI_COMMAND') diff --git a/packages/picasso/src/LexicalEmojiPlugin/index.ts b/packages/picasso/src/LexicalEmojiPlugin/index.ts new file mode 100644 index 00000000000..4fe8385887b --- /dev/null +++ b/packages/picasso/src/LexicalEmojiPlugin/index.ts @@ -0,0 +1,3 @@ +export { default } from './LexicalEmojiPlugin' +export * from './commands' +export * from './nodes' diff --git a/packages/picasso/src/LexicalEmojiPlugin/nodes/CustomEmojiNode.tsx b/packages/picasso/src/LexicalEmojiPlugin/nodes/CustomEmojiNode.tsx new file mode 100644 index 00000000000..07f1e271342 --- /dev/null +++ b/packages/picasso/src/LexicalEmojiPlugin/nodes/CustomEmojiNode.tsx @@ -0,0 +1,153 @@ +import React from 'react' +import { $applyNodeReplacement, DecoratorNode } from 'lexical' +import type { + DOMConversionMap, + DOMConversionOutput, + DOMExportOutput, + EditorConfig, + LexicalNode, + NodeKey, + SerializedElementNode, + Spread, +} from 'lexical' + +export interface CustomEmojiPayload { + src: string + id: string +} + +type SerializedCustomEmojiNode = Spread< + { + src: string + id: string + }, + SerializedElementNode +> + +const convertImageElement = (domNode: Node): null | DOMConversionOutput => { + if (domNode instanceof HTMLImageElement) { + const src = domNode.getAttribute('src') + const id = domNode.getAttribute('data-emoji-name') + + if (src && id) { + return { + node: $createCustomEmojiNode({ + src, + id, + }), + } + } + + return null + } + + return null +} + +export class CustomEmojiNode extends DecoratorNode { + __src: string + __id: string + + static getType() { + return 'custom-emoji' + } + + static clone(node: CustomEmojiNode): CustomEmojiNode { + return new CustomEmojiNode(node.__src, node.__id) + } + + constructor(src: string, id: string, key?: NodeKey) { + super(key) + this.__src = src + this.__id = id + } + + static importJSON( + serializedNode: SerializedCustomEmojiNode + ): CustomEmojiNode { + const { src, id } = serializedNode + + const node = $createCustomEmojiNode({ src, id }) + + return node + } + + createDOM(config: EditorConfig): HTMLElement { + const span = document.createElement('span') + + const theme = config.theme + const className = theme.customEmoji + + if (className !== undefined) { + span.className = className + } + + return span + } + + // update() {} + + updateDOM() { + return false + } + + exportDOM(): DOMExportOutput { + const element = document.createElement('img') + + element.setAttribute('src', this.__src) + element.setAttribute('data-src', this.__src) + element.setAttribute('data-emoji-name', this.__id) + + return { element } + } + + static importDOM(): DOMConversionMap | null { + return { + img: () => ({ + conversion: convertImageElement, + priority: 0, + }), + } + } + + exportJSON(): SerializedCustomEmojiNode { + return { + version: 1, + type: 'custom-emoji', + src: this.__src, + id: this.__id, + children: [], + direction: this.__direction, + format: '', + indent: this.__indent, + } + } + + // isInline() { + // return true + // } + + // canIndent() { + // return false + // } + + decorate() { + return ( + + ) + } +} + +export const $isCustomEmojiNode = ( + node: LexicalNode | null | undefined +): node is CustomEmojiNode => { + return node instanceof CustomEmojiNode +} + +export const $createCustomEmojiNode = ({ + src, + id, +}: CustomEmojiPayload): CustomEmojiNode => { + // return new CustomEmojiNode(src, id) + return $applyNodeReplacement(new CustomEmojiNode(src, id)) +} diff --git a/packages/picasso/src/LexicalEmojiPlugin/nodes/index.ts b/packages/picasso/src/LexicalEmojiPlugin/nodes/index.ts new file mode 100644 index 00000000000..bd2ba9c89d3 --- /dev/null +++ b/packages/picasso/src/LexicalEmojiPlugin/nodes/index.ts @@ -0,0 +1 @@ +export { CustomEmojiNode } from './CustomEmojiNode' diff --git a/packages/picasso/src/LexicalEmojiPlugin/types.ts b/packages/picasso/src/LexicalEmojiPlugin/types.ts new file mode 100644 index 00000000000..8f3ff95a4f9 --- /dev/null +++ b/packages/picasso/src/LexicalEmojiPlugin/types.ts @@ -0,0 +1 @@ +export type { CustomEmojiPayload } from './nodes/CustomEmojiNode' diff --git a/packages/picasso/src/RichTextEditor/RichTextEditor.tsx b/packages/picasso/src/RichTextEditor/RichTextEditor.tsx index a0391f7c38a..35ed55fb8d4 100644 --- a/packages/picasso/src/RichTextEditor/RichTextEditor.tsx +++ b/packages/picasso/src/RichTextEditor/RichTextEditor.tsx @@ -6,8 +6,6 @@ import { useHasMultilineCounter } from '@toptal/picasso-shared' import cx from 'classnames' import noop from '../utils/noop' -// @todo: remove this import once we remove the old QuillEditor -import type { CustomEmojiGroup, EditorPlugin } from '../QuillEditor' import styles from './styles' import { useCounter } from './hooks' import type { ASTType } from '../RichText' @@ -15,7 +13,11 @@ import { usePropDeprecationWarning } from '../utils/use-deprecation-warnings' import type { Status } from '../OutlinedInput' import type { CounterMessageSetter } from './types' import LexicalEditor from '../LexicalEditor' -import type { ChangeHandler } from '../LexicalEditor' +import type { + ChangeHandler, + CustomEmojiGroup, + EditorPlugin, +} from '../LexicalEditor' import InputMultilineAdornment from '../InputMultilineAdornment' export interface Props extends BaseProps { @@ -96,7 +98,7 @@ export const RichTextEditor = forwardRef( function RichTextEditor(props, ref) { const { 'data-testid': dataTestId, - // plugins, + plugins, autoFocus = false, className, // defaultValue, @@ -117,7 +119,7 @@ export const RichTextEditor = forwardRef( setHasMultilineCounter, name, highlight, - // customEmojis, + customEmojis, } = props const classes = useStyles() @@ -188,13 +190,15 @@ export const RichTextEditor = forwardRef( testIds={testIds} disabled={disabled} autoFocus={autoFocus} + plugins={plugins} + customEmojis={customEmojis} /> {hiddenInputId && ( // Native `for` attribute on label does not work for div target )}
diff --git a/packages/picasso/src/RichTextEditor/styles.ts b/packages/picasso/src/RichTextEditor/styles.ts index fac2f317156..7ca017fc7ab 100644 --- a/packages/picasso/src/RichTextEditor/styles.ts +++ b/packages/picasso/src/RichTextEditor/styles.ts @@ -38,6 +38,12 @@ export default (theme: Theme) => { ...outline(palette.primary.main), }, + hiddenInput: { + position: 'absolute', + opacity: 0, + zIndex: -1, + }, + ...highlightAutofillStyles(theme), }) } diff --git a/packages/picasso/src/RichTextEditorEmojiPicker/RichTextEditorEmojiPicker.tsx b/packages/picasso/src/RichTextEditorEmojiPicker/RichTextEditorEmojiPicker.tsx index a114bbc1f3b..3ac4b984032 100644 --- a/packages/picasso/src/RichTextEditorEmojiPicker/RichTextEditorEmojiPicker.tsx +++ b/packages/picasso/src/RichTextEditorEmojiPicker/RichTextEditorEmojiPicker.tsx @@ -8,12 +8,13 @@ import cx from 'classnames' import Container from '../Container' import TextEditorButton from '../RichTextEditorButton' -import type { CustomEmojiGroup } from '../QuillEditor' +import type { CustomEmojiGroup, Emoji } from '../LexicalEditor' interface Props { richEditorId: string customEmojis?: CustomEmojiGroup[] - onInsertEmoji: (emoji: string) => void + onInsertEmoji: (emoji: Emoji) => void + disabled?: boolean } const TRIGGER_EMOJI_PICKER_ID = 'trigger-emoji-picker' @@ -48,6 +49,7 @@ export const RichtTextEditorEmojiPicker = ({ richEditorId, customEmojis, onInsertEmoji, + disabled, }: Props) => { const [showEmojiPicker, setShowEmojiPicker] = React.useState(false) @@ -61,7 +63,7 @@ export const RichtTextEditorEmojiPicker = ({ setShowEmojiPicker(false) } - const handleEmojiInsert = (emoji: string) => { + const handleEmojiInsert = (emoji: Emoji) => { onInsertEmoji(emoji) setShowEmojiPicker(false) } @@ -88,6 +90,7 @@ export const RichtTextEditorEmojiPicker = ({ onClick={handleEmojiPickerClick} icon={🙂} id={TRIGGER_EMOJI_PICKER_ID} + disabled={disabled} /> void + onInsertEmoji: (emoji: Emoji) => void onHeaderChange: SelectOnChangeHandler onUnorderedClick: ButtonHandlerType onOrderedClick: ButtonHandlerType @@ -146,6 +146,7 @@ export const RichTextEditorToolbar = forwardRef( richEditorId={id} customEmojis={customEmojis} onInsertEmoji={onInsertEmoji} + disabled={disabled} /> )}