-
Notifications
You must be signed in to change notification settings - Fork 2k
Add Lexical Shadow DOM Support #7790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add Lexical Shadow DOM Support #7790
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this seems like a very nice improvement, but I think we should consolidate some of these functions to reduce repetition and make usage simpler
* @param rootElement - The root element to check for shadow DOM context (optional) | ||
* @returns A Selection object or null if selection cannot be retrieved | ||
*/ | ||
export function getDOMSelection( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should really just be a different function that takes only the element and computes the window as necessary with getDefaultView
. This function would also be used in getDOMSelectionFromTarget
, which would eliminate that redundant code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to extract the common code from getDOMSelection
and getDOMSelectionFromTarget
, thank you.
const editorWindow = editor._window || window; | ||
const windowDocument = window.document; | ||
const domSelection = getDOMSelection(editorWindow); | ||
const domSelection = getDOMSelection(editorWindow, rootElement); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would make more sense to have a utility function to get the DOM selection from the editor object, e.g. getDOMSelectionForEditor(editor)
rather than force the caller to compute multiple variables based on the editor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added getDOMSelectionForEditor(editor)
, you're right, thank you.
export const DOM_DOCUMENT_TYPE = 9; | ||
export const DOM_DOCUMENT_FRAGMENT_TYPE = 11; | ||
|
||
// Shadow DOM API Types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module exports constants, not interfaces. These should be defined elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These interfaces should still be moved out of this file, this is a file for constants not types that are unrelated to those constants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are absolutely right. I moved some interfaces and types. I hope that soon they can be completely removed from the code when these APIs appear in @types/jsdom
.
const focusKey = focusNode.getKey(); | ||
const range = document.createRange(); | ||
const rootElement = editor.getRootElement(); | ||
const doc = rootElement ? rootElement.ownerDocument || document : document; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a reason for this not to use getDocumentFromElement
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this code, thank you.
There are conflicts with main that need to be resolved, and the lexical-website build is failing which usually means some invalid syntax or broken links in a markdown file or jsdoc string |
6eb6281
to
97600b9
Compare
97600b9
to
50abea0
Compare
50abea0
to
196266b
Compare
There's a new conflict to resolve in lexical-clipboard, looks simple to fix. I think you can just choose this version, I believe it's just fixing a similar bug that happens in mutli-document scenarios other than shadow DOM. |
bf99563
to
3f742e5
Compare
…al-selection][lexical-table][lexical-utils] Add Shadow DOM support
…al-selection][lexical-table][lexical-utils] Fix using getComposedRanges for all browsers
…al-selection][lexical-table][lexical-utils] Fix 1 symbol delete with backspace and cmd+backspace in Shadow DOM
…al-selection][lexical-table][lexical-utils] Fix delete words with opt+backspace in Shadow DOM
…al-selection][lexical-table][lexical-utils] Refactor code, fix unit tests and documentation for utils
…al-selection][lexical-table][lexical-utils] Optimize utils methods
…al-selection][lexical-table][lexical-utils] Move logic from lexical-rich-text to LexicalSelection
…al-selection][lexical-table][lexical-utils] Clean up createSelectionWithComposedRanges
…al-selection][lexical-table][lexical-utils] Clean up createSelectionWithComposedRanges
…al-selection][lexical-table][lexical-utils] Clean up LexicalSelection and international support
…al-selection][lexical-table][lexical-utils] Fix typings for Intl
…al-selection][lexical-table][lexical-utils] Clean up createSelectionWithComposedRanges
…al-selection][lexical-table][lexical-utils] Refactoring LexicalUtils and LexicalSelection
…al-selection][lexical-table][lexical-utils] Fix createSelectionWithComposedRanges and unit-tests
…al-selection][lexical-table][lexical-utils] Fix docs
…al-selection][lexical-table][lexical-utils] Refactor LexicalSelection for shadow-root
…al-selection][lexical-table][lexical-utils] Fix type in LexicalEvents
16931e1
to
93a9b7f
Compare
@etrepum , I refactored everything I could, please take another look. I couldn't completely get rid of copying the Selection object and working with text because the getComposedRanges method is used, which returns an array consisting of StaticRange, from which it is necessary to move to Selection. In LexicalSelection, the standard modify method does not work inside shadow-root, so the logic is implemented there if we are inside shadow-root. If you see something that can be done better, please let me know. |
Lexical Shadow DOM Support - Complete Implementation
Complete Shadow DOM support for Lexical editor implemented through a systematic 4-phase development approach.
🎯 Implementation Overview
This document describes the complete Shadow DOM support implementation in Lexical, developed through 4 systematic phases:
📊 Complete Feature Matrix
🌐 Browser Support Matrix
📋 Quick Start
Testing in Playground
Zero-Config Integration
🏗️ Technical Architecture
Core Solution Strategy
Problem: Standard DOM Selection APIs (
modify()
,getSelection()
) don't always work properly in Shadow DOM, especially for deletion operations.Solution:
getComposedRanges()
for browsers that support itmodify()
APIgetDOMSelection
calls across Lexical ecosystemKey Implementation Details
1. Selection API Enhancement
2. Deletion Command Handling
For Shadow DOM contexts, deletion operations use direct text manipulation instead of
Selection.modify()
:📁 File Structure Overview
✅ What's Working
Complete Deletion Command Support
Universal Ecosystem Integration
rootElement
parameterModern Web Standards
🧪 Testing Strategy
Comprehensive Test Coverage
Browser API Testing
🔧 Implementation Notes
Why Not shadowRoot.getSelection()?
The experimental
shadowRoot.getSelection()
API was evaluated but not used because:getComposedRanges()
getComposedRanges()
provides more reliable resultsDirect Text Manipulation
For deletion operations in Shadow DOM, we use direct text manipulation because:
🎯 Conclusion
This Shadow DOM implementation provides complete parity between standard DOM and Shadow DOM functionality in Lexical editor:
Start testing today: Enable Shadow DOM in the playground and experience seamless rich text editing within web components!
Browser Support: Chrome 125+, Firefox 132+, Safari 17+, Edge 125+
Status: Production Ready ✅