Преглед изворни кода

Added support for keyboard arrows handling when selection the widget "from the outside".

Aleksander Nowodzinski пре 5 година
родитељ
комит
bc3a51a831
1 измењених фајлова са 70 додато и 47 уклоњено
  1. 70 47
      packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js

+ 70 - 47
packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js

@@ -282,63 +282,86 @@ export default class WidgetTypeAround extends Plugin {
 		const editingView = editor.editing.view;
 
 		const keyCode = domEventData.keyCode;
+		const isForward = isForwardArrowKeyCode( keyCode, editor.locale.contentLanguageDirection );
 		const selectedViewElement = editingView.document.selection.getSelectedElement();
 		const selectedModelElement = editor.editing.mapper.toModelElement( selectedViewElement );
-
-		if ( !isTypeAroundWidget( selectedViewElement, selectedModelElement, schema ) ) {
-			return;
-		}
-
-		const isForward = isForwardArrowKeyCode( keyCode, editor.locale.contentLanguageDirection );
-		const typeAroundSelectionAttribute = modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE );
-
-		editor.model.change( writer => {
-			let shouldStopAndPreventDefault;
-
-			// If the selection already has the attribute...
-			if ( typeAroundSelectionAttribute ) {
-				const selectionPosition = isForward ? modelSelection.getLastPosition() : modelSelection.getFirstPosition();
-				const nearestSelectionRange = schema.getNearestSelectionRange( selectionPosition, isForward ? 'forward' : 'backward' );
-				const isLeavingWidget = typeAroundSelectionAttribute === ( isForward ? 'after' : 'before' );
-
-				// ...and the keyboard arrow matches the value of the selection attribute...
-				if ( isLeavingWidget ) {
-					// ...and if there is some place for the selection to go to...
-					if ( nearestSelectionRange ) {
-						// ...then just remove the attribute and let the default Widget plugin listener handle moving the selection.
+		let shouldStopAndPreventDefault;
+
+		// Handle keyboard navigation when a type-around-compatible widget is currently selected.
+		if ( isTypeAroundWidget( selectedViewElement, selectedModelElement, schema ) ) {
+			const typeAroundSelectionAttribute = modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE );
+
+			model.change( writer => {
+				// If the selection already has the attribute...
+				if ( typeAroundSelectionAttribute ) {
+					const selectionPosition = isForward ? modelSelection.getLastPosition() : modelSelection.getFirstPosition();
+					const nearestSelectionRange = schema.getNearestSelectionRange( selectionPosition, isForward ? 'forward' : 'backward' );
+					const isLeavingWidget = typeAroundSelectionAttribute === ( isForward ? 'after' : 'before' );
+
+					// ...and the keyboard arrow matches the value of the selection attribute...
+					if ( isLeavingWidget ) {
+						// ...and if there is some place for the selection to go to...
+						if ( nearestSelectionRange ) {
+							// ...then just remove the attribute and let the default Widget plugin listener handle moving the selection.
+							writer.removeSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE );
+						}
+
+						// If the selection had nowhere to go, let's leave the attribute as it was and pass through
+						// to the Widget plugin listener which will... in fact also do nothing. But this is no longer
+						// the problem of the WidgetTypeAround plugin.
+					}
+					// ...and the keyboard arrow works against the value of the selection attribute...
+					else {
+						// ...then remove the selection attribute but prevent default DOM actions
+						// and do not let the Widget plugin listener move the selection. This brings
+						// the widget back to the state, for instance, like if was selected using the mouse.
 						writer.removeSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE );
+						shouldStopAndPreventDefault = true;
 					}
-
-					// If the selection had nowhere to go, let's leave the attribute as it was and pass through
-					// to the Widget plugin listener which will... in fact also do nothing. But this is no longer
-					// the problem of the WidgetTypeAround plugin.
 				}
-				// ...and the keyboard arrow works against the value of the selection attribute...
+				// If the selection didn't have the attribute, let's set it now according to the direction of the arrow
+				// key press. This also means we cannot let the Widget plugin listener move the selection.
 				else {
-					// ...then remove the selection attribute but prevent default DOM actions
-					// and do not let the Widget plugin listener move the selection. This brings
-					// the widget back to the state, for instance, like if was selected using the mouse.
-					writer.removeSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE );
-					shouldStopAndPreventDefault = true;
-				}
-			}
-			// If the selection didn't have the attribute, let's set it now according to the direction of the arrow
-			// key press. This also means we cannot let the Widget plugin listener move the selection.
-			else {
-				if ( isForward ) {
-					writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'after' );
-					shouldStopAndPreventDefault = true;
-				} else {
-					writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
+					if ( isForward ) {
+						writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'after' );
+					} else {
+						writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
+					}
+
 					shouldStopAndPreventDefault = true;
 				}
-			}
+			} );
+		}
+		// Handle keyboard arrow navigation when the selection is next to a type-around-compatible widget
+		// and the widget is about to be selected.
+		//
+		// This code mirrors the implementation from the Widget plugin but also adds the selection attribute.
+		// Unfortunately, there's no safe way to let the Widget plugin do the selection part first
+		// and then just set the selection attribute here in the WidgetTypeAround plugin. This is why
+		// this code must duplicate some from the Widget plugin.
+		else if ( modelSelection.isCollapsed ) {
+			const widgetPlugin = editor.plugins.get( 'Widget' );
+
+			// This is the widget the selection is about to be set on.
+			const modelElementNextToSelection = widgetPlugin._getObjectElementNextToSelection( isForward );
+			const viewElementNextToSelection = editor.editing.mapper.toViewElement( modelElementNextToSelection );
+
+			if ( isTypeAroundWidget( viewElementNextToSelection, modelElementNextToSelection, schema ) ) {
+				model.change( writer => {
+					widgetPlugin._setSelectionOverElement( modelElementNextToSelection );
+					writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, isForward ? 'before' : 'after' );
+				} );
 
-			if ( shouldStopAndPreventDefault ) {
-				domEventData.preventDefault();
-				evt.stop();
+				// The change() block above does the same job as the Widget plugin. The event can
+				// be safely canceled.
+				shouldStopAndPreventDefault = true;
 			}
-		} );
+		}
+
+		if ( shouldStopAndPreventDefault ) {
+			domEventData.preventDefault();
+			evt.stop();
+		}
 	}
 
 	/**