Cleaned up code, added a new auto doc generator and documented code extensively.

This commit is contained in:
2020-08-30 21:55:21 -07:00
parent 2da77f5177
commit 9346e01bec
8 changed files with 3815 additions and 374 deletions

View File

@ -1,13 +1,33 @@
import { IgniteProperty } from './ignite-html.js';
import { IgniteElement } from './ignite-element.js';
/**
* The outline of a ignite template. Templates are a blueprint that specify's
* how to construct an element and then can be used to construct the element. Everything
* starts with a template.
*
* @example
* //You can easily create a template to construct any html element. See the following:
* class div extends IgniteTemplate {
* constructor(...items) {
* super("div", items);
* }
* }
*/
class IgniteTemplate {
constructor(items) {
/**
* Creates a new IgniteTemplate with the tag name of the element that will be constructed and an
* array of child elements.
* @param {String} tagName The tag name of the element this template will construct.
* @param {String|Number|IgniteProperty|IgniteTemplate} children An array of child elements to be added to this template.
*/
constructor(tagName = null, children = null) {
this.siblings = [];
this.children = [];
this.attributes = {};
this.classes = [];
this.tagName = null;
this.tagName = tagName;
this.element = null;
this.properties = {};
this.refs = [];
@ -15,6 +35,99 @@ class IgniteTemplate {
this.events = {};
this.styles = {};
if (children) {
for (var i = 0; i < children.length; i++) {
if (children[i] instanceof IgniteProperty) {
this.children.push(new html(children[i]));
} else if (children[i] instanceof String || typeof children[i] === 'string') {
this.children.push(new html(children[i]));
} else if (children[i] instanceof Number || typeof children[i] === 'number') {
this.children.push(new html(children[i]));
} else if (children[i] instanceof IgniteTemplate || children[i].prototype instanceof IgniteTemplate) {
this.children.push(children[i]);
} else {
throw `Attempted to add a child for template: ${this.tagName} which is not supported. Child: ${children[i]}`;
}
}
}
}
/**
* Adds a CSS class to this template
* to be added once this template is constructed.
* @param {String|IgniteProperty} name Name of the CSS class to add.
* @param {Function} converter Optional function that can convert the class name into a different one.
* @returns This ignite template so function calls can be chained.
*/
class(name, converter = null) {
if (name instanceof IgniteProperty) {
this.callbacks.push(name.attachOnChange((oldValue, newValue) => this.onClassChanged(oldValue, newValue, converter)));
this.classes.push(converter != null ? converter(name.value) : name.value);
} else {
this.classes.push(converter != null ? converter(name) : name);
}
return this;
}
/**
* Adds a html element attribute to this template to be added once this template is constructed.
* @param {String} name The name of the attribute to add
* @param {String|IgniteProperty} value The value of the attribute to set, can be anything. If Property is passed it will auto update.
* @param {Function} converter Optional function that can convert the value if needed.
* @returns This ignite template so function calls can be chained.
*/
attribute(name, value, converter = null) {
if (value instanceof IgniteProperty) {
this.callbacks.push(value.attachOnChange((oldValue, newValue) => this.onAttributeChanged(oldValue, newValue, name, converter)));
this.attributes[name] = converter != null ? converter(value.value) : value.value;
} else {
this.attributes[name] = converter != null ? converter(value) : value;
}
return this;
}
/**
* Adds a property to this template to be added once this template is constructed.
* @param {String} name Name of the property to set.
* @param {Any|IgniteProperty} value Value of the property to use. If a Property is passed the value will auto update.
* @param {Boolean} reflect If true whenever this property is changed it's value will be passed back to the Property that was passed as value if one was passed.
* @param {Function} converter Optional function that can be used to convert the value if needed.
* @returns This ignite template so function calls can be chained.
*/
property(name, value, reflect = false, converter = null) {
if (this.properties[name]) {
throw `Attempted to set a property twice on a IgniteTemplate: ${this.tagName}. This is not allowed and should be avoided.`;
}
if (reflect && converter != null) {
throw `Cannot add a property to an IgniteTemplate: ${this.tagName} with reflect and a converter used at the same time.`;
}
if (value instanceof IgniteProperty) {
this.callbacks.push(value.attachOnChange((oldValue, newValue) => this.onPropertyChanged(oldValue, newValue, name, converter)));
this.properties[name] = {
value: (converter != null ? converter(value.value) : value.value),
reflect: (reflect == true ? value : null)
};
} else {
this.properties[name] = {
value: (converter != null ? converter(value) : value),
reflect: null
};
}
return this;
}
/**
* Adds a single or series of children to be added once this template
* is constructed. Numbers, Strings, and Properties passed will be added as HTML child elements.
* @param {...Number|String|IgniteProperty|IgniteTemplate} items A series of children to be added to this template.
* @returns This ignite template so function calls can be chained.
*/
child(...items) {
if (items) {
for (var i = 0; i < items.length; i++) {
if (items[i] instanceof IgniteProperty) {
@ -30,141 +143,75 @@ class IgniteTemplate {
}
}
}
return this;
}
/**
* Adds a single or series of classes to this template
* to be added once this template is constructed.
* @param {...any} items
* Adds a reference callback function to be invoked once this template is constructed. The function will be invoked
* with the constructed HTMLElement. If an IgniteProperty is passed it's value will be set to the constructed HTMLElement once
* the template is constructed.
* @param {Function|IgniteProperty} refCallback The callback to be invoked with the HTMLElement of the element this template constructed.
* @returns This ignite template so function calls can be chained.
*/
class(name, converter = null) {
if (name instanceof IgniteProperty) {
this.callbacks.push(name.attachOnChange((oldValue, newValue) => this.onClassChanged(oldValue, newValue, converter)));
this.classes.push(converter != null ? converter(name.value) : name.value);
ref(refCallback) {
if (refCallback instanceof IgniteProperty) {
this.callbacks.push(refCallback.attachOnChange((oldValue, newValue) => this.onRefChanged(oldValue, newValue, refCallback)));
this.refs.push((element) => refCallback.value = element);
} else {
this.classes.push(converter != null ? converter(name) : name);
this.refs.push(refCallback);
}
return this;
}
/**
* Adds a attribute to this template to be added once this template is constructed.
* @param {string} name
* @param {any} value
* Adds an event by its name and the function to invoke once the event fires. Properties may be used
* for the function, but their value must be a valid function in order to get a proper event callback.
* @param {String} eventName The name of the event to add.
* @param {Function|IgniteProperty} eventCallback The callback function to be invoked by the event once it fires.
* @returns This ignite template so function calls can be chained.
*/
attribute(name, value, converter = null) {
if (value instanceof IgniteProperty) {
this.callbacks.push(value.attachOnChange((oldValue, newValue) => this.onAttributeChanged(oldValue, newValue, name, converter)));
this.attributes[name] = converter != null ? converter(value.value) : value.value;
} else {
this.attributes[name] = converter != null ? converter(value) : value;
}
return this;
}
/**
* Adds a property to this template to be added once this template is constructed.
* @param {string} name
* @param {any} value
*/
property(name, value, reflect = false) {
if (this.properties[name]) {
throw `Attempted to set a property twice on a IgniteTemplate: ${this.tagName}. This is not allowed and should be avoided.`;
}
if (value instanceof IgniteProperty) {
this.callbacks.push(value.attachOnChange((oldValue, newValue) => this.onPropertyChanged(oldValue, newValue, name)));
this.properties[name] = { value: value.value, reflect: (reflect == true ? value : null) };
} else {
this.properties[name] = { value: value, reflect: null };
}
return this;
}
/**
* Adds a single or series of children to be added once this template
* is constructed.
* @param {...any} items
*/
child(...items) {
for (var i = 0; i < items.length; i++) {
if (items[i] instanceof IgniteProperty) {
this.children.push(new html(items[i]));
} else if (items[i] instanceof String || typeof items[i] === 'string') {
this.children.push(new html(items[i]));
} else if (items[i] instanceof Number || typeof items[i] === 'number') {
this.children.push(new html(items[i]));
} else if (items[i] instanceof IgniteTemplate || items[i].prototype instanceof IgniteTemplate) {
this.children.push(items[i]);
} else {
throw `Attempted to add a child for template: ${this.tagName} which is not supported. Child: ${items[i]}`;
}
}
return this;
}
/**
* Adds a ref callback function to be invoked once this template is constructed.
* @param {function} item
*/
ref(item) {
if (item instanceof IgniteProperty) {
this.callbacks.push(item.attachOnChange((oldValue, newValue) => this.onRefChanged(oldValue, newValue, item)));
this.refs.push((element) => item.value = element);
} else {
this.refs.push(item);
}
return this;
}
/**
* Adds a event by its name and the func to invoke once the event fires.
* @param {String} eventName
* @param {Any} func
*/
on(eventName, func) {
on(eventName, eventCallback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
if (func instanceof IgniteProperty) {
this.callbacks.push(func.attachOnChange((oldValue, newValue) => this.onEventChanged(oldValue, newValue, eventName)));
this.events[eventName].push(func.value);
if (eventCallback instanceof IgniteProperty) {
this.callbacks.push(eventCallback.attachOnChange((oldValue, newValue) => this.onEventChanged(oldValue, newValue, eventName)));
this.events[eventName].push(eventCallback.value);
} else {
this.events[eventName].push(func);
this.events[eventName].push(eventCallback);
}
return this;
}
/**
* Adds a on click event handler to this template.
* @param {Any} func
* Adds an onclick event handler to this template.
* @param {Function|IgniteProperty} eventCallback The callback function to be invoked by the event once it fires.
* @returns This ignite template so function calls can be chained.
*/
onClick(func) {
this.on("click", func);
onClick(eventCallback) {
this.on("click", eventCallback);
return this;
}
/**
* Adds a on enter key press event handler to this template.
* @param {Any} func
* @param {Function|IgniteProperty} eventCallback The callback function to be invoked by the event once it fires.
* @returns This ignite template so function calls can be chained.
*/
onEnter(func) {
onEnter(eventCallback) {
var eventName = "keydown";
if (!this.events[eventName]) {
this.events[eventName] = [];
}
if (func instanceof IgniteProperty) {
this.callbacks.push(func.attachOnChange((oldValue, newValue) => {
if (eventCallback instanceof IgniteProperty) {
this.callbacks.push(eventCallback.attachOnChange((oldValue, newValue) => {
//Create a new wrapped function to check for the enter key being pressed.
var wrapped = (e) => {
if (e.key === 'Enter') {
@ -174,14 +221,14 @@ class IgniteTemplate {
//Store the wrapped function so that it's the old value next time around
//and the old event can be removed in the future
func._value = wrapped;
eventCallback._value = wrapped;
//Invoke the on event changed with the old value and our wrapped value.
this.onEventChanged(oldValue, wrapped, eventName)
}));
//Create the initial wrapper
var target = func._value;
var target = eventCallback._value;
var wrapped = (e) => {
if (e.key === 'Enter') {
target(e);
@ -189,13 +236,13 @@ class IgniteTemplate {
};
//Store the wrapped so that it's the old value next time around.
func._value = wrapped;
eventCallback._value = wrapped;
this.events[eventName].push(wrapped);
} else {
this.on(eventName, (e) => {
if (e.key === 'Enter') {
func(e);
eventCallback(e);
}
});
}
@ -204,11 +251,12 @@ class IgniteTemplate {
}
/**
* Sets a css style on this template and adds it to the element
* once it's constructed.
* @param {String} name
* @param {String} value
* @param {Boolean} priority
* Adds a CSS property to this template with a value and priority.
* @param {String} name The name of the CSS property to set.
* @param {String|IgniteProperty} value The value to set for the property. If an IgniteProperty is used it will auto update this style.
* @param {String} priority If set to "important" then the style will be marked with !important.
* @param {Function} converter Optional function to convert the value if needed.
* @returns This ignite template so function calls can be chained.
*/
style(name, value, priority = null, converter = null) {
if (value instanceof IgniteProperty) {
@ -224,8 +272,8 @@ class IgniteTemplate {
/**
* Constructs this template and adds it to the DOM if this template
* has not already been constructed.
* @param {HTMLElement} parent
* @param {HTMLElement} sibling
* @param {HTMLElement} parent Parent element that will contain the constructed element.
* @param {HTMLElement} sibling Optional sibling element that can be used to add the element adjacantly.
*/
construct(parent, sibling) {
//Don't construct if we have no parent, no sibling and no element.
@ -357,6 +405,7 @@ class IgniteTemplate {
* on the template's element.
* @param {any} oldValue
* @param {any} newValue
* @ignore
*/
onClassChanged(oldValue, newValue, converter) {
if (converter !== null) {
@ -389,6 +438,7 @@ class IgniteTemplate {
* @param {any} oldValue
* @param {any} newValue
* @param {string} attributeName
* @ignore
*/
onAttributeChanged(oldValue, newValue, attributeName, converter) {
if (converter !== null) {
@ -412,8 +462,13 @@ class IgniteTemplate {
* @param {any} oldValue
* @param {any} newValue
* @param {string} propertyName
* @ignore
*/
onPropertyChanged(oldValue, newValue, propertyName) {
onPropertyChanged(oldValue, newValue, propertyName, converter) {
if (converter !== null) {
newValue = converter(newValue);
}
if (this.element) {
//Use the set value function and don't reflect the change because it came from above which
//would be the reflected property, this reduces some functions being called twice that don't need to be.
@ -429,6 +484,7 @@ class IgniteTemplate {
* @param {any} oldValue
* @param {any} newValue
* @param {any} ref
* @ignore
*/
onRefChanged(oldValue, newValue, ref) {
//Only set the reference value to ourself if it's not our element.
@ -443,6 +499,7 @@ class IgniteTemplate {
* @param {any} oldValue
* @param {any} newValue
* @param {any} eventName
* @ignore
*/
onEventChanged(oldValue, newValue, eventName) {
if (this.element) {
@ -468,7 +525,8 @@ class IgniteTemplate {
* Called when a css value was changed and we need to update the styling.
* @param {any} oldValue
* @param {any} newValue
* @param {any} style
* @param {any} style
* @ignore
*/
onCssValueChanged(oldValue, newValue, name, converter) {
if (this.element) {
@ -479,97 +537,187 @@ class IgniteTemplate {
}
}
/**
* An ignite template that can be used to construct a div element.
*/
class div extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "div";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("div", children);
}
}
/**
* An ignite template that can be used to construct a hyperlink element.
*/
class a extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "a";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("a", children);
}
}
/**
* An ignite template that can be used to construct a input element.
*/
class input extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "input";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("input", children);
}
}
/**
* An ignite template that can be used to construct a button element.
*/
class button extends IgniteTemplate {
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("button", children);
}
}
/**
* An ignite template that can be used to construct a h1 element.
*/
class h1 extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "h1";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("h1", children);
}
}
/**
* An ignite template that can be used to construct a h2 element.
*/
class h2 extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "h2";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("h2", children);
}
}
/**
* An ignite template that can be used to construct a h3 element.
*/
class h3 extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "h3";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("h3", children);
}
}
/**
* An ignite template that can be used to construct a h4 element.
*/
class h4 extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "h4";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("h4", children);
}
}
/**
* An ignite template that can be used to construct a h5 element.
*/
class h5 extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "h5";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("h5", children);
}
}
/**
* An ignite template that can be used to construct a p element.
*/
class p extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "p";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("p", children);
}
}
/**
* An ignite template that can be used to construct a span element.
*/
class span extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "span";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("span", children);
}
}
/**
* An ignite template that can be used to construct an i element.
*/
class i extends IgniteTemplate {
constructor(...items) {
super(items);
this.tagName = "i";
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("i", children);
}
}
/**
* An ignite template that can be used to construct a br element.
*/
class br extends IgniteTemplate {
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("br", children);
}
}
/**
* An ignite template that can be used to construct a img element.
*/
class img extends IgniteTemplate {
/**
* @param {...String|Number|IgniteProperty|IgniteTemplate} children A series of children to be added to this template.
*/
constructor(...children) {
super("img", children);
}
}
/**
* Html is a special template that can construct raw html or properties into the dom and automatically
* update the dom if the property changes.
*
* @example
* new html(`<h1>Hello world!</h1>`)
*/
class html extends IgniteTemplate {
/**
* @param {String|IgniteProperty} code HTML code to be constructed within this template. If an IgniteProperty is passed it's value will be used.
*/
constructor(code) {
super([]);
super();
if (code instanceof IgniteProperty) {
this.callbacks.push(code.attachOnChange((oldValue, newValue) => this.onPropertyChanged(oldValue, newValue)));
@ -625,10 +773,22 @@ class html extends IgniteTemplate {
}
}
/**
* A special ignite template that constructs a list of items using a template
* that is dynamically created for each item.
*
* @example
* new list(["1", "2", "3"], (item) => {
* return new h1(item);
* })
*/
class list extends IgniteTemplate {
/**
* @param {Array|IgniteProperty} list The list of items to construct within this template.
* @param {Function} forEach A function that construct a template for an item from the list that is passed to it.
*/
constructor(list, forEach) {
super([]);
this.tagName = "shadow list";
super();
if (list instanceof IgniteProperty) {
this.callbacks.push(list.attachOnChange((oldValue, newValue) => this.onListChanged(oldValue, newValue)));
@ -642,6 +802,7 @@ class list extends IgniteTemplate {
this.forEach = forEach;
this.elements = [];
this.tagName = "shadow list";
}
construct(parent, sibling) {
@ -731,9 +892,20 @@ class list extends IgniteTemplate {
}
}
/**
* A slot template that mimicks the functionality of a slot element in Web Components. This can
* be used to place children of a IgniteElement anywhere in the DOM. Slots don't actually construct an element,
* they simply just place children in place where the slot was used.
*
* @example
* new slot(this)
*/
class slot extends IgniteTemplate {
/**
* @param {IgniteElement} element The parent IgniteElement that this slot is for.
*/
constructor(element) {
super(null);
super();
this.parent = element;
}
@ -766,6 +938,7 @@ export {
list,
a,
input,
button,
h1,
h2,
h3,
@ -774,5 +947,7 @@ export {
p,
span,
i,
br,
img,
slot
};