Added new options function that can generate a set of options from an input and allow them to be converted. Modified the converter template to support an array of ignite properties as the input. The converter now supports the value converter returning an array of ignite templates. This allows for more complex conversions.

This commit is contained in:
MattMo 2023-11-10 12:10:17 -08:00
parent 2d41cb26a0
commit 7f2b6465c2

View File

@ -39,6 +39,8 @@ class IgniteTemplate {
this._attributes = {}; this._attributes = {};
this._classes = []; this._classes = [];
this._properties = {}; this._properties = {};
this._options = [];
this._optionElements = [];
this._variables = {}; this._variables = {};
this._reflecting = {}; this._reflecting = {};
this._refs = []; this._refs = [];
@ -394,6 +396,77 @@ class IgniteTemplate {
return this; return this;
} }
/**
* Sets the options elements to be constructed by this template.
* Valid options can be in this format:
* [{1: "Option 1"}, {2: "Option 2"}]
* ["A", "B", "C"]
* {1: "Option 1", 2: "Option 2"}
* @param {Array|Object|IgniteProperty|IgniteProperty[]} options The options to be constructed on this template.
* @param {Function} converter Optional function that can be used to convert the options input if needed.
* @returns {IgniteTemplate} This ignite template so function calls can be chained.
*/
options(options, converter = null) {
IgniteRendering.push();
var converted = null;
if (options instanceof IgniteProperty) {
this._callbacks.push(options.attachOnChange((oldValue, newValue) => this.onOptionsChanged(converter ? converter(newValue) : newValue)));
this._callbacks.push(options.attachOnPush((list, items) => this.onOptionsChanged(converter ? converter(list) : list)));
this._callbacks.push(options.attachOnUnshift((list, items) => this.onOptionsChanged(converter ? converter(list) : list)));
this._callbacks.push(options.attachOnPop((list) => this.onOptionsChanged(converter ? converter(list) : list)));
this._callbacks.push(options.attachOnShift((list) => this.onOptionsChanged(converter ? converter(list) : list)));
this._callbacks.push(options.attachOnSplice((list, start, deleteCount, items) => this.onOptionsChanged(converter ? converter(list) : list)));
converted = converter ? converter(options.value) : options.value;
} else if (Array.isArray(options) && options.length > 0 && options[0] instanceof IgniteProperty) {
//There must be a converter for this to work correctly
if (!converter) {
throw "Cannot pass an array of properties without using a converter!";
}
//Attack a callback for all the properties
options.forEach(option => {
if (option instanceof IgniteProperty) {
this._callbacks.push(option.attachOnChange((oldValue, newValue) => this.onOptionsChanged(converter(...options.getPropertyValues()))));
this._callbacks.push(option.attachOnPush((list, items) => this.onOptionsChanged(converter(...options.getPropertyValues()))));
this._callbacks.push(option.attachOnUnshift((list, items) => this.onOptionsChanged(converter(...options.getPropertyValues()))));
this._callbacks.push(option.attachOnPop((list) => this.onOptionsChanged(converter(...options.getPropertyValues()))));
this._callbacks.push(option.attachOnShift((list) => this.onOptionsChanged(converter(...options.getPropertyValues()))));
this._callbacks.push(option.attachOnSplice((list, start, deleteCount, items) => this.onOptionsChanged(converter(...options.getPropertyValues()))));
}
});
converted = converter(...options.getPropertyValues());
} else {
converted = converter ? converter(options) : options;
}
if (Array.isArray(converted)) {
this._options = converted.map((option, index) => {
if (option instanceof Object) {
var keys = Object.keys(option);
if (keys.length == 0) {
return null;
} else {
return { value: keys[0], name: option[keys[0]] };
}
} else {
return { value: index, name: option };
}
});
} else if (converted instanceof Object) {
this._options = Object.keys(converted).map(key => {
return { value: key, name: converted[key] };
});
}
IgniteRendering.pop();
return this;
}
/** /**
* Sets the inner html of the element to be constructed by this template. * Sets the inner html of the element to be constructed by this template.
* @param {String|IgniteProperty|IgniteProperty[]} value InnerHTML to set for element. If a property is passed the html will auto update. * @param {String|IgniteProperty|IgniteProperty[]} value InnerHTML to set for element. If a property is passed the html will auto update.
@ -1410,6 +1483,24 @@ class IgniteTemplate {
this._intersectObserver.observe(this.element); this._intersectObserver.observe(this.element);
} }
//Construct any options if needed
if (this._options.length > 0 && this._optionElements.length == 0) {
this._options.forEach(option => {
if (option) {
var element = window.document.createElement("option");
element.setAttribute("value", option.value);
element.innerHTML = option.name;
this._optionElements.push(element);
//Add this element to the dom.
if (this.element) {
this.element.appendChild(element);
}
}
});
}
//Invoke any custom constructors. //Invoke any custom constructors.
this._constructors.forEach(callback => callback(parent, sibling)); this._constructors.forEach(callback => callback(parent, sibling));
@ -1685,6 +1776,58 @@ class IgniteTemplate {
this._styles[name].value = newValue; this._styles[name].value = newValue;
} }
/**
* Called when the options for this template have changed and need to be updated.
* @param {Array} newValue
*/
onOptionsChanged(newValue) {
//First remove all existing options
if (this._optionElements) {
this._optionElements.forEach(element => element.remove());
this._optionElements = [];
}
//Set the options to null.
this._options = [];
//Convert the newValue into options
if (Array.isArray(newValue)) {
this._options = newValue.map((option, index) => {
if (option instanceof Object) {
var keys = Object.keys(option);
if (keys.length == 0) {
return null;
} else {
return { value: keys[0], name: option[keys[0]] };
}
} else {
return { value: index, name: option };
}
});
} else if (newValue instanceof Object) {
this._options = Object.keys(newValue).map(key => {
return { value: key, name: newValue[key] };
});
}
//Construct the new options
this._options.forEach(option => {
if (option) {
var element = document.createElement("option");
element.setAttribute("value", option.value);
element.innerHTML = option.name;
this._optionElements.push(element);
//Add this element to the dom.
if (this.element) {
this.element.appendChild(element);
}
}
});
}
} }
/** /**
@ -2166,7 +2309,7 @@ class source extends IgniteTemplate {
if (this.element && name && name.trim().toLowerCase() == "src") { if (this.element && name && name.trim().toLowerCase() == "src") {
this.element.parentElement.pause(); this.element.parentElement.pause();
this.element.parentElement.currentTime = 0; this.element.parentElement.currentTime = 0;
//If there is a valid video source, call load on the player. //If there is a valid video source, call load on the player.
if (newValue && newValue.length > 0) { if (newValue && newValue.length > 0) {
this.element.parentElement.load(); this.element.parentElement.load();
@ -2264,7 +2407,7 @@ class line extends IgniteTemplate {
class text extends IgniteTemplate { class text extends IgniteTemplate {
/** /**
* Constructs a text template with the text to render. * Constructs a text template with the text to render.
* @param {String|IgniteProperty} text The text to render within this text template. * @param {String|IgniteProperty|IgniteProperty[]} text The text to render within this text template.
* @param {Function} converter An optional function that can be used to convert the text. * @param {Function} converter An optional function that can be used to convert the text.
*/ */
constructor(text, converter) { constructor(text, converter) {
@ -2717,6 +2860,11 @@ class list extends IgniteTemplate {
* dynamic value changes and dynamic converter changes. * dynamic value changes and dynamic converter changes.
*/ */
class converter extends IgniteTemplate { class converter extends IgniteTemplate {
/**
* Creates a new converter with a value and a callback convert function.
* @param {Any|IgniteProperty|IgniteProperty[]} value
* @param {IgniteProperty|Function} converter
*/
constructor(value, converter) { constructor(value, converter) {
super(); super();
@ -2728,6 +2876,10 @@ class converter extends IgniteTemplate {
this.converter = converter; this.converter = converter;
} }
if (!this.converter) {
throw "A valid converter must be passed.";
}
if (value instanceof IgniteProperty) { if (value instanceof IgniteProperty) {
this._callbacks.push(value.attachOnChange((oldValue, newValue) => this.onValueChanged(newValue))); this._callbacks.push(value.attachOnChange((oldValue, newValue) => this.onValueChanged(newValue)));
this._callbacks.push(value.attachOnPush((list, items) => this.onValueChanged(list))); this._callbacks.push(value.attachOnPush((list, items) => this.onValueChanged(list)));
@ -2736,25 +2888,25 @@ class converter extends IgniteTemplate {
this._callbacks.push(value.attachOnShift(list => this.onValueChanged(list))); this._callbacks.push(value.attachOnShift(list => this.onValueChanged(list)));
this._callbacks.push(value.attachOnSplice((list, start, deleteCount, items) => this.onValueChanged(list))); this._callbacks.push(value.attachOnSplice((list, start, deleteCount, items) => this.onValueChanged(list)));
this.value = value.value; this.value = value;
} else if (Array.isArray(value) && value.length > 0 && value[0] instanceof IgniteProperty) {
//Attach a callback for all the properties
value.forEach(prop => {
if (prop instanceof IgniteProperty) {
this._callbacks.push(prop.attachOnChange((oldValue, newValue) => this.onValueChanged(value)));
this._callbacks.push(prop.attachOnPush((list, items) => this.onValueChanged(value)));
this._callbacks.push(prop.attachOnUnshift((list, items) => this.onValueChanged(value)));
this._callbacks.push(prop.attachOnPop((list) => this.onValueChanged(value)));
this._callbacks.push(prop.attachOnShift((list) => this.onValueChanged(value)));
this._callbacks.push(prop.attachOnSplice((list, start, deleteCount, items) => this.onValueChanged(value)));
}
});
this.value = value;
} else if (value) { } else if (value) {
this.value = value; this.value = value;
} }
var child = this.value;
if (this.converter) {
child = this.converter(this.value);
}
if (child instanceof IgniteTemplate) {
this.child = child;
} else if (child) {
this.child = new html(child);
} else {
this.child = null;
}
this.tagName = "converter"; this.tagName = "converter";
} }
@ -2782,30 +2934,47 @@ class converter extends IgniteTemplate {
parent = this.element.parentElement; parent = this.element.parentElement;
} }
if (this.child) { //Construct the converted value
this.child.construct(parent, this.element); if (this.converted instanceof IgniteTemplate) {
this.converted.construct(parent, this.element);
} else if (Array.isArray(this.converted)) {
var last = this.element;
for (var i = 0; i < this.converted.length; i++) {
if (this.converted[i] instanceof IgniteTemplate) {
this.converted[i].construct(parent, last);
last = this.converted[i].element;
}
}
} }
} }
onValueChanged(newValue) { onValueChanged(newValue) {
this.value = newValue; this.value = newValue;
if (this.child) { //Deconstruct any existing converted elements
this.child.deconstruct(); if (this.converted) {
if (Array.isArray(this.converted)) {
this.converted.forEach(element => {
if (element instanceof IgniteTemplate) {
element.deconstruct();
}
});
} else if (this.converted instanceof IgniteTemplate) {
this.converted.deconstruct();
}
this.converted = null;
} }
var child = newValue; //Convert the value using the latest converter
if (Array.isArray(this.value) && this.value.length > 0 && this.value[0] instanceof IgniteProperty) {
if (this.converter) { this.converted = this.converter ? this.converter(...this.value.getPropertyValues()) : this.value.getPropertyValues();
child = this.converter(child); } else if (this.value instanceof IgniteProperty) {
} this.converted = this.converter ? this.converter(this.value.value) : this.value.value;
if (child instanceof IgniteTemplate) {
this.child = child;
} else if (child) {
this.child = new html(child);
} else { } else {
this.child = null; this.converted = this.converter ? this.converter(this.value) : this.value;
} }
this.construct(null, null); this.construct(null, null);
@ -2814,22 +2983,28 @@ class converter extends IgniteTemplate {
onConverterChanged(newConverter) { onConverterChanged(newConverter) {
this.converter = newConverter; this.converter = newConverter;
if (this.child) { //Deconstruct any existing converted elements
this.child.deconstruct(); if (this.converted) {
if (Array.isArray(this.converted)) {
this.converted.forEach(element => {
if (element instanceof IgniteTemplate) {
element.deconstruct();
}
});
} else if (this.converted instanceof IgniteTemplate) {
this.converted.deconstruct();
}
this.converted = null;
} }
var child = this.value; //Convert the value using the latest converter
if (Array.isArray(this.value) && this.value.length > 0 && this.value[0] instanceof IgniteProperty) {
if (this.converter) { this.converted = this.converter ? this.converter(...this.value.getPropertyValues()) : this.value.getPropertyValues();
child = this.converter(child); } else if (this.value instanceof IgniteProperty) {
} this.converted = this.converter ? this.converter(this.value.value) : this.value.value;
if (child instanceof IgniteTemplate) {
this.child = child;
} else if (child) {
this.child = new html(child);
} else { } else {
this.child = null; this.converted = this.converter ? this.converter(this.value) : this.value;
} }
this.construct(null, null); this.construct(null, null);