Skip to content

Commit ba9ea5c

Browse files
committed
fix(richtext-lexical): fixed toolbar actions not ensuring editor focus, various link editor selection issues
1 parent 53b7d6f commit ba9ea5c

File tree

5 files changed

+36
-12
lines changed

5 files changed

+36
-12
lines changed

packages/richtext-lexical/src/field/features/link/drawer/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ import type { LinkFields } from '../nodes/types.js'
55
export interface Props {
66
drawerSlug: string
77
handleModalSubmit: (fields: FormState, data: Record<string, unknown>) => void
8-
stateData?: LinkFields & { text: string }
8+
stateData: {} | (LinkFields & { text: string })
99
}

packages/richtext-lexical/src/field/features/link/feature.client.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use client'
22

3+
import type { LexicalNode } from 'lexical'
4+
35
import { $findMatchingParent } from '@lexical/utils'
46
import { $getSelection, $isRangeSelection } from 'lexical'
57

@@ -34,22 +36,35 @@ const toolbarGroups: ToolbarGroup[] = [
3436
}
3537
return false
3638
},
39+
isEnabled: ({ selection }) => {
40+
return !!($isRangeSelection(selection) && $getSelection()?.getTextContent()?.length)
41+
},
3742
key: 'link',
3843
label: `Link`,
3944
onSelect: ({ editor, isActive }) => {
4045
if (!isActive) {
41-
let selectedText = null
46+
let selectedText: string = null
47+
let selectedNodes: LexicalNode[] = []
4248
editor.getEditorState().read(() => {
43-
selectedText = $getSelection().getTextContent()
49+
selectedText = $getSelection()?.getTextContent()
50+
// We need to selected nodes here before the drawer opens, as clicking around in the drawer may change the original selection
51+
selectedNodes = $getSelection()?.getNodes() ?? []
4452
})
53+
54+
if (!selectedText?.length) {
55+
return
56+
}
57+
4558
const linkFields: LinkFields = {
4659
doc: null,
4760
linkType: 'custom',
4861
newTab: false,
4962
url: 'https://',
5063
}
64+
5165
editor.dispatchCommand(TOGGLE_LINK_WITH_MODAL_COMMAND, {
5266
fields: linkFields,
67+
selectedNodes,
5368
text: selectedText,
5469
})
5570
} else {

packages/richtext-lexical/src/field/features/link/plugins/floatingLinkEditor/LinkEditor/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
5050

5151
const { i18n, t } = useTranslation()
5252

53-
const [stateData, setStateData] = useState<LinkFields & { text: string }>(null)
53+
const [stateData, setStateData] = useState<{} | (LinkFields & { text: string })>({})
5454

5555
const { closeModal, toggleModal } = useModal()
5656
const editDepth = useEditDepth()
@@ -74,6 +74,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
7474
setLinkUrl(null)
7575
setLinkLabel(null)
7676
setSelectedNodes([])
77+
setStateData({})
7778
}, [setIsLink, setLinkUrl, setLinkLabel, setSelectedNodes])
7879

7980
const updateLinkEditor = useCallback(() => {

packages/richtext-lexical/src/field/features/toolbars/shared/ToolbarButton/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,14 @@ export const ToolbarButton = ({
8181
className={className}
8282
onClick={() => {
8383
if (enabled !== false) {
84-
item.onSelect({
85-
editor,
86-
isActive: active,
87-
})
84+
editor.focus()
85+
// We need to wrap the onSelect in a setTimeout, so the editor is properly focused before the onSelect is called.
86+
setTimeout(() => {
87+
item.onSelect({
88+
editor,
89+
isActive: active,
90+
})
91+
}, 0)
8892
}
8993
}}
9094
onMouseDown={(e) => {

packages/richtext-lexical/src/field/features/toolbars/shared/ToolbarDropdown/DropDown.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,14 @@ export function DropDownItem({
6666
className={className}
6767
onClick={() => {
6868
if (enabled !== false) {
69-
item.onSelect({
70-
editor,
71-
isActive: active,
72-
})
69+
editor.focus()
70+
// We need to wrap the onSelect in a setTimeout, so the editor is properly focused before the onSelect is called.
71+
setTimeout(() => {
72+
item.onSelect({
73+
editor,
74+
isActive: active,
75+
})
76+
}, 0)
7377
}
7478
}}
7579
onMouseDown={(e) => {

0 commit comments

Comments
 (0)