-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Add Safari workaround inside shadow DOM. #5648
Changes from 2 commits
ef538e2
98429a0
c6dbf93
aded41d
b1710e0
b19abdf
65ad49c
346ec53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ | |
DOMElement, | ||
DOMRange, | ||
DOMText, | ||
getActiveElement, | ||
getDefaultView, | ||
isDOMElement, | ||
isDOMNode, | ||
|
@@ -156,6 +157,7 @@ | |
const [placeholderHeight, setPlaceholderHeight] = useState< | ||
number | undefined | ||
>() | ||
const processing = useRef(false) | ||
|
||
const { onUserInput, receivedUserInput } = useTrackUserInput() | ||
|
||
|
@@ -202,6 +204,32 @@ | |
const onDOMSelectionChange = useMemo( | ||
() => | ||
throttle(() => { | ||
const el = ReactEditor.toDOMNode(editor, editor) | ||
const root = el.getRootNode() | ||
const safariVersion = navigator.userAgent.match(/Version\/(\d+\.\d+)/) | ||
const isSafariVersionLessThan17 = | ||
IS_WEBKIT && safariVersion && parseFloat(safariVersion[1]) < 17.0 | ||
|
||
if ( | ||
isSafariVersionLessThan17 && | ||
!processing.current && | ||
IS_WEBKIT && | ||
root instanceof ShadowRoot | ||
) { | ||
processing.current = true | ||
|
||
const active = getActiveElement() | ||
|
||
if (active) { | ||
document.execCommand('indent') | ||
} else { | ||
Transforms.deselect(editor) | ||
} | ||
|
||
processing.current = false | ||
return | ||
} | ||
|
||
const androidInputManager = androidInputManagerRef.current | ||
if ( | ||
(IS_ANDROID || !ReactEditor.isComposing(editor)) && | ||
|
@@ -471,6 +499,39 @@ | |
// https://github.com/facebook/react/issues/11211 | ||
const onDOMBeforeInput = useCallback( | ||
(event: InputEvent) => { | ||
const el = ReactEditor.toDOMNode(editor, editor) | ||
const root = el.getRootNode() | ||
const safariVersion = navigator.userAgent.match(/Version\/(\d+\.\d+)/) | ||
const isSafariVersionLessThan17 = | ||
IS_WEBKIT && safariVersion && parseFloat(safariVersion[1]) < 17.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated in c6dbf93. |
||
|
||
if ( | ||
isSafariVersionLessThan17 && | ||
processing?.current && | ||
IS_WEBKIT && | ||
root instanceof ShadowRoot | ||
) { | ||
// @ts-ignore | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is ignore needed here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. leftover aded41d |
||
const ranges = event.getTargetRanges() | ||
const range = ranges[0] | ||
|
||
const newRange = new window.Range() | ||
|
||
newRange.setStart(range.startContainer, range.startOffset) | ||
newRange.setEnd(range.endContainer, range.endOffset) | ||
|
||
// Translate the DOM Range into a Slate Range | ||
const slateRange = ReactEditor.toSlateRange(editor, newRange, { | ||
exactMatch: false, | ||
suppressThrow: false, | ||
}) | ||
|
||
Transforms.select(editor, slateRange) | ||
|
||
event.preventDefault() | ||
event.stopImmediatePropagation() | ||
return | ||
} | ||
onUserInput() | ||
|
||
if ( | ||
|
@@ -921,17 +982,17 @@ | |
...(disableDefaultStyles | ||
? {} | ||
: { | ||
// Allow positioning relative to the editable element. | ||
position: 'relative', | ||
// Preserve adjacent whitespace and new lines. | ||
whiteSpace: 'pre-wrap', | ||
// Allow words to break if they are too long. | ||
wordWrap: 'break-word', | ||
// Make the minimum height that of the placeholder. | ||
...(placeholderHeight | ||
? { minHeight: placeholderHeight } | ||
: {}), | ||
}), | ||
// Allow positioning relative to the editable element. | ||
position: 'relative', | ||
// Preserve adjacent whitespace and new lines. | ||
whiteSpace: 'pre-wrap', | ||
// Allow words to break if they are too long. | ||
wordWrap: 'break-word', | ||
// Make the minimum height that of the placeholder. | ||
...(placeholderHeight | ||
? { minHeight: placeholderHeight } | ||
: {}), | ||
}), | ||
// Allow for passed-in styles to override anything. | ||
...userStyle, | ||
}} | ||
|
@@ -1414,7 +1475,7 @@ | |
const element = | ||
editor.children[ | ||
selection !== null ? selection.focus.path[0] : 0 | ||
] | ||
] | ||
const isRTL = getDirection(Node.string(element)) === 'rtl' | ||
|
||
// COMPAT: Since we prevent the default behavior on | ||
|
@@ -1722,9 +1783,9 @@ | |
*/ | ||
|
||
export const DefaultPlaceholder = ({ | ||
attributes, | ||
children, | ||
}: RenderPlaceholderProps) => ( | ||
attributes, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure this linting change was intentional? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, it wasn't intentional. Corrected here -> aded41d |
||
children, | ||
}: RenderPlaceholderProps) => ( | ||
// COMPAT: Artificially add a line-break to the end on the placeholder element | ||
// to prevent Android IMEs to pick up its content in autocorrect and to auto-capitalize the first letter | ||
<span {...attributes}> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -170,7 +170,7 @@ export const getEditableChildAndIndex = ( | |
isDOMComment(child) || | ||
(isDOMElement(child) && child.childNodes.length === 0) || | ||
(isDOMElement(child) && child.getAttribute('contenteditable') === 'false') | ||
) { | ||
) { | ||
if (triedForward && triedBackward) { | ||
break | ||
} | ||
|
@@ -314,3 +314,18 @@ export const isTrackedMutation = ( | |
// Target add/remove is tracked. Track the mutation if we track the parent mutation. | ||
return isTrackedMutation(editor, parentMutation, batch) | ||
} | ||
|
||
export function getActiveElement() { | ||
let active = document.activeElement | ||
|
||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why while(true)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is for allowing deep traversal through nested shadow roots to find the actual active element. I think b1710e0 version is better as it places the continuation conditions in the loop to make it visible and understandable. |
||
if (active && active.shadowRoot && active.shadowRoot.activeElement) { | ||
active = active.shadowRoot.activeElement | ||
} else { | ||
break | ||
} | ||
} | ||
|
||
return active | ||
} |
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.
New browser version checks should live inside slate-react/src/utils/environment.ts so they're easy to find/consist/reused and then imported. Thanks.
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.
Updated in c6dbf93.