mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-12 10:14:02 +02:00
Fix Copy/pasting void elements is not working (#5121)
* Create new function hasSelectableTarget and use it instead of hasEditableTarget. Fixes Copy/pasting void elements is not working https://github.com/ianstormtaylor/slate/issues/4808 * Add changeset * Revert a change that made editable void not editable and add cypress test for editing editable void * Extract methoods into easily overridable with help from @alex-vladut
This commit is contained in:
committed by
GitHub
parent
6efe3d9a22
commit
06942c6d7e
@@ -206,12 +206,12 @@ export const Editable = (props: EditableProps) => {
|
||||
const { anchorNode, focusNode } = domSelection
|
||||
|
||||
const anchorNodeSelectable =
|
||||
hasEditableTarget(editor, anchorNode) ||
|
||||
isTargetInsideNonReadonlyVoid(editor, anchorNode)
|
||||
ReactEditor.hasEditableTarget(editor, anchorNode) ||
|
||||
ReactEditor.isTargetInsideNonReadonlyVoid(editor, anchorNode)
|
||||
|
||||
const focusNodeSelectable =
|
||||
hasEditableTarget(editor, focusNode) ||
|
||||
isTargetInsideNonReadonlyVoid(editor, focusNode)
|
||||
ReactEditor.hasEditableTarget(editor, focusNode) ||
|
||||
ReactEditor.isTargetInsideNonReadonlyVoid(editor, focusNode)
|
||||
|
||||
if (anchorNodeSelectable && focusNodeSelectable) {
|
||||
const range = ReactEditor.toSlateRange(editor, domSelection, {
|
||||
@@ -434,7 +434,7 @@ export const Editable = (props: EditableProps) => {
|
||||
|
||||
if (
|
||||
!readOnly &&
|
||||
hasEditableTarget(editor, event.target) &&
|
||||
ReactEditor.hasEditableTarget(editor, event.target) &&
|
||||
!isDOMEventHandled(event, propsOnDOMBeforeInput)
|
||||
) {
|
||||
// COMPAT: BeforeInput events aren't cancelable on android, so we have to handle them differently using the android input manager.
|
||||
@@ -861,7 +861,7 @@ export const Editable = (props: EditableProps) => {
|
||||
!HAS_BEFORE_INPUT_SUPPORT &&
|
||||
!readOnly &&
|
||||
!isEventHandled(event, attributes.onBeforeInput) &&
|
||||
hasEditableTarget(editor, event.target)
|
||||
ReactEditor.hasSelectableTarget(editor, event.target)
|
||||
) {
|
||||
event.preventDefault()
|
||||
if (!ReactEditor.isComposing(editor)) {
|
||||
@@ -892,7 +892,7 @@ export const Editable = (props: EditableProps) => {
|
||||
if (
|
||||
readOnly ||
|
||||
state.isUpdatingSelection ||
|
||||
!hasEditableTarget(editor, event.target) ||
|
||||
!ReactEditor.hasSelectableTarget(editor, event.target) ||
|
||||
isEventHandled(event, attributes.onBlur)
|
||||
) {
|
||||
return
|
||||
@@ -956,7 +956,7 @@ export const Editable = (props: EditableProps) => {
|
||||
onClick={useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
hasTarget(editor, event.target) &&
|
||||
ReactEditor.hasTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onClick) &&
|
||||
isDOMNode(event.target)
|
||||
) {
|
||||
@@ -1013,7 +1013,7 @@ export const Editable = (props: EditableProps) => {
|
||||
)}
|
||||
onCompositionEnd={useCallback(
|
||||
(event: React.CompositionEvent<HTMLDivElement>) => {
|
||||
if (hasEditableTarget(editor, event.target)) {
|
||||
if (ReactEditor.hasSelectableTarget(editor, event.target)) {
|
||||
if (ReactEditor.isComposing(editor)) {
|
||||
setIsComposing(false)
|
||||
IS_COMPOSING.set(editor, false)
|
||||
@@ -1067,7 +1067,7 @@ export const Editable = (props: EditableProps) => {
|
||||
onCompositionUpdate={useCallback(
|
||||
(event: React.CompositionEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
hasEditableTarget(editor, event.target) &&
|
||||
ReactEditor.hasSelectableTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onCompositionUpdate)
|
||||
) {
|
||||
if (!ReactEditor.isComposing(editor)) {
|
||||
@@ -1080,7 +1080,7 @@ export const Editable = (props: EditableProps) => {
|
||||
)}
|
||||
onCompositionStart={useCallback(
|
||||
(event: React.CompositionEvent<HTMLDivElement>) => {
|
||||
if (hasEditableTarget(editor, event.target)) {
|
||||
if (ReactEditor.hasSelectableTarget(editor, event.target)) {
|
||||
androidInputManager?.handleCompositionStart(event)
|
||||
|
||||
if (
|
||||
@@ -1120,7 +1120,7 @@ export const Editable = (props: EditableProps) => {
|
||||
onCopy={useCallback(
|
||||
(event: React.ClipboardEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
hasEditableTarget(editor, event.target) &&
|
||||
ReactEditor.hasSelectableTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onCopy)
|
||||
) {
|
||||
event.preventDefault()
|
||||
@@ -1137,7 +1137,7 @@ export const Editable = (props: EditableProps) => {
|
||||
(event: React.ClipboardEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
!readOnly &&
|
||||
hasEditableTarget(editor, event.target) &&
|
||||
ReactEditor.hasSelectableTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onCut)
|
||||
) {
|
||||
event.preventDefault()
|
||||
@@ -1165,7 +1165,7 @@ export const Editable = (props: EditableProps) => {
|
||||
onDragOver={useCallback(
|
||||
(event: React.DragEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
hasTarget(editor, event.target) &&
|
||||
ReactEditor.hasTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onDragOver)
|
||||
) {
|
||||
// Only when the target is void, call `preventDefault` to signal
|
||||
@@ -1184,7 +1184,7 @@ export const Editable = (props: EditableProps) => {
|
||||
(event: React.DragEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
!readOnly &&
|
||||
hasTarget(editor, event.target) &&
|
||||
ReactEditor.hasTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onDragStart)
|
||||
) {
|
||||
const node = ReactEditor.toSlateNode(editor, event.target)
|
||||
@@ -1215,7 +1215,7 @@ export const Editable = (props: EditableProps) => {
|
||||
(event: React.DragEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
!readOnly &&
|
||||
hasTarget(editor, event.target) &&
|
||||
ReactEditor.hasTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onDrop)
|
||||
) {
|
||||
event.preventDefault()
|
||||
@@ -1260,7 +1260,7 @@ export const Editable = (props: EditableProps) => {
|
||||
!readOnly &&
|
||||
state.isDraggingInternally &&
|
||||
attributes.onDragEnd &&
|
||||
hasTarget(editor, event.target)
|
||||
ReactEditor.hasTarget(editor, event.target)
|
||||
) {
|
||||
attributes.onDragEnd(event)
|
||||
}
|
||||
@@ -1277,7 +1277,7 @@ export const Editable = (props: EditableProps) => {
|
||||
if (
|
||||
!readOnly &&
|
||||
!state.isUpdatingSelection &&
|
||||
hasEditableTarget(editor, event.target) &&
|
||||
ReactEditor.hasSelectableTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onFocus)
|
||||
) {
|
||||
const el = ReactEditor.toDOMNode(editor, editor)
|
||||
@@ -1299,7 +1299,10 @@ export const Editable = (props: EditableProps) => {
|
||||
)}
|
||||
onKeyDown={useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (!readOnly && hasEditableTarget(editor, event.target)) {
|
||||
if (
|
||||
!readOnly &&
|
||||
ReactEditor.hasEditableTarget(editor, event.target)
|
||||
) {
|
||||
androidInputManager?.handleKeyDown(event)
|
||||
|
||||
const { nativeEvent } = event
|
||||
@@ -1573,7 +1576,7 @@ export const Editable = (props: EditableProps) => {
|
||||
(event: React.ClipboardEvent<HTMLDivElement>) => {
|
||||
if (
|
||||
!readOnly &&
|
||||
hasEditableTarget(editor, event.target) &&
|
||||
ReactEditor.hasSelectableTarget(editor, event.target) &&
|
||||
!isEventHandled(event, attributes.onPaste)
|
||||
) {
|
||||
// COMPAT: Certain browsers don't support the `beforeinput` event, so we
|
||||
@@ -1668,46 +1671,6 @@ const defaultScrollSelectionIntoView = (
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the target is in the editor.
|
||||
*/
|
||||
|
||||
export const hasTarget = (
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
): target is DOMNode => {
|
||||
return isDOMNode(target) && ReactEditor.hasDOMNode(editor, target)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the target is editable and in the editor.
|
||||
*/
|
||||
|
||||
export const hasEditableTarget = (
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
): target is DOMNode => {
|
||||
return (
|
||||
isDOMNode(target) &&
|
||||
ReactEditor.hasDOMNode(editor, target, { editable: true })
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the target is inside void and in an non-readonly editor.
|
||||
*/
|
||||
|
||||
export const isTargetInsideNonReadonlyVoid = (
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
): boolean => {
|
||||
if (IS_READ_ONLY.get(editor)) return false
|
||||
|
||||
const slateNode =
|
||||
hasTarget(editor, target) && ReactEditor.toSlateNode(editor, target)
|
||||
return Editor.isVoid(editor, slateNode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an event is overrided by a handler.
|
||||
*/
|
||||
|
@@ -33,6 +33,7 @@ import {
|
||||
DOMStaticRange,
|
||||
isDOMElement,
|
||||
isDOMSelection,
|
||||
isDOMNode,
|
||||
normalizeDOMPoint,
|
||||
hasShadowRoot,
|
||||
DOMText,
|
||||
@@ -52,6 +53,22 @@ export interface ReactEditor extends BaseEditor {
|
||||
originEvent?: 'drag' | 'copy' | 'cut'
|
||||
) => void
|
||||
hasRange: (editor: ReactEditor, range: Range) => boolean
|
||||
hasTarget: (
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
) => target is DOMNode
|
||||
hasEditableTarget: (
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
) => target is DOMNode
|
||||
hasSelectableTarget: (
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
) => boolean
|
||||
isTargetInsideNonReadonlyVoid: (
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
) => boolean
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-redeclare
|
||||
@@ -779,6 +796,57 @@ export const ReactEditor = {
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the target is in the editor.
|
||||
*/
|
||||
hasTarget(
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
): target is DOMNode {
|
||||
return isDOMNode(target) && ReactEditor.hasDOMNode(editor, target)
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the target is editable and in the editor.
|
||||
*/
|
||||
hasEditableTarget(
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
): target is DOMNode {
|
||||
return (
|
||||
isDOMNode(target) &&
|
||||
ReactEditor.hasDOMNode(editor, target, { editable: true })
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the target can be selectable
|
||||
*/
|
||||
hasSelectableTarget(
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
): boolean {
|
||||
return (
|
||||
ReactEditor.hasEditableTarget(editor, target) ||
|
||||
ReactEditor.isTargetInsideNonReadonlyVoid(editor, target)
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the target is inside void and in an non-readonly editor.
|
||||
*/
|
||||
isTargetInsideNonReadonlyVoid(
|
||||
editor: ReactEditor,
|
||||
target: EventTarget | null
|
||||
): boolean {
|
||||
if (IS_READ_ONLY.get(editor)) return false
|
||||
|
||||
const slateNode =
|
||||
ReactEditor.hasTarget(editor, target) &&
|
||||
ReactEditor.toSlateNode(editor, target)
|
||||
return Editor.isVoid(editor, slateNode)
|
||||
},
|
||||
|
||||
/**
|
||||
* Experimental and android specific: Flush all pending diffs and cancel composition at the next possible time.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user