You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

current-block-replacement-block.tsx 1.7KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import {
  2. memo,
  3. useCallback,
  4. useEffect,
  5. } from 'react'
  6. import { $applyNodeReplacement } from 'lexical'
  7. import { mergeRegister } from '@lexical/utils'
  8. import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
  9. import { decoratorTransform } from '../../utils'
  10. import { CURRENT_PLACEHOLDER_TEXT } from '../../constants'
  11. import type { CurrentBlockType } from '../../types'
  12. import {
  13. $createCurrentBlockNode,
  14. CurrentBlockNode,
  15. } from './node'
  16. import { CustomTextNode } from '../custom-text/node'
  17. const REGEX = new RegExp(CURRENT_PLACEHOLDER_TEXT)
  18. const CurrentBlockReplacementBlock = ({
  19. generatorType,
  20. onInsert,
  21. }: CurrentBlockType) => {
  22. const [editor] = useLexicalComposerContext()
  23. useEffect(() => {
  24. if (!editor.hasNodes([CurrentBlockNode]))
  25. throw new Error('CurrentBlockNodePlugin: CurrentBlockNode not registered on editor')
  26. }, [editor])
  27. const createCurrentBlockNode = useCallback((): CurrentBlockNode => {
  28. if (onInsert)
  29. onInsert()
  30. return $applyNodeReplacement($createCurrentBlockNode(generatorType))
  31. }, [onInsert, generatorType])
  32. const getMatch = useCallback((text: string) => {
  33. const matchArr = REGEX.exec(text)
  34. if (matchArr === null)
  35. return null
  36. const startOffset = matchArr.index
  37. const endOffset = startOffset + CURRENT_PLACEHOLDER_TEXT.length
  38. return {
  39. end: endOffset,
  40. start: startOffset,
  41. }
  42. }, [])
  43. useEffect(() => {
  44. REGEX.lastIndex = 0
  45. return mergeRegister(
  46. editor.registerNodeTransform(CustomTextNode, textNode => decoratorTransform(textNode, getMatch, createCurrentBlockNode)),
  47. )
  48. // eslint-disable-next-line react-hooks/exhaustive-deps
  49. }, [])
  50. return null
  51. }
  52. export default memo(CurrentBlockReplacementBlock)