Skip to content

Commit da35afb

Browse files
authored
feat(richtext-lexical)!: upgrade lexical from 0.15.0 to 0.16.0 and port over relevant playground changes (#6620)
**BREAKING:** - This upgrades the required version of lexical from 0.15.0 to 0.16.0. If you are using lexical directly in your project, possibly due to custom features, there might be breaking changes for you. Please consult the lexical 0.16.0 changelog: https://github.com/facebook/lexical/releases/tag/v0.16.0
1 parent cafc13a commit da35afb

File tree

11 files changed

+204
-181
lines changed

11 files changed

+204
-181
lines changed

packages/richtext-lexical/package.json

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@
4343
"dependencies": {
4444
"@faceless-ui/modal": "2.0.2",
4545
"@faceless-ui/scroll-info": "1.3.0",
46-
"@lexical/headless": "0.15.0",
47-
"@lexical/link": "0.15.0",
48-
"@lexical/list": "0.15.0",
49-
"@lexical/mark": "0.15.0",
50-
"@lexical/markdown": "0.15.0",
51-
"@lexical/react": "0.15.0",
52-
"@lexical/rich-text": "0.15.0",
53-
"@lexical/selection": "0.15.0",
54-
"@lexical/utils": "0.15.0",
46+
"@lexical/headless": "0.16.0",
47+
"@lexical/link": "0.16.0",
48+
"@lexical/list": "0.16.0",
49+
"@lexical/mark": "0.16.0",
50+
"@lexical/markdown": "0.16.0",
51+
"@lexical/react": "0.16.0",
52+
"@lexical/rich-text": "0.16.0",
53+
"@lexical/selection": "0.16.0",
54+
"@lexical/utils": "0.16.0",
5555
"@types/uuid": "^9.0.8",
5656
"bson-objectid": "2.0.4",
5757
"classnames": "^2.3.2",
5858
"deep-equal": "2.2.3",
5959
"json-schema": "^0.4.0",
60-
"lexical": "0.15.0",
60+
"lexical": "0.16.0",
6161
"lodash": "4.17.21",
6262
"react-error-boundary": "4.0.13",
6363
"uuid": "^9.0.1"
@@ -77,19 +77,19 @@
7777
"peerDependencies": {
7878
"@faceless-ui/modal": "2.0.2",
7979
"@faceless-ui/scroll-info": "1.3.0",
80-
"@lexical/headless": "0.15.0",
81-
"@lexical/link": "0.15.0",
82-
"@lexical/list": "0.15.0",
83-
"@lexical/mark": "0.15.0",
84-
"@lexical/markdown": "0.15.0",
85-
"@lexical/react": "0.15.0",
86-
"@lexical/rich-text": "0.15.0",
87-
"@lexical/selection": "0.15.0",
88-
"@lexical/utils": "0.15.0",
80+
"@lexical/headless": "0.16.0",
81+
"@lexical/link": "0.16.0",
82+
"@lexical/list": "0.16.0",
83+
"@lexical/mark": "0.16.0",
84+
"@lexical/markdown": "0.16.0",
85+
"@lexical/react": "0.16.0",
86+
"@lexical/rich-text": "0.16.0",
87+
"@lexical/selection": "0.16.0",
88+
"@lexical/utils": "0.16.0",
8989
"@payloadcms/next": "workspace:*",
9090
"@payloadcms/translations": "workspace:*",
9191
"@payloadcms/ui": "workspace:*",
92-
"lexical": "0.15.0",
92+
"lexical": "0.16.0",
9393
"payload": "workspace:*",
9494
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
9595
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"

packages/richtext-lexical/src/field/features/debug/testRecorder/plugin/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const formatStep = (step: Step) => {
8888
}
8989

9090
export function isSelectAll(event: KeyboardEvent): boolean {
91-
return event.code === 'KeyA' && (IS_APPLE ? event.metaKey : event.ctrlKey)
91+
return event.key.toLowerCase() === 'a' && (IS_APPLE ? event.metaKey : event.ctrlKey)
9292
}
9393

9494
// stolen from LexicalSelection-test

packages/richtext-lexical/src/field/features/horizontalRule/component/index.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { NodeKey } from 'lexical'
44

55
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
66
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection.js'
7-
import { mergeRegister } from '@lexical/utils'
7+
import { addClassNamesToElement, mergeRegister, removeClassNamesFromElement } from '@lexical/utils'
88
import {
99
$getNodeByKey,
1010
$getSelection,
@@ -18,6 +18,8 @@ import { useCallback, useEffect } from 'react'
1818

1919
import { $isHorizontalRuleNode } from '../nodes/HorizontalRuleNode.js'
2020

21+
const isSelectedClassName = 'selected'
22+
2123
/**
2224
* React component rendered in the lexical editor, WITHIN the hr element created by createDOM of the HorizontalRuleNode.
2325
*
@@ -69,7 +71,11 @@ export function HorizontalRuleComponent({ nodeKey }: { nodeKey: NodeKey }) {
6971
useEffect(() => {
7072
const hrElem = editor.getElementByKey(nodeKey)
7173
if (hrElem !== null) {
72-
hrElem.className = isSelected ? 'selected' : ''
74+
if (isSelected) {
75+
addClassNamesToElement(hrElem, isSelectedClassName)
76+
} else {
77+
removeClassNamesFromElement(hrElem, isSelectedClassName)
78+
}
7379
}
7480
}, [editor, isSelected, nodeKey])
7581

packages/richtext-lexical/src/field/features/horizontalRule/nodes/HorizontalRuleNode.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import type {
22
DOMConversionMap,
33
DOMConversionOutput,
44
DOMExportOutput,
5+
EditorConfig,
56
LexicalCommand,
67
LexicalNode,
78
SerializedLexicalNode,
89
} from 'lexical'
910

11+
import { addClassNamesToElement } from '@lexical/utils'
1012
import { $applyNodeReplacement, DecoratorNode, createCommand } from 'lexical'
1113
import * as React from 'react'
1214

@@ -65,8 +67,10 @@ export class HorizontalRuleNode extends DecoratorNode<React.ReactElement> {
6567
/**
6668
* Determines how the hr element is rendered in the lexical editor. This is only the "initial" / "outer" HTML element.
6769
*/
68-
createDOM(): HTMLElement {
69-
return document.createElement('hr')
70+
createDOM(config: EditorConfig): HTMLElement {
71+
const element = document.createElement('hr')
72+
addClassNamesToElement(element, config.theme.hr)
73+
return element
7074
}
7175

7276
/**
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
@import '../../../../scss/styles.scss';
22

3-
hr {
3+
.LexicalEditorTheme__hr {
44
padding: 2px 2px;
55
border: none;
66
margin: 1rem 0;
77
cursor: pointer;
88
}
99

10-
hr:after {
10+
.LexicalEditorTheme__hr:after {
1111
content: '';
1212
display: block;
1313
height: 2px;
1414
background-color: var(--theme-elevation-250);
1515
}
1616

17-
hr.selected {
17+
.LexicalEditorTheme__hr.selected {
1818
outline: 2px solid var(--theme-success-500);
1919
user-select: none;
2020
}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
'use client'
2-
import LexicalClickableLinkPlugin from '@lexical/react/LexicalClickableLinkPlugin.js'
2+
import { ClickableLinkPlugin as LexicalClickableLinkPlugin } from '@lexical/react/LexicalClickableLinkPlugin.js'
33
import React from 'react'
44

55
import type { PluginComponent } from '../../../types.js'
66
import type { ClientProps } from '../../feature.client.js'
77

88
export const ClickableLinkPlugin: PluginComponent<ClientProps> = () => {
9-
const Component = LexicalClickableLinkPlugin.default || LexicalClickableLinkPlugin
10-
//@ts-expect-error ts being dumb
11-
return <Component />
9+
return <LexicalClickableLinkPlugin />
1210
}

packages/richtext-lexical/src/field/features/upload/nodes/UploadNode.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ export type UploadData = {
2626
value: number | string
2727
}
2828

29+
function isGoogleDocCheckboxImg(img: HTMLImageElement): boolean {
30+
return (
31+
img.parentElement != null &&
32+
img.parentElement.tagName === 'LI' &&
33+
img.previousSibling === null &&
34+
img.getAttribute('aria-roledescription') === 'checkbox'
35+
)
36+
}
37+
2938
function $convertUploadElement(domNode: HTMLImageElement): DOMConversionOutput | null {
3039
if (
3140
domNode.hasAttribute('data-lexical-upload-relation-to') &&
@@ -45,6 +54,10 @@ function $convertUploadElement(domNode: HTMLImageElement): DOMConversionOutput |
4554
return { node }
4655
}
4756
}
57+
const img = domNode
58+
if (img.src.startsWith('file:///') || isGoogleDocCheckboxImg(img)) {
59+
return null
60+
}
4861
// TODO: Auto-upload functionality here!
4962
//}
5063
return null

packages/richtext-lexical/src/field/lexical/LexicalEditor.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
3-
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary.js'
3+
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary.js'
44
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin.js'
55
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin.js'
66
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin.js'
@@ -98,8 +98,6 @@ export const LexicalEditor: React.FC<
9898
}
9999
}, [isSmallWidthViewport])
100100

101-
const ErrorBoundaryComponent = LexicalErrorBoundary.default || LexicalErrorBoundary
102-
103101
return (
104102
<React.Fragment>
105103
{editorConfig.features.plugins.map((plugin) => {
@@ -116,8 +114,7 @@ export const LexicalEditor: React.FC<
116114
}
117115
})}
118116
<RichTextPlugin
119-
//@ts-expect-error ts being dumb
120-
ErrorBoundary={ErrorBoundaryComponent}
117+
ErrorBoundary={LexicalErrorBoundary}
121118
contentEditable={
122119
<div className="editor-scroller">
123120
<div className="editor" ref={onRef}>

packages/richtext-lexical/src/field/lexical/theme/EditorTheme.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const LexicalEditorTheme: EditorThemeClasses = {
5252
h5: 'LexicalEditorTheme__h5',
5353
h6: 'LexicalEditorTheme__h6',
5454
},
55+
hr: 'LexicalEditorTheme__hr',
5556
indent: 'LexicalEditorTheme__indent',
5657
inlineImage: 'LexicalEditor__inline-image',
5758
link: 'LexicalEditorTheme__link',

0 commit comments

Comments
 (0)