Skip to content

Commit 80f3a31

Browse files
committed
fix: migrate to lexical extensions wrapper
1 parent faada2b commit 80f3a31

4 files changed

Lines changed: 55 additions & 77 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
},
6060
"dependencies": {
6161
"@lexical/code": "^0.38.2",
62+
"@lexical/extension": "^0.38.2",
6263
"@lexical/link": "^0.38.2",
6364
"@lexical/list": "^0.38.2",
6465
"@lexical/markdown": "^0.38.2",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Editor.tsx

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { LexicalComposer } from '@lexical/react/LexicalComposer'
1+
import { defineExtension, configExtension } from 'lexical'
2+
3+
import { RichTextExtension } from '@lexical/rich-text'
4+
import { AutoFocusExtension } from '@lexical/extension'
5+
import { ListExtension } from '@lexical/list'
6+
import { LexicalExtensionComposer } from '@lexical/react/LexicalExtensionComposer'
7+
import { ReactExtension } from '@lexical/react/ReactExtension'
8+
29
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
3-
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
410
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
5-
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin'
6-
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'
711
import { HeadingNode, QuoteNode } from '@lexical/rich-text'
812
import { ListItemNode, ListNode } from '@lexical/list'
913
import { CodeHighlightNode, CodeNode } from '@lexical/code'
10-
import { ListPlugin } from '@lexical/react/LexicalListPlugin'
1114
import { TableCellNode, TableNode, TableRowNode } from '@lexical/table'
1215
import { AutoLinkNode, LinkNode } from '@lexical/link'
1316
import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin'
@@ -26,7 +29,6 @@ import DefaultTheme, {
2629
} from './themes/DefaultTheme'
2730

2831
import type { EditorState } from 'lexical'
29-
import type { InitialConfigType } from '@lexical/react/LexicalComposer'
3032

3133
export interface SliteProps {
3234
initialValue?: string
@@ -35,10 +37,6 @@ export interface SliteProps {
3537
children: React.ReactNode
3638
}
3739

38-
function Placeholder() {
39-
return <div className="editor-placeholder">{'Enter some rich text ...'}</div>
40-
}
41-
4240
// ref: https://stackoverflow.com/questions/71976652/with-lexical-how-do-i-set-default-initial-text
4341
const onChangeHandler = (
4442
editorState: EditorState,
@@ -50,12 +48,17 @@ const onChangeHandler = (
5048
})
5149
}
5250

53-
const getInitialConfig = (
54-
initialValue: string,
55-
editable: boolean
56-
): InitialConfigType => {
57-
return {
58-
editorState: () => {
51+
const getExtensionConfig = (initialValue: string, editable: boolean) => {
52+
const appExtension = defineExtension({
53+
name: 'ReactSlite',
54+
namespace: 'ReactSlite',
55+
dependencies: [
56+
AutoFocusExtension,
57+
RichTextExtension,
58+
ListExtension,
59+
configExtension(ReactExtension, { contentEditable: null }),
60+
],
61+
$initialEditorState: () => {
5962
// ref: https://stackoverflow.com/a/72172529/1410291
6063
// ref: https://github.com/facebook/lexical/issues/2308#issuecomment-1382721253
6164
if (initialValue === '') {
@@ -86,22 +89,26 @@ const getInitialConfig = (
8689
AutoLinkNode,
8790
LinkNode,
8891
],
89-
namespace: '',
9092
editable,
91-
}
93+
})
94+
95+
// ref: https://lexical.dev/docs/extensions/react
96+
return appExtension
9297
}
9398

9499
export function Editor({ readOnly }: { readOnly: SliteProps['readOnly'] }) {
100+
const placeholderText = 'Enter some rich text...'
95101
return (
96102
<div className="editor-inner">
97-
<RichTextPlugin
98-
contentEditable={<ContentEditable className="editor-input" />}
99-
placeholder={readOnly ? null : <Placeholder />}
100-
ErrorBoundary={LexicalErrorBoundary}
103+
<ContentEditable
104+
readOnly={readOnly}
105+
className="editor-input"
106+
aria-placeholder={placeholderText}
107+
placeholder={
108+
<div className="editor-placeholder">{placeholderText}</div>
109+
}
101110
/>
102-
<AutoFocusPlugin />
103111
<CodeHighlightPlugin />
104-
<ListPlugin />
105112
<ListMaxIndentLevelPlugin maxDepth={1} />
106113
</div>
107114
)
@@ -120,8 +127,9 @@ export default function LexicalWrapper({
120127
const editable = !readOnly
121128

122129
return (
123-
<LexicalComposer
124-
initialConfig={getInitialConfig(initialValue || '', editable)}
130+
<LexicalExtensionComposer
131+
extension={getExtensionConfig(initialValue || '', editable)}
132+
contentEditable={null}
125133
>
126134
<div className={SLITE_EDITOR_CONTAINER_CLASS}>
127135
{editable && (
@@ -132,7 +140,7 @@ export default function LexicalWrapper({
132140
{editable && <MarkdownShortcutPlugin transformers={TRANSFORMERS} />}
133141
{children}
134142
</div>
135-
</LexicalComposer>
143+
</LexicalExtensionComposer>
136144
)
137145
}
138146

src/plugins/ToolbarPlugin.jsx

Lines changed: 16 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
// import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'
1717
import {
1818
$isParentElementRTL,
19-
$wrapNodes,
19+
$setBlocksType as $wrapNodes, // ref: https://github.com/facebook/lexical/discussions/5600
2020
$isAtNodeEnd,
2121
} from '@lexical/selection'
2222
import { $getNearestNodeOfType, mergeRegister } from '@lexical/utils'
@@ -51,7 +51,7 @@ import {
5151
CodeIcon,
5252
BoldIcon,
5353
UnderlineIcon,
54-
StrikeThroughIcon,
54+
// StrikeThroughIcon,
5555
ItalicIcon,
5656
TextLeftIcon,
5757
TextCenterIcon,
@@ -493,53 +493,19 @@ export default function ToolbarPlugin() {
493493
const [isBold, setIsBold] = useState(false)
494494
const [isItalic, setIsItalic] = useState(false)
495495
const [isUnderline, setIsUnderline] = useState(false)
496-
const [isStrikethrough, setIsStrikethrough] = useState(false)
496+
// const [isStrikethrough, setIsStrikethrough] = useState(false)
497497
const [isCode, setIsCode] = useState(false)
498498

499499
const updateToolbar = useCallback(() => {
500500
const selection = $getSelection()
501501
if ($isRangeSelection(selection)) {
502-
const anchorNode = selection.anchor.getNode()
503-
const element =
504-
anchorNode.getKey() === 'root'
505-
? anchorNode
506-
: anchorNode.getTopLevelElementOrThrow()
507-
const elementKey = element.getKey()
508-
const elementDOM = editor.getElementByKey(elementKey)
509-
if (elementDOM !== null) {
510-
setSelectedElementKey(elementKey)
511-
if ($isListNode(element)) {
512-
const parentList = $getNearestNodeOfType(anchorNode, ListNode)
513-
const type = parentList ? parentList.getTag() : element.getTag()
514-
setBlockType(type)
515-
} else {
516-
const type = $isHeadingNode(element)
517-
? element.getTag()
518-
: element.getType()
519-
setBlockType(type)
520-
if ($isCodeNode(element)) {
521-
setCodeLanguage(element.getLanguage() || getDefaultCodeLanguage())
522-
}
523-
}
524-
}
525502
// Update text format
526503
setIsBold(selection.hasFormat('bold'))
527504
setIsItalic(selection.hasFormat('italic'))
528505
setIsUnderline(selection.hasFormat('underline'))
529-
setIsStrikethrough(selection.hasFormat('strikethrough'))
530506
setIsCode(selection.hasFormat('code'))
531-
setIsRTL($isParentElementRTL(selection))
532-
533-
// Update links
534-
// const node = getSelectedNode(selection)
535-
// const parent = node.getParent()
536-
// if ($isLinkNode(parent) || $isLinkNode(node)) {
537-
// setIsLink(true)
538-
// } else {
539-
// setIsLink(false)
540-
// }
541507
}
542-
}, [editor])
508+
}, [])
543509

544510
useEffect(() => {
545511
return mergeRegister(
@@ -702,18 +668,18 @@ export default function ToolbarPlugin() {
702668
<UnderlineIcon />
703669
</i>
704670
</button>
705-
<button
706-
type="button"
707-
onClick={() => {
708-
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough')
709-
}}
710-
className={`toolbar-item spaced ${isStrikethrough ? 'active' : ''}`}
711-
aria-label="Format Strikethrough"
712-
>
713-
<i className="format strikethrough">
714-
<StrikeThroughIcon />
715-
</i>
716-
</button>
671+
{/* <button */}
672+
{/* type="button" */}
673+
{/* onClick={() => { */}
674+
{/* editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough') */}
675+
{/* }} */}
676+
{/* className={`toolbar-item spaced ${isStrikethrough ? 'active' : ''}`} */}
677+
{/* aria-label="Format Strikethrough" */}
678+
{/* > */}
679+
{/* <i className="format strikethrough"> */}
680+
{/* <StrikeThroughIcon /> */}
681+
{/* </i> */}
682+
{/* </button> */}
717683
<button
718684
type="button"
719685
onClick={() => {

0 commit comments

Comments
 (0)