Ignite properties now patch lists to capture push/pop events so that when used in a list template the whole list doesn't have to be rerendered. Also cleaned up some code and moved things around.
This commit is contained in:
		| @@ -38,9 +38,8 @@ class IgniteElement extends HTMLElement { | |||||||
|  |  | ||||||
|         var keys = Object.keys(props); |         var keys = Object.keys(props); | ||||||
|         for (var i = 0; i < keys.length; i++) { |         for (var i = 0; i < keys.length; i++) { | ||||||
|             var prop = new IgniteProperty(); |             let prop = new IgniteProperty(props[keys[i]]); | ||||||
|             this[`_${keys[i]}`] = prop; |             this[`_${keys[i]}`] = prop; | ||||||
|             prop._value = props[keys[i]]; |  | ||||||
|  |  | ||||||
|             ((propName) => { |             ((propName) => { | ||||||
|                 Object.defineProperty(this, propName, { |                 Object.defineProperty(this, propName, { | ||||||
| @@ -56,8 +55,6 @@ class IgniteElement extends HTMLElement { | |||||||
|                         this[`_${propName}`].value = value; |                         this[`_${propName}`].value = value; | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|  |  | ||||||
|             })(keys[i]); |             })(keys[i]); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,9 +3,14 @@ | |||||||
|  * can be used to invoke call back functions when the value of the property changes. |  * can be used to invoke call back functions when the value of the property changes. | ||||||
|  */ |  */ | ||||||
| class IgniteProperty { | class IgniteProperty { | ||||||
|     constructor() { |     constructor(val) { | ||||||
|         this.callbacks = []; |         this.onChangeCallbacks = []; | ||||||
|         this._value = null; |         this.onPushCallbacks = []; | ||||||
|  |         this.onPopCallbacks = []; | ||||||
|  |         this._value = val; | ||||||
|  |  | ||||||
|  |         //Attempt to patch the value if it's a list. | ||||||
|  |         this.patchListValue(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     get value() { |     get value() { | ||||||
| @@ -16,15 +21,64 @@ class IgniteProperty { | |||||||
|         var old = this._value; |         var old = this._value; | ||||||
|         this._value = val; |         this._value = val; | ||||||
|  |  | ||||||
|  |         //Attempt to patch the value if it's a list. | ||||||
|  |         this.patchListValue(); | ||||||
|  |  | ||||||
|         //Invoke any callbacks letting them know the value changed. |         //Invoke any callbacks letting them know the value changed. | ||||||
|         for (var i = 0; i < this.callbacks.length; i++) { |         this.invokeOnChange(old, val); | ||||||
|             this.callbacks[i].invoke(old, val); |     } | ||||||
|  |  | ||||||
|  |     patchListValue() { | ||||||
|  |         if (Array.isArray(this._value)) { | ||||||
|  |             let prop = this; | ||||||
|  |  | ||||||
|  |             this._value.push = function () { | ||||||
|  |                 var len = Array.prototype.push.apply(this, arguments); | ||||||
|  |                 prop.invokeOnPush(); | ||||||
|  |                 return len; | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             this._value.pop = function () { | ||||||
|  |                 var len = Array.prototype.pop.apply(this, arguments); | ||||||
|  |                 prop.invokeOnPop(); | ||||||
|  |                 return len; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     attach(onChange) { |     invokeOnChange(oldValue, newValue) { | ||||||
|         var callback = new IgnitePropertyCallback(this, onChange); |         for (var i = 0; i < this.onChangeCallbacks.length; i++) { | ||||||
|         this.callbacks.push(callback); |             this.onChangeCallbacks[i].invoke(oldValue, newValue); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     invokeOnPush() { | ||||||
|  |         for (var i = 0; i < this.onPushCallbacks.length; i++) { | ||||||
|  |             this.onPushCallbacks[i].invoke(null, this._value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     invokeOnPop() { | ||||||
|  |         for (var i = 0; i < this.onPopCallbacks.length; i++) { | ||||||
|  |             this.onPopCallbacks[i].invoke(null, this._value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     attachOnChange(onChange) { | ||||||
|  |         var callback = new IgnitePropertyOnChangeCallback(this, onChange); | ||||||
|  |         this.onChangeCallbacks.push(callback); | ||||||
|  |         return callback; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     attachOnPush(onPush) { | ||||||
|  |         var callback = new IgnitePropertyOnPushCallback(this, onPush); | ||||||
|  |         this.onPushCallbacks.push(callback); | ||||||
|  |         return callback; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     attachOnPop(onPop) { | ||||||
|  |         var callback = new IgnitePropertyOnPopCallback(this, onPop); | ||||||
|  |         this.onPopCallbacks.push(callback); | ||||||
|         return callback; |         return callback; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -38,10 +92,10 @@ IgniteProperty.prototype.toString = function () { | |||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * The outline of a ignite property callback that allows |  * The outline of a ignite property onchange callback that allows | ||||||
|  * disconnecting of callbacks on demand when they are no longer needed. |  * disconnecting of callbacks on demand when they are no longer needed. | ||||||
|  */ |  */ | ||||||
| class IgnitePropertyCallback { | class IgnitePropertyOnChangeCallback { | ||||||
|     constructor(property, callback) { |     constructor(property, callback) { | ||||||
|         this.callback = callback; |         this.callback = callback; | ||||||
|         this.property = property; |         this.property = property; | ||||||
| @@ -57,7 +111,59 @@ class IgnitePropertyCallback { | |||||||
|         this.callback = null; |         this.callback = null; | ||||||
|  |  | ||||||
|         if (this.property) { |         if (this.property) { | ||||||
|             this.property.callbacks = this.property.callbacks.filter(callback => callback != this); |             this.property.onChangeCallbacks = this.property.onChangeCallbacks.filter(callback => callback != this); | ||||||
|  |             this.property = null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * The outline of a ignite property onpush callback that allows | ||||||
|  |  * disconnecting of callbacks on demand when they are no longer needed. | ||||||
|  |  */ | ||||||
|  | class IgnitePropertyOnPushCallback { | ||||||
|  |     constructor(property, callback) { | ||||||
|  |         this.callback = callback; | ||||||
|  |         this.property = property; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     invoke(oldValue, newValue) { | ||||||
|  |         if (this.callback) { | ||||||
|  |             this.callback(oldValue, newValue); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     disconnect() { | ||||||
|  |         this.callback = null; | ||||||
|  |  | ||||||
|  |         if (this.property) { | ||||||
|  |             this.property.onPushCallbacks = this.property.onPushCallbacks.filter(callback => callback != this); | ||||||
|  |             this.property = null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * The outline of a ignite property onpop callback that allows | ||||||
|  |  * disconnecting of callbacks on demand when they are no longer needed. | ||||||
|  |  */ | ||||||
|  | class IgnitePropertyOnPopCallback { | ||||||
|  |     constructor(property, callback) { | ||||||
|  |         this.callback = callback; | ||||||
|  |         this.property = property; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     invoke(oldValue, newValue) { | ||||||
|  |         if (this.callback) { | ||||||
|  |             this.callback(oldValue, newValue); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     disconnect() { | ||||||
|  |         this.callback = null; | ||||||
|  |  | ||||||
|  |         if (this.property) { | ||||||
|  |             this.property.onPopCallbacks = this.property.onPopCallbacks.filter(callback => callback != this); | ||||||
|             this.property = null; |             this.property = null; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ class IgniteTemplate { | |||||||
|     class(...items) { |     class(...items) { | ||||||
|         for (var i = 0; i < items.length; i++) { |         for (var i = 0; i < items.length; i++) { | ||||||
|             if (items[i] instanceof IgniteProperty) { |             if (items[i] instanceof IgniteProperty) { | ||||||
|                 this.callbacks.push(items[i].attach((oldValue, newValue) => this.onClassChanged(oldValue, newValue))); |                 this.callbacks.push(items[i].attachOnChange((oldValue, newValue) => this.onClassChanged(oldValue, newValue))); | ||||||
|                 this.classes.push(items[i].value); |                 this.classes.push(items[i].value); | ||||||
|             } else { |             } else { | ||||||
|                 this.classes.push(items[i]); |                 this.classes.push(items[i]); | ||||||
| @@ -56,7 +56,7 @@ class IgniteTemplate { | |||||||
|      */ |      */ | ||||||
|     attribute(name, value) { |     attribute(name, value) { | ||||||
|         if (value instanceof IgniteProperty) { |         if (value instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(value.attach((oldValue, newValue) => this.onAttributeChanged(oldValue, newValue, name))); |             this.callbacks.push(value.attachOnChange((oldValue, newValue) => this.onAttributeChanged(oldValue, newValue, name))); | ||||||
|             this.attributes[name] = value.value; |             this.attributes[name] = value.value; | ||||||
|         } else { |         } else { | ||||||
|             this.attributes[name] = value; |             this.attributes[name] = value; | ||||||
| @@ -72,7 +72,7 @@ class IgniteTemplate { | |||||||
|      */ |      */ | ||||||
|     property(name, value) { |     property(name, value) { | ||||||
|         if (value instanceof IgniteProperty) { |         if (value instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(value.attach((oldValue, newValue) => this.onPropertyChanged(oldValue, newValue, name))); |             this.callbacks.push(value.attachOnChange((oldValue, newValue) => this.onPropertyChanged(oldValue, newValue, name))); | ||||||
|             this.properties[name] = value.value; |             this.properties[name] = value.value; | ||||||
|         } else { |         } else { | ||||||
|             this.properties[name] = value; |             this.properties[name] = value; | ||||||
| @@ -110,7 +110,7 @@ class IgniteTemplate { | |||||||
|      */ |      */ | ||||||
|     ref(item) { |     ref(item) { | ||||||
|         if (item instanceof IgniteProperty) { |         if (item instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(item.attach((oldValue, newValue) => this.onRefChanged(oldValue, newValue, item))); |             this.callbacks.push(item.attachOnChange((oldValue, newValue) => this.onRefChanged(oldValue, newValue, item))); | ||||||
|             this.refs.push((element) => item.value = element); |             this.refs.push((element) => item.value = element); | ||||||
|         } else { |         } else { | ||||||
|             this.refs.push(item); |             this.refs.push(item); | ||||||
| @@ -130,7 +130,7 @@ class IgniteTemplate { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (func instanceof IgniteProperty) { |         if (func instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(func.attach((oldValue, newValue) => this.onEventChanged(oldValue, newValue, eventName))); |             this.callbacks.push(func.attachOnChange((oldValue, newValue) => this.onEventChanged(oldValue, newValue, eventName))); | ||||||
|             this.events[eventName].push(func.value); |             this.events[eventName].push(func.value); | ||||||
|         } else { |         } else { | ||||||
|             this.events[eventName].push(func); |             this.events[eventName].push(func); | ||||||
| @@ -153,7 +153,7 @@ class IgniteTemplate { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (func instanceof IgniteProperty) { |         if (func instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(func.attach((oldValue, newValue) => { |             this.callbacks.push(func.attachOnChange((oldValue, newValue) => { | ||||||
|                 //Create a new wrapped function to check for the enter key being pressed. |                 //Create a new wrapped function to check for the enter key being pressed. | ||||||
|                 var wrapped = (e) => { |                 var wrapped = (e) => { | ||||||
|                     if (e.key === 'Enter') { |                     if (e.key === 'Enter') { | ||||||
| @@ -201,7 +201,7 @@ class IgniteTemplate { | |||||||
|      */ |      */ | ||||||
|     style(name, value, priority = null) { |     style(name, value, priority = null) { | ||||||
|         if (value instanceof IgniteProperty) { |         if (value instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(value.attach((oldValue, newValue) => this.onCssValueChanged(oldValue, newValue, name))); |             this.callbacks.push(value.attachOnChange((oldValue, newValue) => this.onCssValueChanged(oldValue, newValue, name))); | ||||||
|             this.styles[name] = { name: name, value: value.value, priority: priority }; |             this.styles[name] = { name: name, value: value.value, priority: priority }; | ||||||
|         } else { |         } else { | ||||||
|             this.styles[name] = { name: name, value: value, priority: priority }; |             this.styles[name] = { name: name, value: value, priority: priority }; | ||||||
| @@ -480,7 +480,7 @@ class html extends IgniteTemplate { | |||||||
|         super([]); |         super([]); | ||||||
|  |  | ||||||
|         if (code instanceof IgniteProperty) { |         if (code instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(code.attach((oldValue, newValue) => this.onPropertyChanged(oldValue, newValue))); |             this.callbacks.push(code.attachOnChange((oldValue, newValue) => this.onPropertyChanged(oldValue, newValue))); | ||||||
|             this.code = code.value; |             this.code = code.value; | ||||||
|         } else { |         } else { | ||||||
|             this.code = code; |             this.code = code; | ||||||
| @@ -539,7 +539,9 @@ class list extends IgniteTemplate { | |||||||
|         this.tagName = "shadow list"; |         this.tagName = "shadow list"; | ||||||
|  |  | ||||||
|         if (list instanceof IgniteProperty) { |         if (list instanceof IgniteProperty) { | ||||||
|             this.callbacks.push(list.attach((oldValue, newValue) => this.onListChanged(oldValue, newValue))); |             this.callbacks.push(list.attachOnChange((oldValue, newValue) => this.onListChanged(oldValue, newValue))); | ||||||
|  |             this.callbacks.push(list.attachOnPush((oldValue, newValue) => this.onListPush(oldValue, newValue))); | ||||||
|  |             this.callbacks.push(list.attachOnPop((oldValue, newValue) => this.onListPop(oldValue, newValue))); | ||||||
|  |  | ||||||
|             this.list = list.value; |             this.list = list.value; | ||||||
|         } else { |         } else { | ||||||
| @@ -608,6 +610,26 @@ class list extends IgniteTemplate { | |||||||
|  |  | ||||||
|         IgniteRenderingContext.leave(); |         IgniteRenderingContext.leave(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     onListPush(oldValue, newValue) { | ||||||
|  |         IgniteRenderingContext.enter(); | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             var template = this.forEach(this.list[this.list.length - 1]); | ||||||
|  |             template.construct(this.element.parentElement, this.elements[this.elements.length - 1].nextSibling); | ||||||
|  |  | ||||||
|  |             this.children.push(template); | ||||||
|  |             this.elements.push(template.element); | ||||||
|  |         } catch { } | ||||||
|  |  | ||||||
|  |         IgniteRenderingContext.leave(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     onListPop(oldValue, newValue) { | ||||||
|  |         this.children[this.children.length - 1].deconstruct(); | ||||||
|  |         this.children.pop(); | ||||||
|  |         this.elements.pop(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| class slot extends IgniteTemplate { | class slot extends IgniteTemplate { | ||||||
|   | |||||||
| @@ -35,9 +35,11 @@ class Sheet extends IgniteElement { | |||||||
|                 new div( |                 new div( | ||||||
|                     new input().attribute("type", "text").onEnter(this.enter), |                     new input().attribute("type", "text").onEnter(this.enter), | ||||||
|                     new html("<h2>this is before</h2>"), |                     new html("<h2>this is before</h2>"), | ||||||
|  |                     new div( | ||||||
|                         new list(this.items, (item) => { |                         new list(this.items, (item) => { | ||||||
|                             return new a(new html(`<h3>${item}</h3>`)).attribute("href", this.href) |                             return new a(new html(`<h3>${item}</h3>`)).attribute("href", this.href) | ||||||
|                     }), |                         }) | ||||||
|  |                     ), | ||||||
|                     new html("<h2>this is after</h2>"), |                     new html("<h2>this is after</h2>"), | ||||||
|                     new html("<h3>---- begin sheet's slot ----<h3>"), |                     new html("<h3>---- begin sheet's slot ----<h3>"), | ||||||
|                     new slot(this), |                     new slot(this), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user