From f9f6a3f60085daaaf72349b3c17f0d7e1c521185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 24 Jan 2019 16:11:24 +0100 Subject: [PATCH 01/14] Introduce inline widget manual test. --- tests/manual/inline-widget.html | 28 ++++++++++ tests/manual/inline-widget.js | 95 +++++++++++++++++++++++++++++++++ tests/manual/inline-widget.md | 3 ++ 3 files changed, 126 insertions(+) create mode 100644 tests/manual/inline-widget.html create mode 100644 tests/manual/inline-widget.js create mode 100644 tests/manual/inline-widget.md diff --git a/tests/manual/inline-widget.html b/tests/manual/inline-widget.html new file mode 100644 index 00000000..bbdf7811 --- /dev/null +++ b/tests/manual/inline-widget.html @@ -0,0 +1,28 @@ +
+

Hello name!

+ +

We would like to invite you to place that will take place on date.

+
+ +

Model contents:

+ +

+
+
diff --git a/tests/manual/inline-widget.js b/tests/manual/inline-widget.js
new file mode 100644
index 00000000..8b3c1e8e
--- /dev/null
+++ b/tests/manual/inline-widget.js
@@ -0,0 +1,95 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global console */
+
+import { getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import global from '@ckeditor/ckeditor5-utils/src/dom/global';
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+import Undo from '@ckeditor/ckeditor5-undo/src/undo';
+import Widget from '@ckeditor/ckeditor5-widget/src/widget';
+import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
+
+class InlineWidget extends Plugin {
+	constructor( editor ) {
+		super( editor );
+		editor.model.schema.register( 'placeholder', {
+			allowWhere: '$text',
+			isObject: true,
+			allowAttributes: [ 'type' ]
+		} );
+
+		editor.conversion.for( 'editingDowncast' ).elementToElement( {
+			model: 'placeholder',
+			view: ( modelItem, viewWriter ) => {
+				const widgetElement = createPlaceholderView( viewWriter, modelItem );
+
+				return toWidget( widgetElement, viewWriter );
+			}
+		} );
+
+		editor.conversion.for( 'dataDowncast' ).elementToElement( {
+			model: 'placeholder',
+			view: createPlaceholderView
+		} );
+
+		editor.conversion.for( 'upcast' ).elementToElement( {
+			view: 'placeholder',
+			model: ( viewElement, modelWriter ) => {
+				let type = 'general';
+
+				if ( viewElement.childCount ) {
+					const text = viewElement.getChild( 0 );
+
+					if ( text.is( 'text' ) ) {
+						type = text.data;
+					}
+				}
+
+				return modelWriter.createElement( 'placeholder', { type } );
+			}
+		} );
+
+		function createPlaceholderView( viewWriter, modelItem ) {
+			const widgetElement = viewWriter.createContainerElement( 'placeholder' );
+
+			viewWriter.insert( viewWriter.createPositionAt( widgetElement, 0 ), viewWriter.createText( modelItem.getAttribute( 'type' ) ) );
+			return widgetElement;
+		}
+	}
+}
+
+ClassicEditor
+	.create( global.document.querySelector( '#editor' ), {
+		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Widget, InlineWidget ],
+		toolbar: [ 'heading', '|', 'bold', 'undo', 'redo' ]
+	} )
+	.then( editor => {
+		editor.model.document.on( 'change', () => {
+			printModelContents( editor );
+		} );
+
+		printModelContents( editor );
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );
+
+const modelDiv = global.document.querySelector( '#model' );
+
+function printModelContents( editor ) {
+	modelDiv.innerText = formatData( getData( editor.model ) );
+}
+
+function formatData( data ) {
+	return data.replace( /<(paragraph)>/g, '\n<$1>' );
+}
diff --git a/tests/manual/inline-widget.md b/tests/manual/inline-widget.md
new file mode 100644
index 00000000..c9148446
--- /dev/null
+++ b/tests/manual/inline-widget.md
@@ -0,0 +1,3 @@
+### Inline widget
+
+* TODO

From 6b8bcd0170df5e81c8dc543c5b9f0597ad5d1e2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Thu, 24 Jan 2019 16:22:29 +0100
Subject: [PATCH 02/14] Add toolbar button for creating inline widget in manual
 test.

---
 tests/manual/inline-widget.js | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/tests/manual/inline-widget.js b/tests/manual/inline-widget.js
index 8b3c1e8e..fe693c8a 100644
--- a/tests/manual/inline-widget.js
+++ b/tests/manual/inline-widget.js
@@ -18,10 +18,12 @@ import Typing from '@ckeditor/ckeditor5-typing/src/typing';
 import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import Widget from '@ckeditor/ckeditor5-widget/src/widget';
 import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 
 class InlineWidget extends Plugin {
 	constructor( editor ) {
 		super( editor );
+
 		editor.model.schema.register( 'placeholder', {
 			allowWhere: '$text',
 			isObject: true,
@@ -65,13 +67,44 @@ class InlineWidget extends Plugin {
 			viewWriter.insert( viewWriter.createPositionAt( widgetElement, 0 ), viewWriter.createText( modelItem.getAttribute( 'type' ) ) );
 			return widgetElement;
 		}
+
+		this._createToolbarButton();
+	}
+
+	_createToolbarButton() {
+		const editor = this.editor;
+		const t = editor.t;
+
+		editor.ui.componentFactory.add( 'placeholder', locale => {
+			const buttonView = new ButtonView( locale );
+
+			buttonView.set( {
+				label: t( 'Insert placeholder' ),
+				tooltip: true,
+				withText: true
+			} );
+
+			this.listenTo( buttonView, 'execute', () => {
+				const model = editor.model;
+
+				model.change( writer => {
+					const placeholder = writer.createElement( 'placeholder', { type: 'placeholder' } );
+
+					model.insertContent( placeholder );
+
+					writer.setSelection( placeholder, 'on' );
+				} );
+			} );
+
+			return buttonView;
+		} );
 	}
 }
 
 ClassicEditor
 	.create( global.document.querySelector( '#editor' ), {
 		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Widget, InlineWidget ],
-		toolbar: [ 'heading', '|', 'bold', 'undo', 'redo' ]
+		toolbar: [ 'heading', '|', 'bold', '|', 'placeholder', '|', 'undo', 'redo' ]
 	} )
 	.then( editor => {
 		editor.model.document.on( 'change', () => {

From 9e8a4a5c66abc8ffb09b9a35c68d718343e74496 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Fri, 25 Jan 2019 17:01:55 +0100
Subject: [PATCH 03/14] Add Clipboard plugin and fix the sample code.

---
 tests/manual/inline-widget.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/tests/manual/inline-widget.js b/tests/manual/inline-widget.js
index fe693c8a..2a921e4f 100644
--- a/tests/manual/inline-widget.js
+++ b/tests/manual/inline-widget.js
@@ -19,6 +19,7 @@ import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import Widget from '@ckeditor/ckeditor5-widget/src/widget';
 import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 
 class InlineWidget extends Plugin {
 	constructor( editor ) {
@@ -33,7 +34,7 @@ class InlineWidget extends Plugin {
 		editor.conversion.for( 'editingDowncast' ).elementToElement( {
 			model: 'placeholder',
 			view: ( modelItem, viewWriter ) => {
-				const widgetElement = createPlaceholderView( viewWriter, modelItem );
+				const widgetElement = createPlaceholderView( modelItem, viewWriter );
 
 				return toWidget( widgetElement, viewWriter );
 			}
@@ -61,7 +62,7 @@ class InlineWidget extends Plugin {
 			}
 		} );
 
-		function createPlaceholderView( viewWriter, modelItem ) {
+		function createPlaceholderView( modelItem, viewWriter ) {
 			const widgetElement = viewWriter.createContainerElement( 'placeholder' );
 
 			viewWriter.insert( viewWriter.createPositionAt( widgetElement, 0 ), viewWriter.createText( modelItem.getAttribute( 'type' ) ) );
@@ -103,7 +104,7 @@ class InlineWidget extends Plugin {
 
 ClassicEditor
 	.create( global.document.querySelector( '#editor' ), {
-		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Widget, InlineWidget ],
+		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Clipboard, Widget, InlineWidget ],
 		toolbar: [ 'heading', '|', 'bold', '|', 'placeholder', '|', 'undo', 'redo' ]
 	} )
 	.then( editor => {

From 4cc12eaec442204dbb347359fd316c69b4529f59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Wed, 30 Jan 2019 11:21:57 +0100
Subject: [PATCH 04/14] Add isInline=true to placeholder's schema definition in
 manual test.

---
 tests/manual/inline-widget.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/manual/inline-widget.js b/tests/manual/inline-widget.js
index 2a921e4f..d1637641 100644
--- a/tests/manual/inline-widget.js
+++ b/tests/manual/inline-widget.js
@@ -28,6 +28,7 @@ class InlineWidget extends Plugin {
 		editor.model.schema.register( 'placeholder', {
 			allowWhere: '$text',
 			isObject: true,
+			isInline: true,
 			allowAttributes: [ 'type' ]
 		} );
 

From 9ee4d0b2078715e45626442ccb1f8cdce9ca1ee3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Wed, 30 Jan 2019 16:08:02 +0100
Subject: [PATCH 05/14] Add ShiftEnter plugin to inline widget manual sample.

---
 tests/manual/inline-widget.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/tests/manual/inline-widget.js b/tests/manual/inline-widget.js
index d1637641..4da02c2d 100644
--- a/tests/manual/inline-widget.js
+++ b/tests/manual/inline-widget.js
@@ -20,6 +20,7 @@ import Widget from '@ckeditor/ckeditor5-widget/src/widget';
 import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
+import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
 
 class InlineWidget extends Plugin {
 	constructor( editor ) {
@@ -63,14 +64,15 @@ class InlineWidget extends Plugin {
 			}
 		} );
 
+		this._createToolbarButton();
+
 		function createPlaceholderView( modelItem, viewWriter ) {
 			const widgetElement = viewWriter.createContainerElement( 'placeholder' );
 
 			viewWriter.insert( viewWriter.createPositionAt( widgetElement, 0 ), viewWriter.createText( modelItem.getAttribute( 'type' ) ) );
+
 			return widgetElement;
 		}
-
-		this._createToolbarButton();
 	}
 
 	_createToolbarButton() {
@@ -105,7 +107,7 @@ class InlineWidget extends Plugin {
 
 ClassicEditor
 	.create( global.document.querySelector( '#editor' ), {
-		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Clipboard, Widget, InlineWidget ],
+		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Clipboard, Widget, ShiftEnter, InlineWidget ],
 		toolbar: [ 'heading', '|', 'bold', '|', 'placeholder', '|', 'undo', 'redo' ]
 	} )
 	.then( editor => {

From 41aa892fec692619dcf0ebea32f69b6155ff97f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Wed, 30 Jan 2019 16:14:06 +0100
Subject: [PATCH 06/14] Add description for inline widget manual test.

---
 tests/manual/inline-widget.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/manual/inline-widget.md b/tests/manual/inline-widget.md
index c9148446..806474d4 100644
--- a/tests/manual/inline-widget.md
+++ b/tests/manual/inline-widget.md
@@ -1,3 +1,4 @@
 ### Inline widget
 
-* TODO
+* You should be able to type after inline widget (if it is last child of parent  or )
+* Select text before/after widget and expand selection beyond widget. The widget should be selected and selection expanded beyond it on further expanding.

From 0e173b2b89197461846f5f3a64b8df8dc163e918 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Wed, 30 Jan 2019 17:05:53 +0100
Subject: [PATCH 07/14] Add table feature to inline widget manual test.

---
 tests/manual/inline-widget.js | 10 +++++++---
 tests/manual/inline-widget.md |  4 +++-
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/tests/manual/inline-widget.js b/tests/manual/inline-widget.js
index 4da02c2d..7765019d 100644
--- a/tests/manual/inline-widget.js
+++ b/tests/manual/inline-widget.js
@@ -21,6 +21,7 @@ import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
+import Table from '@ckeditor/ckeditor5-table/src/table';
 
 class InlineWidget extends Plugin {
 	constructor( editor ) {
@@ -107,8 +108,8 @@ class InlineWidget extends Plugin {
 
 ClassicEditor
 	.create( global.document.querySelector( '#editor' ), {
-		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Clipboard, Widget, ShiftEnter, InlineWidget ],
-		toolbar: [ 'heading', '|', 'bold', '|', 'placeholder', '|', 'undo', 'redo' ]
+		plugins: [ Enter, Typing, Paragraph, Heading, Bold, Undo, Clipboard, Widget, ShiftEnter, InlineWidget, Table ],
+		toolbar: [ 'heading', '|', 'bold', '|', 'placeholder', '|', 'insertTable', '|', 'undo', 'redo' ]
 	} )
 	.then( editor => {
 		editor.model.document.on( 'change', () => {
@@ -128,5 +129,8 @@ function printModelContents( editor ) {
 }
 
 function formatData( data ) {
-	return data.replace( /<(paragraph)>/g, '\n<$1>' );
+	return data
+		.replace( /<(paragraph|\/tableRow|tableCell|table|heading[0-5])>/g, '\n<$1>' )
+		.replace( /()\n()/g, '$1$2' )
+		.replace( /\n()/g, '\n\t$1' );
 }
diff --git a/tests/manual/inline-widget.md b/tests/manual/inline-widget.md
index 806474d4..2b48826a 100644
--- a/tests/manual/inline-widget.md
+++ b/tests/manual/inline-widget.md
@@ -1,4 +1,6 @@
 ### Inline widget
 
-* You should be able to type after inline widget (if it is last child of parent  or )
+* You should be able to type after inline widget (if it is last child of parent  or ).
 * Select text before/after widget and expand selection beyond widget. The widget should be selected and selection expanded beyond it on further expanding.
+* The inline-widget should work in nested editable widget (table).
+* Use "insert placeholder" toolbar button to insert inline widget.

From e097009d12417fef39929b5fb81182ccd1a3a614 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Thu, 31 Jan 2019 15:20:47 +0100
Subject: [PATCH 08/14] The findOptimalInsertionPosition() function should
 treat inline elements as text.

---
 src/utils.js   |  2 +-
 tests/utils.js | 23 ++++++++++++++++++++++-
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/utils.js b/src/utils.js
index 0a7f448a..0cde4bd1 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -265,7 +265,7 @@ export function toWidgetEditable( editable, writer ) {
 export function findOptimalInsertionPosition( selection, model ) {
 	const selectedElement = selection.getSelectedElement();
 
-	if ( selectedElement ) {
+	if ( selectedElement && !model.schema.isInline( selectedElement ) ) {
 		return model.createPositionAfter( selectedElement );
 	}
 
diff --git a/tests/utils.js b/tests/utils.js
index 31a99906..b3fe2bf6 100644
--- a/tests/utils.js
+++ b/tests/utils.js
@@ -392,7 +392,7 @@ describe( 'widget utils', () => {
 			expect( pos.path ).to.deep.equal( [ 1 ] );
 		} );
 
-		it( 'returns position before block if in the middle of that block', () => {
+		it( 'returns position before block if in the middle of that block (collapsed selection)', () => {
 			setData( model, 'xf[]ooy' );
 
 			const pos = findOptimalInsertionPosition( doc.selection, model );
@@ -400,6 +400,14 @@ describe( 'widget utils', () => {
 			expect( pos.path ).to.deep.equal( [ 1 ] );
 		} );
 
+		it( 'returns position before block if in the middle of that block (non-collapsed selection)', () => {
+			setData( model, 'xf[o]oy' );
+
+			const pos = findOptimalInsertionPosition( doc.selection, model );
+
+			expect( pos.path ).to.deep.equal( [ 1 ] );
+		} );
+
 		it( 'returns position after block if at the end of that block', () => {
 			setData( model, 'xfoo[]y' );
 
@@ -425,5 +433,18 @@ describe( 'widget utils', () => {
 
 			expect( pos.path ).to.deep.equal( [ 3 ] );
 		} );
+
+		it( 'returns position before block selection is on inline element', () => {
+			model.schema.register( 'placeholder', {
+				allowWhere: '$text',
+				isInline: true
+			} );
+
+			setData( model, 'xf[]ooy' );
+
+			const pos = findOptimalInsertionPosition( doc.selection, model );
+
+			expect( pos.path ).to.deep.equal( [ 1 ] );
+		} );
 	} );
 } );

From 90c55d8c2d70d3569102a6362db14e896d88f538 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= 
Date: Tue, 5 Feb 2019 10:42:26 +0100
Subject: [PATCH 09/14] Add fake selection indicator to the inline widget
 manual test.

---
 tests/manual/inline-widget.html | 16 ++++++++++++++++
 tests/manual/inline-widget.md   |  3 +++
 2 files changed, 19 insertions(+)

diff --git a/tests/manual/inline-widget.html b/tests/manual/inline-widget.html
index bbdf7811..535b31e3 100644
--- a/tests/manual/inline-widget.html
+++ b/tests/manual/inline-widget.html
@@ -25,4 +25,20 @@ 

Model contents:

placeholder:after{ content: '}' } + + /* This will show box when the fake selection is active. */ + .ck.ck-content div[style*="left: -9999px;"]:before { + background: hsla(9,100%,56%,.3); + border: 1px dotted hsl(15, 100%, 43%); + color: #333; + content: 'fake selection set'; + display: block; + height: 20px; + left: calc(50% - 60px); + line-height: 20px; + padding: 2px 5px; + position: fixed; + top: 5px; + z-index: 1; + } diff --git a/tests/manual/inline-widget.md b/tests/manual/inline-widget.md index 2b48826a..8543eca9 100644 --- a/tests/manual/inline-widget.md +++ b/tests/manual/inline-widget.md @@ -4,3 +4,6 @@ * Select text before/after widget and expand selection beyond widget. The widget should be selected and selection expanded beyond it on further expanding. * The inline-widget should work in nested editable widget (table). * Use "insert placeholder" toolbar button to insert inline widget. +* Selected inline widget should have + * widget border (blue) + * activate fake selection - the "fake selection" element will be shown above the editor From 0a5cfef6801adfefacb12acc693753401c3dfe88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 5 Feb 2019 12:32:43 +0100 Subject: [PATCH 10/14] Improve text visibility around selected inline widget in its manual test. --- tests/manual/inline-widget.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/manual/inline-widget.html b/tests/manual/inline-widget.html index 535b31e3..c6034842 100644 --- a/tests/manual/inline-widget.html +++ b/tests/manual/inline-widget.html @@ -11,7 +11,9 @@

Model contents: