Explorar o código

Code refactorin and documentation in Template class.

Aleksander Nowodzinski %!s(int64=9) %!d(string=hai) anos
pai
achega
939d3e9e6e
Modificáronse 1 ficheiros con 195 adicións e 61 borrados
  1. 195 61
      packages/ckeditor5-ui/src/template.js

+ 195 - 61
packages/ckeditor5-ui/src/template.js

@@ -18,6 +18,8 @@ const bindDOMEvtSymbol = Symbol( 'bindDomEvt' );
 /**
  * Basic Template class.
  *
+ * See {@link ui.TemplateDefinition}.
+ *
  * @memberOf ui
  */
 export default class Template {
@@ -142,7 +144,7 @@ export default class Template {
 	 * @returns {Text} A rendered Text.
 	 */
 	_renderText( valueSchemaOrText, textNode = document.createTextNode( '' ) ) {
-		// Check if there's a binder available for this Text Node. Cases:
+		// Check if this Text Node is bound to Observable. Cases:
 		//		{ text: [ Template.bind.to( ... ) ] }
 		//		{ text: [ 'foo', Template.bind.to( ... ), ... ] }
 		if ( isBound( valueSchemaOrText.text ) ) {
@@ -177,7 +179,7 @@ export default class Template {
 			attrValue = attributes[ attrName ];
 			attrNs = attrValue[ 0 ].ns || null;
 
-			// Activate binder if one. Cases:
+			// Activate binding if one is found. Cases:
 			// 		{ class: [ Template.bind.to( ... ) ] }
 			// 		{ class: [ 'bar', Template.bind.to( ... ), 'baz' ] }
 			// 		{ class: { ns: 'abc', value: Template.bind.to( ... ) } }
@@ -266,7 +268,7 @@ export default class Template {
 	 * Note: {@link ui.TemplateValueSchema} can be for HTMLElement attributes or Text Node `textContent`.
 	 *
 	 * @protected
-	 * @param {ui.TemplateValueSchema}
+	 * @param {ui.TemplateValueSchema} valueSchema
 	 * @param {Node} node DOM Node to be updated when {@link utils.ObservableMixin} changes.
 	 * @param {Function} domUpdater A function which updates DOM (like attribute or text).
 	 */
@@ -291,12 +293,19 @@ export default class Template {
 mix( Template, EmitterMixin );
 
 /**
- * An entry point to the interface which allows binding attributes of {@link utils.ObservableMixin}
- * to the DOM items like HTMLElement attributes or Text Node `textContent`, so their state
- * is synchronized with {@link View#model}.
+ * An entry point to the interface which allows binding DOM nodes to {@link utils.ObservableMixin}.
+ * There are two types of bindings:
+ *
+ * * `HTMLElement` attributes or Text Node `textContent` can be synchronized with {@link utils.ObservableMixin}
+ * instance attributes. See {@link ui.Template.bind.binder#to} and {@link ui.Template.bind.binder#if}.
+ *
+ * * DOM events fired on `HTMLElement` can be propagated through {@link utils.ObservableMixin}.
+ * See See {@link ui.Template.bind#binder}.
  *
  * @param {utils.ObservableMixin} observable An instance of ObservableMixin class.
- * @param {utils.EmitterMixin} emitter An instance of EmitterMixin class.
+ * @param {utils.EmitterMixin} emitter An instance of `EmitterMixin` class. It listens
+ * to `observable` attribute changes and DOM Events, depending on the binding. Usually {@link ui.View} instance.
+ * @returns {ui.Template.bind#binder}
  */
 Template.bind = ( observable, emitter ) => {
 	/**
@@ -320,12 +329,14 @@ Template.bind = ( observable, emitter ) => {
 	 *			}
 	 *		} ).render();
 	 *
+	 * It also provides {@link ui.Template.bind.binder#to} and {@link ui.Template.bind.binder#if} interface.
+	 *
 	 * @static
-	 * @property {ui.Template.bind#eventBinder}
+	 * @property {ui.Template.bind#binder}
 	 * @param {String} eventNameOrFuncion Name of the DOM event to be fired along with DOM event or custom function.
 	 * @return {ui.TemplateBinding}
 	 */
-	const eventBinder = ( eventNameOrFuncion ) => {
+	const binder = ( eventNameOrFuncion ) => {
 		return {
 			type: bindDOMEvtSymbol,
 			observable, emitter,
@@ -352,12 +363,12 @@ Template.bind = ( observable, emitter ) => {
 	 *		} ).render();
 	 *
 	 * @static
-	 * @property {ui.Template.bind.eventBinder#to}
+	 * @property {ui.Template.bind.binder#to}
 	 * @param {String} attribute Name of {@link utils.ObservableMixin} used in the binding.
 	 * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
 	 * @return {ui.TemplateBinding}
 	 */
-	eventBinder.to = ( attribute, callback ) => {
+	binder.to = ( attribute, callback ) => {
 		return {
 			type: bindToSymbol,
 			observable, emitter,
@@ -367,7 +378,7 @@ Template.bind = ( observable, emitter ) => {
 
 	/**
 	 * Binds {@link utils.ObservableMixin} to HTMLElement attribute or Text Node `textContent`
-	 * so remains in sync with the Model when it changes. Unlike {@link ui.Template.bind.eventBinder#to},
+	 * so remains in sync with the Model when it changes. Unlike {@link ui.Template.bind.binder#to},
 	 * it controls the presence of the attribute/`textContent` depending on the "falseness" of
 	 * {@link utils.ObservableMixin} attribute.
 	 *
@@ -390,13 +401,13 @@ Template.bind = ( observable, emitter ) => {
 	 *		} ).render();
 	 *
 	 * @static
-	 * @property {ui.Template.bind.eventBinder#if}
+	 * @property {ui.Template.bind.binder#if}
 	 * @param {String} attribute An attribute name of {@link utils.ObservableMixin} used in the binding.
 	 * @param {String} [valueIfTrue] Value set when {@link utils.ObservableMixin} attribute is not undefined/null/false/''.
 	 * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
 	 * @return {ui.TemplateBinding}
 	 */
-	eventBinder.if = ( attribute, valueIfTrue, callback ) => {
+	binder.if = ( attribute, valueIfTrue, callback ) => {
 		return {
 			type: bindIfSymbol,
 			observable, emitter,
@@ -404,7 +415,7 @@ Template.bind = ( observable, emitter ) => {
 		};
 	};
 
-	return eventBinder;
+	return binder;
 };
 
 /**
@@ -412,8 +423,10 @@ Template.bind = ( observable, emitter ) => {
  * an Array. Each entry of an Array corresponds to one of {@link ui.TemplateValueSchema}
  * items.
  *
+ * @ignore
  * @private
- * @param {Node} domNode
+ * @param {ui.TemplateValueSchema} valueSchema
+ * @param {Node} node DOM Node updated when {@link utils.ObservableMixin} changes.
  * @return {Array}
  */
 function getBoundValue( valueSchema, domNode ) {
@@ -442,6 +455,12 @@ function getBoundValue( valueSchema, domNode ) {
 /**
  * A function executed each time bound Observable attribute changes, which updates DOM with a value
  * constructed from {@link ui.TemplateValueSchema}.
+ *
+ * @ignore
+ * @private
+ * @param {ui.TemplateValueSchema} valueSchema
+ * @param {Node} node DOM Node updated when {@link utils.ObservableMixin} changes.
+ * @param {Function} domUpdater A function which updates DOM (like attribute or text).
  */
 function syncDom( valueSchema, domNode, domUpdater ) {
 	let value = getBoundValue( valueSchema, domNode );
@@ -468,23 +487,12 @@ function syncDom( valueSchema, domNode, domUpdater ) {
 	}
 }
 
-/**
- * Describes Model binding created by {@link Template#bind}.
- *
- * @typedef ui.TemplateBinding
- * @type Object
- * @property {Symbol} type
- * @property {ui.Model} model
- * @property {String} attribute
- * @property {String} [valueIfTrue]
- * @property {Function} [callback]
- */
-
 /*
  * Returns an object consisting of `set` and `remove` functions, which
  * can be used in the context of DOM Node to set or reset `textContent`.
  * @see ui.View#_bindToObservable
  *
+ * @ignore
  * @private
  * @param {Node} node DOM Node to be modified.
  * @returns {Object}
@@ -506,10 +514,11 @@ function getTextUpdater( node ) {
  * can be used in the context of DOM Node to set or reset an attribute.
  * @see ui.View#_bindToObservable
  *
+ * @ignore
  * @private
  * @param {Node} node DOM Node to be modified.
  * @param {String} attrName Name of the attribute to be modified.
- * @param {String} [ns] Namespace to use.
+ * @param {String} [ns=null] Namespace to use.
  * @returns {Object}
  */
 function getAttributeUpdater( el, attrName, ns = null ) {
@@ -525,21 +534,12 @@ function getAttributeUpdater( el, attrName, ns = null ) {
 }
 
 /**
- * Normalizes given {@link ui.TemplateValueSchema} it's always in an Array–like format:
- *
- * 		{ attr|text: 'bar' } ->
- * 			{ attr|text: [ 'bar' ] }
- *
- * 		{ attr|text: { model: ..., modelAttributeName: ..., callback: ... } } ->
- * 			{ attr|text: [ { model: ..., modelAttributeName: ..., callback: ... } ] }
- *
- * 		{ attr|text: [ 'bar', { model: ..., modelAttributeName: ... }, 'baz' ] }
+ * Normalizes given {@link ui.TemplateDefinition}.
  *
- *		'foo@selector': 'bar' ->
- * 			'foo@selector': [ 'bar' ],
- *
- *		'foo@selector': [ 'bar', () => { ... } ] ->
- *			'foo@selector': [ 'bar', () => { ... } ],
+ * See:
+ *  * {@link normalizeAttributes}
+ *  * {@link normalizeListeners}
+ *  * {@link normalizeTextChildren}
  *
  * @ignore
  * @private
@@ -560,6 +560,31 @@ function normalize( def ) {
 	}
 }
 
+/**
+ * Normalizes "attributes" section of {@link ui.TemplateDefinition}.
+ *
+ *		attributes: {
+ *			a: 'bar',
+ *			b: {@link ui.TemplateBinding},
+ *			c: {
+ *				value: 'bar'
+ *			}
+ *		}
+ *
+ * becomes
+ *
+ *		attributes: {
+ *			a: [ 'bar' ],
+ *			b: [ {@link ui.TemplateBinding} ],
+ *			c: {
+ *				value: [ 'bar' ]
+ *			}
+ *		}
+ *
+ * @ignore
+ * @private
+ * @param {Object} attrs
+ */
 function normalizeAttributes( attrs ) {
 	for ( let a in attrs ) {
 		if ( attrs[ a ].value ) {
@@ -570,12 +595,54 @@ function normalizeAttributes( attrs ) {
 	}
 }
 
+/**
+ * Normalizes "on" section of {@link ui.TemplateDefinition}.
+ *
+ *		on: {
+ *			a: 'bar',
+ *			b: {@link ui.TemplateBinding},
+ *			c: [ {@link ui.TemplateBinding}, () => { ... } ]
+ *		}
+ *
+ * becomes
+ *
+ *		on: {
+ *			a: [ 'bar' ],
+ *			b: [ {@link ui.TemplateBinding} ],
+ *			c: [ {@link ui.TemplateBinding}, () => { ... } ]
+ *		}
+ *
+ * @ignore
+ * @private
+ * @param {Object} listeners
+ */
 function normalizeListeners( listeners ) {
 	for ( let l in listeners ) {
 		arrayify( listeners, l );
 	}
 }
 
+/**
+ * Normalizes text in "children" section of {@link ui.TemplateDefinition}.
+ *
+ *		children: [
+ *			'abc',
+ *			{ text: 'def' },
+ *			{ text: {@link ui.TemplateBinding} }
+ *		]
+ *
+ * becomes
+ *
+ *		children: [
+ *			{ text: [ 'abc' ] },
+ *			{ text: [ 'def' ] },
+ *			{ text: [ {@link ui.TemplateBinding} ] }
+ *		]
+ *
+ * @ignore
+ * @private
+ * @param {ui.TemplateDefinition} def
+ */
 function normalizeTextChildren( def ) {
 	def.children = def.children.map( c => {
 		if ( typeof c == 'string' ) {
@@ -592,6 +659,26 @@ function normalizeTextChildren( def ) {
 	} );
 }
 
+/**
+ * Wraps an entry in Object in an Array, if not already one.
+ *
+ *		{
+ *			x: 'y',
+ *			a: [ 'b' ]
+ *		}
+ *
+ * becomes
+ *
+ *		{
+ *			x: [ 'y' ],
+ *			a: [ 'b' ]
+ *		}
+ *
+ * @ignore
+ * @private
+ * @param {Object} obj
+ * @param {String} key
+ */
 function arrayify( obj, key ) {
 	if ( !Array.isArray( obj[ key ] ) ) {
 		obj[ key ] = [ obj[ key ] ];
@@ -645,10 +732,10 @@ function isBound( valueSchema ) {
 }
 
 /**
- * Definition of {@link Template}.
+ * A definition of {@link ui.Template}.
  * See: {@link ui.TemplateValueSchema}.
  *
- *		{
+ *		new Template( {
  *			tag: 'p',
  *			children: [
  *				{
@@ -664,19 +751,16 @@ function isBound( valueSchema ) {
  *				...
  *			],
  *			attributes: {
- *				'class': [ 'class-a', 'class-b' ],
- *				id: 'element-id',
- *				style: callback,
+ *				class: {@link ui.TemplateValueSchema},
+ *				id: {@link ui.TemplateValueSchema},
  *				...
  *			},
  *			on: {
- *				'click': 'clicked'
- *				'mouseup': [ 'view-event-a', 'view-event-b', callback ],
- *				'keyup@selector': 'view-event',
- *				'focus@selector': [ 'view-event-a', 'view-event-b', callback ],
+ *				'click': {@link ui.TemplateListenerSchema}
+ *				'keyup@.some-class': {@link ui.TemplateListenerSchema},
  *				...
  *			}
- *		}
+ *		} );
  *
  * @typedef ui.TemplateDefinition
  * @type Object
@@ -685,33 +769,83 @@ function isBound( valueSchema ) {
  * @property {Object} [attributes]
  * @property {String} [text]
  * @property {Object} [on]
- * @property {Object} _modelBinders
  */
 
 /**
  * Describes a value of HTMLElement attribute or `textContent`.
  * See: {@link ui.TemplateDefinition}.
  *
- *		{
+ *		new Template( {
  *			tag: 'p',
  *			attributes: {
  *				// Plain String schema.
- *				class: 'class-foo'
+ *				class: 'static-text'
  *
- *				// Object schema, a Model binding.
- *				class: { model: m, attribute: 'foo', callback... }
+ *				// Object schema, an `ObservableMixin` binding.
+ *				class: {@link ui.TemplateBinding}
  *
  *				// Array schema, combines the above.
- *				class: [ 'foo', { model: m, attribute: 'bar' }, 'baz' ],
+ *				class: [
+ *					'static-text',
+ *					{@link ui.TemplateBinding}
+ *				],
  *
  *				// Array schema, with custom namespace.
  *				class: {
  *					ns: 'http://ns.url',
- *					value: [ 'foo', { model: m, attribute: 'bar' }, 'baz' ]
+ *					value: [
+ *						{@link ui.TemplateBinding},
+ *						'static-text'
+ *					]
  *				}
  *			}
- *		}
+ *		} );
  *
  * @typedef ui.TemplateValueSchema
  * @type {Object|String|Array}
  */
+
+/**
+ * Describes a listener attached to HTMLElement. See: {@link ui.TemplateDefinition}.
+ *
+ *		new Template( {
+ *			tag: 'p',
+ *			on: {
+ *				// Plain String schema.
+ *				click: 'clicked'
+ *
+ *				// Object schema, an `ObservableMixin` binding.
+ *				click: {@link ui.TemplateBinding}
+ *
+ *				// Array schema, combines the above.
+ *				click: [
+ *					'clicked',
+ *					{@link ui.TemplateBinding}
+ *				],
+ *
+ *				// Array schema, with custom callback.
+ *				// Note: It will work for "click" event on class=".foo" children only.
+ *				'click@.foo': {
+ *					'clicked',
+ *					{@link ui.TemplateBinding},
+ *					() => { ... }
+ *				}
+ *			}
+ *		} );
+ *
+ * @typedef ui.TemplateListenerSchema
+ * @type {Object|String|Array}
+ */
+
+/**
+ * Describes Model binding created by {@link ui.Template#bind}.
+ *
+ * @typedef ui.TemplateBinding
+ * @type Object
+ * @property {utils.ObservableMixin} observable
+ * @property {utils.EmitterMixin} emitter
+ * @property {Symbol} type
+ * @property {String} attribute
+ * @property {String} [valueIfTrue]
+ * @property {Function} [callback]
+ */