Skip to content

Commit

Permalink
Adjusting logic of finding widget to select on mouse click.
Browse files Browse the repository at this point in the history
  • Loading branch information
niegowski committed Aug 21, 2024
1 parent 38d51b1 commit 26c970c
Showing 1 changed file with 43 additions and 47 deletions.
90 changes: 43 additions & 47 deletions packages/ckeditor5-widget/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import {
type Element,
type Node,
type ViewDocumentArrowKeyEvent,
type ViewDocumentFragment,
type ViewDocumentMouseDownEvent,
type ViewElement,
type Schema,
type Position,
type EditingView,
type ViewDocumentTabEvent,
type ViewDocumentKeyDownEvent
} from '@ckeditor/ckeditor5-engine';
Expand Down Expand Up @@ -291,40 +291,23 @@ export default class Widget extends Plugin {

// If target is not a widget element - check if one of the ancestors is.
if ( !isWidget( element ) ) {
const widgetElement = element.findAncestor( isWidget );
const editableOrWidgetElement = findClosestEditableOrWidgetAncestor( element );

if ( !widgetElement || isInsideNestedEditable( element ) ) {
const editableElement = findClosestEditableAncestor( element );

if ( !editableElement || !element.childCount ) {
return;
}
if ( !editableOrWidgetElement ) {
return;
}

if ( isWidget( editableOrWidgetElement ) ) {
element = editableOrWidgetElement;
} else {
// Pick view range from the point where the mouse was clicked.
const clickTargetFromPoint = ( () => {
const domRange = getRangeFromMouseEvent( domEventData!.domEvent );
const viewRange = domRange && view.domConverter.domRangeToView( domRange );

return viewRange && viewRange.start.parent;
} )();
const clickTargetFromPoint = getElementFromMouseEvent( view, domEventData );

// If the click target is a text node, we need to abort heuristics. This is because the text node
// can be draggable and focusing the parent node breaks the drag and drop.
if ( !clickTargetFromPoint || clickTargetFromPoint.is( '$text' ) ) {
if ( clickTargetFromPoint && isWidget( clickTargetFromPoint ) ) {
element = clickTargetFromPoint;
} else {
return;
}

// If the element is a widget, we need to select the widget itself otherwise we need to select the first ancestor widget.
if ( clickTargetFromPoint && clickTargetFromPoint.is( 'element' ) ) {
element = isWidget( clickTargetFromPoint ) ?
clickTargetFromPoint : clickTargetFromPoint.findAncestor( isWidget );
}

if ( !element ) {
return;
}
} else {
element = widgetElement;
}
}

Expand Down Expand Up @@ -638,37 +621,50 @@ export default class Widget extends Plugin {
}

/**
* Returns `true` when element is a nested editable or is placed inside one.
* TODO
*/
function isInsideNestedEditable( element: ViewElement ) {
let currentElement: ViewElement | ViewDocumentFragment | null = element;
function findClosestEditableOrWidgetAncestor( element: ViewElement ): ViewElement | null {
let currentElement: ViewElement | null = element;

while ( currentElement ) {
if ( currentElement.is( 'editableElement' ) && !currentElement.is( 'rootElement' ) ) {
return true;
if ( currentElement.is( 'editableElement' ) || isWidget( currentElement ) ) {
return currentElement;
}

// Click on nested widget should select it.
if ( isWidget( currentElement ) ) {
return false;
}

currentElement = currentElement.parent;
currentElement = currentElement.parent as ViewElement | null;
}

/* istanbul ignore next -- @preserve */
return false;
return null;
}

/**
* Returns the closest editable ancestor element, it includes the element itself.
* TODO
*/
function findClosestEditableAncestor( element: ViewElement ) {
if ( element.is( 'editableElement' ) ) {
return element;
function getElementFromMouseEvent( view: EditingView, domEventData: DomEventData<MouseEvent> ): ViewElement | null {
const domRange = getRangeFromMouseEvent( domEventData.domEvent );

if ( !domRange ) {
return null;
}

const viewRange = view.domConverter.domRangeToView( domRange );

if ( !viewRange ) {
return null;
}

const viewPosition = viewRange.start;

// Click after a widget tend to return position at the end of the editable element
// so use the node before it if range is at the end of a parent.
const viewNode = viewPosition.parent.is( 'editableElement' ) && viewPosition.isAtEnd && viewPosition.nodeBefore || viewPosition.parent;

// TODO There was a comment about broken drag&drop but without further explanation. What was happening exactly?
if ( viewNode.is( '$text' ) ) {
return viewNode.parent as ViewElement;
}

return element.findAncestor( element => element.is( 'editableElement' ) );
return viewNode as ViewElement;
}

/**
Expand Down

0 comments on commit 26c970c

Please sign in to comment.