fix(richtext-lexical): lexical editor behind a drawer was incorrectly registered as parent editor of lexical editor within drawer (#10502)

Fixes https://github.com/payloadcms/payload/issues/10462

This behavior caused the fixed toolbar of the lexical editor within the
drawer to trigger overlap behavior of the fixed toolbar belonging to the
lexical editor behind the drawer.

Editors within drawers should be treated as separate, instead of being
able to form parent-child relationships between editors behind or in
nested drawers
This commit is contained in:
Alessio Gravili
2025-01-10 15:48:05 -07:00
committed by GitHub
parent 1af7d8745c
commit fd96a9afe3
3 changed files with 16 additions and 5 deletions

View File

@@ -1,9 +1,9 @@
'use client' 'use client'
import type { InitialConfigType } from '@lexical/react/LexicalComposer.js' import type { InitialConfigType } from '@lexical/react/LexicalComposer.js'
import type { EditorState, LexicalEditor, SerializedEditorState } from 'lexical' import type { EditorState, LexicalEditor, SerializedEditorState } from 'lexical'
import type { ClientField } from 'payload'
import { LexicalComposer } from '@lexical/react/LexicalComposer.js' import { LexicalComposer } from '@lexical/react/LexicalComposer.js'
import { useEditDepth } from '@payloadcms/ui'
import * as React from 'react' import * as React from 'react'
import { useMemo } from 'react' import { useMemo } from 'react'
@@ -53,6 +53,8 @@ export const LexicalProvider: React.FC<LexicalProviderProps> = (props) => {
const parentContext = useEditorConfigContext() const parentContext = useEditorConfigContext()
const editDepth = useEditDepth()
const editorContainerRef = React.useRef<HTMLDivElement>(null) const editorContainerRef = React.useRef<HTMLDivElement>(null)
// useMemo for the initialConfig that depends on readOnly and value // useMemo for the initialConfig that depends on readOnly and value
@@ -102,7 +104,10 @@ export const LexicalProvider: React.FC<LexicalProviderProps> = (props) => {
editorConfig={editorConfig} editorConfig={editorConfig}
editorContainerRef={editorContainerRef} editorContainerRef={editorContainerRef}
fieldProps={fieldProps} fieldProps={fieldProps}
parentContext={parentContext} /**
* Parent editor is not truly the parent editor, if the current editor is part of a drawer and the parent editor is the main editor.
*/
parentContext={parentContext?.editDepth === editDepth ? parentContext : undefined}
> >
<NestProviders providers={editorConfig.features.providers}> <NestProviders providers={editorConfig.features.providers}>
<LexicalEditorComponent <LexicalEditorComponent

View File

@@ -4,6 +4,7 @@ import type { LexicalEditor } from 'lexical'
import type { MarkRequired } from 'ts-essentials' import type { MarkRequired } from 'ts-essentials'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
import { useEditDepth } from '@payloadcms/ui'
import * as React from 'react' import * as React from 'react'
import { createContext, useContext, useMemo, useRef, useState } from 'react' import { createContext, useContext, useMemo, useRef, useState } from 'react'
@@ -21,9 +22,9 @@ export interface EditorConfigContextType {
blurEditor: (editorContext: EditorConfigContextType) => void blurEditor: (editorContext: EditorConfigContextType) => void
childrenEditors: React.RefObject<Map<string, EditorConfigContextType>> childrenEditors: React.RefObject<Map<string, EditorConfigContextType>>
createdInlineBlock?: InlineBlockNode createdInlineBlock?: InlineBlockNode
editDepth: number
editor: LexicalEditor editor: LexicalEditor
editorConfig: SanitizedClientEditorConfig editorConfig: SanitizedClientEditorConfig
editorContainerRef: React.RefObject<HTMLDivElement> editorContainerRef: React.RefObject<HTMLDivElement>
fieldProps: MarkRequired<LexicalRichTextFieldProps, 'path' | 'schemaPath'> fieldProps: MarkRequired<LexicalRichTextFieldProps, 'path' | 'schemaPath'>
focusedEditor: EditorConfigContextType | null focusedEditor: EditorConfigContextType | null
@@ -59,13 +60,15 @@ export const EditorConfigProvider = ({
}): React.ReactNode => { }): React.ReactNode => {
const [editor] = useLexicalComposerContext() const [editor] = useLexicalComposerContext()
// State to store the UUID // State to store the UUID
const [uuid] = useState(generateQuickGuid()) const [uuid] = useState(() => generateQuickGuid())
const childrenEditors = useRef<Map<string, EditorConfigContextType>>(new Map()) const childrenEditors = useRef<Map<string, EditorConfigContextType>>(new Map())
const [focusedEditor, setFocusedEditor] = useState<EditorConfigContextType | null>(null) const [focusedEditor, setFocusedEditor] = useState<EditorConfigContextType | null>(null)
const focusHistory = useRef<Set<string>>(new Set()) const focusHistory = useRef<Set<string>>(new Set())
const [createdInlineBlock, setCreatedInlineBlock] = useState<InlineBlockNode>() const [createdInlineBlock, setCreatedInlineBlock] = useState<InlineBlockNode>()
const editDepth = useEditDepth()
const editorContext = useMemo( const editorContext = useMemo(
() => () =>
({ ({
@@ -75,6 +78,7 @@ export const EditorConfigProvider = ({
}, },
childrenEditors, childrenEditors,
createdInlineBlock, createdInlineBlock,
editDepth,
editor, editor,
editorConfig, editorConfig,
editorContainerRef, editorContainerRef,
@@ -128,6 +132,7 @@ export const EditorConfigProvider = ({
childrenEditors, childrenEditors,
editorConfig, editorConfig,
editorContainerRef, editorContainerRef,
editDepth,
fieldProps, fieldProps,
focusedEditor, focusedEditor,
parentContext, parentContext,

View File

@@ -2,6 +2,7 @@ import type { CollectionConfig } from 'payload'
import { import {
defaultEditorFeatures, defaultEditorFeatures,
FixedToolbarFeature,
lexicalEditor, lexicalEditor,
RelationshipFeature, RelationshipFeature,
} from '@payloadcms/richtext-lexical' } from '@payloadcms/richtext-lexical'
@@ -30,7 +31,7 @@ export const LexicalRelationshipsFields: CollectionConfig = {
name: 'richText2', name: 'richText2',
type: 'richText', type: 'richText',
editor: lexicalEditor({ editor: lexicalEditor({
features: [...defaultEditorFeatures, RelationshipFeature()], features: [...defaultEditorFeatures, RelationshipFeature(), FixedToolbarFeature()],
}), }),
}, },
], ],