Browse Source

Merge pull request #74 from ckeditor/t/57

Feature: Introduced `toWidgetEditable()`. Closes #57.

The styling and behavior of image's caption will now be reusable in other widgets.

BREAKING CHANGE: Widget utility function `widgetize()` was renamed to `toWidget()`.
Piotrek Koszuliński 8 years ago
parent
commit
9ff22d2469

+ 3 - 3
packages/ckeditor5-image/src/image/utils.js

@@ -7,7 +7,7 @@
  * @module image/image/utils
  */
 
-import { widgetize, isWidget } from '../widget/utils';
+import { toWidget, isWidget } from '../widget/utils';
 import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
 
 const imageSymbol = Symbol( 'isImage' );
@@ -15,7 +15,7 @@ const imageSymbol = Symbol( 'isImage' );
 /**
  * Converts given {@link module:engine/view/element~Element} to image widget:
  * * adds {@link module:engine/view/element~Element#setCustomProperty custom property} allowing to recognize image widget element,
- * * calls {@link module:image/widget/utils~widgetize widgetize} function with proper element's label creator.
+ * * calls {@link module:image/widget/utils~toWidget toWidget} function with proper element's label creator.
  *
  * @param {module:engine/view/element~Element} viewElement
  * @param {String} label Element's label. It will be concatenated with image's `alt` attribute if one is present.
@@ -24,7 +24,7 @@ const imageSymbol = Symbol( 'isImage' );
 export function toImageWidget( viewElement, label ) {
 	viewElement.setCustomProperty( imageSymbol, true );
 
-	return widgetize( viewElement, { label: labelCreator } );
+	return toWidget( viewElement, { label: labelCreator } );
 
 	function labelCreator() {
 		const imgElement = viewElement.getChild( 0 );

+ 4 - 11
packages/ckeditor5-image/src/imagecaption/utils.js

@@ -7,8 +7,9 @@
  * @module image/imagecaption/utils
  */
 
-import ViewEditableElement from '@ckeditor/ckeditor5-engine/src/view/editableelement';
 import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
+import ViewEditableElement from '@ckeditor/ckeditor5-engine/src/view/editableelement';
+import { toWidgetEditable } from '../widget/utils';
 
 const captionSymbol = Symbol( 'imageCaption' );
 
@@ -20,19 +21,11 @@ const captionSymbol = Symbol( 'imageCaption' );
  */
 export function captionElementCreator( viewDocument ) {
 	return () => {
-		const editable = new ViewEditableElement( 'figcaption', { contenteditable: true } );
+		const editable = new ViewEditableElement( 'figcaption' );
 		editable.document = viewDocument;
 		editable.setCustomProperty( captionSymbol, true );
 
-		editable.on( 'change:isFocused', ( evt, property, is ) => {
-			if ( is ) {
-				editable.addClass( 'focused' );
-			} else {
-				editable.removeClass( 'focused' );
-			}
-		} );
-
-		return editable;
+		return toWidgetEditable( editable );
 	};
 }
 

+ 26 - 2
packages/ckeditor5-image/src/widget/utils.js

@@ -35,7 +35,7 @@ export function isWidget( element ) {
 }
 
 /**
- * "Widgetizes" given {@link module:engine/view/element~Element}:
+ * Converts given {@link module:engine/view/element~Element} to widget in following way:
  * * sets `contenteditable` attribute to `true`,
  * * adds custom `getFillerOffset` method returning `null`,
  * * adds `ck-widget` CSS class,
@@ -47,7 +47,7 @@ export function isWidget( element ) {
  * a plain string or a function returning a string.
  * @returns {module:engine/view/element~Element} Returns same element.
  */
-export function widgetize( element, options ) {
+export function toWidget( element, options ) {
 	options = options || {};
 	element.setAttribute( 'contenteditable', false );
 	element.getFillerOffset = getFillerOffset;
@@ -89,6 +89,30 @@ export function getLabel( element ) {
 	return typeof labelCreator == 'function' ? labelCreator() : labelCreator;
 }
 
+/**
+ * Adds functionality to provided {module:engine/view/editableelement~EditableElement} to act as a widget's editable:
+ * * sets `contenteditable` attribute to `true`,
+ * * adds `ck-editable` CSS class,
+ * * adds `ck-editable_focused` CSS class when editable is focused and removes it when it's blurred.
+ *
+ * @param {module:engine/view/editableelement~EditableElement} editable
+ * @returns {module:engine/view/editableelement~EditableElement} Returns same element that was provided in `editable` param.
+ */
+export function toWidgetEditable( editable ) {
+	editable.setAttribute( 'contenteditable', 'true' );
+	editable.addClass( 'ck-editable' );
+
+	editable.on( 'change:isFocused', ( evt, property, is ) => {
+		if ( is ) {
+			editable.addClass( 'ck-editable_focused' );
+		} else {
+			editable.removeClass( 'ck-editable_focused' );
+		}
+	} );
+
+	return editable;
+}
+
 // Default filler offset function applied to all widget elements.
 //
 // @returns {null}

+ 2 - 0
packages/ckeditor5-image/src/widget/widget.js

@@ -18,6 +18,8 @@ import RootEditableElement from '@ckeditor/ckeditor5-engine/src/view/rooteditabl
 import { isWidget } from './utils';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 
+import '../../theme/widget/theme.scss';
+
 /**
  * The widget plugin.
  * Adds default {@link module:engine/view/document~Document#event:mousedown mousedown} handling on widget elements.

+ 9 - 9
packages/ckeditor5-image/tests/imagecaption/imagecaptionengine.js

@@ -101,7 +101,7 @@ describe( 'ImageCaptionEngine', () => {
 				expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
 					'<figure class="image ck-widget" contenteditable="false">' +
 						'<img src="img.png"></img>' +
-						'<figcaption contenteditable="true">Foo bar baz.</figcaption>' +
+						'<figcaption class="ck-editable" contenteditable="true">Foo bar baz.</figcaption>' +
 					'</figure>'
 				);
 			} );
@@ -214,7 +214,7 @@ describe( 'ImageCaptionEngine', () => {
 			expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
 				'<figure class="image ck-widget" contenteditable="false">' +
 					'<img src=""></img>' +
-					'<figcaption contenteditable="true">foo bar baz</figcaption>' +
+					'<figcaption class="ck-editable" contenteditable="true">foo bar baz</figcaption>' +
 				'</figure>'
 			);
 		} );
@@ -227,7 +227,7 @@ describe( 'ImageCaptionEngine', () => {
 			expect( getViewData( viewDocument ) ).to.equal(
 				'[<figure class="image ck-widget ck-widget_selected" contenteditable="false">' +
 					'<img src=""></img>' +
-					'<figcaption contenteditable="true"></figcaption>' +
+					'<figcaption class="ck-editable" contenteditable="true"></figcaption>' +
 				'</figure>]'
 			);
 		} );
@@ -248,7 +248,7 @@ describe( 'ImageCaptionEngine', () => {
 			expect( getViewData( viewDocument ) ).to.equal(
 				'[<figure class="image ck-widget ck-widget_selected" contenteditable="false">' +
 					'<img src=""></img>' +
-					'<figcaption contenteditable="true">foo bar</figcaption>' +
+					'<figcaption class="ck-editable" contenteditable="true">foo bar</figcaption>' +
 				'</figure>]'
 			);
 		} );
@@ -277,7 +277,7 @@ describe( 'ImageCaptionEngine', () => {
 			expect( getViewData( viewDocument ) ).to.equal(
 				'<figure class="image ck-widget" contenteditable="false">' +
 					'<img src=""></img>' +
-					'<figcaption contenteditable="true">[]</figcaption>' +
+					'<figcaption class="ck-editable" contenteditable="true">[]</figcaption>' +
 				'</figure>'
 			);
 		} );
@@ -294,7 +294,7 @@ describe( 'ImageCaptionEngine', () => {
 			expect( getViewData( viewDocument ) ).to.equal(
 				'[<figure class="image ck-widget ck-widget_selected" contenteditable="false">' +
 					'<img src=""></img>' +
-					'<figcaption contenteditable="true"></figcaption>' +
+					'<figcaption class="ck-editable" contenteditable="true"></figcaption>' +
 				'</figure>]'
 			);
 		} );
@@ -310,11 +310,11 @@ describe( 'ImageCaptionEngine', () => {
 			expect( getViewData( viewDocument ) ).to.equal(
 				'<figure class="image ck-widget" contenteditable="false">' +
 					'<img src=""></img>' +
-					'<figcaption contenteditable="true">foo bar</figcaption>' +
+					'<figcaption class="ck-editable" contenteditable="true">foo bar</figcaption>' +
 				'</figure>' +
 				'[<figure class="image ck-widget ck-widget_selected" contenteditable="false">' +
 					'<img src=""></img>' +
-					'<figcaption contenteditable="true"></figcaption>' +
+					'<figcaption class="ck-editable" contenteditable="true"></figcaption>' +
 				'</figure>]'
 			);
 		} );
@@ -346,7 +346,7 @@ describe( 'ImageCaptionEngine', () => {
 				expect( getViewData( viewDocument ) ).to.equal(
 					'<figure class="image ck-widget" contenteditable="false">' +
 						'<img src=""></img>' +
-						'<figcaption contenteditable="true">{foo bar baz}</figcaption>' +
+						'<figcaption class="ck-editable" contenteditable="true">{foo bar baz}</figcaption>' +
 					'</figure>'
 				);
 			} );

+ 0 - 12
packages/ckeditor5-image/tests/imagecaption/utils.js

@@ -29,18 +29,6 @@ describe( 'image captioning utils', () => {
 			expect( element.name ).to.equal( 'figcaption' );
 			expect( isCaption( element ) ).to.be.true;
 		} );
-
-		it( 'should be created in context of proper document', () => {
-			expect( element.document ).to.equal( document );
-		} );
-
-		it( 'should add proper class when element is focused', () => {
-			element.isFocused = true;
-			expect( element.hasClass( 'focused' ) ).to.be.true;
-
-			element.isFocused = false;
-			expect( element.hasClass( 'focused' ) ).to.be.false;
-		} );
 	} );
 
 	describe( 'isCaptionEditable', () => {

+ 41 - 5
packages/ckeditor5-image/tests/widget/utils.js

@@ -4,17 +4,26 @@
  */
 
 import ViewElement from '@ckeditor/ckeditor5-engine/src/view/element';
-import { widgetize, isWidget, WIDGET_CLASS_NAME, setLabel, getLabel } from '../../src/widget/utils';
+import ViewEditableElement from '@ckeditor/ckeditor5-engine/src/view/editableelement';
+import ViewDocument from '@ckeditor/ckeditor5-engine/src/view/document';
+import {
+	toWidget,
+	isWidget,
+	setLabel,
+	getLabel,
+	toWidgetEditable,
+	WIDGET_CLASS_NAME
+} from '../../src/widget/utils';
 
 describe( 'widget utils', () => {
 	let element;
 
 	beforeEach( () => {
 		element = new ViewElement( 'div' );
-		widgetize( element );
+		toWidget( element );
 	} );
 
-	describe( 'widgetize()', () => {
+	describe( 'toWidget()', () => {
 		it( 'should set contenteditable to false', () => {
 			expect( element.getAttribute( 'contenteditable' ) ).to.be.false;
 		} );
@@ -30,14 +39,14 @@ describe( 'widget utils', () => {
 
 		it( 'should add element\'s label if one is provided', () => {
 			element = new ViewElement( 'div' );
-			widgetize( element, { label: 'foo bar baz label' } );
+			toWidget( element, { label: 'foo bar baz label' } );
 
 			expect( getLabel( element ) ).to.equal( 'foo bar baz label' );
 		} );
 
 		it( 'should add element\'s label if one is provided as function', () => {
 			element = new ViewElement( 'div' );
-			widgetize( element, { label: () => 'foo bar baz label' } );
+			toWidget( element, { label: () => 'foo bar baz label' } );
 
 			expect( getLabel( element ) ).to.equal( 'foo bar baz label' );
 		} );
@@ -77,4 +86,31 @@ describe( 'widget utils', () => {
 			expect( getLabel( element ) ).to.equal( 'bar' );
 		} );
 	} );
+
+	describe( 'toWidgetEditable', () => {
+		let viewDocument, element;
+
+		beforeEach( () => {
+			viewDocument = new ViewDocument();
+			element = new ViewEditableElement( 'div' );
+			element.document = viewDocument;
+			toWidgetEditable( element );
+		} );
+
+		it( 'should be created in context of proper document', () => {
+			expect( element.document ).to.equal( viewDocument );
+		} );
+
+		it( 'should add proper class', () => {
+			expect( element.hasClass( 'ck-editable' ) ).to.be.true;
+		} );
+
+		it( 'should add proper class when element is focused', () => {
+			element.isFocused = true;
+			expect( element.hasClass( 'ck-editable_focused' ) ).to.be.true;
+
+			element.isFocused = false;
+			expect( element.hasClass( 'ck-editable_focused' ) ).to.be.false;
+		} );
+	} );
 } );

+ 2 - 2
packages/ckeditor5-image/tests/widget/widget.js

@@ -7,7 +7,7 @@ import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtest
 import Widget from '../../src/widget/widget';
 import MouseObserver from '@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver';
 import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
-import { widgetize } from '../../src/widget/utils';
+import { toWidget } from '../../src/widget/utils';
 import ViewContainer from '@ckeditor/ckeditor5-engine/src/view/containerelement';
 import ViewEditable from '@ckeditor/ckeditor5-engine/src/view/editableelement';
 import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
@@ -48,7 +48,7 @@ describe( 'Widget', () => {
 						const b = new AttributeContainer( 'b' );
 						const div = new ViewContainer( 'div', null, b );
 
-						return widgetize( div );
+						return toWidget( div );
 					} );
 
 				buildModelConverter().for( editor.editing.modelToView )

+ 2 - 2
packages/ckeditor5-image/tests/widget/widgetengine.js

@@ -10,7 +10,7 @@ import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-util
 import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 import ViewContainer from '@ckeditor/ckeditor5-engine/src/view/containerelement';
 import ViewEditable from '@ckeditor/ckeditor5-engine/src/view/editableelement';
-import { widgetize } from '../../src/widget/utils';
+import { toWidget } from '../../src/widget/utils';
 
 describe( 'WidgetEngine', () => {
 	let editor, document, viewDocument;
@@ -32,7 +32,7 @@ describe( 'WidgetEngine', () => {
 				buildModelConverter().for( editor.editing.modelToView )
 					.fromElement( 'widget' )
 					.toElement( () => {
-						const element = widgetize( new ViewContainer( 'div' ), { label: 'element label' } );
+						const element = toWidget( new ViewContainer( 'div' ), { label: 'element label' } );
 
 						return element;
 					} );

+ 6 - 19
packages/ckeditor5-image/theme/imagecaption/theme.scss

@@ -2,27 +2,14 @@
 // For licensing, see LICENSE.md or http://ckeditor.com/license
 
 @import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_colors.scss';
-@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_shadow.scss';
-@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_states.scss';
+@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_spacing.scss';
+@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_fonts.scss';
 
-.ck-widget.image {
-	figcaption {
+.ck-editor__editable {
+	.image > figcaption {
 		background-color: ck-color( 'foreground' );
-		padding: 10px;
-		font-size: .8em;
+		padding: ck-spacing();
+		font-size: ck-font-size( -1 );
 		color: ck-color( 'text', 40 );
-
-		// The `:focus` styles is applied before `.focused` class inside editables.
-		// These styles show different border for a blink of an eye.
-		&:focus {
-			outline: none;
-			box-shadow: none;
-		}
-
-		&.focused {
-			@include ck-focus-ring( 'outline' );
-			@include ck-box-shadow( $ck-inner-shadow );
-			background-color: ck-color( 'background' );;
-		}
 	}
 }

+ 17 - 34
packages/ckeditor5-image/theme/theme.scss

@@ -1,43 +1,26 @@
 // Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
 // For licensing, see LICENSE.md or http://ckeditor.com/license
 
-// Common styles applied to all widgets.
-.ck-widget {
-	margin: 10px 0;
-	padding: 0;
-
-	&.ck-widget_selected, &.ck-widget_selected:hover {
-		outline: 2px solid #ace;
-	}
-
-	.ck-editor__editable.ck-blurred &.ck-widget_selected {
-		outline: 2px solid #ddd;
+.ck-editor__editable {
+	.image {
+		text-align: center;
+		clear: both;
+
+		&.image-style-side {
+			float: right;
+			margin-left: 0.8em;
+			max-width: 50%;
+		}
 	}
 
-	&:hover {
-		outline: 2px solid yellow;
-	}
-}
+	.image > img {
+		// Prevent unnecessary margins caused by line-height (see #44).
+		display: block;
 
-// Image widget's styles.
-.image {
-	text-align: center;
-	clear: both;
+		// Center the image if its width is smaller than content's width.
+		margin: 0 auto;
 
-	&.image-style-side {
-		float: right;
-		margin-left: 0.8em;
-		max-width: 50%;
+		// Make sure the image never exceeds the size of the parent container (#67).
+		max-width: 100%;
 	}
 }
-
-.image > img {
-	// Prevent unnecessary margins caused by line-height (see #44).
-	display: block;
-
-	// Center the image if its width is smaller than content's width.
-	margin: 0 auto;
-
-	// Make sure the image never exceeds the size of the parent container (#67).
-	max-width: 100%;
-}

+ 41 - 0
packages/ckeditor5-image/theme/widget/theme.scss

@@ -0,0 +1,41 @@
+// Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+// For licensing, see LICENSE.md or http://ckeditor.com/license
+
+@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_colors.scss';
+@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_shadow.scss';
+@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_states.scss';
+@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_spacing.scss';
+
+@include ck-color-add( (
+	'widget-blurred': #ddd,
+	'widget-hover': yellow
+) );
+
+$widget-outline-thickness: 2px;
+
+.ck-widget {
+	margin: ck-spacing() 0;
+	padding: 0;
+
+	&.ck-widget_selected, &.ck-widget_selected:hover {
+		outline: $widget-outline-thickness solid ck-color( 'focus' );
+	}
+
+	.ck-editor__editable.ck-blurred &.ck-widget_selected {
+		outline: $widget-outline-thickness solid ck-color( 'widget-blurred' );
+	}
+
+	&:hover {
+		outline: $widget-outline-thickness solid ck-color( 'widget-hover' );
+	}
+
+	.ck-editable {
+		// The `:focus` style is applied before `.ck-editable_focused` class is rendered in the view.
+		// These styles show a different border for a blink of an eye, so `:focus` need to have same styles applied.
+		&.ck-editable_focused, &:focus {
+			@include ck-focus-ring( 'outline' );
+			@include ck-box-shadow( $ck-inner-shadow );
+			background-color: ck-color( 'background' );
+		}
+	}
+}