fix(richtext-lexical): floating link editor did not properly hide if selection is not a range selection
This commit is contained in:
@@ -64,89 +64,101 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
depth: editDepth,
|
||||
})
|
||||
|
||||
const setNotLink = useCallback(() => {
|
||||
setIsLink(false)
|
||||
if (editorRef && editorRef.current) {
|
||||
editorRef.current.style.opacity = '0'
|
||||
editorRef.current.style.transform = 'translate(-10000px, -10000px)'
|
||||
}
|
||||
setIsAutoLink(false)
|
||||
setLinkUrl(null)
|
||||
setLinkLabel(null)
|
||||
setSelectedNodes([])
|
||||
}, [setIsLink, setLinkUrl, setLinkLabel, setSelectedNodes])
|
||||
|
||||
const updateLinkEditor = useCallback(() => {
|
||||
const selection = $getSelection()
|
||||
let selectedNodeDomRect: DOMRect | undefined = null
|
||||
|
||||
if (!$isRangeSelection(selection) || !selection) {
|
||||
setNotLink()
|
||||
return
|
||||
}
|
||||
|
||||
// Handle the data displayed in the floating link editor & drawer when you click on a link node
|
||||
if ($isRangeSelection(selection)) {
|
||||
const focusNode = getSelectedNode(selection)
|
||||
selectedNodeDomRect = editor.getElementByKey(focusNode.getKey())?.getBoundingClientRect()
|
||||
const focusLinkParent: LinkNode = $findMatchingParent(focusNode, $isLinkNode)
|
||||
|
||||
// Prevent link modal from showing if selection spans further than the link: https://github.com/facebook/lexical/issues/4064
|
||||
const badNode = selection
|
||||
.getNodes()
|
||||
.filter((node) => !$isLineBreakNode(node))
|
||||
.find((node) => {
|
||||
const linkNode = $findMatchingParent(node, $isLinkNode)
|
||||
return (
|
||||
(focusLinkParent && !focusLinkParent.is(linkNode)) ||
|
||||
(linkNode && !linkNode.is(focusLinkParent))
|
||||
)
|
||||
})
|
||||
const focusNode = getSelectedNode(selection)
|
||||
selectedNodeDomRect = editor.getElementByKey(focusNode.getKey())?.getBoundingClientRect()
|
||||
const focusLinkParent: LinkNode = $findMatchingParent(focusNode, $isLinkNode)
|
||||
|
||||
if (focusLinkParent == null || badNode) {
|
||||
setIsLink(false)
|
||||
setIsAutoLink(false)
|
||||
setLinkUrl(null)
|
||||
setLinkLabel(null)
|
||||
setSelectedNodes([])
|
||||
return
|
||||
}
|
||||
// Prevent link modal from showing if selection spans further than the link: https://github.com/facebook/lexical/issues/4064
|
||||
const badNode = selection
|
||||
.getNodes()
|
||||
.filter((node) => !$isLineBreakNode(node))
|
||||
.find((node) => {
|
||||
const linkNode = $findMatchingParent(node, $isLinkNode)
|
||||
return (
|
||||
(focusLinkParent && !focusLinkParent.is(linkNode)) ||
|
||||
(linkNode && !linkNode.is(focusLinkParent))
|
||||
)
|
||||
})
|
||||
|
||||
// Initial state:
|
||||
const data: LinkFields & { text: string } = {
|
||||
doc: undefined,
|
||||
linkType: undefined,
|
||||
newTab: undefined,
|
||||
url: '',
|
||||
...focusLinkParent.getFields(),
|
||||
text: focusLinkParent.getTextContent(),
|
||||
}
|
||||
if (focusLinkParent == null || badNode) {
|
||||
setNotLink()
|
||||
return
|
||||
}
|
||||
|
||||
if (focusLinkParent.getFields()?.linkType === 'custom') {
|
||||
setLinkUrl(focusLinkParent.getFields()?.url ?? null)
|
||||
setLinkLabel(null)
|
||||
} else {
|
||||
// internal link
|
||||
// Initial state:
|
||||
const data: LinkFields & { text: string } = {
|
||||
doc: undefined,
|
||||
linkType: undefined,
|
||||
newTab: undefined,
|
||||
url: '',
|
||||
...focusLinkParent.getFields(),
|
||||
text: focusLinkParent.getTextContent(),
|
||||
}
|
||||
|
||||
if (focusLinkParent.getFields()?.linkType === 'custom') {
|
||||
setLinkUrl(focusLinkParent.getFields()?.url ?? null)
|
||||
setLinkLabel(null)
|
||||
} else {
|
||||
// internal link
|
||||
setLinkUrl(
|
||||
`/admin/collections/${focusLinkParent.getFields()?.doc?.relationTo}/${
|
||||
focusLinkParent.getFields()?.doc?.value
|
||||
}`,
|
||||
)
|
||||
|
||||
const relatedField = config.collections.find(
|
||||
(coll) => coll.slug === focusLinkParent.getFields()?.doc?.relationTo,
|
||||
)
|
||||
if (!relatedField) {
|
||||
// Usually happens if the user removed all default fields. In this case, we let them specify the label or do not display the label at all.
|
||||
// label could be a virtual field the user added. This is useful if they want to use the link feature for things other than links.
|
||||
setLinkLabel(
|
||||
focusLinkParent.getFields()?.label ? String(focusLinkParent.getFields()?.label) : null,
|
||||
)
|
||||
setLinkUrl(
|
||||
`/admin/collections/${focusLinkParent.getFields()?.doc?.relationTo}/${
|
||||
focusLinkParent.getFields()?.doc?.value
|
||||
}`,
|
||||
focusLinkParent.getFields()?.url ? String(focusLinkParent.getFields()?.url) : null,
|
||||
)
|
||||
|
||||
const relatedField = config.collections.find(
|
||||
(coll) => coll.slug === focusLinkParent.getFields()?.doc?.relationTo,
|
||||
)
|
||||
if (!relatedField) {
|
||||
// Usually happens if the user removed all default fields. In this case, we let them specify the label or do not display the label at all.
|
||||
// label could be a virtual field the user added. This is useful if they want to use the link feature for things other than links.
|
||||
setLinkLabel(
|
||||
focusLinkParent.getFields()?.label ? String(focusLinkParent.getFields()?.label) : null,
|
||||
)
|
||||
setLinkUrl(
|
||||
focusLinkParent.getFields()?.url ? String(focusLinkParent.getFields()?.url) : null,
|
||||
)
|
||||
} else {
|
||||
const label = t('fields:linkedTo', {
|
||||
label: getTranslation(relatedField.labels.singular, i18n),
|
||||
}).replace(/<[^>]*>?/g, '')
|
||||
setLinkLabel(label)
|
||||
}
|
||||
}
|
||||
|
||||
setStateData(data)
|
||||
setIsLink(true)
|
||||
setSelectedNodes(selection ? selection?.getNodes() : [])
|
||||
|
||||
if ($isAutoLinkNode(focusLinkParent)) {
|
||||
setIsAutoLink(true)
|
||||
} else {
|
||||
setIsAutoLink(false)
|
||||
const label = t('fields:linkedTo', {
|
||||
label: getTranslation(relatedField.labels.singular, i18n),
|
||||
}).replace(/<[^>]*>?/g, '')
|
||||
setLinkLabel(label)
|
||||
}
|
||||
}
|
||||
|
||||
setStateData(data)
|
||||
setIsLink(true)
|
||||
setSelectedNodes(selection ? selection?.getNodes() : [])
|
||||
|
||||
if ($isAutoLinkNode(focusLinkParent)) {
|
||||
setIsAutoLink(true)
|
||||
} else {
|
||||
setIsAutoLink(false)
|
||||
}
|
||||
|
||||
const editorElem = editorRef.current
|
||||
const nativeSelection = window.getSelection()
|
||||
const { activeElement } = document
|
||||
@@ -158,7 +170,6 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
const rootElement = editor.getRootElement()
|
||||
|
||||
if (
|
||||
selection !== null &&
|
||||
nativeSelection !== null &&
|
||||
rootElement !== null &&
|
||||
rootElement.contains(nativeSelection.anchorNode)
|
||||
@@ -182,7 +193,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
}
|
||||
|
||||
return true
|
||||
}, [anchorElem, editor, config, t, i18n])
|
||||
}, [editor, setNotLink, config.collections, t, i18n, anchorElem])
|
||||
|
||||
useEffect(() => {
|
||||
return mergeRegister(
|
||||
@@ -202,13 +213,6 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
)
|
||||
}, [editor, updateLinkEditor, toggleModal, drawerSlug])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLink && editorRef) {
|
||||
editorRef.current.style.opacity = '0'
|
||||
editorRef.current.style.transform = 'translate(-10000px, -10000px)'
|
||||
}
|
||||
}, [isLink])
|
||||
|
||||
useEffect(() => {
|
||||
const scrollerElem = anchorElem.parentElement
|
||||
|
||||
@@ -253,8 +257,8 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
KEY_ESCAPE_COMMAND,
|
||||
() => {
|
||||
if (isLink) {
|
||||
setIsLink(false)
|
||||
setIsAutoLink(false)
|
||||
setNotLink()
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -262,7 +266,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
COMMAND_PRIORITY_HIGH,
|
||||
),
|
||||
)
|
||||
}, [editor, updateLinkEditor, setIsLink, isLink])
|
||||
}, [editor, updateLinkEditor, isLink, setNotLink])
|
||||
|
||||
useEffect(() => {
|
||||
editor.getEditorState().read(() => {
|
||||
|
||||
Reference in New Issue
Block a user