Simplifying ignite html repo and adding latest changes.
This commit is contained in:
313
ignite-element.js
Normal file
313
ignite-element.js
Normal file
@ -0,0 +1,313 @@
|
||||
import { IgniteProperty, IgniteCallback } from './ignite-html.js';
|
||||
import { IgniteTemplate } from './ignite-template.js';
|
||||
|
||||
/**
|
||||
* The outline of a Ignite Element that extends the html element
|
||||
* and can be used to create custom components. Ignite Element's use an Ignite Template
|
||||
* for the render function.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* class MainApp extends IgniteElement {
|
||||
* constructor() {
|
||||
* super();
|
||||
* }
|
||||
*
|
||||
* get properties() {
|
||||
* return {
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* return this.template
|
||||
* .child(
|
||||
* new h1(`<i class="fad fa-fire-alt" style="--fa-primary-color: #FFC107; --fa-secondary-color: #FF5722; --fa-secondary-opacity: 1.0;"></i> Ignite HTML`),
|
||||
* new h4(`Adding more fire to the web.`)
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* customElements.define("main-app", MainApp);
|
||||
*
|
||||
* @example
|
||||
* //If you want to easily use an Ignite Element with templates see the following which can be added
|
||||
* //to any component file
|
||||
* class MainAppTemplate extends IgniteTemplate {
|
||||
* constructor(...children) {
|
||||
* super("main-app", children);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* export {
|
||||
* MainAppTemplate as MainApp
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* //If a template was created for a Ignite Element (like the previous example) it can be used just like any other template:
|
||||
* new MainApp()
|
||||
*/
|
||||
class IgniteElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.connected = false;
|
||||
this.onDisconnected = null;
|
||||
this.template = null;
|
||||
this.elements = [];
|
||||
this.createProperties();
|
||||
this.readyCallback = new IgniteCallback(() => this.ready());
|
||||
|
||||
//Init the element before connected callback is fired
|
||||
//Create a new rendering context so the init method can access properties correctly.
|
||||
IgniteRenderingContext.push();
|
||||
this.init();
|
||||
IgniteRenderingContext.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object with all the properties for this ignite element. If null or empty
|
||||
* then no properties will be created. To change a property and read it's current value see below.
|
||||
*
|
||||
* @returns An object with properties that will be assigned to this ignite element.
|
||||
*
|
||||
* @example
|
||||
* get properties() {
|
||||
* return {
|
||||
* show: false,
|
||||
* title: "This is a title",
|
||||
* items: [1, 2, 3]
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* //To change a property access it via this.PROP_NAME
|
||||
* this.show = false;
|
||||
*
|
||||
* //To get a properties value access if via this.PROP_NAME
|
||||
* console.log(this.title);
|
||||
*/
|
||||
get properties() {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any CSS styling code for this ignite element. If this returns a non null
|
||||
* value the CSS will auto be injected into the current HTML page once and reused for the life
|
||||
* of the ignite element.
|
||||
*
|
||||
* @returns A string containing CSS code to be used with this ignite element.
|
||||
*
|
||||
* @example
|
||||
* get styles() {
|
||||
* return `
|
||||
* h1 {
|
||||
* color: black;
|
||||
* }
|
||||
* `;
|
||||
* }
|
||||
*/
|
||||
get styles() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the getters/setters for properties in this
|
||||
* ignite element and initializes everything.
|
||||
* @ignore
|
||||
*/
|
||||
createProperties() {
|
||||
var props = this.properties;
|
||||
|
||||
if (props != null) {
|
||||
var keys = Object.keys(props);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
let propValue = props[keys[i]];
|
||||
let propName = keys[i];
|
||||
|
||||
//Create a new property, if the propValue is a property, use that instead.
|
||||
var prop = null;
|
||||
if (propValue instanceof IgniteProperty) {
|
||||
prop = propValue;
|
||||
} else {
|
||||
prop = new IgniteProperty(propValue);
|
||||
}
|
||||
|
||||
this[`_${propName}`] = prop;
|
||||
|
||||
Object.defineProperty(this, propName, {
|
||||
get: () => {
|
||||
if (IgniteRenderingContext.rendering == false) {
|
||||
return this[`_${propName}`].value;
|
||||
} else {
|
||||
return this[`_${propName}`];
|
||||
}
|
||||
},
|
||||
|
||||
set: (value) => {
|
||||
this[`_${propName}`].value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the properties for this element back to their original default
|
||||
* value.
|
||||
*/
|
||||
resetProperties() {
|
||||
var props = this.properties;
|
||||
|
||||
if (props != null) {
|
||||
var keys = Object.keys(props);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
this[keys[i]] = props[keys[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups this ignite element and constructs it's template when
|
||||
* this function is called by the DOM upon this element being created somewhere.
|
||||
* @ignore
|
||||
*/
|
||||
connectedCallback() {
|
||||
//Only run this if we haven't been connected before.
|
||||
//If this element is moved the instance will be the same but it will call this again
|
||||
//and we want to trap this and not run the code below twice.
|
||||
if (this.connected) {
|
||||
return;
|
||||
} else {
|
||||
this.connected = true;
|
||||
}
|
||||
|
||||
//See if a styling sheet has been created for this element if it's needed.
|
||||
if (this.styles !== null && this.styles !== "") {
|
||||
if (document.getElementsByClassName(`_${this.tagName}_styling_`).length == 0) {
|
||||
var styleEl = document.createElement("style");
|
||||
styleEl.classList.add(`_${this.tagName}_styling_`);
|
||||
styleEl.innerHTML = this.styles;
|
||||
document.body.prepend(styleEl);
|
||||
}
|
||||
}
|
||||
|
||||
//If we don't already have a template, make sure we create one,
|
||||
//this can happen if this element was constructed in the DOM instead of within a template.
|
||||
if (!this.template) {
|
||||
this.template = new IgniteTemplate();
|
||||
this.template.element = this;
|
||||
this.template.tagName = this.tagName;
|
||||
}
|
||||
|
||||
//Add any childNodes we have to the elements list within this
|
||||
this.childNodes.forEach((item) => this.elements.push(item));
|
||||
|
||||
//Enter a rendering context so properties don't expose their values until we are done.
|
||||
IgniteRenderingContext.enter();
|
||||
|
||||
//Make sure the render template is our template, if not, add it as a child.
|
||||
var renderTemplate = this.render();
|
||||
if (renderTemplate !== this.template && renderTemplate) {
|
||||
this.template.child(renderTemplate);
|
||||
} else if (!renderTemplate) {
|
||||
throw `RenderTemplate was null for template: ${this.tagName}. Is render() returning null or not returning a template?`;
|
||||
}
|
||||
|
||||
//Construct our template.
|
||||
try {
|
||||
this.template.construct(this.parentElement);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
//Leave the rendering context.
|
||||
IgniteRenderingContext.leave();
|
||||
|
||||
//Let the rendering context know this element is ready.
|
||||
IgniteRenderingContext.ready(this.readyCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanups this element and deconstructs everything when this element is removed from
|
||||
* the DOM.
|
||||
* @ignore
|
||||
*/
|
||||
disconnectedCallback() {
|
||||
//Run a test here, if the element was moved but not removed it will have a disconnect
|
||||
//get called but then be reconnected, so use a timer to see if this is what happened.
|
||||
setTimeout(() => {
|
||||
//If the element is connected then don't do this, more than likely
|
||||
//the element was moved or something.
|
||||
if (this.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
//If we still have a reference to our template, deconstruct it.
|
||||
if (this.template) {
|
||||
this.template.deconstruct();
|
||||
}
|
||||
|
||||
//If we have a onDisconnected callback, call it and then remove the reference.
|
||||
if (this.onDisconnected) {
|
||||
this.onDisconnected();
|
||||
this.onDisconnected = null;
|
||||
}
|
||||
|
||||
//Detach the after render callback
|
||||
this.readyCallback.disconnect();
|
||||
|
||||
//Cleanup this element if we need to.
|
||||
this.cleanup();
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the template to be rendered for this element.
|
||||
*
|
||||
* @returns An ignite template to be used to construct this ignite element.
|
||||
*
|
||||
* @see this.template is automatically created for each IgniteElement and must be used in the render() function.
|
||||
*
|
||||
* @example
|
||||
* render() {
|
||||
* return this.template
|
||||
* .child(
|
||||
* new h1(`<i class="fad fa-fire-alt" style="--fa-primary-color: #FFC107; --fa-secondary-color: #FF5722; --fa-secondary-opacity: 1.0;"></i> Ignite HTML`),
|
||||
* new h4(`Adding more fire to the web.`)
|
||||
* );
|
||||
* }
|
||||
*/
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called wehn this ignite element is being initialized. When this is called
|
||||
* the element has not been created. This is good for login checking code or special setup code.
|
||||
*/
|
||||
init() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this ignite element is ready. This will invoke once all
|
||||
* ignite elements on the current page have been rendered and setup. You can safely know
|
||||
* all elements have be initialized and are ready once this is called.
|
||||
*/
|
||||
ready() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this ignite element should cleanup
|
||||
* any resources like events, timers, ect. This is called when the element
|
||||
* is being destroyed.
|
||||
*/
|
||||
cleanup() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
IgniteElement
|
||||
};
|
Reference in New Issue
Block a user