Skip to content

Commit

Permalink
Adjust widget mousedown handler to select first widget if focused ele…
Browse files Browse the repository at this point in the history
…ment is not widget
  • Loading branch information
Mati365 committed Aug 16, 2024
1 parent 09e2684 commit 422ec79
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 39 deletions.
27 changes: 2 additions & 25 deletions packages/ckeditor5-clipboard/src/clipboardobserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { EventInfo } from '@ckeditor/ckeditor5-utils';
import {
DataTransfer,
DomEventObserver,
getPointViewRange,
type DomEventData,
type EditingView,
type ViewDocumentFragment,
Expand Down Expand Up @@ -92,7 +93,7 @@ export default class ClipboardObserver extends DomEventObserver<
};

if ( domEvent.type == 'drop' || domEvent.type == 'dragover' ) {
evtData.dropRange = getDropViewRange( this.view, domEvent as DragEvent );
evtData.dropRange = getPointViewRange( this.view, domEvent as DragEvent );
}

this.fire( domEvent.type, domEvent, evtData );
Expand All @@ -115,30 +116,6 @@ export interface ClipboardEventData {
dropRange?: ViewRange | null;
}

function getDropViewRange( view: EditingView, domEvent: DragEvent & { rangeParent?: Node; rangeOffset?: number } ) {
const domDoc = ( domEvent.target as Node ).ownerDocument!;
const x = domEvent.clientX;
const y = domEvent.clientY;
let domRange;

// Webkit & Blink.
if ( domDoc.caretRangeFromPoint && domDoc.caretRangeFromPoint( x, y ) ) {
domRange = domDoc.caretRangeFromPoint( x, y );
}
// FF.
else if ( domEvent.rangeParent ) {
domRange = domDoc.createRange();
domRange.setStart( domEvent.rangeParent, domEvent.rangeOffset! );
domRange.collapse( true );
}

if ( domRange ) {
return view.domConverter.domRangeToView( domRange );
}

return null;
}

/**
* Fired as a continuation of the {@link module:engine/view/document~Document#event:paste} and
* {@link module:engine/view/document~Document#event:drop} events.
Expand Down
5 changes: 4 additions & 1 deletion packages/ckeditor5-editor-multi-root/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"devDependencies": {
"@ckeditor/ckeditor5-basic-styles": "43.0.0",
"@ckeditor/ckeditor5-dev-utils": "^42.0.0",
"@ckeditor/ckeditor5-essentials": "43.0.0",
"@ckeditor/ckeditor5-enter": "43.0.0",
"@ckeditor/ckeditor5-heading": "43.0.0",
"@ckeditor/ckeditor5-paragraph": "43.0.0",
Expand All @@ -32,6 +31,10 @@
"@ckeditor/ckeditor5-undo": "43.0.0",
"@ckeditor/ckeditor5-clipboard": "43.0.0",
"@ckeditor/ckeditor5-watchdog": "43.0.0",
"@ckeditor/ckeditor5-image": "43.0.0",
"@ckeditor/ckeditor5-link": "43.0.0",
"@ckeditor/ckeditor5-adapter-ckfinder": "43.0.0",
"@ckeditor/ckeditor5-ckfinder": "43.0.0",
"typescript": "5.0.4",
"webpack": "^5.58.1",
"webpack-cli": "^4.9.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
<head>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://ckeditor.com 'unsafe-inline' 'unsafe-eval'">
</head>

<script src="https://ckeditor.com/apps/ckfinder/3.5.0/ckfinder.js"></script>

<p>
<button id="destroyEditor">Destroy editor</button>
<button id="initEditor">Init editor</button>
Expand All @@ -12,7 +18,19 @@ <h2>The toolbar</h2>
<h2>The editable</h2>
<div class="editable-container">
<div id="editor-intro"><p><strong>Exciting</strong> intro text to an article.</p></div>
<div id="editor-content"><h2>Exciting news!</h2><p>Lorem ipsum dolor sit amet.</p></div>
<div id="editor-content">
<table>
<tr>
<td>First cell</td>
<td>Second cell</td>
</tr>
</table>

<br />
<br />

<p>Hello World</p>
</div>
<div id="editor-outro"><p>Closing text.</p></div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import Heading from '@ckeditor/ckeditor5-heading/src/heading.js';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph.js';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold.js';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic.js';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials.js';
import Image from '@ckeditor/ckeditor5-image/src/image.js';
import AutoImage from '@ckeditor/ckeditor5-image/src/autoimage.js';
import ImageInsert from '@ckeditor/ckeditor5-image/src/imageinsert.js';
import LinkImage from '@ckeditor/ckeditor5-link/src/linkimage.js';
import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset.js';
import CKFinderUploadAdapter from '@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter.js';
import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder.js';

const editorData = {
intro: document.querySelector( '#editor-intro' ),
Expand All @@ -23,8 +29,26 @@ let editor;
function initEditor() {
MultiRootEditor
.create( editorData, {
plugins: [ Essentials, Paragraph, Heading, Bold, Italic ],
toolbar: [ 'heading', '|', 'bold', 'italic', 'undo', 'redo' ]
plugins: [
Paragraph, Heading, Bold, Italic,
Image, ImageInsert, AutoImage, LinkImage,
ArticlePluginSet, CKFinderUploadAdapter, CKFinder
],
toolbar: [
'heading', '|', 'bold', 'italic', 'undo', 'redo', '|',
'insertImage', 'insertTable', 'blockQuote'
],
image: {
toolbar: [
'imageStyle:inline', 'imageStyle:block',
'imageStyle:wrapText', '|', 'toggleImageCaption',
'imageTextAlternative'
]
},
ckfinder: {
// eslint-disable-next-line max-len
uploadUrl: 'https://ckeditor.com/apps/ckfinder/3.5.0/core/connector/php/connector.php?command=QuickUpload&type=Files&responseType=json'
}
} )
.then( newEditor => {
console.log( 'Editor was initialized', newEditor );
Expand Down Expand Up @@ -55,3 +79,5 @@ function destroyEditor() {

document.getElementById( 'initEditor' ).addEventListener( 'click', initEditor );
document.getElementById( 'destroyEditor' ).addEventListener( 'click', destroyEditor );

initEditor();
2 changes: 1 addition & 1 deletion packages/ckeditor5-engine/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export type { SelectionChangeRangeEvent } from './model/selection.js';
export { default as DataTransfer } from './view/datatransfer.js';
export { default as DomConverter } from './view/domconverter.js';
export { default as Renderer } from './view/renderer.js';
export { default as EditingView } from './view/view.js';
export { default as EditingView, getPointViewRange } from './view/view.js';
export { default as ViewDocument } from './view/document.js';
export { default as ViewText } from './view/text.js';
export { default as ViewElement, type ElementAttributes as ViewElementAttributes } from './view/element.js';
Expand Down
38 changes: 38 additions & 0 deletions packages/ckeditor5-engine/src/view/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,44 @@ export default class View extends /* #__PURE__ */ ObservableMixin() {
}
}

/**
* Get the view range from the given DOM event.
*
* @param view The view instance.
* @param domEvent The DOM event.
* @returns The view range or `null` if the range cannot be created.
*/
export function getPointViewRange(
view: View,
domEvent: MouseEvent & {
rangeParent?: HTMLElement;
rangeOffset?: number;
}
): Range | null {
const domDoc = ( domEvent.target as HTMLElement ).ownerDocument!;
const x = domEvent.clientX;
const y = domEvent.clientY;
let domRange;

// Webkit & Blink.
if ( domDoc.caretRangeFromPoint && domDoc.caretRangeFromPoint( x, y ) ) {
domRange = domDoc.caretRangeFromPoint( x, y );
}

// FF.
else if ( domEvent.rangeParent ) {
domRange = domDoc.createRange();
domRange.setStart( domEvent.rangeParent, domEvent.rangeOffset! );
domRange.collapse( true );
}

if ( domRange ) {
return view.domConverter.domRangeToView( domRange );
}

return null;
}

/**
* Fired after a topmost {@link module:engine/view/view~View#change change block} and all
* {@link module:engine/view/document~Document#registerPostFixer post-fixers} are executed.
Expand Down
50 changes: 42 additions & 8 deletions packages/ckeditor5-widget/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Plugin } from '@ckeditor/ckeditor5-core';
import {
MouseObserver,
TreeWalker,
getPointViewRange,
type DomEventData,
type DowncastSelectionEvent,
type DowncastWriter,
Expand Down Expand Up @@ -288,17 +289,39 @@ export default class Widget extends Plugin {
return;
}

// Do nothing for single or double click inside nested editable.
if ( isInsideNestedEditable( element ) ) {
return;
}

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

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

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

// Pick view range from the point where the mouse was clicked.
const clickTargetFromPoint = ( () => {
const range = getPointViewRange( view, domEventData!.domEvent );

return range && range.start.parent;
} )();

// If the click target is a text node, we need to get the parent element.
const clickElementFromPoint = clickTargetFromPoint && clickTargetFromPoint.is( '$text' ) ?
clickTargetFromPoint.parent : clickTargetFromPoint;

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

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

Expand Down Expand Up @@ -633,6 +656,17 @@ function isInsideNestedEditable( element: ViewElement ) {
return false;
}

/**
* Returns the closest editable ancestor element, it includes the element itself.
*/
function findClosestEditableAncestor( element: ViewElement ) {
if ( element.is( 'editableElement' ) ) {
return element;
}

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

/**
* Checks whether the specified `element` is a child of the `parent` element.
*
Expand Down

0 comments on commit 422ec79

Please sign in to comment.