fix(richtext-lexical): inline editor is overlapping the clickable link editor for the first line

This commit is contained in:
Alessio Gravili
2024-05-06 14:54:35 -04:00
parent 20455f4fc2
commit 9814fd705e
6 changed files with 56 additions and 35 deletions

View File

@@ -7,11 +7,11 @@ html[data-theme='light'] {
}
.link-editor {
z-index: 10;
z-index: 2;
display: flex;
align-items: center;
background: var(--color-base-0);
padding: 0px 3.72px 0px 6.25px;
padding: 0 3.72px 0 6.25px;
vertical-align: middle;
position: absolute;
top: 0;

View File

@@ -52,7 +52,7 @@ html[data-theme='dark'] {
vertical-align: middle;
height: 37.5px;
position: sticky;
z-index: 2;
z-index: 3;
top: var(--doc-controls-height);
margin-bottom: $baseline;
border: $style-stroke-width-s solid var(--theme-elevation-150);

View File

@@ -15,7 +15,7 @@ html[data-theme='light'] {
position: absolute;
top: 0;
left: 0;
z-index: 1;
z-index: 2;
opacity: 0;
border-radius: 6.25px;
transition: opacity 0.2s;

View File

@@ -179,7 +179,7 @@ function InlineToolbar({
floatingToolbarRef.current.style.pointerEvents = 'auto'
}
}
}, [floatingToolbarRef])
}, [])
useEffect(() => {
document.addEventListener('mousemove', mouseMoveListener)
@@ -200,6 +200,12 @@ function InlineToolbar({
return
}
const possibleLinkEditor = anchorElem.querySelector(':scope > .link-editor')
const isLinkEditorVisible =
possibleLinkEditor !== null &&
'style' in possibleLinkEditor &&
possibleLinkEditor?.style?.['opacity'] === '1'
const rootElement = editor.getRootElement()
if (
selection !== null &&
@@ -211,28 +217,31 @@ function InlineToolbar({
const rangeRect = getDOMRangeRect(nativeSelection, rootElement)
// Position floating toolbar
const offsetIfFlipped = setFloatingElemPosition(
rangeRect, // selection to position around
floatingToolbarRef.current, // what to position
anchorElem, // anchor elem
'center',
)
const offsetIfFlipped = setFloatingElemPosition({
alwaysDisplayOnTop: isLinkEditorVisible,
anchorElem,
floatingElem: floatingToolbarRef.current,
horizontalPosition: 'center',
targetRect: rangeRect,
})
// Position caret
if (caretRef.current) {
setFloatingElemPosition(
rangeRect, // selection to position around
caretRef.current, // what to position
floatingToolbarRef.current, // anchor elem
'center',
10,
5,
true,
offsetIfFlipped,
)
setFloatingElemPosition({
anchorElem: floatingToolbarRef.current,
anchorFlippedOffset: offsetIfFlipped,
floatingElem: caretRef.current,
horizontalOffset: 5,
horizontalPosition: 'center',
specialHandlingForCaret: true,
targetRect: rangeRect,
verticalGap: 10,
})
}
} else {
closeFloatingToolbar()
}
}, [editor, anchorElem])
}, [editor, closeFloatingToolbar, anchorElem])
useEffect(() => {
const scrollerElem = anchorElem.parentElement

View File

@@ -5,16 +5,28 @@ const HORIZONTAL_OFFSET = 5
// This is supposed to position the floatingElem based on the parent (anchorElem) and the target (targetRect) which is usually the selected text.
// So basically, it positions the floatingElem either below or above the target (targetRect) and aligns it to the left or center of the target (targetRect).
// This is used for positioning the floating toolbar (anchor: richtext editor) and its caret (anchor: floating toolbar)
export function setFloatingElemPosition(
targetRect: ClientRect | null,
floatingElem: HTMLElement,
anchorElem: HTMLElement,
horizontalPosition: 'center' | 'left' = 'left',
verticalGap: number = VERTICAL_GAP,
horizontalOffset: number = HORIZONTAL_OFFSET,
specialHandlingForCaret = false,
anchorFlippedOffset = 0, // Offset which was added to the anchor (for caret, floating toolbar) if it was flipped
): number {
export function setFloatingElemPosition(args: {
alwaysDisplayOnTop?: boolean
anchorElem: HTMLElement
anchorFlippedOffset?: number // Offset which was added to the anchor (for caret, floating toolbar) if it was flipped
floatingElem: HTMLElement
horizontalOffset?: number
horizontalPosition?: 'center' | 'left'
specialHandlingForCaret?: boolean
targetRect: ClientRect | null
verticalGap?: number
}): number {
const {
alwaysDisplayOnTop = false,
anchorElem,
anchorFlippedOffset = 0, // Offset which was added to the anchor (for caret, floating toolbar) if it was flipped
floatingElem,
horizontalOffset = HORIZONTAL_OFFSET,
horizontalPosition = 'left',
specialHandlingForCaret = false,
targetRect,
verticalGap = VERTICAL_GAP,
} = args
// Returns the top offset if the target was flipped
const scrollerElem = anchorElem.parentElement
@@ -37,7 +49,7 @@ export function setFloatingElemPosition(
}
let addedToTop = 0
if (top < editorScrollerRect.top && !specialHandlingForCaret) {
if (!alwaysDisplayOnTop && top < editorScrollerRect.top && !specialHandlingForCaret) {
addedToTop = floatingElemRect.height + targetRect.height + verticalGap * 2
top += addedToTop

View File

@@ -5,7 +5,7 @@
position: sticky;
top: 0;
width: 100%;
z-index: 2;
z-index: 5;
display: flex;
align-items: center;
@@ -27,7 +27,7 @@
align-items: center;
gap: var(--base);
padding-bottom: 1px;
z-index: 1;
z-index: 4;
height: var(--doc-controls-height);
}