sykpcomposer 0.0.396 → 0.0.397
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/{ImageComponent-fc39a03e.js → ImageComponent-ced58b97.js} +192 -185
- package/dist/ImageComponent-ced58b97.js.map +1 -0
- package/dist/{InlineImageComponent-377a250f.js → InlineImageComponent-531380f5.js} +3 -3
- package/dist/{InlineImageComponent-377a250f.js.map → InlineImageComponent-531380f5.js.map} +1 -1
- package/dist/{LexicalNestedComposer.esm-6289c7f2.js → LexicalNestedComposer.esm-9f194204.js} +2 -2
- package/dist/{LexicalNestedComposer.esm-6289c7f2.js.map → LexicalNestedComposer.esm-9f194204.js.map} +1 -1
- package/dist/{PollComponent-8d21e358.js → PollComponent-008dc9fe.js} +2 -2
- package/dist/{PollComponent-8d21e358.js.map → PollComponent-008dc9fe.js.map} +1 -1
- package/dist/{index-75a3fc49.js → index-7c78a104.js} +4 -4
- package/dist/{index-75a3fc49.js.map → index-7c78a104.js.map} +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/dist/ImageComponent-fc39a03e.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InlineImageComponent-377a250f.js","sources":["../src/lexical/utils/getDOMRangeRect.ts","../src/lexical/utils/setFloatingElemPosition.ts","../src/lexical/themes/CommentEditorTheme.ts","../src/lexical/plugins/CommentPlugin/index.tsx","../src/lexical/plugins/FloatingTextFormatToolbarPlugin/index.tsx","../src/lexical/ui/Select.tsx","../src/lexical/nodes/InlineImageComponent.tsx"],"sourcesContent":["/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\nexport function getDOMRangeRect(\r\n nativeSelection: Selection,\r\n rootElement: HTMLElement,\r\n): DOMRect {\r\n const domRange = nativeSelection.getRangeAt(0);\r\n\r\n let rect;\r\n\r\n if (nativeSelection.anchorNode === rootElement) {\r\n let inner = rootElement;\r\n while (inner.firstElementChild != null) {\r\n inner = inner.firstElementChild as HTMLElement;\r\n }\r\n rect = inner.getBoundingClientRect();\r\n } else {\r\n rect = domRange.getBoundingClientRect();\r\n }\r\n\r\n return rect;\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\nconst VERTICAL_GAP = 10;\r\nconst HORIZONTAL_OFFSET = 5;\r\n\r\nexport function setFloatingElemPosition(\r\n targetRect: DOMRect | null,\r\n floatingElem: HTMLElement,\r\n anchorElem: HTMLElement,\r\n isLink: boolean = false,\r\n verticalGap: number = VERTICAL_GAP,\r\n horizontalOffset: number = HORIZONTAL_OFFSET,\r\n): void {\r\n const scrollerElem = anchorElem.parentElement;\r\n\r\n if (targetRect === null || !scrollerElem) {\r\n floatingElem.style.opacity = '0';\r\n floatingElem.style.transform = 'translate(-10000px, -10000px)';\r\n return;\r\n }\r\n\r\n const floatingElemRect = floatingElem.getBoundingClientRect();\r\n const anchorElementRect = anchorElem.getBoundingClientRect();\r\n const editorScrollerRect = scrollerElem.getBoundingClientRect();\r\n\r\n let top = targetRect.top - floatingElemRect.height - verticalGap;\r\n let left = targetRect.left - horizontalOffset;\r\n\r\n if (top < editorScrollerRect.top) {\r\n // adjusted height for link element if the element is at top\r\n top +=\r\n floatingElemRect.height +\r\n targetRect.height +\r\n verticalGap * (isLink ? 9 : 2);\r\n }\r\n\r\n if (left + floatingElemRect.width > editorScrollerRect.right) {\r\n left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;\r\n }\r\n\r\n top -= anchorElementRect.top;\r\n left -= anchorElementRect.left;\r\n\r\n floatingElem.style.opacity = '1';\r\n floatingElem.style.transform = `translate(${left}px, ${top}px)`;\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {EditorThemeClasses} from 'lexical';\r\n\r\nimport './CommentEditorTheme.css';\r\n\r\nimport baseTheme from './PlaygroundEditorTheme';\r\n\r\nconst theme: EditorThemeClasses = {\r\n ...baseTheme,\r\n paragraph: 'CommentEditorTheme__paragraph',\r\n};\r\n\r\nexport default theme;\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {Provider} from '@lexical/yjs';\r\nimport type {\r\n EditorState,\r\n LexicalCommand,\r\n LexicalEditor,\r\n NodeKey,\r\n RangeSelection,\r\n} from 'lexical';\r\nimport type {Doc} from 'yjs';\r\n\r\nimport './index.css';\r\n\r\nimport {\r\n $createMarkNode,\r\n $getMarkIDs,\r\n $isMarkNode,\r\n $unwrapMarkNode,\r\n $wrapSelectionInMarkNode,\r\n MarkNode,\r\n} from '@lexical/mark';\r\nimport {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';\r\nimport {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin';\r\nimport {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';\r\nimport {LexicalComposer} from '@lexical/react/LexicalComposer';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport {EditorRefPlugin} from '@lexical/react/LexicalEditorRefPlugin';\r\nimport LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';\r\nimport {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';\r\nimport {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin';\r\nimport {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin';\r\nimport {createDOMRange, createRectsFromDOMRange} from '@lexical/selection';\r\nimport {$isRootTextContentEmpty, $rootTextContent} from '@lexical/text';\r\nimport {mergeRegister, registerNestedElementResolver} from '@lexical/utils';\r\nimport {\r\n $getNodeByKey,\r\n $getSelection,\r\n $isRangeSelection,\r\n $isTextNode,\r\n CLEAR_EDITOR_COMMAND,\r\n COMMAND_PRIORITY_EDITOR,\r\n createCommand,\r\n KEY_ESCAPE_COMMAND,\r\n} from 'lexical';\r\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\r\nimport * as React from 'react';\r\nimport {createPortal} from 'react-dom';\r\n\r\nimport {\r\n Comment,\r\n Comments,\r\n CommentStore,\r\n createComment,\r\n createThread,\r\n Thread,\r\n useCommentStore,\r\n} from '../../commenting';\r\nimport useModal from '../../hooks/useModal';\r\nimport CommentEditorTheme from '../../themes/CommentEditorTheme';\r\nimport Button from '../../ui/Button';\r\nimport ContentEditable from '../../ui/ContentEditable';\r\nimport Placeholder from '../../ui/Placeholder';\r\n\r\nexport const INSERT_INLINE_COMMAND: LexicalCommand<void> = createCommand(\r\n 'INSERT_INLINE_COMMAND',\r\n);\r\n\r\nfunction AddCommentBox({\r\n anchorKey,\r\n editor,\r\n onAddComment,\r\n}: {\r\n anchorKey: NodeKey;\r\n editor: LexicalEditor;\r\n onAddComment: () => void;\r\n}): JSX.Element {\r\n const boxRef = useRef<HTMLDivElement>(null);\r\n\r\n const updatePosition = useCallback(() => {\r\n const boxElem = boxRef.current;\r\n const rootElement = editor.getRootElement();\r\n const anchorElement = editor.getElementByKey(anchorKey);\r\n\r\n if (boxElem !== null && rootElement !== null && anchorElement !== null) {\r\n const {right} = rootElement.getBoundingClientRect();\r\n const {top} = anchorElement.getBoundingClientRect();\r\n boxElem.style.left = `${right - 20}px`;\r\n boxElem.style.top = `${top - 30}px`;\r\n }\r\n }, [anchorKey, editor]);\r\n\r\n useEffect(() => {\r\n window.addEventListener('resize', updatePosition);\r\n\r\n return () => {\r\n window.removeEventListener('resize', updatePosition);\r\n };\r\n }, [editor, updatePosition]);\r\n\r\n React.useLayoutEffect(() => {\r\n updatePosition();\r\n }, [anchorKey, editor, updatePosition]);\r\n\r\n return (\r\n <div className=\"CommentPlugin_AddCommentBox\" ref={boxRef}>\r\n <button\r\n className=\"CommentPlugin_AddCommentBox_button\"\r\n onClick={onAddComment}>\r\n <i className=\"icon add-comment\" />\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\nfunction EscapeHandlerPlugin({\r\n onEscape,\r\n}: {\r\n onEscape: (e: KeyboardEvent) => boolean;\r\n}): null {\r\n const [editor] = useLexicalComposerContext();\r\n\r\n useEffect(() => {\r\n return editor.registerCommand(\r\n KEY_ESCAPE_COMMAND,\r\n (event: KeyboardEvent) => {\r\n return onEscape(event);\r\n },\r\n 2,\r\n );\r\n }, [editor, onEscape]);\r\n\r\n return null;\r\n}\r\n\r\nfunction PlainTextEditor({\r\n className,\r\n autoFocus,\r\n onEscape,\r\n onChange,\r\n editorRef,\r\n placeholder = 'Type a comment...',\r\n}: {\r\n autoFocus?: boolean;\r\n className?: string;\r\n editorRef?: {current: null | LexicalEditor};\r\n onChange: (editorState: EditorState, editor: LexicalEditor) => void;\r\n onEscape: (e: KeyboardEvent) => boolean;\r\n placeholder?: string;\r\n}) {\r\n const initialConfig = {\r\n namespace: 'Commenting',\r\n nodes: [],\r\n onError: (error: Error) => {\r\n throw error;\r\n },\r\n theme: CommentEditorTheme,\r\n };\r\n\r\n return (\r\n <LexicalComposer initialConfig={initialConfig}>\r\n <div className=\"CommentPlugin_CommentInputBox_EditorContainer\">\r\n <PlainTextPlugin\r\n contentEditable={<ContentEditable className={className} />}\r\n placeholder={<Placeholder>{placeholder}</Placeholder>}\r\n ErrorBoundary={LexicalErrorBoundary}\r\n />\r\n <OnChangePlugin onChange={onChange} />\r\n <HistoryPlugin />\r\n {autoFocus !== false && <AutoFocusPlugin />}\r\n <EscapeHandlerPlugin onEscape={onEscape} />\r\n <ClearEditorPlugin />\r\n {editorRef !== undefined && <EditorRefPlugin editorRef={editorRef} />}\r\n </div>\r\n </LexicalComposer>\r\n );\r\n}\r\n\r\nfunction useOnChange(\r\n setContent: (text: string) => void,\r\n setCanSubmit: (canSubmit: boolean) => void,\r\n) {\r\n return useCallback(\r\n (editorState: EditorState, _editor: LexicalEditor) => {\r\n editorState.read(() => {\r\n setContent($rootTextContent());\r\n setCanSubmit(!$isRootTextContentEmpty(_editor.isComposing(), true));\r\n });\r\n },\r\n [setCanSubmit, setContent],\r\n );\r\n}\r\n\r\nfunction CommentInputBox({\r\n editor,\r\n cancelAddComment,\r\n submitAddComment,\r\n}: {\r\n cancelAddComment: () => void;\r\n editor: LexicalEditor;\r\n submitAddComment: (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n selection?: RangeSelection | null,\r\n ) => void;\r\n}) {\r\n const [content, setContent] = useState('');\r\n const [canSubmit, setCanSubmit] = useState(false);\r\n const boxRef = useRef<HTMLDivElement>(null);\r\n const selectionState = useMemo(\r\n () => ({\r\n container: document.createElement('div'),\r\n elements: [],\r\n }),\r\n [],\r\n );\r\n const selectionRef = useRef<RangeSelection | null>(null);\r\n const author = useCollabAuthorName();\r\n\r\n const updateLocation = useCallback(() => {\r\n editor.getEditorState().read(() => {\r\n const selection = $getSelection();\r\n\r\n if ($isRangeSelection(selection)) {\r\n selectionRef.current = selection.clone();\r\n const anchor = selection.anchor;\r\n const focus = selection.focus;\r\n const range = createDOMRange(\r\n editor,\r\n anchor.getNode(),\r\n anchor.offset,\r\n focus.getNode(),\r\n focus.offset,\r\n );\r\n const boxElem = boxRef.current;\r\n if (range !== null && boxElem !== null) {\r\n const {left, bottom, width} = range.getBoundingClientRect();\r\n const selectionRects = createRectsFromDOMRange(editor, range);\r\n let correctedLeft =\r\n selectionRects.length === 1 ? left + width / 2 - 125 : left - 125;\r\n if (correctedLeft < 10) {\r\n correctedLeft = 10;\r\n }\r\n boxElem.style.left = `${correctedLeft}px`;\r\n boxElem.style.top = `${\r\n bottom +\r\n 20 +\r\n (window.pageYOffset || document.documentElement.scrollTop)\r\n }px`;\r\n const selectionRectsLength = selectionRects.length;\r\n const {container} = selectionState;\r\n const elements: Array<HTMLSpanElement> = selectionState.elements;\r\n const elementsLength = elements.length;\r\n\r\n for (let i = 0; i < selectionRectsLength; i++) {\r\n const selectionRect = selectionRects[i];\r\n let elem: HTMLSpanElement = elements[i];\r\n if (elem === undefined) {\r\n elem = document.createElement('span');\r\n elements[i] = elem;\r\n container.appendChild(elem);\r\n }\r\n const color = '255, 212, 0';\r\n const style = `position:absolute;top:${\r\n selectionRect.top +\r\n (window.pageYOffset || document.documentElement.scrollTop)\r\n }px;left:${selectionRect.left}px;height:${\r\n selectionRect.height\r\n }px;width:${\r\n selectionRect.width\r\n }px;background-color:rgba(${color}, 0.3);pointer-events:none;z-index:5;`;\r\n elem.style.cssText = style;\r\n }\r\n for (let i = elementsLength - 1; i >= selectionRectsLength; i--) {\r\n const elem = elements[i];\r\n container.removeChild(elem);\r\n elements.pop();\r\n }\r\n }\r\n }\r\n });\r\n }, [editor, selectionState]);\r\n\r\n React.useLayoutEffect(() => {\r\n updateLocation();\r\n const container = selectionState.container;\r\n const body = document.body;\r\n if (body !== null) {\r\n body.appendChild(container);\r\n return () => {\r\n body.removeChild(container);\r\n };\r\n }\r\n }, [selectionState.container, updateLocation]);\r\n\r\n useEffect(() => {\r\n window.addEventListener('resize', updateLocation);\r\n\r\n return () => {\r\n window.removeEventListener('resize', updateLocation);\r\n };\r\n }, [updateLocation]);\r\n\r\n const onEscape = (event: KeyboardEvent): boolean => {\r\n event.preventDefault();\r\n cancelAddComment();\r\n return true;\r\n };\r\n\r\n const submitComment = () => {\r\n if (canSubmit) {\r\n let quote = editor.getEditorState().read(() => {\r\n const selection = selectionRef.current;\r\n return selection ? selection.getTextContent() : '';\r\n });\r\n if (quote.length > 100) {\r\n quote = quote.slice(0, 99) + '…';\r\n }\r\n submitAddComment(\r\n createThread(quote, [createComment(content, author)]),\r\n true,\r\n undefined,\r\n selectionRef.current,\r\n );\r\n selectionRef.current = null;\r\n }\r\n };\r\n\r\n const onChange = useOnChange(setContent, setCanSubmit);\r\n\r\n return (\r\n <div className=\"CommentPlugin_CommentInputBox\" ref={boxRef}>\r\n <PlainTextEditor\r\n className=\"CommentPlugin_CommentInputBox_Editor\"\r\n onEscape={onEscape}\r\n onChange={onChange}\r\n />\r\n <div className=\"CommentPlugin_CommentInputBox_Buttons\">\r\n <Button\r\n onClick={cancelAddComment}\r\n className=\"CommentPlugin_CommentInputBox_Button\">\r\n Cancel\r\n </Button>\r\n <Button\r\n onClick={submitComment}\r\n disabled={!canSubmit}\r\n className=\"CommentPlugin_CommentInputBox_Button primary\">\r\n Comment\r\n </Button>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction CommentsComposer({\r\n submitAddComment,\r\n thread,\r\n placeholder,\r\n}: {\r\n placeholder?: string;\r\n submitAddComment: (\r\n commentOrThread: Comment,\r\n isInlineComment: boolean,\r\n // eslint-disable-next-line no-shadow\r\n thread?: Thread,\r\n ) => void;\r\n thread?: Thread;\r\n}) {\r\n const [content, setContent] = useState('');\r\n const [canSubmit, setCanSubmit] = useState(false);\r\n const editorRef = useRef<LexicalEditor>(null);\r\n const author = useCollabAuthorName();\r\n\r\n const onChange = useOnChange(setContent, setCanSubmit);\r\n\r\n const submitComment = () => {\r\n if (canSubmit) {\r\n submitAddComment(createComment(content, author), false, thread);\r\n const editor = editorRef.current;\r\n if (editor !== null) {\r\n editor.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);\r\n }\r\n }\r\n };\r\n\r\n return (\r\n <>\r\n <PlainTextEditor\r\n className=\"CommentPlugin_CommentsPanel_Editor\"\r\n autoFocus={false}\r\n onEscape={() => {\r\n return true;\r\n }}\r\n onChange={onChange}\r\n editorRef={editorRef}\r\n placeholder={placeholder}\r\n />\r\n <Button\r\n className=\"CommentPlugin_CommentsPanel_SendButton\"\r\n onClick={submitComment}\r\n disabled={!canSubmit}>\r\n <i className=\"send\" />\r\n </Button>\r\n </>\r\n );\r\n}\r\n\r\nfunction ShowDeleteCommentOrThreadDialog({\r\n commentOrThread,\r\n deleteCommentOrThread,\r\n onClose,\r\n thread = undefined,\r\n}: {\r\n commentOrThread: Comment | Thread;\r\n\r\n deleteCommentOrThread: (\r\n comment: Comment | Thread,\r\n // eslint-disable-next-line no-shadow\r\n thread?: Thread,\r\n ) => void;\r\n onClose: () => void;\r\n thread?: Thread;\r\n}): JSX.Element {\r\n return (\r\n <>\r\n Are you sure you want to delete this {commentOrThread.type}?\r\n <div className=\"Modal__content\">\r\n <Button\r\n onClick={() => {\r\n deleteCommentOrThread(commentOrThread, thread);\r\n onClose();\r\n }}>\r\n Delete\r\n </Button>{' '}\r\n <Button\r\n onClick={() => {\r\n onClose();\r\n }}>\r\n Cancel\r\n </Button>\r\n </div>\r\n </>\r\n );\r\n}\r\n\r\nfunction CommentsPanelListComment({\r\n comment,\r\n deleteComment,\r\n thread,\r\n rtf,\r\n}: {\r\n comment: Comment;\r\n deleteComment: (\r\n commentOrThread: Comment | Thread,\r\n // eslint-disable-next-line no-shadow\r\n thread?: Thread,\r\n ) => void;\r\n rtf: Intl.RelativeTimeFormat;\r\n thread?: Thread;\r\n}): JSX.Element {\r\n const seconds = Math.round((comment.timeStamp - performance.now()) / 1000);\r\n const minutes = Math.round(seconds / 60);\r\n const [modal, showModal] = useModal();\r\n\r\n return (\r\n <li className=\"CommentPlugin_CommentsPanel_List_Comment\">\r\n <div className=\"CommentPlugin_CommentsPanel_List_Details\">\r\n <span className=\"CommentPlugin_CommentsPanel_List_Comment_Author\">\r\n {comment.author}\r\n </span>\r\n <span className=\"CommentPlugin_CommentsPanel_List_Comment_Time\">\r\n · {seconds > -10 ? 'Just now' : rtf.format(minutes, 'minute')}\r\n </span>\r\n </div>\r\n <p\r\n className={\r\n comment.deleted ? 'CommentPlugin_CommentsPanel_DeletedComment' : ''\r\n }>\r\n {comment.content}\r\n </p>\r\n {!comment.deleted && (\r\n <>\r\n <Button\r\n onClick={() => {\r\n showModal('Delete Comment', (onClose) => (\r\n <ShowDeleteCommentOrThreadDialog\r\n commentOrThread={comment}\r\n deleteCommentOrThread={deleteComment}\r\n thread={thread}\r\n onClose={onClose}\r\n />\r\n ));\r\n }}\r\n className=\"CommentPlugin_CommentsPanel_List_DeleteButton\">\r\n <i className=\"delete\" />\r\n </Button>\r\n {modal}\r\n </>\r\n )}\r\n </li>\r\n );\r\n}\r\n\r\nfunction CommentsPanelList({\r\n activeIDs,\r\n comments,\r\n deleteCommentOrThread,\r\n listRef,\r\n submitAddComment,\r\n markNodeMap,\r\n}: {\r\n activeIDs: Array<string>;\r\n comments: Comments;\r\n deleteCommentOrThread: (\r\n commentOrThread: Comment | Thread,\r\n thread?: Thread,\r\n ) => void;\r\n listRef: {current: null | HTMLUListElement};\r\n markNodeMap: Map<string, Set<NodeKey>>;\r\n submitAddComment: (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n ) => void;\r\n}): JSX.Element {\r\n const [editor] = useLexicalComposerContext();\r\n const [counter, setCounter] = useState(0);\r\n const [modal, showModal] = useModal();\r\n const rtf = useMemo(\r\n () =>\r\n new Intl.RelativeTimeFormat('en', {\r\n localeMatcher: 'best fit',\r\n numeric: 'auto',\r\n style: 'short',\r\n }),\r\n [],\r\n );\r\n\r\n useEffect(() => {\r\n // Used to keep the time stamp up to date\r\n const id = setTimeout(() => {\r\n setCounter(counter + 1);\r\n }, 10000);\r\n\r\n return () => {\r\n clearTimeout(id);\r\n };\r\n }, [counter]);\r\n\r\n return (\r\n <ul className=\"CommentPlugin_CommentsPanel_List\" ref={listRef}>\r\n {comments.map((commentOrThread) => {\r\n const id = commentOrThread.id;\r\n if (commentOrThread.type === 'thread') {\r\n const handleClickThread = () => {\r\n const markNodeKeys = markNodeMap.get(id);\r\n if (\r\n markNodeKeys !== undefined &&\r\n (activeIDs === null || activeIDs.indexOf(id) === -1)\r\n ) {\r\n const activeElement = document.activeElement;\r\n // Move selection to the start of the mark, so that we\r\n // update the UI with the selected thread.\r\n editor.update(\r\n () => {\r\n const markNodeKey = Array.from(markNodeKeys)[0];\r\n const markNode = $getNodeByKey<MarkNode>(markNodeKey);\r\n if ($isMarkNode(markNode)) {\r\n markNode.selectStart();\r\n }\r\n },\r\n {\r\n onUpdate() {\r\n // Restore selection to the previous element\r\n if (activeElement !== null) {\r\n (activeElement as HTMLElement).focus();\r\n }\r\n },\r\n },\r\n );\r\n }\r\n };\r\n\r\n return (\r\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions\r\n <li\r\n key={id}\r\n onClick={handleClickThread}\r\n className={`CommentPlugin_CommentsPanel_List_Thread ${\r\n markNodeMap.has(id) ? 'interactive' : ''\r\n } ${activeIDs.indexOf(id) === -1 ? '' : 'active'}`}>\r\n <div className=\"CommentPlugin_CommentsPanel_List_Thread_QuoteBox\">\r\n <blockquote className=\"CommentPlugin_CommentsPanel_List_Thread_Quote\">\r\n {'> '}\r\n <span>{commentOrThread.quote}</span>\r\n </blockquote>\r\n {/* INTRODUCE DELETE THREAD HERE*/}\r\n <Button\r\n onClick={() => {\r\n showModal('Delete Thread', (onClose) => (\r\n <ShowDeleteCommentOrThreadDialog\r\n commentOrThread={commentOrThread}\r\n deleteCommentOrThread={deleteCommentOrThread}\r\n onClose={onClose}\r\n />\r\n ));\r\n }}\r\n className=\"CommentPlugin_CommentsPanel_List_DeleteButton\">\r\n <i className=\"delete\" />\r\n </Button>\r\n {modal}\r\n </div>\r\n <ul className=\"CommentPlugin_CommentsPanel_List_Thread_Comments\">\r\n {commentOrThread.comments.map((comment) => (\r\n <CommentsPanelListComment\r\n key={comment.id}\r\n comment={comment}\r\n deleteComment={deleteCommentOrThread}\r\n thread={commentOrThread}\r\n rtf={rtf}\r\n />\r\n ))}\r\n </ul>\r\n <div className=\"CommentPlugin_CommentsPanel_List_Thread_Editor\">\r\n <CommentsComposer\r\n submitAddComment={submitAddComment}\r\n thread={commentOrThread}\r\n placeholder=\"Reply to comment...\"\r\n />\r\n </div>\r\n </li>\r\n );\r\n }\r\n return (\r\n <CommentsPanelListComment\r\n key={id}\r\n comment={commentOrThread}\r\n deleteComment={deleteCommentOrThread}\r\n rtf={rtf}\r\n />\r\n );\r\n })}\r\n </ul>\r\n );\r\n}\r\n\r\nfunction CommentsPanel({\r\n activeIDs,\r\n deleteCommentOrThread,\r\n comments,\r\n submitAddComment,\r\n markNodeMap,\r\n}: {\r\n activeIDs: Array<string>;\r\n comments: Comments;\r\n deleteCommentOrThread: (\r\n commentOrThread: Comment | Thread,\r\n thread?: Thread,\r\n ) => void;\r\n markNodeMap: Map<string, Set<NodeKey>>;\r\n submitAddComment: (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n ) => void;\r\n}): JSX.Element {\r\n const listRef = useRef<HTMLUListElement>(null);\r\n const isEmpty = comments.length === 0;\r\n\r\n return (\r\n <div className=\"CommentPlugin_CommentsPanel\">\r\n <h2 className=\"CommentPlugin_CommentsPanel_Heading\">Comments</h2>\r\n {isEmpty ? (\r\n <div className=\"CommentPlugin_CommentsPanel_Empty\">No Comments</div>\r\n ) : (\r\n <CommentsPanelList\r\n activeIDs={activeIDs}\r\n comments={comments}\r\n deleteCommentOrThread={deleteCommentOrThread}\r\n listRef={listRef}\r\n submitAddComment={submitAddComment}\r\n markNodeMap={markNodeMap}\r\n />\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction useCollabAuthorName(): string {\r\n const collabContext = useCollaborationContext();\r\n const {yjsDocMap, name} = collabContext;\r\n return yjsDocMap.has('comments') ? name : 'Playground User';\r\n}\r\n\r\nexport default function CommentPlugin({\r\n providerFactory,\r\n}: {\r\n providerFactory?: (id: string, yjsDocMap: Map<string, Doc>) => Provider;\r\n}): JSX.Element {\r\n const collabContext = useCollaborationContext();\r\n const [editor] = useLexicalComposerContext();\r\n const commentStore = useMemo(() => new CommentStore(editor), [editor]);\r\n const comments = useCommentStore(commentStore);\r\n const markNodeMap = useMemo<Map<string, Set<NodeKey>>>(() => {\r\n return new Map();\r\n }, []);\r\n const [activeAnchorKey, setActiveAnchorKey] = useState<NodeKey | null>();\r\n const [activeIDs, setActiveIDs] = useState<Array<string>>([]);\r\n const [showCommentInput, setShowCommentInput] = useState(false);\r\n const [showComments, setShowComments] = useState(false);\r\n const {yjsDocMap} = collabContext;\r\n\r\n useEffect(() => {\r\n if (providerFactory) {\r\n const provider = providerFactory('comments', yjsDocMap);\r\n return commentStore.registerCollaboration(provider);\r\n }\r\n }, [commentStore, providerFactory, yjsDocMap]);\r\n\r\n const cancelAddComment = useCallback(() => {\r\n editor.update(() => {\r\n const selection = $getSelection();\r\n // Restore selection\r\n if (selection !== null) {\r\n selection.dirty = true;\r\n }\r\n });\r\n setShowCommentInput(false);\r\n }, [editor]);\r\n\r\n const deleteCommentOrThread = useCallback(\r\n (comment: Comment | Thread, thread?: Thread) => {\r\n if (comment.type === 'comment') {\r\n const deletionInfo = commentStore.deleteCommentOrThread(\r\n comment,\r\n thread,\r\n );\r\n if (!deletionInfo) {\r\n return;\r\n }\r\n const {markedComment, index} = deletionInfo;\r\n commentStore.addComment(markedComment, thread, index);\r\n } else {\r\n commentStore.deleteCommentOrThread(comment);\r\n // Remove ids from associated marks\r\n const id = thread !== undefined ? thread.id : comment.id;\r\n const markNodeKeys = markNodeMap.get(id);\r\n if (markNodeKeys !== undefined) {\r\n // Do async to avoid causing a React infinite loop\r\n setTimeout(() => {\r\n editor.update(() => {\r\n for (const key of markNodeKeys) {\r\n const node: null | MarkNode = $getNodeByKey(key);\r\n if ($isMarkNode(node)) {\r\n node.deleteID(id);\r\n if (node.getIDs().length === 0) {\r\n $unwrapMarkNode(node);\r\n }\r\n }\r\n }\r\n });\r\n });\r\n }\r\n }\r\n },\r\n [commentStore, editor, markNodeMap],\r\n );\r\n\r\n const submitAddComment = useCallback(\r\n (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n selection?: RangeSelection | null,\r\n ) => {\r\n commentStore.addComment(commentOrThread, thread);\r\n if (isInlineComment) {\r\n editor.update(() => {\r\n if ($isRangeSelection(selection)) {\r\n const isBackward = selection.isBackward();\r\n const id = commentOrThread.id;\r\n\r\n // Wrap content in a MarkNode\r\n $wrapSelectionInMarkNode(selection, isBackward, id);\r\n }\r\n });\r\n setShowCommentInput(false);\r\n }\r\n },\r\n [commentStore, editor],\r\n );\r\n\r\n useEffect(() => {\r\n const changedElems: Array<HTMLElement> = [];\r\n for (let i = 0; i < activeIDs.length; i++) {\r\n const id = activeIDs[i];\r\n const keys = markNodeMap.get(id);\r\n if (keys !== undefined) {\r\n for (const key of keys) {\r\n const elem = editor.getElementByKey(key);\r\n if (elem !== null) {\r\n elem.classList.add('selected');\r\n changedElems.push(elem);\r\n setShowComments(true);\r\n }\r\n }\r\n }\r\n }\r\n return () => {\r\n for (let i = 0; i < changedElems.length; i++) {\r\n const changedElem = changedElems[i];\r\n changedElem.classList.remove('selected');\r\n }\r\n };\r\n }, [activeIDs, editor, markNodeMap]);\r\n\r\n useEffect(() => {\r\n const markNodeKeysToIDs: Map<NodeKey, Array<string>> = new Map();\r\n\r\n return mergeRegister(\r\n registerNestedElementResolver<MarkNode>(\r\n editor,\r\n MarkNode,\r\n (from: MarkNode) => {\r\n return $createMarkNode(from.getIDs());\r\n },\r\n (from: MarkNode, to: MarkNode) => {\r\n // Merge the IDs\r\n const ids = from.getIDs();\r\n ids.forEach((id) => {\r\n to.addID(id);\r\n });\r\n },\r\n ),\r\n editor.registerMutationListener(MarkNode, (mutations) => {\r\n editor.getEditorState().read(() => {\r\n for (const [key, mutation] of mutations) {\r\n const node: null | MarkNode = $getNodeByKey(key);\r\n let ids: NodeKey[] = [];\r\n\r\n if (mutation === 'destroyed') {\r\n ids = markNodeKeysToIDs.get(key) || [];\r\n } else if ($isMarkNode(node)) {\r\n ids = node.getIDs();\r\n }\r\n\r\n for (let i = 0; i < ids.length; i++) {\r\n const id = ids[i];\r\n let markNodeKeys = markNodeMap.get(id);\r\n markNodeKeysToIDs.set(key, ids);\r\n\r\n if (mutation === 'destroyed') {\r\n if (markNodeKeys !== undefined) {\r\n markNodeKeys.delete(key);\r\n if (markNodeKeys.size === 0) {\r\n markNodeMap.delete(id);\r\n }\r\n }\r\n } else {\r\n if (markNodeKeys === undefined) {\r\n markNodeKeys = new Set();\r\n markNodeMap.set(id, markNodeKeys);\r\n }\r\n if (!markNodeKeys.has(key)) {\r\n markNodeKeys.add(key);\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }),\r\n editor.registerUpdateListener(({editorState, tags}) => {\r\n editorState.read(() => {\r\n const selection = $getSelection();\r\n let hasActiveIds = false;\r\n let hasAnchorKey = false;\r\n\r\n if ($isRangeSelection(selection)) {\r\n const anchorNode = selection.anchor.getNode();\r\n\r\n if ($isTextNode(anchorNode)) {\r\n const commentIDs = $getMarkIDs(\r\n anchorNode,\r\n selection.anchor.offset,\r\n );\r\n if (commentIDs !== null) {\r\n setActiveIDs(commentIDs);\r\n hasActiveIds = true;\r\n }\r\n if (!selection.isCollapsed()) {\r\n setActiveAnchorKey(anchorNode.getKey());\r\n hasAnchorKey = true;\r\n }\r\n }\r\n }\r\n if (!hasActiveIds) {\r\n setActiveIDs((_activeIds) =>\r\n _activeIds.length === 0 ? _activeIds : [],\r\n );\r\n }\r\n if (!hasAnchorKey) {\r\n setActiveAnchorKey(null);\r\n }\r\n if (!tags.has('collaboration') && $isRangeSelection(selection)) {\r\n setShowCommentInput(false);\r\n }\r\n });\r\n }),\r\n editor.registerCommand(\r\n INSERT_INLINE_COMMAND,\r\n () => {\r\n const domSelection = window.getSelection();\r\n if (domSelection !== null) {\r\n domSelection.removeAllRanges();\r\n }\r\n setShowCommentInput(true);\r\n return true;\r\n },\r\n COMMAND_PRIORITY_EDITOR,\r\n ),\r\n );\r\n }, [editor, markNodeMap]);\r\n\r\n const onAddComment = () => {\r\n editor.dispatchCommand(INSERT_INLINE_COMMAND, undefined);\r\n };\r\n\r\n return (\r\n <>\r\n {showCommentInput &&\r\n createPortal(\r\n <CommentInputBox\r\n editor={editor}\r\n cancelAddComment={cancelAddComment}\r\n submitAddComment={submitAddComment}\r\n />,\r\n document.body,\r\n )}\r\n {activeAnchorKey !== null &&\r\n activeAnchorKey !== undefined &&\r\n !showCommentInput &&\r\n createPortal(\r\n <AddCommentBox\r\n anchorKey={activeAnchorKey}\r\n editor={editor}\r\n onAddComment={onAddComment}\r\n />,\r\n document.body,\r\n )}\r\n {createPortal(\r\n <Button\r\n className={`CommentPlugin_ShowCommentsButton ${\r\n showComments ? 'active' : ''\r\n }`}\r\n onClick={() => setShowComments(!showComments)}\r\n title={showComments ? 'Hide Comments' : 'Show Comments'}>\r\n <i className=\"comments\" />\r\n </Button>,\r\n document.body,\r\n )}\r\n {showComments &&\r\n createPortal(\r\n <CommentsPanel\r\n comments={comments}\r\n submitAddComment={submitAddComment}\r\n deleteCommentOrThread={deleteCommentOrThread}\r\n activeIDs={activeIDs}\r\n markNodeMap={markNodeMap}\r\n />,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport './index.css';\r\n\r\nimport {$isCodeHighlightNode} from '@lexical/code';\r\nimport {$isLinkNode, TOGGLE_LINK_COMMAND} from '@lexical/link';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport {mergeRegister} from '@lexical/utils';\r\nimport {\r\n $getSelection,\r\n $isParagraphNode,\r\n $isRangeSelection,\r\n $isTextNode,\r\n COMMAND_PRIORITY_LOW,\r\n FORMAT_TEXT_COMMAND,\r\n LexicalEditor,\r\n SELECTION_CHANGE_COMMAND,\r\n} from 'lexical';\r\nimport {useCallback, useEffect, useRef, useState} from 'react';\r\nimport * as React from 'react';\r\nimport {createPortal} from 'react-dom';\r\n\r\nimport {getDOMRangeRect} from '../../utils/getDOMRangeRect';\r\nimport {getSelectedNode} from '../../utils/getSelectedNode';\r\nimport {setFloatingElemPosition} from '../../utils/setFloatingElemPosition';\r\nimport {INSERT_INLINE_COMMAND} from '../CommentPlugin';\r\n\r\nfunction TextFormatFloatingToolbar({\r\n editor,\r\n anchorElem,\r\n isLink,\r\n isBold,\r\n isItalic,\r\n isUnderline,\r\n isCode,\r\n isStrikethrough,\r\n isSubscript,\r\n isSuperscript,\r\n}: {\r\n editor: LexicalEditor;\r\n anchorElem: HTMLElement;\r\n isBold: boolean;\r\n isCode: boolean;\r\n isItalic: boolean;\r\n isLink: boolean;\r\n isStrikethrough: boolean;\r\n isSubscript: boolean;\r\n isSuperscript: boolean;\r\n isUnderline: boolean;\r\n}): JSX.Element {\r\n const popupCharStylesEditorRef = useRef<HTMLDivElement | null>(null);\r\n\r\n const insertLink = useCallback(() => {\r\n if (!isLink) {\r\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, 'https://');\r\n } else {\r\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);\r\n }\r\n }, [editor, isLink]);\r\n\r\n const insertComment = () => {\r\n editor.dispatchCommand(INSERT_INLINE_COMMAND, undefined);\r\n };\r\n\r\n function mouseMoveListener(e: MouseEvent) {\r\n if (\r\n popupCharStylesEditorRef?.current &&\r\n (e.buttons === 1 || e.buttons === 3)\r\n ) {\r\n if (popupCharStylesEditorRef.current.style.pointerEvents !== 'none') {\r\n const x = e.clientX;\r\n const y = e.clientY;\r\n const elementUnderMouse = document.elementFromPoint(x, y);\r\n\r\n if (!popupCharStylesEditorRef.current.contains(elementUnderMouse)) {\r\n // Mouse is not over the target element => not a normal click, but probably a drag\r\n popupCharStylesEditorRef.current.style.pointerEvents = 'none';\r\n }\r\n }\r\n }\r\n }\r\n function mouseUpListener(e: MouseEvent) {\r\n if (popupCharStylesEditorRef?.current) {\r\n if (popupCharStylesEditorRef.current.style.pointerEvents !== 'auto') {\r\n popupCharStylesEditorRef.current.style.pointerEvents = 'auto';\r\n }\r\n }\r\n }\r\n\r\n useEffect(() => {\r\n if (popupCharStylesEditorRef?.current) {\r\n document.addEventListener('mousemove', mouseMoveListener);\r\n document.addEventListener('mouseup', mouseUpListener);\r\n\r\n return () => {\r\n document.removeEventListener('mousemove', mouseMoveListener);\r\n document.removeEventListener('mouseup', mouseUpListener);\r\n };\r\n }\r\n }, [popupCharStylesEditorRef]);\r\n\r\n const updateTextFormatFloatingToolbar = useCallback(() => {\r\n const selection = $getSelection();\r\n\r\n const popupCharStylesEditorElem = popupCharStylesEditorRef.current;\r\n const nativeSelection = window.getSelection();\r\n\r\n if (popupCharStylesEditorElem === null) {\r\n return;\r\n }\r\n\r\n const rootElement = editor.getRootElement();\r\n if (\r\n selection !== null &&\r\n nativeSelection !== null &&\r\n !nativeSelection.isCollapsed &&\r\n rootElement !== null &&\r\n rootElement.contains(nativeSelection.anchorNode)\r\n ) {\r\n const rangeRect = getDOMRangeRect(nativeSelection, rootElement);\r\n\r\n setFloatingElemPosition(\r\n rangeRect,\r\n popupCharStylesEditorElem,\r\n anchorElem,\r\n isLink,\r\n );\r\n }\r\n }, [editor, anchorElem, isLink]);\r\n\r\n useEffect(() => {\r\n const scrollerElem = anchorElem.parentElement;\r\n\r\n const update = () => {\r\n editor.getEditorState().read(() => {\r\n updateTextFormatFloatingToolbar();\r\n });\r\n };\r\n\r\n window.addEventListener('resize', update);\r\n if (scrollerElem) {\r\n scrollerElem.addEventListener('scroll', update);\r\n }\r\n\r\n return () => {\r\n window.removeEventListener('resize', update);\r\n if (scrollerElem) {\r\n scrollerElem.removeEventListener('scroll', update);\r\n }\r\n };\r\n }, [editor, updateTextFormatFloatingToolbar, anchorElem]);\r\n\r\n useEffect(() => {\r\n editor.getEditorState().read(() => {\r\n updateTextFormatFloatingToolbar();\r\n });\r\n return mergeRegister(\r\n editor.registerUpdateListener(({editorState}) => {\r\n editorState.read(() => {\r\n updateTextFormatFloatingToolbar();\r\n });\r\n }),\r\n\r\n editor.registerCommand(\r\n SELECTION_CHANGE_COMMAND,\r\n () => {\r\n updateTextFormatFloatingToolbar();\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n );\r\n }, [editor, updateTextFormatFloatingToolbar]);\r\n\r\n return (\r\n <div ref={popupCharStylesEditorRef} className=\"floating-text-format-popup\">\r\n {editor.isEditable() && (\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold');\r\n }}\r\n className={'popup-item spaced ' + (isBold ? 'active' : '')}\r\n aria-label=\"Format text as bold\">\r\n <i className=\"format bold\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic');\r\n }}\r\n className={'popup-item spaced ' + (isItalic ? 'active' : '')}\r\n aria-label=\"Format text as italics\">\r\n <i className=\"format italic\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline');\r\n }}\r\n className={'popup-item spaced ' + (isUnderline ? 'active' : '')}\r\n aria-label=\"Format text to underlined\">\r\n <i className=\"format underline\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough');\r\n }}\r\n className={'popup-item spaced ' + (isStrikethrough ? 'active' : '')}\r\n aria-label=\"Format text with a strikethrough\">\r\n <i className=\"format strikethrough\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'subscript');\r\n }}\r\n className={'popup-item spaced ' + (isSubscript ? 'active' : '')}\r\n title=\"Subscript\"\r\n aria-label=\"Format Subscript\">\r\n <i className=\"format subscript\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'superscript');\r\n }}\r\n className={'popup-item spaced ' + (isSuperscript ? 'active' : '')}\r\n title=\"Superscript\"\r\n aria-label=\"Format Superscript\">\r\n <i className=\"format superscript\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code');\r\n }}\r\n className={'popup-item spaced ' + (isCode ? 'active' : '')}\r\n aria-label=\"Insert code block\">\r\n <i className=\"format code\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={insertLink}\r\n className={'popup-item spaced ' + (isLink ? 'active' : '')}\r\n aria-label=\"Insert link\">\r\n <i className=\"format link\" />\r\n </button>\r\n </>\r\n )}\r\n <button\r\n type=\"button\"\r\n onClick={insertComment}\r\n className={'popup-item spaced insert-comment'}\r\n aria-label=\"Insert comment\">\r\n <i className=\"format add-comment\" />\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\nfunction useFloatingTextFormatToolbar(\r\n editor: LexicalEditor,\r\n anchorElem: HTMLElement,\r\n): JSX.Element | null {\r\n const [isText, setIsText] = useState(false);\r\n const [isLink, setIsLink] = useState(false);\r\n const [isBold, setIsBold] = useState(false);\r\n const [isItalic, setIsItalic] = useState(false);\r\n const [isUnderline, setIsUnderline] = useState(false);\r\n const [isStrikethrough, setIsStrikethrough] = useState(false);\r\n const [isSubscript, setIsSubscript] = useState(false);\r\n const [isSuperscript, setIsSuperscript] = useState(false);\r\n const [isCode, setIsCode] = useState(false);\r\n\r\n const updatePopup = useCallback(() => {\r\n editor.getEditorState().read(() => {\r\n // Should not to pop up the floating toolbar when using IME input\r\n if (editor.isComposing()) {\r\n return;\r\n }\r\n const selection = $getSelection();\r\n const nativeSelection = window.getSelection();\r\n const rootElement = editor.getRootElement();\r\n\r\n if (\r\n nativeSelection !== null &&\r\n (!$isRangeSelection(selection) ||\r\n rootElement === null ||\r\n !rootElement.contains(nativeSelection.anchorNode))\r\n ) {\r\n setIsText(false);\r\n return;\r\n }\r\n\r\n if (!$isRangeSelection(selection)) {\r\n return;\r\n }\r\n\r\n const node = getSelectedNode(selection);\r\n\r\n // Update text format\r\n setIsBold(selection.hasFormat('bold'));\r\n setIsItalic(selection.hasFormat('italic'));\r\n setIsUnderline(selection.hasFormat('underline'));\r\n setIsStrikethrough(selection.hasFormat('strikethrough'));\r\n setIsSubscript(selection.hasFormat('subscript'));\r\n setIsSuperscript(selection.hasFormat('superscript'));\r\n setIsCode(selection.hasFormat('code'));\r\n\r\n // Update links\r\n const parent = node.getParent();\r\n if ($isLinkNode(parent) || $isLinkNode(node)) {\r\n setIsLink(true);\r\n } else {\r\n setIsLink(false);\r\n }\r\n\r\n if (\r\n !$isCodeHighlightNode(selection.anchor.getNode()) &&\r\n selection.getTextContent() !== ''\r\n ) {\r\n setIsText($isTextNode(node) || $isParagraphNode(node));\r\n } else {\r\n setIsText(false);\r\n }\r\n\r\n const rawTextContent = selection.getTextContent().replace(/\\n/g, '');\r\n if (!selection.isCollapsed() && rawTextContent === '') {\r\n setIsText(false);\r\n return;\r\n }\r\n });\r\n }, [editor]);\r\n\r\n useEffect(() => {\r\n document.addEventListener('selectionchange', updatePopup);\r\n return () => {\r\n document.removeEventListener('selectionchange', updatePopup);\r\n };\r\n }, [updatePopup]);\r\n\r\n useEffect(() => {\r\n return mergeRegister(\r\n editor.registerUpdateListener(() => {\r\n updatePopup();\r\n }),\r\n editor.registerRootListener(() => {\r\n if (editor.getRootElement() === null) {\r\n setIsText(false);\r\n }\r\n }),\r\n );\r\n }, [editor, updatePopup]);\r\n\r\n if (!isText) {\r\n return null;\r\n }\r\n\r\n return createPortal(\r\n <TextFormatFloatingToolbar\r\n editor={editor}\r\n anchorElem={anchorElem}\r\n isLink={isLink}\r\n isBold={isBold}\r\n isItalic={isItalic}\r\n isStrikethrough={isStrikethrough}\r\n isSubscript={isSubscript}\r\n isSuperscript={isSuperscript}\r\n isUnderline={isUnderline}\r\n isCode={isCode}\r\n />,\r\n anchorElem,\r\n );\r\n}\r\n\r\nexport default function FloatingTextFormatToolbarPlugin({\r\n anchorElem = document.body,\r\n}: {\r\n anchorElem?: HTMLElement;\r\n}): JSX.Element | null {\r\n const [editor] = useLexicalComposerContext();\r\n return useFloatingTextFormatToolbar(editor, anchorElem);\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport './Select.css';\r\n\r\nimport * as React from 'react';\r\n\r\ntype SelectIntrinsicProps = JSX.IntrinsicElements['select'];\r\ninterface SelectProps extends SelectIntrinsicProps {\r\n label: string;\r\n}\r\n\r\nexport default function Select({\r\n children,\r\n label,\r\n className,\r\n ...other\r\n}: SelectProps): JSX.Element {\r\n return (\r\n <div className=\"Input__wrapper\">\r\n <label style={{marginTop: '-1em'}} className=\"Input__label\">\r\n {label}\r\n </label>\r\n <select {...other} className={className || 'select'}>\r\n {children}\r\n </select>\r\n </div>\r\n );\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\nimport type {Position} from './InlineImageNode';\r\nimport type {BaseSelection, LexicalEditor, NodeKey} from 'lexical';\r\n\r\nimport './InlineImageNode.css';\r\n\r\nimport {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';\r\nimport {LexicalNestedComposer} from '@lexical/react/LexicalNestedComposer';\r\nimport {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';\r\nimport {useLexicalNodeSelection} from '@lexical/react/useLexicalNodeSelection';\r\nimport {mergeRegister} from '@lexical/utils';\r\nimport {\r\n $getNodeByKey,\r\n $getSelection,\r\n $isNodeSelection,\r\n $setSelection,\r\n CLICK_COMMAND,\r\n COMMAND_PRIORITY_LOW,\r\n DRAGSTART_COMMAND,\r\n KEY_BACKSPACE_COMMAND,\r\n KEY_DELETE_COMMAND,\r\n KEY_ENTER_COMMAND,\r\n KEY_ESCAPE_COMMAND,\r\n SELECTION_CHANGE_COMMAND,\r\n} from 'lexical';\r\nimport * as React from 'react';\r\nimport {Suspense, useCallback, useEffect, useRef, useState} from 'react';\r\n\r\nimport useModal from '../hooks/useModal';\r\nimport FloatingTextFormatToolbarPlugin from '../plugins/FloatingTextFormatToolbarPlugin/index';\r\nimport LinkPlugin from '../plugins/LinkPlugin';\r\nimport Button from '../ui/Button';\r\nimport ContentEditable from '../ui/ContentEditable';\r\nimport {DialogActions} from '../ui/Dialog';\r\nimport Placeholder from '../ui/Placeholder';\r\nimport Select from '../ui/Select';\r\nimport TextInput from '../ui/TextInput';\r\nimport {$isInlineImageNode, InlineImageNode} from './InlineImageNode';\r\n\r\nconst imageCache = new Set();\r\n\r\nfunction useSuspenseImage(src: string) {\r\n if (!imageCache.has(src)) {\r\n throw new Promise((resolve) => {\r\n const img = new Image();\r\n img.src = src;\r\n img.onload = () => {\r\n imageCache.add(src);\r\n resolve(null);\r\n };\r\n });\r\n }\r\n}\r\n\r\nfunction LazyImage({\r\n altText,\r\n className,\r\n imageRef,\r\n src,\r\n width,\r\n height,\r\n position,\r\n}: {\r\n altText: string;\r\n className: string | null;\r\n height: 'inherit' | number;\r\n imageRef: {current: null | HTMLImageElement};\r\n src: string;\r\n width: 'inherit' | number;\r\n position: Position;\r\n}): JSX.Element {\r\n useSuspenseImage(src);\r\n return (\r\n <img\r\n className={className || undefined}\r\n src={src}\r\n alt={altText}\r\n ref={imageRef}\r\n data-position={position}\r\n style={{\r\n display: 'block',\r\n height,\r\n width,\r\n }}\r\n draggable=\"false\"\r\n />\r\n );\r\n}\r\n\r\nexport function UpdateInlineImageDialog({\r\n activeEditor,\r\n nodeKey,\r\n onClose,\r\n}: {\r\n activeEditor: LexicalEditor;\r\n nodeKey: NodeKey;\r\n onClose: () => void;\r\n}): JSX.Element {\r\n const editorState = activeEditor.getEditorState();\r\n const node = editorState.read(\r\n () => $getNodeByKey(nodeKey) as InlineImageNode,\r\n );\r\n const [altText, setAltText] = useState(node.getAltText());\r\n const [showCaption, setShowCaption] = useState(node.getShowCaption());\r\n const [position, setPosition] = useState<Position>(node.getPosition());\r\n\r\n const handleShowCaptionChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n setShowCaption(e.target.checked);\r\n };\r\n\r\n const handlePositionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\r\n setPosition(e.target.value as Position);\r\n };\r\n\r\n const handleOnConfirm = () => {\r\n const payload = {altText, position, showCaption};\r\n if (node) {\r\n activeEditor.update(() => {\r\n node.update(payload);\r\n });\r\n }\r\n onClose();\r\n };\r\n\r\n return (\r\n <>\r\n <div style={{marginBottom: '1em'}}>\r\n <TextInput\r\n label=\"Alt Text\"\r\n placeholder=\"Descriptive alternative text\"\r\n onChange={setAltText}\r\n value={altText}\r\n data-test-id=\"image-modal-alt-text-input\"\r\n />\r\n </div>\r\n\r\n <Select\r\n style={{marginBottom: '1em', width: '208px'}}\r\n value={position}\r\n label=\"Position\"\r\n name=\"position\"\r\n id=\"position-select\"\r\n onChange={handlePositionChange}>\r\n <option value=\"left\">Left</option>\r\n <option value=\"right\">Right</option>\r\n <option value=\"full\">Full Width</option>\r\n </Select>\r\n\r\n <div className=\"Input__wrapper\">\r\n <input\r\n id=\"caption\"\r\n type=\"checkbox\"\r\n checked={showCaption}\r\n onChange={handleShowCaptionChange}\r\n />\r\n <label htmlFor=\"caption\">Show Caption</label>\r\n </div>\r\n\r\n <DialogActions>\r\n <Button\r\n data-test-id=\"image-modal-file-upload-btn\"\r\n onClick={() => handleOnConfirm()}>\r\n Confirm\r\n </Button>\r\n </DialogActions>\r\n </>\r\n );\r\n}\r\n\r\nexport default function InlineImageComponent({\r\n src,\r\n altText,\r\n nodeKey,\r\n width,\r\n height,\r\n showCaption,\r\n caption,\r\n position,\r\n}: {\r\n altText: string;\r\n caption: LexicalEditor;\r\n height: 'inherit' | number;\r\n nodeKey: NodeKey;\r\n showCaption: boolean;\r\n src: string;\r\n width: 'inherit' | number;\r\n position: Position;\r\n}): JSX.Element {\r\n const [modal, showModal] = useModal();\r\n const imageRef = useRef<null | HTMLImageElement>(null);\r\n const buttonRef = useRef<HTMLButtonElement | null>(null);\r\n const [isSelected, setSelected, clearSelection] =\r\n useLexicalNodeSelection(nodeKey);\r\n const [editor] = useLexicalComposerContext();\r\n const [selection, setSelection] = useState<BaseSelection | null>(null);\r\n const activeEditorRef = useRef<LexicalEditor | null>(null);\r\n\r\n const onDelete = useCallback(\r\n (payload: KeyboardEvent) => {\r\n if (isSelected && $isNodeSelection($getSelection())) {\r\n const event: KeyboardEvent = payload;\r\n event.preventDefault();\r\n const node = $getNodeByKey(nodeKey);\r\n if ($isInlineImageNode(node)) {\r\n node.remove();\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n [isSelected, nodeKey],\r\n );\r\n\r\n const onEnter = useCallback(\r\n (event: KeyboardEvent) => {\r\n const latestSelection = $getSelection();\r\n const buttonElem = buttonRef.current;\r\n if (\r\n isSelected &&\r\n $isNodeSelection(latestSelection) &&\r\n latestSelection.getNodes().length === 1\r\n ) {\r\n if (showCaption) {\r\n // Move focus into nested editor\r\n $setSelection(null);\r\n event.preventDefault();\r\n caption.focus();\r\n return true;\r\n } else if (\r\n buttonElem !== null &&\r\n buttonElem !== document.activeElement\r\n ) {\r\n event.preventDefault();\r\n buttonElem.focus();\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n [caption, isSelected, showCaption],\r\n );\r\n\r\n const onEscape = useCallback(\r\n (event: KeyboardEvent) => {\r\n if (\r\n activeEditorRef.current === caption ||\r\n buttonRef.current === event.target\r\n ) {\r\n $setSelection(null);\r\n editor.update(() => {\r\n setSelected(true);\r\n const parentRootElement = editor.getRootElement();\r\n if (parentRootElement !== null) {\r\n parentRootElement.focus();\r\n }\r\n });\r\n return true;\r\n }\r\n return false;\r\n },\r\n [caption, editor, setSelected],\r\n );\r\n\r\n useEffect(() => {\r\n let isMounted = true;\r\n const unregister = mergeRegister(\r\n editor.registerUpdateListener(({editorState}) => {\r\n if (isMounted) {\r\n setSelection(editorState.read(() => $getSelection()));\r\n }\r\n }),\r\n editor.registerCommand(\r\n SELECTION_CHANGE_COMMAND,\r\n (_, activeEditor) => {\r\n activeEditorRef.current = activeEditor;\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand<MouseEvent>(\r\n CLICK_COMMAND,\r\n (payload) => {\r\n const event = payload;\r\n if (event.target === imageRef.current) {\r\n if (event.shiftKey) {\r\n setSelected(!isSelected);\r\n } else {\r\n clearSelection();\r\n setSelected(true);\r\n }\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n DRAGSTART_COMMAND,\r\n (event) => {\r\n if (event.target === imageRef.current) {\r\n // TODO This is just a temporary workaround for FF to behave like other browsers.\r\n // Ideally, this handles drag & drop too (and all browsers).\r\n event.preventDefault();\r\n return true;\r\n }\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_DELETE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_BACKSPACE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW),\r\n editor.registerCommand(\r\n KEY_ESCAPE_COMMAND,\r\n onEscape,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n );\r\n return () => {\r\n isMounted = false;\r\n unregister();\r\n };\r\n }, [\r\n clearSelection,\r\n editor,\r\n isSelected,\r\n nodeKey,\r\n onDelete,\r\n onEnter,\r\n onEscape,\r\n setSelected,\r\n ]);\r\n\r\n const draggable = isSelected && $isNodeSelection(selection);\r\n const isFocused = isSelected;\r\n return (\r\n <Suspense fallback={null}>\r\n <>\r\n <div draggable={draggable}>\r\n <button\r\n className=\"image-edit-button\"\r\n ref={buttonRef}\r\n onClick={() => {\r\n showModal('Update Inline Image', (onClose) => (\r\n <UpdateInlineImageDialog\r\n activeEditor={editor}\r\n nodeKey={nodeKey}\r\n onClose={onClose}\r\n />\r\n ));\r\n }}>\r\n Edit\r\n </button>\r\n <LazyImage\r\n className={\r\n isFocused\r\n ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}`\r\n : null\r\n }\r\n src={src}\r\n altText={altText}\r\n imageRef={imageRef}\r\n width={width}\r\n height={height}\r\n position={position}\r\n />\r\n </div>\r\n {showCaption && (\r\n <div className=\"image-caption-container\">\r\n <LexicalNestedComposer initialEditor={caption}>\r\n <AutoFocusPlugin />\r\n <LinkPlugin />\r\n <FloatingTextFormatToolbarPlugin />\r\n <RichTextPlugin\r\n contentEditable={\r\n <ContentEditable className=\"InlineImageNode__contentEditable\" />\r\n }\r\n placeholder={\r\n <Placeholder className=\"InlineImageNode__placeholder\">\r\n Enter a caption...\r\n </Placeholder>\r\n }\r\n ErrorBoundary={LexicalErrorBoundary}\r\n />\r\n </LexicalNestedComposer>\r\n </div>\r\n )}\r\n </>\r\n {modal}\r\n </Suspense>\r\n );\r\n}\r\n"],"names":["getDOMRangeRect","nativeSelection","rootElement","domRange","rect","inner","VERTICAL_GAP","HORIZONTAL_OFFSET","setFloatingElemPosition","targetRect","floatingElem","anchorElem","isLink","verticalGap","horizontalOffset","scrollerElem","floatingElemRect","anchorElementRect","editorScrollerRect","top","left","baseTheme","INSERT_INLINE_COMMAND","createCommand","TextFormatFloatingToolbar","editor","isBold","isItalic","isUnderline","isCode","isStrikethrough","isSubscript","isSuperscript","popupCharStylesEditorRef","useRef","insertLink","useCallback","TOGGLE_LINK_COMMAND","insertComment","mouseMoveListener","e","x","y","elementUnderMouse","mouseUpListener","useEffect","updateTextFormatFloatingToolbar","selection","$getSelection","popupCharStylesEditorElem","rangeRect","update","mergeRegister","editorState","SELECTION_CHANGE_COMMAND","COMMAND_PRIORITY_LOW","jsxs","Fragment","jsx","FORMAT_TEXT_COMMAND","useFloatingTextFormatToolbar","isText","setIsText","useState","setIsLink","setIsBold","setIsItalic","setIsUnderline","setIsStrikethrough","setIsSubscript","setIsSuperscript","setIsCode","updatePopup","$isRangeSelection","node","getSelectedNode","parent","$isLinkNode","$isCodeHighlightNode","$isTextNode","$isParagraphNode","rawTextContent","createPortal","FloatingTextFormatToolbarPlugin","useLexicalComposerContext","Select","children","label","className","other","imageCache","useSuspenseImage","src","resolve","img","LazyImage","altText","imageRef","width","height","position","UpdateInlineImageDialog","activeEditor","nodeKey","onClose","$getNodeByKey","setAltText","showCaption","setShowCaption","setPosition","handleShowCaptionChange","handlePositionChange","handleOnConfirm","payload","TextInput","DialogActions","Button","InlineImageComponent","caption","modal","showModal","useModal","buttonRef","isSelected","setSelected","clearSelection","useLexicalNodeSelection","setSelection","activeEditorRef","onDelete","$isNodeSelection","$isInlineImageNode","onEnter","event","latestSelection","buttonElem","$setSelection","onEscape","parentRootElement","isMounted","unregister","_","CLICK_COMMAND","DRAGSTART_COMMAND","KEY_DELETE_COMMAND","KEY_BACKSPACE_COMMAND","KEY_ENTER_COMMAND","KEY_ESCAPE_COMMAND","draggable","isFocused","Suspense","LexicalNestedComposer","AutoFocusPlugin","LinkPlugin","RichTextPlugin","ContentEditable","Placeholder","LexicalErrorBoundary"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InlineImageComponent-531380f5.js","sources":["../src/lexical/utils/getDOMRangeRect.ts","../src/lexical/utils/setFloatingElemPosition.ts","../src/lexical/themes/CommentEditorTheme.ts","../src/lexical/plugins/CommentPlugin/index.tsx","../src/lexical/plugins/FloatingTextFormatToolbarPlugin/index.tsx","../src/lexical/ui/Select.tsx","../src/lexical/nodes/InlineImageComponent.tsx"],"sourcesContent":["/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\nexport function getDOMRangeRect(\r\n nativeSelection: Selection,\r\n rootElement: HTMLElement,\r\n): DOMRect {\r\n const domRange = nativeSelection.getRangeAt(0);\r\n\r\n let rect;\r\n\r\n if (nativeSelection.anchorNode === rootElement) {\r\n let inner = rootElement;\r\n while (inner.firstElementChild != null) {\r\n inner = inner.firstElementChild as HTMLElement;\r\n }\r\n rect = inner.getBoundingClientRect();\r\n } else {\r\n rect = domRange.getBoundingClientRect();\r\n }\r\n\r\n return rect;\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\nconst VERTICAL_GAP = 10;\r\nconst HORIZONTAL_OFFSET = 5;\r\n\r\nexport function setFloatingElemPosition(\r\n targetRect: DOMRect | null,\r\n floatingElem: HTMLElement,\r\n anchorElem: HTMLElement,\r\n isLink: boolean = false,\r\n verticalGap: number = VERTICAL_GAP,\r\n horizontalOffset: number = HORIZONTAL_OFFSET,\r\n): void {\r\n const scrollerElem = anchorElem.parentElement;\r\n\r\n if (targetRect === null || !scrollerElem) {\r\n floatingElem.style.opacity = '0';\r\n floatingElem.style.transform = 'translate(-10000px, -10000px)';\r\n return;\r\n }\r\n\r\n const floatingElemRect = floatingElem.getBoundingClientRect();\r\n const anchorElementRect = anchorElem.getBoundingClientRect();\r\n const editorScrollerRect = scrollerElem.getBoundingClientRect();\r\n\r\n let top = targetRect.top - floatingElemRect.height - verticalGap;\r\n let left = targetRect.left - horizontalOffset;\r\n\r\n if (top < editorScrollerRect.top) {\r\n // adjusted height for link element if the element is at top\r\n top +=\r\n floatingElemRect.height +\r\n targetRect.height +\r\n verticalGap * (isLink ? 9 : 2);\r\n }\r\n\r\n if (left + floatingElemRect.width > editorScrollerRect.right) {\r\n left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;\r\n }\r\n\r\n top -= anchorElementRect.top;\r\n left -= anchorElementRect.left;\r\n\r\n floatingElem.style.opacity = '1';\r\n floatingElem.style.transform = `translate(${left}px, ${top}px)`;\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {EditorThemeClasses} from 'lexical';\r\n\r\nimport './CommentEditorTheme.css';\r\n\r\nimport baseTheme from './PlaygroundEditorTheme';\r\n\r\nconst theme: EditorThemeClasses = {\r\n ...baseTheme,\r\n paragraph: 'CommentEditorTheme__paragraph',\r\n};\r\n\r\nexport default theme;\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {Provider} from '@lexical/yjs';\r\nimport type {\r\n EditorState,\r\n LexicalCommand,\r\n LexicalEditor,\r\n NodeKey,\r\n RangeSelection,\r\n} from 'lexical';\r\nimport type {Doc} from 'yjs';\r\n\r\nimport './index.css';\r\n\r\nimport {\r\n $createMarkNode,\r\n $getMarkIDs,\r\n $isMarkNode,\r\n $unwrapMarkNode,\r\n $wrapSelectionInMarkNode,\r\n MarkNode,\r\n} from '@lexical/mark';\r\nimport {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';\r\nimport {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin';\r\nimport {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';\r\nimport {LexicalComposer} from '@lexical/react/LexicalComposer';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport {EditorRefPlugin} from '@lexical/react/LexicalEditorRefPlugin';\r\nimport LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';\r\nimport {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';\r\nimport {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin';\r\nimport {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin';\r\nimport {createDOMRange, createRectsFromDOMRange} from '@lexical/selection';\r\nimport {$isRootTextContentEmpty, $rootTextContent} from '@lexical/text';\r\nimport {mergeRegister, registerNestedElementResolver} from '@lexical/utils';\r\nimport {\r\n $getNodeByKey,\r\n $getSelection,\r\n $isRangeSelection,\r\n $isTextNode,\r\n CLEAR_EDITOR_COMMAND,\r\n COMMAND_PRIORITY_EDITOR,\r\n createCommand,\r\n KEY_ESCAPE_COMMAND,\r\n} from 'lexical';\r\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\r\nimport * as React from 'react';\r\nimport {createPortal} from 'react-dom';\r\n\r\nimport {\r\n Comment,\r\n Comments,\r\n CommentStore,\r\n createComment,\r\n createThread,\r\n Thread,\r\n useCommentStore,\r\n} from '../../commenting';\r\nimport useModal from '../../hooks/useModal';\r\nimport CommentEditorTheme from '../../themes/CommentEditorTheme';\r\nimport Button from '../../ui/Button';\r\nimport ContentEditable from '../../ui/ContentEditable';\r\nimport Placeholder from '../../ui/Placeholder';\r\n\r\nexport const INSERT_INLINE_COMMAND: LexicalCommand<void> = createCommand(\r\n 'INSERT_INLINE_COMMAND',\r\n);\r\n\r\nfunction AddCommentBox({\r\n anchorKey,\r\n editor,\r\n onAddComment,\r\n}: {\r\n anchorKey: NodeKey;\r\n editor: LexicalEditor;\r\n onAddComment: () => void;\r\n}): JSX.Element {\r\n const boxRef = useRef<HTMLDivElement>(null);\r\n\r\n const updatePosition = useCallback(() => {\r\n const boxElem = boxRef.current;\r\n const rootElement = editor.getRootElement();\r\n const anchorElement = editor.getElementByKey(anchorKey);\r\n\r\n if (boxElem !== null && rootElement !== null && anchorElement !== null) {\r\n const {right} = rootElement.getBoundingClientRect();\r\n const {top} = anchorElement.getBoundingClientRect();\r\n boxElem.style.left = `${right - 20}px`;\r\n boxElem.style.top = `${top - 30}px`;\r\n }\r\n }, [anchorKey, editor]);\r\n\r\n useEffect(() => {\r\n window.addEventListener('resize', updatePosition);\r\n\r\n return () => {\r\n window.removeEventListener('resize', updatePosition);\r\n };\r\n }, [editor, updatePosition]);\r\n\r\n React.useLayoutEffect(() => {\r\n updatePosition();\r\n }, [anchorKey, editor, updatePosition]);\r\n\r\n return (\r\n <div className=\"CommentPlugin_AddCommentBox\" ref={boxRef}>\r\n <button\r\n className=\"CommentPlugin_AddCommentBox_button\"\r\n onClick={onAddComment}>\r\n <i className=\"icon add-comment\" />\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\nfunction EscapeHandlerPlugin({\r\n onEscape,\r\n}: {\r\n onEscape: (e: KeyboardEvent) => boolean;\r\n}): null {\r\n const [editor] = useLexicalComposerContext();\r\n\r\n useEffect(() => {\r\n return editor.registerCommand(\r\n KEY_ESCAPE_COMMAND,\r\n (event: KeyboardEvent) => {\r\n return onEscape(event);\r\n },\r\n 2,\r\n );\r\n }, [editor, onEscape]);\r\n\r\n return null;\r\n}\r\n\r\nfunction PlainTextEditor({\r\n className,\r\n autoFocus,\r\n onEscape,\r\n onChange,\r\n editorRef,\r\n placeholder = 'Type a comment...',\r\n}: {\r\n autoFocus?: boolean;\r\n className?: string;\r\n editorRef?: {current: null | LexicalEditor};\r\n onChange: (editorState: EditorState, editor: LexicalEditor) => void;\r\n onEscape: (e: KeyboardEvent) => boolean;\r\n placeholder?: string;\r\n}) {\r\n const initialConfig = {\r\n namespace: 'Commenting',\r\n nodes: [],\r\n onError: (error: Error) => {\r\n throw error;\r\n },\r\n theme: CommentEditorTheme,\r\n };\r\n\r\n return (\r\n <LexicalComposer initialConfig={initialConfig}>\r\n <div className=\"CommentPlugin_CommentInputBox_EditorContainer\">\r\n <PlainTextPlugin\r\n contentEditable={<ContentEditable className={className} />}\r\n placeholder={<Placeholder>{placeholder}</Placeholder>}\r\n ErrorBoundary={LexicalErrorBoundary}\r\n />\r\n <OnChangePlugin onChange={onChange} />\r\n <HistoryPlugin />\r\n {autoFocus !== false && <AutoFocusPlugin />}\r\n <EscapeHandlerPlugin onEscape={onEscape} />\r\n <ClearEditorPlugin />\r\n {editorRef !== undefined && <EditorRefPlugin editorRef={editorRef} />}\r\n </div>\r\n </LexicalComposer>\r\n );\r\n}\r\n\r\nfunction useOnChange(\r\n setContent: (text: string) => void,\r\n setCanSubmit: (canSubmit: boolean) => void,\r\n) {\r\n return useCallback(\r\n (editorState: EditorState, _editor: LexicalEditor) => {\r\n editorState.read(() => {\r\n setContent($rootTextContent());\r\n setCanSubmit(!$isRootTextContentEmpty(_editor.isComposing(), true));\r\n });\r\n },\r\n [setCanSubmit, setContent],\r\n );\r\n}\r\n\r\nfunction CommentInputBox({\r\n editor,\r\n cancelAddComment,\r\n submitAddComment,\r\n}: {\r\n cancelAddComment: () => void;\r\n editor: LexicalEditor;\r\n submitAddComment: (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n selection?: RangeSelection | null,\r\n ) => void;\r\n}) {\r\n const [content, setContent] = useState('');\r\n const [canSubmit, setCanSubmit] = useState(false);\r\n const boxRef = useRef<HTMLDivElement>(null);\r\n const selectionState = useMemo(\r\n () => ({\r\n container: document.createElement('div'),\r\n elements: [],\r\n }),\r\n [],\r\n );\r\n const selectionRef = useRef<RangeSelection | null>(null);\r\n const author = useCollabAuthorName();\r\n\r\n const updateLocation = useCallback(() => {\r\n editor.getEditorState().read(() => {\r\n const selection = $getSelection();\r\n\r\n if ($isRangeSelection(selection)) {\r\n selectionRef.current = selection.clone();\r\n const anchor = selection.anchor;\r\n const focus = selection.focus;\r\n const range = createDOMRange(\r\n editor,\r\n anchor.getNode(),\r\n anchor.offset,\r\n focus.getNode(),\r\n focus.offset,\r\n );\r\n const boxElem = boxRef.current;\r\n if (range !== null && boxElem !== null) {\r\n const {left, bottom, width} = range.getBoundingClientRect();\r\n const selectionRects = createRectsFromDOMRange(editor, range);\r\n let correctedLeft =\r\n selectionRects.length === 1 ? left + width / 2 - 125 : left - 125;\r\n if (correctedLeft < 10) {\r\n correctedLeft = 10;\r\n }\r\n boxElem.style.left = `${correctedLeft}px`;\r\n boxElem.style.top = `${\r\n bottom +\r\n 20 +\r\n (window.pageYOffset || document.documentElement.scrollTop)\r\n }px`;\r\n const selectionRectsLength = selectionRects.length;\r\n const {container} = selectionState;\r\n const elements: Array<HTMLSpanElement> = selectionState.elements;\r\n const elementsLength = elements.length;\r\n\r\n for (let i = 0; i < selectionRectsLength; i++) {\r\n const selectionRect = selectionRects[i];\r\n let elem: HTMLSpanElement = elements[i];\r\n if (elem === undefined) {\r\n elem = document.createElement('span');\r\n elements[i] = elem;\r\n container.appendChild(elem);\r\n }\r\n const color = '255, 212, 0';\r\n const style = `position:absolute;top:${\r\n selectionRect.top +\r\n (window.pageYOffset || document.documentElement.scrollTop)\r\n }px;left:${selectionRect.left}px;height:${\r\n selectionRect.height\r\n }px;width:${\r\n selectionRect.width\r\n }px;background-color:rgba(${color}, 0.3);pointer-events:none;z-index:5;`;\r\n elem.style.cssText = style;\r\n }\r\n for (let i = elementsLength - 1; i >= selectionRectsLength; i--) {\r\n const elem = elements[i];\r\n container.removeChild(elem);\r\n elements.pop();\r\n }\r\n }\r\n }\r\n });\r\n }, [editor, selectionState]);\r\n\r\n React.useLayoutEffect(() => {\r\n updateLocation();\r\n const container = selectionState.container;\r\n const body = document.body;\r\n if (body !== null) {\r\n body.appendChild(container);\r\n return () => {\r\n body.removeChild(container);\r\n };\r\n }\r\n }, [selectionState.container, updateLocation]);\r\n\r\n useEffect(() => {\r\n window.addEventListener('resize', updateLocation);\r\n\r\n return () => {\r\n window.removeEventListener('resize', updateLocation);\r\n };\r\n }, [updateLocation]);\r\n\r\n const onEscape = (event: KeyboardEvent): boolean => {\r\n event.preventDefault();\r\n cancelAddComment();\r\n return true;\r\n };\r\n\r\n const submitComment = () => {\r\n if (canSubmit) {\r\n let quote = editor.getEditorState().read(() => {\r\n const selection = selectionRef.current;\r\n return selection ? selection.getTextContent() : '';\r\n });\r\n if (quote.length > 100) {\r\n quote = quote.slice(0, 99) + '…';\r\n }\r\n submitAddComment(\r\n createThread(quote, [createComment(content, author)]),\r\n true,\r\n undefined,\r\n selectionRef.current,\r\n );\r\n selectionRef.current = null;\r\n }\r\n };\r\n\r\n const onChange = useOnChange(setContent, setCanSubmit);\r\n\r\n return (\r\n <div className=\"CommentPlugin_CommentInputBox\" ref={boxRef}>\r\n <PlainTextEditor\r\n className=\"CommentPlugin_CommentInputBox_Editor\"\r\n onEscape={onEscape}\r\n onChange={onChange}\r\n />\r\n <div className=\"CommentPlugin_CommentInputBox_Buttons\">\r\n <Button\r\n onClick={cancelAddComment}\r\n className=\"CommentPlugin_CommentInputBox_Button\">\r\n Cancel\r\n </Button>\r\n <Button\r\n onClick={submitComment}\r\n disabled={!canSubmit}\r\n className=\"CommentPlugin_CommentInputBox_Button primary\">\r\n Comment\r\n </Button>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction CommentsComposer({\r\n submitAddComment,\r\n thread,\r\n placeholder,\r\n}: {\r\n placeholder?: string;\r\n submitAddComment: (\r\n commentOrThread: Comment,\r\n isInlineComment: boolean,\r\n // eslint-disable-next-line no-shadow\r\n thread?: Thread,\r\n ) => void;\r\n thread?: Thread;\r\n}) {\r\n const [content, setContent] = useState('');\r\n const [canSubmit, setCanSubmit] = useState(false);\r\n const editorRef = useRef<LexicalEditor>(null);\r\n const author = useCollabAuthorName();\r\n\r\n const onChange = useOnChange(setContent, setCanSubmit);\r\n\r\n const submitComment = () => {\r\n if (canSubmit) {\r\n submitAddComment(createComment(content, author), false, thread);\r\n const editor = editorRef.current;\r\n if (editor !== null) {\r\n editor.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);\r\n }\r\n }\r\n };\r\n\r\n return (\r\n <>\r\n <PlainTextEditor\r\n className=\"CommentPlugin_CommentsPanel_Editor\"\r\n autoFocus={false}\r\n onEscape={() => {\r\n return true;\r\n }}\r\n onChange={onChange}\r\n editorRef={editorRef}\r\n placeholder={placeholder}\r\n />\r\n <Button\r\n className=\"CommentPlugin_CommentsPanel_SendButton\"\r\n onClick={submitComment}\r\n disabled={!canSubmit}>\r\n <i className=\"send\" />\r\n </Button>\r\n </>\r\n );\r\n}\r\n\r\nfunction ShowDeleteCommentOrThreadDialog({\r\n commentOrThread,\r\n deleteCommentOrThread,\r\n onClose,\r\n thread = undefined,\r\n}: {\r\n commentOrThread: Comment | Thread;\r\n\r\n deleteCommentOrThread: (\r\n comment: Comment | Thread,\r\n // eslint-disable-next-line no-shadow\r\n thread?: Thread,\r\n ) => void;\r\n onClose: () => void;\r\n thread?: Thread;\r\n}): JSX.Element {\r\n return (\r\n <>\r\n Are you sure you want to delete this {commentOrThread.type}?\r\n <div className=\"Modal__content\">\r\n <Button\r\n onClick={() => {\r\n deleteCommentOrThread(commentOrThread, thread);\r\n onClose();\r\n }}>\r\n Delete\r\n </Button>{' '}\r\n <Button\r\n onClick={() => {\r\n onClose();\r\n }}>\r\n Cancel\r\n </Button>\r\n </div>\r\n </>\r\n );\r\n}\r\n\r\nfunction CommentsPanelListComment({\r\n comment,\r\n deleteComment,\r\n thread,\r\n rtf,\r\n}: {\r\n comment: Comment;\r\n deleteComment: (\r\n commentOrThread: Comment | Thread,\r\n // eslint-disable-next-line no-shadow\r\n thread?: Thread,\r\n ) => void;\r\n rtf: Intl.RelativeTimeFormat;\r\n thread?: Thread;\r\n}): JSX.Element {\r\n const seconds = Math.round((comment.timeStamp - performance.now()) / 1000);\r\n const minutes = Math.round(seconds / 60);\r\n const [modal, showModal] = useModal();\r\n\r\n return (\r\n <li className=\"CommentPlugin_CommentsPanel_List_Comment\">\r\n <div className=\"CommentPlugin_CommentsPanel_List_Details\">\r\n <span className=\"CommentPlugin_CommentsPanel_List_Comment_Author\">\r\n {comment.author}\r\n </span>\r\n <span className=\"CommentPlugin_CommentsPanel_List_Comment_Time\">\r\n · {seconds > -10 ? 'Just now' : rtf.format(minutes, 'minute')}\r\n </span>\r\n </div>\r\n <p\r\n className={\r\n comment.deleted ? 'CommentPlugin_CommentsPanel_DeletedComment' : ''\r\n }>\r\n {comment.content}\r\n </p>\r\n {!comment.deleted && (\r\n <>\r\n <Button\r\n onClick={() => {\r\n showModal('Delete Comment', (onClose) => (\r\n <ShowDeleteCommentOrThreadDialog\r\n commentOrThread={comment}\r\n deleteCommentOrThread={deleteComment}\r\n thread={thread}\r\n onClose={onClose}\r\n />\r\n ));\r\n }}\r\n className=\"CommentPlugin_CommentsPanel_List_DeleteButton\">\r\n <i className=\"delete\" />\r\n </Button>\r\n {modal}\r\n </>\r\n )}\r\n </li>\r\n );\r\n}\r\n\r\nfunction CommentsPanelList({\r\n activeIDs,\r\n comments,\r\n deleteCommentOrThread,\r\n listRef,\r\n submitAddComment,\r\n markNodeMap,\r\n}: {\r\n activeIDs: Array<string>;\r\n comments: Comments;\r\n deleteCommentOrThread: (\r\n commentOrThread: Comment | Thread,\r\n thread?: Thread,\r\n ) => void;\r\n listRef: {current: null | HTMLUListElement};\r\n markNodeMap: Map<string, Set<NodeKey>>;\r\n submitAddComment: (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n ) => void;\r\n}): JSX.Element {\r\n const [editor] = useLexicalComposerContext();\r\n const [counter, setCounter] = useState(0);\r\n const [modal, showModal] = useModal();\r\n const rtf = useMemo(\r\n () =>\r\n new Intl.RelativeTimeFormat('en', {\r\n localeMatcher: 'best fit',\r\n numeric: 'auto',\r\n style: 'short',\r\n }),\r\n [],\r\n );\r\n\r\n useEffect(() => {\r\n // Used to keep the time stamp up to date\r\n const id = setTimeout(() => {\r\n setCounter(counter + 1);\r\n }, 10000);\r\n\r\n return () => {\r\n clearTimeout(id);\r\n };\r\n }, [counter]);\r\n\r\n return (\r\n <ul className=\"CommentPlugin_CommentsPanel_List\" ref={listRef}>\r\n {comments.map((commentOrThread) => {\r\n const id = commentOrThread.id;\r\n if (commentOrThread.type === 'thread') {\r\n const handleClickThread = () => {\r\n const markNodeKeys = markNodeMap.get(id);\r\n if (\r\n markNodeKeys !== undefined &&\r\n (activeIDs === null || activeIDs.indexOf(id) === -1)\r\n ) {\r\n const activeElement = document.activeElement;\r\n // Move selection to the start of the mark, so that we\r\n // update the UI with the selected thread.\r\n editor.update(\r\n () => {\r\n const markNodeKey = Array.from(markNodeKeys)[0];\r\n const markNode = $getNodeByKey<MarkNode>(markNodeKey);\r\n if ($isMarkNode(markNode)) {\r\n markNode.selectStart();\r\n }\r\n },\r\n {\r\n onUpdate() {\r\n // Restore selection to the previous element\r\n if (activeElement !== null) {\r\n (activeElement as HTMLElement).focus();\r\n }\r\n },\r\n },\r\n );\r\n }\r\n };\r\n\r\n return (\r\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions\r\n <li\r\n key={id}\r\n onClick={handleClickThread}\r\n className={`CommentPlugin_CommentsPanel_List_Thread ${\r\n markNodeMap.has(id) ? 'interactive' : ''\r\n } ${activeIDs.indexOf(id) === -1 ? '' : 'active'}`}>\r\n <div className=\"CommentPlugin_CommentsPanel_List_Thread_QuoteBox\">\r\n <blockquote className=\"CommentPlugin_CommentsPanel_List_Thread_Quote\">\r\n {'> '}\r\n <span>{commentOrThread.quote}</span>\r\n </blockquote>\r\n {/* INTRODUCE DELETE THREAD HERE*/}\r\n <Button\r\n onClick={() => {\r\n showModal('Delete Thread', (onClose) => (\r\n <ShowDeleteCommentOrThreadDialog\r\n commentOrThread={commentOrThread}\r\n deleteCommentOrThread={deleteCommentOrThread}\r\n onClose={onClose}\r\n />\r\n ));\r\n }}\r\n className=\"CommentPlugin_CommentsPanel_List_DeleteButton\">\r\n <i className=\"delete\" />\r\n </Button>\r\n {modal}\r\n </div>\r\n <ul className=\"CommentPlugin_CommentsPanel_List_Thread_Comments\">\r\n {commentOrThread.comments.map((comment) => (\r\n <CommentsPanelListComment\r\n key={comment.id}\r\n comment={comment}\r\n deleteComment={deleteCommentOrThread}\r\n thread={commentOrThread}\r\n rtf={rtf}\r\n />\r\n ))}\r\n </ul>\r\n <div className=\"CommentPlugin_CommentsPanel_List_Thread_Editor\">\r\n <CommentsComposer\r\n submitAddComment={submitAddComment}\r\n thread={commentOrThread}\r\n placeholder=\"Reply to comment...\"\r\n />\r\n </div>\r\n </li>\r\n );\r\n }\r\n return (\r\n <CommentsPanelListComment\r\n key={id}\r\n comment={commentOrThread}\r\n deleteComment={deleteCommentOrThread}\r\n rtf={rtf}\r\n />\r\n );\r\n })}\r\n </ul>\r\n );\r\n}\r\n\r\nfunction CommentsPanel({\r\n activeIDs,\r\n deleteCommentOrThread,\r\n comments,\r\n submitAddComment,\r\n markNodeMap,\r\n}: {\r\n activeIDs: Array<string>;\r\n comments: Comments;\r\n deleteCommentOrThread: (\r\n commentOrThread: Comment | Thread,\r\n thread?: Thread,\r\n ) => void;\r\n markNodeMap: Map<string, Set<NodeKey>>;\r\n submitAddComment: (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n ) => void;\r\n}): JSX.Element {\r\n const listRef = useRef<HTMLUListElement>(null);\r\n const isEmpty = comments.length === 0;\r\n\r\n return (\r\n <div className=\"CommentPlugin_CommentsPanel\">\r\n <h2 className=\"CommentPlugin_CommentsPanel_Heading\">Comments</h2>\r\n {isEmpty ? (\r\n <div className=\"CommentPlugin_CommentsPanel_Empty\">No Comments</div>\r\n ) : (\r\n <CommentsPanelList\r\n activeIDs={activeIDs}\r\n comments={comments}\r\n deleteCommentOrThread={deleteCommentOrThread}\r\n listRef={listRef}\r\n submitAddComment={submitAddComment}\r\n markNodeMap={markNodeMap}\r\n />\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction useCollabAuthorName(): string {\r\n const collabContext = useCollaborationContext();\r\n const {yjsDocMap, name} = collabContext;\r\n return yjsDocMap.has('comments') ? name : 'Playground User';\r\n}\r\n\r\nexport default function CommentPlugin({\r\n providerFactory,\r\n}: {\r\n providerFactory?: (id: string, yjsDocMap: Map<string, Doc>) => Provider;\r\n}): JSX.Element {\r\n const collabContext = useCollaborationContext();\r\n const [editor] = useLexicalComposerContext();\r\n const commentStore = useMemo(() => new CommentStore(editor), [editor]);\r\n const comments = useCommentStore(commentStore);\r\n const markNodeMap = useMemo<Map<string, Set<NodeKey>>>(() => {\r\n return new Map();\r\n }, []);\r\n const [activeAnchorKey, setActiveAnchorKey] = useState<NodeKey | null>();\r\n const [activeIDs, setActiveIDs] = useState<Array<string>>([]);\r\n const [showCommentInput, setShowCommentInput] = useState(false);\r\n const [showComments, setShowComments] = useState(false);\r\n const {yjsDocMap} = collabContext;\r\n\r\n useEffect(() => {\r\n if (providerFactory) {\r\n const provider = providerFactory('comments', yjsDocMap);\r\n return commentStore.registerCollaboration(provider);\r\n }\r\n }, [commentStore, providerFactory, yjsDocMap]);\r\n\r\n const cancelAddComment = useCallback(() => {\r\n editor.update(() => {\r\n const selection = $getSelection();\r\n // Restore selection\r\n if (selection !== null) {\r\n selection.dirty = true;\r\n }\r\n });\r\n setShowCommentInput(false);\r\n }, [editor]);\r\n\r\n const deleteCommentOrThread = useCallback(\r\n (comment: Comment | Thread, thread?: Thread) => {\r\n if (comment.type === 'comment') {\r\n const deletionInfo = commentStore.deleteCommentOrThread(\r\n comment,\r\n thread,\r\n );\r\n if (!deletionInfo) {\r\n return;\r\n }\r\n const {markedComment, index} = deletionInfo;\r\n commentStore.addComment(markedComment, thread, index);\r\n } else {\r\n commentStore.deleteCommentOrThread(comment);\r\n // Remove ids from associated marks\r\n const id = thread !== undefined ? thread.id : comment.id;\r\n const markNodeKeys = markNodeMap.get(id);\r\n if (markNodeKeys !== undefined) {\r\n // Do async to avoid causing a React infinite loop\r\n setTimeout(() => {\r\n editor.update(() => {\r\n for (const key of markNodeKeys) {\r\n const node: null | MarkNode = $getNodeByKey(key);\r\n if ($isMarkNode(node)) {\r\n node.deleteID(id);\r\n if (node.getIDs().length === 0) {\r\n $unwrapMarkNode(node);\r\n }\r\n }\r\n }\r\n });\r\n });\r\n }\r\n }\r\n },\r\n [commentStore, editor, markNodeMap],\r\n );\r\n\r\n const submitAddComment = useCallback(\r\n (\r\n commentOrThread: Comment | Thread,\r\n isInlineComment: boolean,\r\n thread?: Thread,\r\n selection?: RangeSelection | null,\r\n ) => {\r\n commentStore.addComment(commentOrThread, thread);\r\n if (isInlineComment) {\r\n editor.update(() => {\r\n if ($isRangeSelection(selection)) {\r\n const isBackward = selection.isBackward();\r\n const id = commentOrThread.id;\r\n\r\n // Wrap content in a MarkNode\r\n $wrapSelectionInMarkNode(selection, isBackward, id);\r\n }\r\n });\r\n setShowCommentInput(false);\r\n }\r\n },\r\n [commentStore, editor],\r\n );\r\n\r\n useEffect(() => {\r\n const changedElems: Array<HTMLElement> = [];\r\n for (let i = 0; i < activeIDs.length; i++) {\r\n const id = activeIDs[i];\r\n const keys = markNodeMap.get(id);\r\n if (keys !== undefined) {\r\n for (const key of keys) {\r\n const elem = editor.getElementByKey(key);\r\n if (elem !== null) {\r\n elem.classList.add('selected');\r\n changedElems.push(elem);\r\n setShowComments(true);\r\n }\r\n }\r\n }\r\n }\r\n return () => {\r\n for (let i = 0; i < changedElems.length; i++) {\r\n const changedElem = changedElems[i];\r\n changedElem.classList.remove('selected');\r\n }\r\n };\r\n }, [activeIDs, editor, markNodeMap]);\r\n\r\n useEffect(() => {\r\n const markNodeKeysToIDs: Map<NodeKey, Array<string>> = new Map();\r\n\r\n return mergeRegister(\r\n registerNestedElementResolver<MarkNode>(\r\n editor,\r\n MarkNode,\r\n (from: MarkNode) => {\r\n return $createMarkNode(from.getIDs());\r\n },\r\n (from: MarkNode, to: MarkNode) => {\r\n // Merge the IDs\r\n const ids = from.getIDs();\r\n ids.forEach((id) => {\r\n to.addID(id);\r\n });\r\n },\r\n ),\r\n editor.registerMutationListener(MarkNode, (mutations) => {\r\n editor.getEditorState().read(() => {\r\n for (const [key, mutation] of mutations) {\r\n const node: null | MarkNode = $getNodeByKey(key);\r\n let ids: NodeKey[] = [];\r\n\r\n if (mutation === 'destroyed') {\r\n ids = markNodeKeysToIDs.get(key) || [];\r\n } else if ($isMarkNode(node)) {\r\n ids = node.getIDs();\r\n }\r\n\r\n for (let i = 0; i < ids.length; i++) {\r\n const id = ids[i];\r\n let markNodeKeys = markNodeMap.get(id);\r\n markNodeKeysToIDs.set(key, ids);\r\n\r\n if (mutation === 'destroyed') {\r\n if (markNodeKeys !== undefined) {\r\n markNodeKeys.delete(key);\r\n if (markNodeKeys.size === 0) {\r\n markNodeMap.delete(id);\r\n }\r\n }\r\n } else {\r\n if (markNodeKeys === undefined) {\r\n markNodeKeys = new Set();\r\n markNodeMap.set(id, markNodeKeys);\r\n }\r\n if (!markNodeKeys.has(key)) {\r\n markNodeKeys.add(key);\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }),\r\n editor.registerUpdateListener(({editorState, tags}) => {\r\n editorState.read(() => {\r\n const selection = $getSelection();\r\n let hasActiveIds = false;\r\n let hasAnchorKey = false;\r\n\r\n if ($isRangeSelection(selection)) {\r\n const anchorNode = selection.anchor.getNode();\r\n\r\n if ($isTextNode(anchorNode)) {\r\n const commentIDs = $getMarkIDs(\r\n anchorNode,\r\n selection.anchor.offset,\r\n );\r\n if (commentIDs !== null) {\r\n setActiveIDs(commentIDs);\r\n hasActiveIds = true;\r\n }\r\n if (!selection.isCollapsed()) {\r\n setActiveAnchorKey(anchorNode.getKey());\r\n hasAnchorKey = true;\r\n }\r\n }\r\n }\r\n if (!hasActiveIds) {\r\n setActiveIDs((_activeIds) =>\r\n _activeIds.length === 0 ? _activeIds : [],\r\n );\r\n }\r\n if (!hasAnchorKey) {\r\n setActiveAnchorKey(null);\r\n }\r\n if (!tags.has('collaboration') && $isRangeSelection(selection)) {\r\n setShowCommentInput(false);\r\n }\r\n });\r\n }),\r\n editor.registerCommand(\r\n INSERT_INLINE_COMMAND,\r\n () => {\r\n const domSelection = window.getSelection();\r\n if (domSelection !== null) {\r\n domSelection.removeAllRanges();\r\n }\r\n setShowCommentInput(true);\r\n return true;\r\n },\r\n COMMAND_PRIORITY_EDITOR,\r\n ),\r\n );\r\n }, [editor, markNodeMap]);\r\n\r\n const onAddComment = () => {\r\n editor.dispatchCommand(INSERT_INLINE_COMMAND, undefined);\r\n };\r\n\r\n return (\r\n <>\r\n {showCommentInput &&\r\n createPortal(\r\n <CommentInputBox\r\n editor={editor}\r\n cancelAddComment={cancelAddComment}\r\n submitAddComment={submitAddComment}\r\n />,\r\n document.body,\r\n )}\r\n {activeAnchorKey !== null &&\r\n activeAnchorKey !== undefined &&\r\n !showCommentInput &&\r\n createPortal(\r\n <AddCommentBox\r\n anchorKey={activeAnchorKey}\r\n editor={editor}\r\n onAddComment={onAddComment}\r\n />,\r\n document.body,\r\n )}\r\n {createPortal(\r\n <Button\r\n className={`CommentPlugin_ShowCommentsButton ${\r\n showComments ? 'active' : ''\r\n }`}\r\n onClick={() => setShowComments(!showComments)}\r\n title={showComments ? 'Hide Comments' : 'Show Comments'}>\r\n <i className=\"comments\" />\r\n </Button>,\r\n document.body,\r\n )}\r\n {showComments &&\r\n createPortal(\r\n <CommentsPanel\r\n comments={comments}\r\n submitAddComment={submitAddComment}\r\n deleteCommentOrThread={deleteCommentOrThread}\r\n activeIDs={activeIDs}\r\n markNodeMap={markNodeMap}\r\n />,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport './index.css';\r\n\r\nimport {$isCodeHighlightNode} from '@lexical/code';\r\nimport {$isLinkNode, TOGGLE_LINK_COMMAND} from '@lexical/link';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport {mergeRegister} from '@lexical/utils';\r\nimport {\r\n $getSelection,\r\n $isParagraphNode,\r\n $isRangeSelection,\r\n $isTextNode,\r\n COMMAND_PRIORITY_LOW,\r\n FORMAT_TEXT_COMMAND,\r\n LexicalEditor,\r\n SELECTION_CHANGE_COMMAND,\r\n} from 'lexical';\r\nimport {useCallback, useEffect, useRef, useState} from 'react';\r\nimport * as React from 'react';\r\nimport {createPortal} from 'react-dom';\r\n\r\nimport {getDOMRangeRect} from '../../utils/getDOMRangeRect';\r\nimport {getSelectedNode} from '../../utils/getSelectedNode';\r\nimport {setFloatingElemPosition} from '../../utils/setFloatingElemPosition';\r\nimport {INSERT_INLINE_COMMAND} from '../CommentPlugin';\r\n\r\nfunction TextFormatFloatingToolbar({\r\n editor,\r\n anchorElem,\r\n isLink,\r\n isBold,\r\n isItalic,\r\n isUnderline,\r\n isCode,\r\n isStrikethrough,\r\n isSubscript,\r\n isSuperscript,\r\n}: {\r\n editor: LexicalEditor;\r\n anchorElem: HTMLElement;\r\n isBold: boolean;\r\n isCode: boolean;\r\n isItalic: boolean;\r\n isLink: boolean;\r\n isStrikethrough: boolean;\r\n isSubscript: boolean;\r\n isSuperscript: boolean;\r\n isUnderline: boolean;\r\n}): JSX.Element {\r\n const popupCharStylesEditorRef = useRef<HTMLDivElement | null>(null);\r\n\r\n const insertLink = useCallback(() => {\r\n if (!isLink) {\r\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, 'https://');\r\n } else {\r\n editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);\r\n }\r\n }, [editor, isLink]);\r\n\r\n const insertComment = () => {\r\n editor.dispatchCommand(INSERT_INLINE_COMMAND, undefined);\r\n };\r\n\r\n function mouseMoveListener(e: MouseEvent) {\r\n if (\r\n popupCharStylesEditorRef?.current &&\r\n (e.buttons === 1 || e.buttons === 3)\r\n ) {\r\n if (popupCharStylesEditorRef.current.style.pointerEvents !== 'none') {\r\n const x = e.clientX;\r\n const y = e.clientY;\r\n const elementUnderMouse = document.elementFromPoint(x, y);\r\n\r\n if (!popupCharStylesEditorRef.current.contains(elementUnderMouse)) {\r\n // Mouse is not over the target element => not a normal click, but probably a drag\r\n popupCharStylesEditorRef.current.style.pointerEvents = 'none';\r\n }\r\n }\r\n }\r\n }\r\n function mouseUpListener(e: MouseEvent) {\r\n if (popupCharStylesEditorRef?.current) {\r\n if (popupCharStylesEditorRef.current.style.pointerEvents !== 'auto') {\r\n popupCharStylesEditorRef.current.style.pointerEvents = 'auto';\r\n }\r\n }\r\n }\r\n\r\n useEffect(() => {\r\n if (popupCharStylesEditorRef?.current) {\r\n document.addEventListener('mousemove', mouseMoveListener);\r\n document.addEventListener('mouseup', mouseUpListener);\r\n\r\n return () => {\r\n document.removeEventListener('mousemove', mouseMoveListener);\r\n document.removeEventListener('mouseup', mouseUpListener);\r\n };\r\n }\r\n }, [popupCharStylesEditorRef]);\r\n\r\n const updateTextFormatFloatingToolbar = useCallback(() => {\r\n const selection = $getSelection();\r\n\r\n const popupCharStylesEditorElem = popupCharStylesEditorRef.current;\r\n const nativeSelection = window.getSelection();\r\n\r\n if (popupCharStylesEditorElem === null) {\r\n return;\r\n }\r\n\r\n const rootElement = editor.getRootElement();\r\n if (\r\n selection !== null &&\r\n nativeSelection !== null &&\r\n !nativeSelection.isCollapsed &&\r\n rootElement !== null &&\r\n rootElement.contains(nativeSelection.anchorNode)\r\n ) {\r\n const rangeRect = getDOMRangeRect(nativeSelection, rootElement);\r\n\r\n setFloatingElemPosition(\r\n rangeRect,\r\n popupCharStylesEditorElem,\r\n anchorElem,\r\n isLink,\r\n );\r\n }\r\n }, [editor, anchorElem, isLink]);\r\n\r\n useEffect(() => {\r\n const scrollerElem = anchorElem.parentElement;\r\n\r\n const update = () => {\r\n editor.getEditorState().read(() => {\r\n updateTextFormatFloatingToolbar();\r\n });\r\n };\r\n\r\n window.addEventListener('resize', update);\r\n if (scrollerElem) {\r\n scrollerElem.addEventListener('scroll', update);\r\n }\r\n\r\n return () => {\r\n window.removeEventListener('resize', update);\r\n if (scrollerElem) {\r\n scrollerElem.removeEventListener('scroll', update);\r\n }\r\n };\r\n }, [editor, updateTextFormatFloatingToolbar, anchorElem]);\r\n\r\n useEffect(() => {\r\n editor.getEditorState().read(() => {\r\n updateTextFormatFloatingToolbar();\r\n });\r\n return mergeRegister(\r\n editor.registerUpdateListener(({editorState}) => {\r\n editorState.read(() => {\r\n updateTextFormatFloatingToolbar();\r\n });\r\n }),\r\n\r\n editor.registerCommand(\r\n SELECTION_CHANGE_COMMAND,\r\n () => {\r\n updateTextFormatFloatingToolbar();\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n );\r\n }, [editor, updateTextFormatFloatingToolbar]);\r\n\r\n return (\r\n <div ref={popupCharStylesEditorRef} className=\"floating-text-format-popup\">\r\n {editor.isEditable() && (\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold');\r\n }}\r\n className={'popup-item spaced ' + (isBold ? 'active' : '')}\r\n aria-label=\"Format text as bold\">\r\n <i className=\"format bold\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic');\r\n }}\r\n className={'popup-item spaced ' + (isItalic ? 'active' : '')}\r\n aria-label=\"Format text as italics\">\r\n <i className=\"format italic\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline');\r\n }}\r\n className={'popup-item spaced ' + (isUnderline ? 'active' : '')}\r\n aria-label=\"Format text to underlined\">\r\n <i className=\"format underline\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough');\r\n }}\r\n className={'popup-item spaced ' + (isStrikethrough ? 'active' : '')}\r\n aria-label=\"Format text with a strikethrough\">\r\n <i className=\"format strikethrough\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'subscript');\r\n }}\r\n className={'popup-item spaced ' + (isSubscript ? 'active' : '')}\r\n title=\"Subscript\"\r\n aria-label=\"Format Subscript\">\r\n <i className=\"format subscript\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'superscript');\r\n }}\r\n className={'popup-item spaced ' + (isSuperscript ? 'active' : '')}\r\n title=\"Superscript\"\r\n aria-label=\"Format Superscript\">\r\n <i className=\"format superscript\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code');\r\n }}\r\n className={'popup-item spaced ' + (isCode ? 'active' : '')}\r\n aria-label=\"Insert code block\">\r\n <i className=\"format code\" />\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={insertLink}\r\n className={'popup-item spaced ' + (isLink ? 'active' : '')}\r\n aria-label=\"Insert link\">\r\n <i className=\"format link\" />\r\n </button>\r\n </>\r\n )}\r\n <button\r\n type=\"button\"\r\n onClick={insertComment}\r\n className={'popup-item spaced insert-comment'}\r\n aria-label=\"Insert comment\">\r\n <i className=\"format add-comment\" />\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\nfunction useFloatingTextFormatToolbar(\r\n editor: LexicalEditor,\r\n anchorElem: HTMLElement,\r\n): JSX.Element | null {\r\n const [isText, setIsText] = useState(false);\r\n const [isLink, setIsLink] = useState(false);\r\n const [isBold, setIsBold] = useState(false);\r\n const [isItalic, setIsItalic] = useState(false);\r\n const [isUnderline, setIsUnderline] = useState(false);\r\n const [isStrikethrough, setIsStrikethrough] = useState(false);\r\n const [isSubscript, setIsSubscript] = useState(false);\r\n const [isSuperscript, setIsSuperscript] = useState(false);\r\n const [isCode, setIsCode] = useState(false);\r\n\r\n const updatePopup = useCallback(() => {\r\n editor.getEditorState().read(() => {\r\n // Should not to pop up the floating toolbar when using IME input\r\n if (editor.isComposing()) {\r\n return;\r\n }\r\n const selection = $getSelection();\r\n const nativeSelection = window.getSelection();\r\n const rootElement = editor.getRootElement();\r\n\r\n if (\r\n nativeSelection !== null &&\r\n (!$isRangeSelection(selection) ||\r\n rootElement === null ||\r\n !rootElement.contains(nativeSelection.anchorNode))\r\n ) {\r\n setIsText(false);\r\n return;\r\n }\r\n\r\n if (!$isRangeSelection(selection)) {\r\n return;\r\n }\r\n\r\n const node = getSelectedNode(selection);\r\n\r\n // Update text format\r\n setIsBold(selection.hasFormat('bold'));\r\n setIsItalic(selection.hasFormat('italic'));\r\n setIsUnderline(selection.hasFormat('underline'));\r\n setIsStrikethrough(selection.hasFormat('strikethrough'));\r\n setIsSubscript(selection.hasFormat('subscript'));\r\n setIsSuperscript(selection.hasFormat('superscript'));\r\n setIsCode(selection.hasFormat('code'));\r\n\r\n // Update links\r\n const parent = node.getParent();\r\n if ($isLinkNode(parent) || $isLinkNode(node)) {\r\n setIsLink(true);\r\n } else {\r\n setIsLink(false);\r\n }\r\n\r\n if (\r\n !$isCodeHighlightNode(selection.anchor.getNode()) &&\r\n selection.getTextContent() !== ''\r\n ) {\r\n setIsText($isTextNode(node) || $isParagraphNode(node));\r\n } else {\r\n setIsText(false);\r\n }\r\n\r\n const rawTextContent = selection.getTextContent().replace(/\\n/g, '');\r\n if (!selection.isCollapsed() && rawTextContent === '') {\r\n setIsText(false);\r\n return;\r\n }\r\n });\r\n }, [editor]);\r\n\r\n useEffect(() => {\r\n document.addEventListener('selectionchange', updatePopup);\r\n return () => {\r\n document.removeEventListener('selectionchange', updatePopup);\r\n };\r\n }, [updatePopup]);\r\n\r\n useEffect(() => {\r\n return mergeRegister(\r\n editor.registerUpdateListener(() => {\r\n updatePopup();\r\n }),\r\n editor.registerRootListener(() => {\r\n if (editor.getRootElement() === null) {\r\n setIsText(false);\r\n }\r\n }),\r\n );\r\n }, [editor, updatePopup]);\r\n\r\n if (!isText) {\r\n return null;\r\n }\r\n\r\n return createPortal(\r\n <TextFormatFloatingToolbar\r\n editor={editor}\r\n anchorElem={anchorElem}\r\n isLink={isLink}\r\n isBold={isBold}\r\n isItalic={isItalic}\r\n isStrikethrough={isStrikethrough}\r\n isSubscript={isSubscript}\r\n isSuperscript={isSuperscript}\r\n isUnderline={isUnderline}\r\n isCode={isCode}\r\n />,\r\n anchorElem,\r\n );\r\n}\r\n\r\nexport default function FloatingTextFormatToolbarPlugin({\r\n anchorElem = document.body,\r\n}: {\r\n anchorElem?: HTMLElement;\r\n}): JSX.Element | null {\r\n const [editor] = useLexicalComposerContext();\r\n return useFloatingTextFormatToolbar(editor, anchorElem);\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport './Select.css';\r\n\r\nimport * as React from 'react';\r\n\r\ntype SelectIntrinsicProps = JSX.IntrinsicElements['select'];\r\ninterface SelectProps extends SelectIntrinsicProps {\r\n label: string;\r\n}\r\n\r\nexport default function Select({\r\n children,\r\n label,\r\n className,\r\n ...other\r\n}: SelectProps): JSX.Element {\r\n return (\r\n <div className=\"Input__wrapper\">\r\n <label style={{marginTop: '-1em'}} className=\"Input__label\">\r\n {label}\r\n </label>\r\n <select {...other} className={className || 'select'}>\r\n {children}\r\n </select>\r\n </div>\r\n );\r\n}\r\n","/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\nimport type {Position} from './InlineImageNode';\r\nimport type {BaseSelection, LexicalEditor, NodeKey} from 'lexical';\r\n\r\nimport './InlineImageNode.css';\r\n\r\nimport {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';\r\nimport {LexicalNestedComposer} from '@lexical/react/LexicalNestedComposer';\r\nimport {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';\r\nimport {useLexicalNodeSelection} from '@lexical/react/useLexicalNodeSelection';\r\nimport {mergeRegister} from '@lexical/utils';\r\nimport {\r\n $getNodeByKey,\r\n $getSelection,\r\n $isNodeSelection,\r\n $setSelection,\r\n CLICK_COMMAND,\r\n COMMAND_PRIORITY_LOW,\r\n DRAGSTART_COMMAND,\r\n KEY_BACKSPACE_COMMAND,\r\n KEY_DELETE_COMMAND,\r\n KEY_ENTER_COMMAND,\r\n KEY_ESCAPE_COMMAND,\r\n SELECTION_CHANGE_COMMAND,\r\n} from 'lexical';\r\nimport * as React from 'react';\r\nimport {Suspense, useCallback, useEffect, useRef, useState} from 'react';\r\n\r\nimport useModal from '../hooks/useModal';\r\nimport FloatingTextFormatToolbarPlugin from '../plugins/FloatingTextFormatToolbarPlugin/index';\r\nimport LinkPlugin from '../plugins/LinkPlugin';\r\nimport Button from '../ui/Button';\r\nimport ContentEditable from '../ui/ContentEditable';\r\nimport {DialogActions} from '../ui/Dialog';\r\nimport Placeholder from '../ui/Placeholder';\r\nimport Select from '../ui/Select';\r\nimport TextInput from '../ui/TextInput';\r\nimport {$isInlineImageNode, InlineImageNode} from './InlineImageNode';\r\n\r\nconst imageCache = new Set();\r\n\r\nfunction useSuspenseImage(src: string) {\r\n if (!imageCache.has(src)) {\r\n throw new Promise((resolve) => {\r\n const img = new Image();\r\n img.src = src;\r\n img.onload = () => {\r\n imageCache.add(src);\r\n resolve(null);\r\n };\r\n });\r\n }\r\n}\r\n\r\nfunction LazyImage({\r\n altText,\r\n className,\r\n imageRef,\r\n src,\r\n width,\r\n height,\r\n position,\r\n}: {\r\n altText: string;\r\n className: string | null;\r\n height: 'inherit' | number;\r\n imageRef: {current: null | HTMLImageElement};\r\n src: string;\r\n width: 'inherit' | number;\r\n position: Position;\r\n}): JSX.Element {\r\n useSuspenseImage(src);\r\n return (\r\n <img\r\n className={className || undefined}\r\n src={src}\r\n alt={altText}\r\n ref={imageRef}\r\n data-position={position}\r\n style={{\r\n display: 'block',\r\n height,\r\n width,\r\n }}\r\n draggable=\"false\"\r\n />\r\n );\r\n}\r\n\r\nexport function UpdateInlineImageDialog({\r\n activeEditor,\r\n nodeKey,\r\n onClose,\r\n}: {\r\n activeEditor: LexicalEditor;\r\n nodeKey: NodeKey;\r\n onClose: () => void;\r\n}): JSX.Element {\r\n const editorState = activeEditor.getEditorState();\r\n const node = editorState.read(\r\n () => $getNodeByKey(nodeKey) as InlineImageNode,\r\n );\r\n const [altText, setAltText] = useState(node.getAltText());\r\n const [showCaption, setShowCaption] = useState(node.getShowCaption());\r\n const [position, setPosition] = useState<Position>(node.getPosition());\r\n\r\n const handleShowCaptionChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n setShowCaption(e.target.checked);\r\n };\r\n\r\n const handlePositionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\r\n setPosition(e.target.value as Position);\r\n };\r\n\r\n const handleOnConfirm = () => {\r\n const payload = {altText, position, showCaption};\r\n if (node) {\r\n activeEditor.update(() => {\r\n node.update(payload);\r\n });\r\n }\r\n onClose();\r\n };\r\n\r\n return (\r\n <>\r\n <div style={{marginBottom: '1em'}}>\r\n <TextInput\r\n label=\"Alt Text\"\r\n placeholder=\"Descriptive alternative text\"\r\n onChange={setAltText}\r\n value={altText}\r\n data-test-id=\"image-modal-alt-text-input\"\r\n />\r\n </div>\r\n\r\n <Select\r\n style={{marginBottom: '1em', width: '208px'}}\r\n value={position}\r\n label=\"Position\"\r\n name=\"position\"\r\n id=\"position-select\"\r\n onChange={handlePositionChange}>\r\n <option value=\"left\">Left</option>\r\n <option value=\"right\">Right</option>\r\n <option value=\"full\">Full Width</option>\r\n </Select>\r\n\r\n <div className=\"Input__wrapper\">\r\n <input\r\n id=\"caption\"\r\n type=\"checkbox\"\r\n checked={showCaption}\r\n onChange={handleShowCaptionChange}\r\n />\r\n <label htmlFor=\"caption\">Show Caption</label>\r\n </div>\r\n\r\n <DialogActions>\r\n <Button\r\n data-test-id=\"image-modal-file-upload-btn\"\r\n onClick={() => handleOnConfirm()}>\r\n Confirm\r\n </Button>\r\n </DialogActions>\r\n </>\r\n );\r\n}\r\n\r\nexport default function InlineImageComponent({\r\n src,\r\n altText,\r\n nodeKey,\r\n width,\r\n height,\r\n showCaption,\r\n caption,\r\n position,\r\n}: {\r\n altText: string;\r\n caption: LexicalEditor;\r\n height: 'inherit' | number;\r\n nodeKey: NodeKey;\r\n showCaption: boolean;\r\n src: string;\r\n width: 'inherit' | number;\r\n position: Position;\r\n}): JSX.Element {\r\n const [modal, showModal] = useModal();\r\n const imageRef = useRef<null | HTMLImageElement>(null);\r\n const buttonRef = useRef<HTMLButtonElement | null>(null);\r\n const [isSelected, setSelected, clearSelection] =\r\n useLexicalNodeSelection(nodeKey);\r\n const [editor] = useLexicalComposerContext();\r\n const [selection, setSelection] = useState<BaseSelection | null>(null);\r\n const activeEditorRef = useRef<LexicalEditor | null>(null);\r\n\r\n const onDelete = useCallback(\r\n (payload: KeyboardEvent) => {\r\n if (isSelected && $isNodeSelection($getSelection())) {\r\n const event: KeyboardEvent = payload;\r\n event.preventDefault();\r\n const node = $getNodeByKey(nodeKey);\r\n if ($isInlineImageNode(node)) {\r\n node.remove();\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n [isSelected, nodeKey],\r\n );\r\n\r\n const onEnter = useCallback(\r\n (event: KeyboardEvent) => {\r\n const latestSelection = $getSelection();\r\n const buttonElem = buttonRef.current;\r\n if (\r\n isSelected &&\r\n $isNodeSelection(latestSelection) &&\r\n latestSelection.getNodes().length === 1\r\n ) {\r\n if (showCaption) {\r\n // Move focus into nested editor\r\n $setSelection(null);\r\n event.preventDefault();\r\n caption.focus();\r\n return true;\r\n } else if (\r\n buttonElem !== null &&\r\n buttonElem !== document.activeElement\r\n ) {\r\n event.preventDefault();\r\n buttonElem.focus();\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n [caption, isSelected, showCaption],\r\n );\r\n\r\n const onEscape = useCallback(\r\n (event: KeyboardEvent) => {\r\n if (\r\n activeEditorRef.current === caption ||\r\n buttonRef.current === event.target\r\n ) {\r\n $setSelection(null);\r\n editor.update(() => {\r\n setSelected(true);\r\n const parentRootElement = editor.getRootElement();\r\n if (parentRootElement !== null) {\r\n parentRootElement.focus();\r\n }\r\n });\r\n return true;\r\n }\r\n return false;\r\n },\r\n [caption, editor, setSelected],\r\n );\r\n\r\n useEffect(() => {\r\n let isMounted = true;\r\n const unregister = mergeRegister(\r\n editor.registerUpdateListener(({editorState}) => {\r\n if (isMounted) {\r\n setSelection(editorState.read(() => $getSelection()));\r\n }\r\n }),\r\n editor.registerCommand(\r\n SELECTION_CHANGE_COMMAND,\r\n (_, activeEditor) => {\r\n activeEditorRef.current = activeEditor;\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand<MouseEvent>(\r\n CLICK_COMMAND,\r\n (payload) => {\r\n const event = payload;\r\n if (event.target === imageRef.current) {\r\n if (event.shiftKey) {\r\n setSelected(!isSelected);\r\n } else {\r\n clearSelection();\r\n setSelected(true);\r\n }\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n DRAGSTART_COMMAND,\r\n (event) => {\r\n if (event.target === imageRef.current) {\r\n // TODO This is just a temporary workaround for FF to behave like other browsers.\r\n // Ideally, this handles drag & drop too (and all browsers).\r\n event.preventDefault();\r\n return true;\r\n }\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_DELETE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_BACKSPACE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW),\r\n editor.registerCommand(\r\n KEY_ESCAPE_COMMAND,\r\n onEscape,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n );\r\n return () => {\r\n isMounted = false;\r\n unregister();\r\n };\r\n }, [\r\n clearSelection,\r\n editor,\r\n isSelected,\r\n nodeKey,\r\n onDelete,\r\n onEnter,\r\n onEscape,\r\n setSelected,\r\n ]);\r\n\r\n const draggable = isSelected && $isNodeSelection(selection);\r\n const isFocused = isSelected;\r\n return (\r\n <Suspense fallback={null}>\r\n <>\r\n <div draggable={draggable}>\r\n <button\r\n className=\"image-edit-button\"\r\n ref={buttonRef}\r\n onClick={() => {\r\n showModal('Update Inline Image', (onClose) => (\r\n <UpdateInlineImageDialog\r\n activeEditor={editor}\r\n nodeKey={nodeKey}\r\n onClose={onClose}\r\n />\r\n ));\r\n }}>\r\n Edit\r\n </button>\r\n <LazyImage\r\n className={\r\n isFocused\r\n ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}`\r\n : null\r\n }\r\n src={src}\r\n altText={altText}\r\n imageRef={imageRef}\r\n width={width}\r\n height={height}\r\n position={position}\r\n />\r\n </div>\r\n {showCaption && (\r\n <div className=\"image-caption-container\">\r\n <LexicalNestedComposer initialEditor={caption}>\r\n <AutoFocusPlugin />\r\n <LinkPlugin />\r\n <FloatingTextFormatToolbarPlugin />\r\n <RichTextPlugin\r\n contentEditable={\r\n <ContentEditable className=\"InlineImageNode__contentEditable\" />\r\n }\r\n placeholder={\r\n <Placeholder className=\"InlineImageNode__placeholder\">\r\n Enter a caption...\r\n </Placeholder>\r\n }\r\n ErrorBoundary={LexicalErrorBoundary}\r\n />\r\n </LexicalNestedComposer>\r\n </div>\r\n )}\r\n </>\r\n {modal}\r\n </Suspense>\r\n );\r\n}\r\n"],"names":["getDOMRangeRect","nativeSelection","rootElement","domRange","rect","inner","VERTICAL_GAP","HORIZONTAL_OFFSET","setFloatingElemPosition","targetRect","floatingElem","anchorElem","isLink","verticalGap","horizontalOffset","scrollerElem","floatingElemRect","anchorElementRect","editorScrollerRect","top","left","baseTheme","INSERT_INLINE_COMMAND","createCommand","TextFormatFloatingToolbar","editor","isBold","isItalic","isUnderline","isCode","isStrikethrough","isSubscript","isSuperscript","popupCharStylesEditorRef","useRef","insertLink","useCallback","TOGGLE_LINK_COMMAND","insertComment","mouseMoveListener","e","x","y","elementUnderMouse","mouseUpListener","useEffect","updateTextFormatFloatingToolbar","selection","$getSelection","popupCharStylesEditorElem","rangeRect","update","mergeRegister","editorState","SELECTION_CHANGE_COMMAND","COMMAND_PRIORITY_LOW","jsxs","Fragment","jsx","FORMAT_TEXT_COMMAND","useFloatingTextFormatToolbar","isText","setIsText","useState","setIsLink","setIsBold","setIsItalic","setIsUnderline","setIsStrikethrough","setIsSubscript","setIsSuperscript","setIsCode","updatePopup","$isRangeSelection","node","getSelectedNode","parent","$isLinkNode","$isCodeHighlightNode","$isTextNode","$isParagraphNode","rawTextContent","createPortal","FloatingTextFormatToolbarPlugin","useLexicalComposerContext","Select","children","label","className","other","imageCache","useSuspenseImage","src","resolve","img","LazyImage","altText","imageRef","width","height","position","UpdateInlineImageDialog","activeEditor","nodeKey","onClose","$getNodeByKey","setAltText","showCaption","setShowCaption","setPosition","handleShowCaptionChange","handlePositionChange","handleOnConfirm","payload","TextInput","DialogActions","Button","InlineImageComponent","caption","modal","showModal","useModal","buttonRef","isSelected","setSelected","clearSelection","useLexicalNodeSelection","setSelection","activeEditorRef","onDelete","$isNodeSelection","$isInlineImageNode","onEnter","event","latestSelection","buttonElem","$setSelection","onEscape","parentRootElement","isMounted","unregister","_","CLICK_COMMAND","DRAGSTART_COMMAND","KEY_DELETE_COMMAND","KEY_BACKSPACE_COMMAND","KEY_ENTER_COMMAND","KEY_ESCAPE_COMMAND","draggable","isFocused","Suspense","LexicalNestedComposer","AutoFocusPlugin","LinkPlugin","RichTextPlugin","ContentEditable","Placeholder","LexicalErrorBoundary"],"mappings":"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"}
|
package/dist/{LexicalNestedComposer.esm-6289c7f2.js → LexicalNestedComposer.esm-9f194204.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as u, G as y, H as w } from "./index-
|
|
1
|
+
import { E as u, G as y, H as w } from "./index-7c78a104.js";
|
|
2
2
|
import * as M from "react";
|
|
3
3
|
import { useRef as O, useContext as D, useMemo as L, useEffect as m } from "react";
|
|
4
4
|
function K({
|
|
@@ -106,4 +106,4 @@ const N = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
|
106
106
|
export {
|
|
107
107
|
W as L
|
|
108
108
|
};
|
|
109
|
-
//# sourceMappingURL=LexicalNestedComposer.esm-
|
|
109
|
+
//# sourceMappingURL=LexicalNestedComposer.esm-9f194204.js.map
|
package/dist/{LexicalNestedComposer.esm-6289c7f2.js.map → LexicalNestedComposer.esm-9f194204.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LexicalNestedComposer.esm-6289c7f2.js","sources":["../node_modules/@lexical/react/LexicalNestedComposer.dev.esm.js","../node_modules/@lexical/react/LexicalNestedComposer.prod.esm.js","../node_modules/@lexical/react/LexicalNestedComposer.esm.js"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport { useCollaborationContext } from '@lexical/react/LexicalCollaborationContext';\nimport { LexicalComposerContext, createLexicalComposerContext } from '@lexical/react/LexicalComposerContext';\nimport * as React from 'react';\nimport { useRef, useContext, useMemo, useEffect } from 'react';\n\n/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\nfunction LexicalNestedComposer({\n initialEditor,\n children,\n initialNodes,\n initialTheme,\n skipCollabChecks\n}) {\n const wasCollabPreviouslyReadyRef = useRef(false);\n const parentContext = useContext(LexicalComposerContext);\n if (parentContext == null) {\n {\n throw Error(`Unexpected parent context null on a nested composer`);\n }\n }\n const [parentEditor, {\n getTheme: getParentTheme\n }] = parentContext;\n const composerContext = useMemo(() => {\n const composerTheme = initialTheme || getParentTheme() || undefined;\n const context = createLexicalComposerContext(parentContext, composerTheme);\n if (composerTheme !== undefined) {\n initialEditor._config.theme = composerTheme;\n }\n initialEditor._parentEditor = parentEditor;\n if (!initialNodes) {\n const parentNodes = initialEditor._nodes = new Map(parentEditor._nodes);\n for (const [type, entry] of parentNodes) {\n initialEditor._nodes.set(type, {\n exportDOM: entry.exportDOM,\n klass: entry.klass,\n replace: entry.replace,\n replaceWithKlass: entry.replaceWithKlass,\n transforms: new Set()\n });\n }\n } else {\n for (let klass of initialNodes) {\n let replace = null;\n let replaceWithKlass = null;\n if (typeof klass !== 'function') {\n const options = klass;\n klass = options.replace;\n replace = options.with;\n replaceWithKlass = options.withKlass || null;\n }\n const registeredKlass = initialEditor._nodes.get(klass.getType());\n initialEditor._nodes.set(klass.getType(), {\n exportDOM: registeredKlass ? registeredKlass.exportDOM : undefined,\n klass,\n replace,\n replaceWithKlass,\n transforms: new Set()\n });\n }\n }\n initialEditor._config.namespace = parentEditor._config.namespace;\n initialEditor._editable = parentEditor._editable;\n return [initialEditor, context];\n },\n // We only do this for init\n // eslint-disable-next-line react-hooks/exhaustive-deps\n []);\n\n // If collaboration is enabled, make sure we don't render the children until the collaboration subdocument is ready.\n const {\n isCollabActive,\n yjsDocMap\n } = useCollaborationContext();\n const isCollabReady = skipCollabChecks || wasCollabPreviouslyReadyRef.current || yjsDocMap.has(initialEditor.getKey());\n useEffect(() => {\n if (isCollabReady) {\n wasCollabPreviouslyReadyRef.current = true;\n }\n }, [isCollabReady]);\n\n // Update `isEditable` state of nested editor in response to the same change on parent editor.\n useEffect(() => {\n return parentEditor.registerEditableListener(editable => {\n initialEditor.setEditable(editable);\n });\n }, [initialEditor, parentEditor]);\n return /*#__PURE__*/React.createElement(LexicalComposerContext.Provider, {\n value: composerContext\n }, !isCollabActive || isCollabReady ? children : null);\n}\n\nexport { LexicalNestedComposer };\n","/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport{useCollaborationContext as e}from\"@lexical/react/LexicalCollaborationContext\";import{LexicalComposerContext as t,createLexicalComposerContext as r}from\"@lexical/react/LexicalComposerContext\";import*as o from\"react\";import{useRef as n,useContext as i,useMemo as l,useEffect as a}from\"react\";var s=function(e){const t=new URLSearchParams;t.append(\"code\",e);for(let e=1;e<arguments.length;e++)t.append(\"v\",arguments[e]);throw Error(`Minified Lexical error #${e}; visit https://lexical.dev/docs/error?${t} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)};function c({initialEditor:c,children:p,initialNodes:d,initialTheme:f,skipCollabChecks:m}){const h=n(!1),u=i(t);null==u&&s(9);const[x,{getTheme:g}]=u,v=l((()=>{const e=f||g()||void 0,t=r(u,e);if(void 0!==e&&(c._config.theme=e),c._parentEditor=x,d)for(let e of d){let t=null,r=null;if(\"function\"!=typeof e){const o=e;e=o.replace,t=o.with,r=o.withKlass||null}const o=c._nodes.get(e.getType());c._nodes.set(e.getType(),{exportDOM:o?o.exportDOM:void 0,klass:e,replace:t,replaceWithKlass:r,transforms:new Set})}else{const e=c._nodes=new Map(x._nodes);for(const[t,r]of e)c._nodes.set(t,{exportDOM:r.exportDOM,klass:r.klass,replace:r.replace,replaceWithKlass:r.replaceWithKlass,transforms:new Set})}return c._config.namespace=x._config.namespace,c._editable=x._editable,[c,t]}),[]),{isCollabActive:_,yjsDocMap:w}=e(),b=m||h.current||w.has(c.getKey());return a((()=>{b&&(h.current=!0)}),[b]),a((()=>x.registerEditableListener((e=>{c.setEditable(e)}))),[c,x]),o.createElement(t.Provider,{value:v},!_||b?p:null)}export{c as LexicalNestedComposer};\n","/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport * as modDev from './LexicalNestedComposer.dev.esm.js';\nimport * as modProd from './LexicalNestedComposer.prod.esm.js';\nconst mod = process.env.NODE_ENV === 'development' ? modDev : modProd;\nexport const LexicalNestedComposer = mod.LexicalNestedComposer;"],"names":["LexicalNestedComposer","initialEditor","children","initialNodes","initialTheme","skipCollabChecks","wasCollabPreviouslyReadyRef","useRef","parentContext","useContext","LexicalComposerContext","parentEditor","getParentTheme","composerContext","useMemo","composerTheme","context","createLexicalComposerContext","klass","replace","replaceWithKlass","options","registeredKlass","parentNodes","type","entry","isCollabActive","yjsDocMap","useCollaborationContext","isCollabReady","useEffect","editable","React","s","t","e","c","p","d","f","m","h","n","u","i","x","g","v","l","r","o","_","w","b","a","mod","modDev","modProd"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
1
|
+
{"version":3,"file":"LexicalNestedComposer.esm-9f194204.js","sources":["../node_modules/@lexical/react/LexicalNestedComposer.dev.esm.js","../node_modules/@lexical/react/LexicalNestedComposer.prod.esm.js","../node_modules/@lexical/react/LexicalNestedComposer.esm.js"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport { useCollaborationContext } from '@lexical/react/LexicalCollaborationContext';\nimport { LexicalComposerContext, createLexicalComposerContext } from '@lexical/react/LexicalComposerContext';\nimport * as React from 'react';\nimport { useRef, useContext, useMemo, useEffect } from 'react';\n\n/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\nfunction LexicalNestedComposer({\n initialEditor,\n children,\n initialNodes,\n initialTheme,\n skipCollabChecks\n}) {\n const wasCollabPreviouslyReadyRef = useRef(false);\n const parentContext = useContext(LexicalComposerContext);\n if (parentContext == null) {\n {\n throw Error(`Unexpected parent context null on a nested composer`);\n }\n }\n const [parentEditor, {\n getTheme: getParentTheme\n }] = parentContext;\n const composerContext = useMemo(() => {\n const composerTheme = initialTheme || getParentTheme() || undefined;\n const context = createLexicalComposerContext(parentContext, composerTheme);\n if (composerTheme !== undefined) {\n initialEditor._config.theme = composerTheme;\n }\n initialEditor._parentEditor = parentEditor;\n if (!initialNodes) {\n const parentNodes = initialEditor._nodes = new Map(parentEditor._nodes);\n for (const [type, entry] of parentNodes) {\n initialEditor._nodes.set(type, {\n exportDOM: entry.exportDOM,\n klass: entry.klass,\n replace: entry.replace,\n replaceWithKlass: entry.replaceWithKlass,\n transforms: new Set()\n });\n }\n } else {\n for (let klass of initialNodes) {\n let replace = null;\n let replaceWithKlass = null;\n if (typeof klass !== 'function') {\n const options = klass;\n klass = options.replace;\n replace = options.with;\n replaceWithKlass = options.withKlass || null;\n }\n const registeredKlass = initialEditor._nodes.get(klass.getType());\n initialEditor._nodes.set(klass.getType(), {\n exportDOM: registeredKlass ? registeredKlass.exportDOM : undefined,\n klass,\n replace,\n replaceWithKlass,\n transforms: new Set()\n });\n }\n }\n initialEditor._config.namespace = parentEditor._config.namespace;\n initialEditor._editable = parentEditor._editable;\n return [initialEditor, context];\n },\n // We only do this for init\n // eslint-disable-next-line react-hooks/exhaustive-deps\n []);\n\n // If collaboration is enabled, make sure we don't render the children until the collaboration subdocument is ready.\n const {\n isCollabActive,\n yjsDocMap\n } = useCollaborationContext();\n const isCollabReady = skipCollabChecks || wasCollabPreviouslyReadyRef.current || yjsDocMap.has(initialEditor.getKey());\n useEffect(() => {\n if (isCollabReady) {\n wasCollabPreviouslyReadyRef.current = true;\n }\n }, [isCollabReady]);\n\n // Update `isEditable` state of nested editor in response to the same change on parent editor.\n useEffect(() => {\n return parentEditor.registerEditableListener(editable => {\n initialEditor.setEditable(editable);\n });\n }, [initialEditor, parentEditor]);\n return /*#__PURE__*/React.createElement(LexicalComposerContext.Provider, {\n value: composerContext\n }, !isCollabActive || isCollabReady ? children : null);\n}\n\nexport { LexicalNestedComposer };\n","/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport{useCollaborationContext as e}from\"@lexical/react/LexicalCollaborationContext\";import{LexicalComposerContext as t,createLexicalComposerContext as r}from\"@lexical/react/LexicalComposerContext\";import*as o from\"react\";import{useRef as n,useContext as i,useMemo as l,useEffect as a}from\"react\";var s=function(e){const t=new URLSearchParams;t.append(\"code\",e);for(let e=1;e<arguments.length;e++)t.append(\"v\",arguments[e]);throw Error(`Minified Lexical error #${e}; visit https://lexical.dev/docs/error?${t} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)};function c({initialEditor:c,children:p,initialNodes:d,initialTheme:f,skipCollabChecks:m}){const h=n(!1),u=i(t);null==u&&s(9);const[x,{getTheme:g}]=u,v=l((()=>{const e=f||g()||void 0,t=r(u,e);if(void 0!==e&&(c._config.theme=e),c._parentEditor=x,d)for(let e of d){let t=null,r=null;if(\"function\"!=typeof e){const o=e;e=o.replace,t=o.with,r=o.withKlass||null}const o=c._nodes.get(e.getType());c._nodes.set(e.getType(),{exportDOM:o?o.exportDOM:void 0,klass:e,replace:t,replaceWithKlass:r,transforms:new Set})}else{const e=c._nodes=new Map(x._nodes);for(const[t,r]of e)c._nodes.set(t,{exportDOM:r.exportDOM,klass:r.klass,replace:r.replace,replaceWithKlass:r.replaceWithKlass,transforms:new Set})}return c._config.namespace=x._config.namespace,c._editable=x._editable,[c,t]}),[]),{isCollabActive:_,yjsDocMap:w}=e(),b=m||h.current||w.has(c.getKey());return a((()=>{b&&(h.current=!0)}),[b]),a((()=>x.registerEditableListener((e=>{c.setEditable(e)}))),[c,x]),o.createElement(t.Provider,{value:v},!_||b?p:null)}export{c as LexicalNestedComposer};\n","/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport * as modDev from './LexicalNestedComposer.dev.esm.js';\nimport * as modProd from './LexicalNestedComposer.prod.esm.js';\nconst mod = process.env.NODE_ENV === 'development' ? modDev : modProd;\nexport const LexicalNestedComposer = mod.LexicalNestedComposer;"],"names":["LexicalNestedComposer","initialEditor","children","initialNodes","initialTheme","skipCollabChecks","wasCollabPreviouslyReadyRef","useRef","parentContext","useContext","LexicalComposerContext","parentEditor","getParentTheme","composerContext","useMemo","composerTheme","context","createLexicalComposerContext","klass","replace","replaceWithKlass","options","registeredKlass","parentNodes","type","entry","isCollabActive","yjsDocMap","useCollaborationContext","isCollabReady","useEffect","editable","React","s","t","e","c","p","d","f","m","h","n","u","i","x","g","v","l","r","o","_","w","b","a","mod","modDev","modProd"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as b, u as S, $ as g, b as v, c as P, I as j, m as D, g as E, C as h, K as I, h as M, j as t, B as $, H as A, J as O, M as R } from "./index-
|
|
1
|
+
import { a as b, u as S, $ as g, b as v, c as P, I as j, m as D, g as E, C as h, K as I, h as M, j as t, B as $, H as A, J as O, M as R } from "./index-7c78a104.js";
|
|
2
2
|
import { useMemo as L, useState as V, useRef as k, useCallback as y, useEffect as B } from "react";
|
|
3
3
|
import "react-dom";
|
|
4
4
|
function T(s) {
|
|
@@ -166,4 +166,4 @@ function H({
|
|
|
166
166
|
export {
|
|
167
167
|
H as default
|
|
168
168
|
};
|
|
169
|
-
//# sourceMappingURL=PollComponent-
|
|
169
|
+
//# sourceMappingURL=PollComponent-008dc9fe.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PollComponent-8d21e358.js","sources":["../src/lexical/nodes/PollComponent.tsx"],"sourcesContent":["/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {Option, Options, PollNode} from './PollNode';\r\n\r\nimport './PollNode.css';\r\n\r\nimport {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport {useLexicalNodeSelection} from '@lexical/react/useLexicalNodeSelection';\r\nimport {mergeRegister} from '@lexical/utils';\r\nimport {\r\n $getNodeByKey,\r\n $getSelection,\r\n $isNodeSelection,\r\n BaseSelection,\r\n CLICK_COMMAND,\r\n COMMAND_PRIORITY_LOW,\r\n KEY_BACKSPACE_COMMAND,\r\n KEY_DELETE_COMMAND,\r\n NodeKey,\r\n} from 'lexical';\r\nimport * as React from 'react';\r\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\r\n\r\nimport Button from '../ui/Button';\r\nimport joinClasses from '../utils/joinClasses';\r\nimport {$isPollNode, createPollOption} from './PollNode';\r\n\r\nfunction getTotalVotes(options: Options): number {\r\n return options.reduce((totalVotes, next) => {\r\n return totalVotes + next.votes.length;\r\n }, 0);\r\n}\r\n\r\nfunction PollOptionComponent({\r\n option,\r\n index,\r\n options,\r\n totalVotes,\r\n withPollNode,\r\n}: {\r\n index: number;\r\n option: Option;\r\n options: Options;\r\n totalVotes: number;\r\n withPollNode: (\r\n cb: (pollNode: PollNode) => void,\r\n onSelect?: () => void,\r\n ) => void;\r\n}): JSX.Element {\r\n const {clientID} = useCollaborationContext();\r\n const checkboxRef = useRef(null);\r\n const votesArray = option.votes;\r\n const checkedIndex = votesArray.indexOf(clientID);\r\n const checked = checkedIndex !== -1;\r\n const votes = votesArray.length;\r\n const text = option.text;\r\n\r\n return (\r\n <div className=\"PollNode__optionContainer\">\r\n <div\r\n className={joinClasses(\r\n 'PollNode__optionCheckboxWrapper',\r\n checked && 'PollNode__optionCheckboxChecked',\r\n )}>\r\n <input\r\n ref={checkboxRef}\r\n className=\"PollNode__optionCheckbox\"\r\n type=\"checkbox\"\r\n onChange={(e) => {\r\n withPollNode((node) => {\r\n node.toggleVote(option, clientID);\r\n });\r\n }}\r\n checked={checked}\r\n />\r\n </div>\r\n <div className=\"PollNode__optionInputWrapper\">\r\n <div\r\n className=\"PollNode__optionInputVotes\"\r\n style={{width: `${votes === 0 ? 0 : (votes / totalVotes) * 100}%`}}\r\n />\r\n <span className=\"PollNode__optionInputVotesCount\">\r\n {votes > 0 && (votes === 1 ? '1 vote' : `${votes} votes`)}\r\n </span>\r\n <input\r\n className=\"PollNode__optionInput\"\r\n type=\"text\"\r\n value={text}\r\n onChange={(e) => {\r\n const target = e.target;\r\n const value = target.value;\r\n const selectionStart = target.selectionStart;\r\n const selectionEnd = target.selectionEnd;\r\n withPollNode(\r\n (node) => {\r\n node.setOptionText(option, value);\r\n },\r\n () => {\r\n target.selectionStart = selectionStart;\r\n target.selectionEnd = selectionEnd;\r\n },\r\n );\r\n }}\r\n placeholder={`Option ${index + 1}`}\r\n />\r\n </div>\r\n <button\r\n disabled={options.length < 3}\r\n className={joinClasses(\r\n 'PollNode__optionDelete',\r\n options.length < 3 && 'PollNode__optionDeleteDisabled',\r\n )}\r\n aria-label=\"Remove\"\r\n onClick={() => {\r\n withPollNode((node) => {\r\n node.deleteOption(option);\r\n });\r\n }}\r\n />\r\n </div>\r\n );\r\n}\r\n\r\nexport default function PollComponent({\r\n question,\r\n options,\r\n nodeKey,\r\n}: {\r\n nodeKey: NodeKey;\r\n options: Options;\r\n question: string;\r\n}): JSX.Element {\r\n const [editor] = useLexicalComposerContext();\r\n const totalVotes = useMemo(() => getTotalVotes(options), [options]);\r\n const [isSelected, setSelected, clearSelection] =\r\n useLexicalNodeSelection(nodeKey);\r\n const [selection, setSelection] = useState<BaseSelection | null>(null);\r\n const ref = useRef(null);\r\n\r\n const onDelete = useCallback(\r\n (payload: KeyboardEvent) => {\r\n if (isSelected && $isNodeSelection($getSelection())) {\r\n const event: KeyboardEvent = payload;\r\n event.preventDefault();\r\n const node = $getNodeByKey(nodeKey);\r\n if ($isPollNode(node)) {\r\n node.remove();\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n [isSelected, nodeKey],\r\n );\r\n\r\n useEffect(() => {\r\n return mergeRegister(\r\n editor.registerUpdateListener(({editorState}) => {\r\n setSelection(editorState.read(() => $getSelection()));\r\n }),\r\n editor.registerCommand<MouseEvent>(\r\n CLICK_COMMAND,\r\n (payload) => {\r\n const event = payload;\r\n\r\n if (event.target === ref.current) {\r\n if (!event.shiftKey) {\r\n clearSelection();\r\n }\r\n setSelected(!isSelected);\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_DELETE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_BACKSPACE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n );\r\n }, [clearSelection, editor, isSelected, nodeKey, onDelete, setSelected]);\r\n\r\n const withPollNode = (\r\n cb: (node: PollNode) => void,\r\n onUpdate?: () => void,\r\n ): void => {\r\n editor.update(\r\n () => {\r\n const node = $getNodeByKey(nodeKey);\r\n if ($isPollNode(node)) {\r\n cb(node);\r\n }\r\n },\r\n {onUpdate},\r\n );\r\n };\r\n\r\n const addOption = () => {\r\n withPollNode((node) => {\r\n node.addOption(createPollOption());\r\n });\r\n };\r\n\r\n const isFocused = $isNodeSelection(selection) && isSelected;\r\n\r\n return (\r\n <div\r\n className={`PollNode__container ${isFocused ? 'focused' : ''}`}\r\n ref={ref}>\r\n <div className=\"PollNode__inner\">\r\n <h2 className=\"PollNode__heading\">{question}</h2>\r\n {options.map((option, index) => {\r\n const key = option.uid;\r\n return (\r\n <PollOptionComponent\r\n key={key}\r\n withPollNode={withPollNode}\r\n option={option}\r\n index={index}\r\n options={options}\r\n totalVotes={totalVotes}\r\n />\r\n );\r\n })}\r\n <div className=\"PollNode__footer\">\r\n <Button onClick={addOption} small={true}>\r\n Add Option\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"names":["getTotalVotes","options","totalVotes","next","PollOptionComponent","option","index","withPollNode","clientID","useCollaborationContext","checkboxRef","useRef","votesArray","checked","votes","text","jsxs","jsx","joinClasses","e","node","target","value","selectionStart","selectionEnd","PollComponent","question","nodeKey","editor","useLexicalComposerContext","useMemo","isSelected","setSelected","clearSelection","useLexicalNodeSelection","selection","setSelection","useState","ref","onDelete","useCallback","payload","$isNodeSelection","$getSelection","$getNodeByKey","$isPollNode","useEffect","mergeRegister","editorState","CLICK_COMMAND","event","COMMAND_PRIORITY_LOW","KEY_DELETE_COMMAND","KEY_BACKSPACE_COMMAND","cb","onUpdate","addOption","createPollOption","isFocused","key","Button"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PollComponent-008dc9fe.js","sources":["../src/lexical/nodes/PollComponent.tsx"],"sourcesContent":["/**\r\n * Copyright (c) Meta Platforms, Inc. and affiliates.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n */\r\n\r\nimport type {Option, Options, PollNode} from './PollNode';\r\n\r\nimport './PollNode.css';\r\n\r\nimport {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';\r\nimport {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';\r\nimport {useLexicalNodeSelection} from '@lexical/react/useLexicalNodeSelection';\r\nimport {mergeRegister} from '@lexical/utils';\r\nimport {\r\n $getNodeByKey,\r\n $getSelection,\r\n $isNodeSelection,\r\n BaseSelection,\r\n CLICK_COMMAND,\r\n COMMAND_PRIORITY_LOW,\r\n KEY_BACKSPACE_COMMAND,\r\n KEY_DELETE_COMMAND,\r\n NodeKey,\r\n} from 'lexical';\r\nimport * as React from 'react';\r\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\r\n\r\nimport Button from '../ui/Button';\r\nimport joinClasses from '../utils/joinClasses';\r\nimport {$isPollNode, createPollOption} from './PollNode';\r\n\r\nfunction getTotalVotes(options: Options): number {\r\n return options.reduce((totalVotes, next) => {\r\n return totalVotes + next.votes.length;\r\n }, 0);\r\n}\r\n\r\nfunction PollOptionComponent({\r\n option,\r\n index,\r\n options,\r\n totalVotes,\r\n withPollNode,\r\n}: {\r\n index: number;\r\n option: Option;\r\n options: Options;\r\n totalVotes: number;\r\n withPollNode: (\r\n cb: (pollNode: PollNode) => void,\r\n onSelect?: () => void,\r\n ) => void;\r\n}): JSX.Element {\r\n const {clientID} = useCollaborationContext();\r\n const checkboxRef = useRef(null);\r\n const votesArray = option.votes;\r\n const checkedIndex = votesArray.indexOf(clientID);\r\n const checked = checkedIndex !== -1;\r\n const votes = votesArray.length;\r\n const text = option.text;\r\n\r\n return (\r\n <div className=\"PollNode__optionContainer\">\r\n <div\r\n className={joinClasses(\r\n 'PollNode__optionCheckboxWrapper',\r\n checked && 'PollNode__optionCheckboxChecked',\r\n )}>\r\n <input\r\n ref={checkboxRef}\r\n className=\"PollNode__optionCheckbox\"\r\n type=\"checkbox\"\r\n onChange={(e) => {\r\n withPollNode((node) => {\r\n node.toggleVote(option, clientID);\r\n });\r\n }}\r\n checked={checked}\r\n />\r\n </div>\r\n <div className=\"PollNode__optionInputWrapper\">\r\n <div\r\n className=\"PollNode__optionInputVotes\"\r\n style={{width: `${votes === 0 ? 0 : (votes / totalVotes) * 100}%`}}\r\n />\r\n <span className=\"PollNode__optionInputVotesCount\">\r\n {votes > 0 && (votes === 1 ? '1 vote' : `${votes} votes`)}\r\n </span>\r\n <input\r\n className=\"PollNode__optionInput\"\r\n type=\"text\"\r\n value={text}\r\n onChange={(e) => {\r\n const target = e.target;\r\n const value = target.value;\r\n const selectionStart = target.selectionStart;\r\n const selectionEnd = target.selectionEnd;\r\n withPollNode(\r\n (node) => {\r\n node.setOptionText(option, value);\r\n },\r\n () => {\r\n target.selectionStart = selectionStart;\r\n target.selectionEnd = selectionEnd;\r\n },\r\n );\r\n }}\r\n placeholder={`Option ${index + 1}`}\r\n />\r\n </div>\r\n <button\r\n disabled={options.length < 3}\r\n className={joinClasses(\r\n 'PollNode__optionDelete',\r\n options.length < 3 && 'PollNode__optionDeleteDisabled',\r\n )}\r\n aria-label=\"Remove\"\r\n onClick={() => {\r\n withPollNode((node) => {\r\n node.deleteOption(option);\r\n });\r\n }}\r\n />\r\n </div>\r\n );\r\n}\r\n\r\nexport default function PollComponent({\r\n question,\r\n options,\r\n nodeKey,\r\n}: {\r\n nodeKey: NodeKey;\r\n options: Options;\r\n question: string;\r\n}): JSX.Element {\r\n const [editor] = useLexicalComposerContext();\r\n const totalVotes = useMemo(() => getTotalVotes(options), [options]);\r\n const [isSelected, setSelected, clearSelection] =\r\n useLexicalNodeSelection(nodeKey);\r\n const [selection, setSelection] = useState<BaseSelection | null>(null);\r\n const ref = useRef(null);\r\n\r\n const onDelete = useCallback(\r\n (payload: KeyboardEvent) => {\r\n if (isSelected && $isNodeSelection($getSelection())) {\r\n const event: KeyboardEvent = payload;\r\n event.preventDefault();\r\n const node = $getNodeByKey(nodeKey);\r\n if ($isPollNode(node)) {\r\n node.remove();\r\n return true;\r\n }\r\n }\r\n return false;\r\n },\r\n [isSelected, nodeKey],\r\n );\r\n\r\n useEffect(() => {\r\n return mergeRegister(\r\n editor.registerUpdateListener(({editorState}) => {\r\n setSelection(editorState.read(() => $getSelection()));\r\n }),\r\n editor.registerCommand<MouseEvent>(\r\n CLICK_COMMAND,\r\n (payload) => {\r\n const event = payload;\r\n\r\n if (event.target === ref.current) {\r\n if (!event.shiftKey) {\r\n clearSelection();\r\n }\r\n setSelected(!isSelected);\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_DELETE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n editor.registerCommand(\r\n KEY_BACKSPACE_COMMAND,\r\n onDelete,\r\n COMMAND_PRIORITY_LOW,\r\n ),\r\n );\r\n }, [clearSelection, editor, isSelected, nodeKey, onDelete, setSelected]);\r\n\r\n const withPollNode = (\r\n cb: (node: PollNode) => void,\r\n onUpdate?: () => void,\r\n ): void => {\r\n editor.update(\r\n () => {\r\n const node = $getNodeByKey(nodeKey);\r\n if ($isPollNode(node)) {\r\n cb(node);\r\n }\r\n },\r\n {onUpdate},\r\n );\r\n };\r\n\r\n const addOption = () => {\r\n withPollNode((node) => {\r\n node.addOption(createPollOption());\r\n });\r\n };\r\n\r\n const isFocused = $isNodeSelection(selection) && isSelected;\r\n\r\n return (\r\n <div\r\n className={`PollNode__container ${isFocused ? 'focused' : ''}`}\r\n ref={ref}>\r\n <div className=\"PollNode__inner\">\r\n <h2 className=\"PollNode__heading\">{question}</h2>\r\n {options.map((option, index) => {\r\n const key = option.uid;\r\n return (\r\n <PollOptionComponent\r\n key={key}\r\n withPollNode={withPollNode}\r\n option={option}\r\n index={index}\r\n options={options}\r\n totalVotes={totalVotes}\r\n />\r\n );\r\n })}\r\n <div className=\"PollNode__footer\">\r\n <Button onClick={addOption} small={true}>\r\n Add Option\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"names":["getTotalVotes","options","totalVotes","next","PollOptionComponent","option","index","withPollNode","clientID","useCollaborationContext","checkboxRef","useRef","votesArray","checked","votes","text","jsxs","jsx","joinClasses","e","node","target","value","selectionStart","selectionEnd","PollComponent","question","nodeKey","editor","useLexicalComposerContext","useMemo","isSelected","setSelected","clearSelection","useLexicalNodeSelection","selection","setSelection","useState","ref","onDelete","useCallback","payload","$isNodeSelection","$getSelection","$getNodeByKey","$isPollNode","useEffect","mergeRegister","editorState","CLICK_COMMAND","event","COMMAND_PRIORITY_LOW","KEY_DELETE_COMMAND","KEY_BACKSPACE_COMMAND","cb","onUpdate","addOption","createPollOption","isFocused","key","Button"],"mappings":"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"}
|
|
@@ -23163,7 +23163,7 @@ class Ay extends mx {
|
|
|
23163
23163
|
function VB(i) {
|
|
23164
23164
|
return new Ay(i);
|
|
23165
23165
|
}
|
|
23166
|
-
const GB = Wt.lazy(() => import("./ImageComponent-
|
|
23166
|
+
const GB = Wt.lazy(() => import("./ImageComponent-ced58b97.js"));
|
|
23167
23167
|
function YB(i) {
|
|
23168
23168
|
const t = i;
|
|
23169
23169
|
if (t.src.startsWith("file:///"))
|
|
@@ -23309,7 +23309,7 @@ function bx({
|
|
|
23309
23309
|
function XB(i) {
|
|
23310
23310
|
return i instanceof Id;
|
|
23311
23311
|
}
|
|
23312
|
-
const ZB = Wt.lazy(() => import("./InlineImageComponent-
|
|
23312
|
+
const ZB = Wt.lazy(() => import("./InlineImageComponent-531380f5.js"));
|
|
23313
23313
|
function QB(i) {
|
|
23314
23314
|
if (i instanceof HTMLImageElement) {
|
|
23315
23315
|
const { alt: t, src: n, width: e, height: s } = i;
|
|
@@ -23748,7 +23748,7 @@ function rR() {
|
|
|
23748
23748
|
function s8(i) {
|
|
23749
23749
|
return i instanceof Vp;
|
|
23750
23750
|
}
|
|
23751
|
-
const a8 = Wt.lazy(() => import("./PollComponent-
|
|
23751
|
+
const a8 = Wt.lazy(() => import("./PollComponent-008dc9fe.js"));
|
|
23752
23752
|
function l8() {
|
|
23753
23753
|
return Math.random().toString(36).replace(/[^a-z]+/g, "").substr(0, 5);
|
|
23754
23754
|
}
|
|
@@ -69000,4 +69000,4 @@ export {
|
|
|
69000
69000
|
hm as y,
|
|
69001
69001
|
xx as z
|
|
69002
69002
|
};
|
|
69003
|
-
//# sourceMappingURL=index-
|
|
69003
|
+
//# sourceMappingURL=index-7c78a104.js.map
|