-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
Use shadow dom if available #3749
Use shadow dom if available #3749
Conversation
@CameronAckermanSEL would be great if you could have a look. THX! |
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 found myself facing the same problem so thanks for putting this PR together!
*/ | ||
|
||
export const getDocumentOrShadowRoot = (): Document | ShadowRoot => { | ||
return window.document.activeElement?.shadowRoot ?? window.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.
This seems work for one level of shadowRoot but not if they are nested. I think it needs to be done bottom up from the element in question to find the shadowRoot that contains that element.
I've only come across searches up the node tree to find the appropriate root with something like below (illustrative only). It's probably something that'd be cached on the editor for use when it's needed.
let n = editor;
for (let n = n.parentNode; n; n = n.parentNode) {
if (n instanceof Document || n instanceof ShadowRoot) break;
}
const documentOrShadowRoot = n || window.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.
Hmm, interesting. Could you provide a simple example codesandbox (or something similar)?
You can look at the example I created in this PR as a starting point.
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 forked your branch of this pull request and updated the Shadow DOM example to also include a nested Shadow DOM.
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've updated that branch with a commit that gets the nested version working too. It's far from being PR-ready and there are some behaviours that I can't yet fully explain so if you're more familiar with these APIs it'd be great to hear what you think.
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 had another go at this (branch) after coming across getRootNode()
to avoid the tree traversal. However, I'm still running into an issue with key events that seems to be a React issue with shadow DOM. On Chrome, the undo key combo doesn't trigger the event handler and so the undo doesn't happen.
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.
Nice work. I merged your changes into this PR. I've also updated a few smaller things (sorry for the mess with all those commits. I was trying out a few things...)
@CameronAckermanSEL , @ianstormtaylor , @BrentFarese , @timbuckley could one of you guys please have a look at this PR? |
Hey @flavordaaave thanks for putting this PR together! I also have this use case and am wondering what the status of this PR is. I've really enjoyed Slate in other projects and would like to use it again. Thank you! |
Hey @ellenblaine unfortunately none of the maintainers seems to be interested in reviewing/merging this PR which is why we have created our own (internal) fork for now. We still hope that at some point slate will be compatible with beeing rendered within a ShadowDom. |
|
||
if (!(root instanceof Document || root instanceof ShadowRoot)) | ||
throw new Error( | ||
`Unable to find DocumentOrShadowRoot for editor element: ${el}` |
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.
Noticed an interesting issue experimenting with this branch myself: The selectionchange
event fires as the editor unmounts (we have a drawer component that contains the editor -- closing the drawer causes selectionchange
to fire). When this happens, the selectionchange
event fires and findDocumentOrShadowRoot
is called. This error is hit like so:
// ... selectionchange event calls this function ...
const el = ReactEditor.toDOMNode(editor, editor) // el is the editor, which isn't currently in the DOM
const root = el.getRootNode() // root is another element that is not in the DOM or the shadow root
// error throws because root is not Document or ShadowRoot
Any ideas? No worries if not -- this could definitely be an issue that's unrelated to Slate.
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 couldn't reproduce this myself, yet. We are also unmounting our slate editor when the user navigates to a different section/page but so far I didn't get this error.
Could you setup a minimal example illustrating this issue?
One idea how to get around this would be to call blur()
before the drawer is closed but obviously this would just be a temporally workaround.
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.
FWIW, I'm seeing this same behavior with an unmounting editor... I tried blur
ing it with a useEffect
but that's probably too late.. still errors, but this time in the ReactEditor.blur(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'm trying to fix an issue related to this change, can you explain why we need to throw an exception here @davidruisinger? I believe I'm missing context. For reference here's my draft pr for fixing the iframe crash, would be good to get some 👀 on it before I add cypress tests
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.
At the very least, these instanceof
checks should use getWindow
the way other similar checks do
slate/packages/slate-react/src/components/editable.tsx
Lines 383 to 384 in 6a13763
const window = ReactEditor.getWindow(editor) | |
if (data instanceof window.DataTransfer) { |
slate/packages/slate-react/src/utils/dom.ts
Lines 67 to 68 in 8a2a60b
const window = getDefaultView(value) | |
return !!window && value instanceof window.Node |
Hey @flavordaaave have you attempted to contact the maintainers through the Slate Slack channel by any chance? It looks like the maintainers have their hands full trying to establish a release system for Slate and dealing with issues with the default branch for this repo. Perhaps you might find better luck there raising an issue there since there's a lot of noise with the active issues/PRs here. |
@tomdng Yes, I posted the PR there as well ( |
Can we make this fix happen? Regularly utilize Slate inside a shadow DOM. PR looks good. |
* Moved getDirtyPaths() into the editor object so it can be customized via plugin
Co-authored-by: liuchengshuai001 <liuchengshuai001@ke.com>
Shadow DOM brings different behaviours for selection and active elements. This adds an example where the editor is found within a shadow DOM, in fact, the editor is two levels deep in nested shadow DOMs. The handling of selections means that this editor doesn't work properly so Slate will need to be made aware of the shadow DOM in order to fix this.
If the editor is within a ShadowDom, the selections and active element APIs are implemented on the ShadowRoot for Chrome. Other browsers still use the Document's version of these APIs for the shadow DOM. Instead of defaulting to `window.document`, find the appropriate root to use for the editor in question.
Chrome will always return true for isCollapsed on a selection from the shadow DOM. Work around this by instead computing this property on Chrome. https://bugs.chromium.org/p/chromium/issues/detail?id=447523
Unfortunately the issue still remains with React 17. So these changes are still needed to get slate working within a shadow DOM. I updated this PR to resolve all open conflicts with the master branch. |
🦋 Changeset detectedLatest commit: dcf48dd The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Is it possible that this PR is causing crash in Chromium, @davidruisinger ? See #4170 |
Indeed this brakes iframe support, here's is the code with the exception thrown: if (!(root instanceof Document || root instanceof ShadowRoot))
throw new Error(
`Unable to find DocumentOrShadowRoot for editor element: ${el}`
) for context: gets thrown if |
I'm getting |
- quick hack to test support for rendering inside of shadow dom - based on similar support added to Slate in ianstormtaylor/slate#3749
no issue - quick hack to test support for mobiledoc instances being rendered inside of shadow dom - based on similar support added to Slate in ianstormtaylor/slate#3749
Is this adding or improving a feature or fixing a bug?
bug
What's the new behavior?
If
<Editable />
is rendered within a shadowRoot, internaleditor.selection
is not updated correctly which (amongst other issues) leads to the cursor always jumping back to the beginning of the text while typing.How does this change work?
window.getSelection
(which is internally used inslate-react
) refers to the shadow host selection. Instead of always relying onwindow.document
I've added agetDocumentOrShadowRoot
util which either returns theShadowRoot
(if active Element is rendered within a ShadowDom) orDocument
.So instead of
window.getSelection()
we usegetDocumentOrShadowRoot ().getSelection()
Have you checked that...?
yarn test
.yarn lint
. (Fix errors withyarn fix
.)yarn start
.)