- Added to assets-package
This commit is contained in:
commit
fa69a8281b
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
import { ElementObserver } from "./element-observer.js";
|
||||||
|
|
||||||
|
import { UiTabs } from "./module/ui-tabs.js";
|
||||||
|
window.customElements.define("ui-tabs", UiTabs);
|
||||||
|
|
||||||
|
import { UiPopup } from "./module/ui-popup.js";
|
||||||
|
window.customElements.define("ui-popup", UiPopup);
|
||||||
|
|
||||||
|
import { UiTextarea } from "./module/ui-textarea.js";
|
||||||
|
window.customElements.define("ui-textarea", UiTextarea);
|
||||||
|
|
||||||
|
import { UiSelect } from "./module/ui-select.js";
|
||||||
|
window.customElements.define("ui-select", UiSelect);
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* @author Dave Mc Nicoll
|
||||||
|
* @version 1.0.0
|
||||||
|
*
|
||||||
|
* Analysis elements entering or quitting the DOM, triggering events whenever
|
||||||
|
* a matching element is found.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
import { Hourglass } from "./hourglass.js";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
resize: {
|
||||||
|
timer: 200
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ACTION_ADDED = 0,
|
||||||
|
ACTION_REMOVED = 1,
|
||||||
|
ACTION_RESIZED = 2,
|
||||||
|
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
||||||
|
|
||||||
|
class ElementObserver {
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
this.observer = null;
|
||||||
|
this.listeners = [ [] , [] ];
|
||||||
|
}
|
||||||
|
|
||||||
|
added(selector, callback) {
|
||||||
|
this._observe(selector, callback, ACTION_ADDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
removed(selector, callback) {
|
||||||
|
this._observe(selector, callback, ACTION_REMOVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a little hacky function, awaiting the ResizeObserver specs to be ready */
|
||||||
|
resize(element, callback) {
|
||||||
|
var width = 0;
|
||||||
|
|
||||||
|
if ( ! this.hourglass ) {
|
||||||
|
element.dataset.element_width = element.getBoundingClientRect().width;
|
||||||
|
|
||||||
|
this.hourglass = new Hourglass(function() {
|
||||||
|
this.resizing.forEach(function(item) {
|
||||||
|
width = item.element.getBoundingClientRect().width;
|
||||||
|
|
||||||
|
if ( item.element.dataset !== width ) {
|
||||||
|
item.element.dataset.element_width = width;
|
||||||
|
item.callback.call(item.element, width);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}.bind(this), {
|
||||||
|
start : false,
|
||||||
|
repeat : false,
|
||||||
|
timer : config.resize.timer
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
this.hourglass.restart();
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizing = this.resizing || [];
|
||||||
|
|
||||||
|
this.resizing.push({
|
||||||
|
element: element,
|
||||||
|
callback: callback
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_observe(selector, callback, action) {
|
||||||
|
this.listeners[action].push({
|
||||||
|
selector : selector,
|
||||||
|
callback : callback
|
||||||
|
});
|
||||||
|
|
||||||
|
this._register_observer();
|
||||||
|
}
|
||||||
|
|
||||||
|
_analyze_element(element, action) {
|
||||||
|
let split_selector = [];
|
||||||
|
|
||||||
|
for (let j = 0, l1 = this.listeners[action].length; j < l1; j++) {
|
||||||
|
split_selector = this.listeners[action][j].selector.split(' ');
|
||||||
|
|
||||||
|
if ( split_selector.length > 1 ) {
|
||||||
|
console.log("@todo!", split_selector);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( $element.is( this.listeners[action][j].selector ) ) {
|
||||||
|
this.listeners[action][j].callback.call(element, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_analyze_added_element() {
|
||||||
|
return this._analyze_element(this, ACTION_ADDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
_analyze_removed_element() {
|
||||||
|
return this._analyze_element(this, ACTION_REMOVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
_observe_element(e) {
|
||||||
|
for(var i = 0; i < e.length; i++) {
|
||||||
|
if (e[i].type === 'childList') {
|
||||||
|
console.error("THIS PART IS NOT TRANSLATED INTO PURE JS AS OF NOW ... SOME THINKING NEED TO BE DONE FIRST !");
|
||||||
|
// $(e[i].addedNodes).each(this.analyze_added_element);
|
||||||
|
// $(e[i].removedNodes).each(this.analyze_removed_element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_register_observer() {
|
||||||
|
if ( this.observer || ( this.observer = new MutationObserver(this._observe_element) ) ) {
|
||||||
|
this.observer.observe(document.documentElement, {
|
||||||
|
subtree: true,
|
||||||
|
childList: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set observer(value) {
|
||||||
|
return this._observer = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get observer() {
|
||||||
|
return this._observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
set listeners(value) {
|
||||||
|
return this._listeners = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get listeners() {
|
||||||
|
return this._listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hourglass() {
|
||||||
|
return this._hourglass;
|
||||||
|
}
|
||||||
|
|
||||||
|
set hourglass(value) {
|
||||||
|
return this._hourglass = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get resizing() {
|
||||||
|
return this._resizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
set resizing(value) {
|
||||||
|
return this._resizing = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ElementObserver }
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Really simple timer / timeout object
|
||||||
|
*
|
||||||
|
* @author Dave Mc Nicoll
|
||||||
|
* @version 2.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
timer: 1000,
|
||||||
|
start: false,
|
||||||
|
loop: true,
|
||||||
|
run: false, // If you need a defined number of code execution, set a number to run and loop to false.
|
||||||
|
complete: function(){} // This complete callback function is called if the run exec number is reached.
|
||||||
|
};
|
||||||
|
|
||||||
|
class Hourglass {
|
||||||
|
|
||||||
|
constructor(callback, options) {
|
||||||
|
this.object_id = null;
|
||||||
|
this.counter = null;
|
||||||
|
this.callback = callback !== undefined ? callback : function() {};
|
||||||
|
|
||||||
|
if ( config.start ) {
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start(timeout) {
|
||||||
|
let time = ( timeout !== undefined ? timeout : config.timer );
|
||||||
|
|
||||||
|
if ( config.loop ) {
|
||||||
|
if ( this.counter === null ) {
|
||||||
|
this.counter = config.loop ? 0 : null
|
||||||
|
}
|
||||||
|
|
||||||
|
this.object_id = window.setInterval( this.call_interval.bind(this), time);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.object_id = window.setTimeout( this.call_timeout.bind(this), time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restart(timeout) {
|
||||||
|
this.clear();
|
||||||
|
this.start(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if ( config.loop ) {
|
||||||
|
this.object_id && window.clearInterval(this.object_id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.object_id && window.clearTimeout(this.object_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.stop();
|
||||||
|
this.counter = null;
|
||||||
|
this.object_id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
call_timeout() {
|
||||||
|
this.callback();
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
call_interval() {
|
||||||
|
this.callback();
|
||||||
|
|
||||||
|
if ( this.counter !== null && ++this.counter === config.run ) {
|
||||||
|
this.clear();
|
||||||
|
config.complete !== undefined && config.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
running() {
|
||||||
|
return !! this.object_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get callback() {
|
||||||
|
return this._callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
set callback(value) {
|
||||||
|
return this._callback = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get counter() {
|
||||||
|
return this._counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
set counter(value) {
|
||||||
|
return this._counter = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get object_id() {
|
||||||
|
return this._object_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
set object_id(value) {
|
||||||
|
return this._object_id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Hourglass }
|
|
@ -0,0 +1,324 @@
|
||||||
|
import { Webcomponent } from "./webcomponent.js";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
item: ".tab-item",
|
||||||
|
content: ".tab-content"
|
||||||
|
};
|
||||||
|
|
||||||
|
class UiPopup extends Webcomponent {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.fetch_template();
|
||||||
|
this.message = this.querySelector('.message');
|
||||||
|
this.title = this.querySelector('.title');
|
||||||
|
|
||||||
|
this.replaceVarsPlaceholder().attach().render();
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
this.handleOptions();
|
||||||
|
}
|
||||||
|
handleOptions() {
|
||||||
|
if ( this.options ) {
|
||||||
|
if ( ! this.options['skip-keys'] ) {
|
||||||
|
document.addEventListener('keyup', function(e) {
|
||||||
|
if ( this.visible() ) {
|
||||||
|
let trigger, ev;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// There's no real value in checking if the popup is shown or not.
|
||||||
|
switch ( e.keyCode ) {
|
||||||
|
// RETURN
|
||||||
|
case 13:
|
||||||
|
trigger = this.querySelector('[action="confirm"]');
|
||||||
|
ev = new CustomEvent('click', { trigger: trigger } );
|
||||||
|
|
||||||
|
trigger.dispatchEvent(ev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ESC
|
||||||
|
case 27:
|
||||||
|
trigger = this.querySelector('[action="close"]');
|
||||||
|
ev = new CustomEvent('click', { trigger: trigger } );
|
||||||
|
|
||||||
|
trigger.dispatchEvent(ev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
document.addEventListener('keyup', function(e) {
|
||||||
|
if ( this.visible() ) {
|
||||||
|
let trigger, ev;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// There's no real value in checking if the popup is shown or not.
|
||||||
|
switch ( e.keyCode ) {
|
||||||
|
case 13: // RETURN
|
||||||
|
trigger = this.querySelector('[action="confirm"]');
|
||||||
|
ev = new CustomEvent('click', { trigger: trigger } );
|
||||||
|
|
||||||
|
trigger.dispatchEvent(ev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case 8: // BACKSPACE
|
||||||
|
case 27: // ESC
|
||||||
|
trigger = this.querySelector('[action="close"]');
|
||||||
|
ev = new CustomEvent('click', { trigger: trigger } );
|
||||||
|
|
||||||
|
trigger.dispatchEvent(ev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
let triggers = document.querySelectorAll('[ui-popup]');
|
||||||
|
|
||||||
|
if ( triggers.length ) {
|
||||||
|
triggers.forEach(function(item) {
|
||||||
|
let trigger_args = this._parseTriggerArguments(item);
|
||||||
|
|
||||||
|
if ( trigger_args.name === this.getAttribute('name') ) {
|
||||||
|
item.addEventListener('click', this.triggerAction.bind(this, item, trigger_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options = trigger_args.options;
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.querySelectorAll('[action]').forEach(function(element) {
|
||||||
|
element.addEventListener("click", this.action.bind(this, element), false);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
action(element, e) {
|
||||||
|
if ( ! element.attributes.action ) {
|
||||||
|
throw "A button was clicked onto which no action was bound.";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(element.attributes.action.value) {
|
||||||
|
case "confirm":
|
||||||
|
case "yes":
|
||||||
|
case "ok":
|
||||||
|
this.actionFollowLink(element) ||
|
||||||
|
this.actionClickButton(element) ||
|
||||||
|
this.actionSendForm(this.trigger) ||
|
||||||
|
this.actionSendForm(element);
|
||||||
|
|
||||||
|
case "cancel":
|
||||||
|
case "no":
|
||||||
|
case "close":
|
||||||
|
this.hide();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchEvent(new CustomEvent('action:' + element.attributes.action.value));
|
||||||
|
this.trigger.dispatchEvent(new CustomEvent('action:' + element.attributes.action.value));
|
||||||
|
|
||||||
|
if ( element.getAttribute("action") ) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.classList.add('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
visible() {
|
||||||
|
return this.classList.contains('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
actionFollowLink(element) {
|
||||||
|
|
||||||
|
//if ( element.getAttribute('follow-link') !== null ) {
|
||||||
|
// Automatically redirect to given location if it exists.
|
||||||
|
if ( this.trigger.tagName === "A" ) {
|
||||||
|
let href = this.trigger.getAttribute('href');
|
||||||
|
|
||||||
|
if ( href ) {
|
||||||
|
window.location = href;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionClickButton(element) {
|
||||||
|
|
||||||
|
//if ( element.getAttribute('button-click') !== null ) {
|
||||||
|
let tag = this.trigger.tagName.toLowerCase();
|
||||||
|
|
||||||
|
// Automatically redirect to given location if it exists.
|
||||||
|
if ( tag === "button" || ( ( tag === "input" ) && ( this.trigger.getAttribute("type").toLowerCase() === "submit") ) ) {
|
||||||
|
this.pause = true;
|
||||||
|
|
||||||
|
this.trigger.click();
|
||||||
|
|
||||||
|
this.pause = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionSendForm(element) {
|
||||||
|
//if ( element.getAttribute('send-form') !== null ) {
|
||||||
|
//let form = this.trigger.tagName === "FORM" ? this.trigger : this.trigger.closest('FORM');
|
||||||
|
let form = element.tagName === "FORM" ? element : element.closest('FORM');
|
||||||
|
|
||||||
|
if ( form ) {
|
||||||
|
form.submit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw "A send-form action was set on a popup where the trigger as no parent form.";
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerAction(item, args, e) {
|
||||||
|
if (this.pause) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.trigger = item;
|
||||||
|
|
||||||
|
args.vars && Object.keys(args.vars).map(function(objectKey, index) {
|
||||||
|
// let element = this.message.querySelectorAll(`.${objectKey}`);
|
||||||
|
let element = Array.prototype.slice.call( this.message.querySelectorAll(`.${objectKey}`) ).concat(Array.prototype.slice.call(this.title.querySelectorAll(`.${objectKey}`)));
|
||||||
|
|
||||||
|
element.length ? element.forEach(function(item) {
|
||||||
|
item.innerHTML = args.vars[objectKey];
|
||||||
|
}) : ( function() {
|
||||||
|
console.warn(`Variable '${objectKey}' could not be found within popup's message`);
|
||||||
|
} )();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
if ( args.options ) {
|
||||||
|
args.options['input'] && Object.keys(args.options['input']).forEach(
|
||||||
|
key => this.querySelectorAll(`[name="${key}"]`).forEach(function(f) {
|
||||||
|
if (f.tagName === "TEXTAREA") {
|
||||||
|
f.value = args.options['input'][key];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f.setAttribute("value", args.options['input'][key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.dispatchEvent(new Event("change"));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.show();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceVarsPlaceholder() {
|
||||||
|
let setVar = function(match, varname) {
|
||||||
|
let element = document.createElement('span');
|
||||||
|
element.classList.add('variable', varname);
|
||||||
|
|
||||||
|
return element.outerHTML;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.message.innerHTML = this.message.innerHTML.replace(/{\$(.*?)}/g, setVar);
|
||||||
|
this.title.innerHTML = this.title.innerHTML.replace(/{\$(.*?)}/g, setVar);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get trigger() {
|
||||||
|
return this._trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
set trigger(value) {
|
||||||
|
return this._trigger = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pause() {
|
||||||
|
return this._pause;
|
||||||
|
}
|
||||||
|
|
||||||
|
set pause(value) {
|
||||||
|
return this._pause = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get message() {
|
||||||
|
return this._message;
|
||||||
|
}
|
||||||
|
|
||||||
|
set message(value) {
|
||||||
|
return this._message = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this._title;
|
||||||
|
}
|
||||||
|
|
||||||
|
set title(value) {
|
||||||
|
return this._title = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get action_func() {
|
||||||
|
return this._action_func;
|
||||||
|
}
|
||||||
|
|
||||||
|
set action_func(value) {
|
||||||
|
return this._action_func = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_parseTriggerArguments(item) {
|
||||||
|
let attr = item.getAttribute('ui-popup').trim();
|
||||||
|
|
||||||
|
if ( attr.charAt(0) === '{' ) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(attr);
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
console.error("Popup trigger failed on JSON parsing of " + attr + " from object:", item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( typeof attr === "string" && attr.length ) {
|
||||||
|
return {
|
||||||
|
name: attr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw "A popup name must be given to attach this object.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { UiPopup }
|
|
@ -0,0 +1,283 @@
|
||||||
|
|
||||||
|
import { Webcomponent } from './webcomponent.js';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
content: {
|
||||||
|
wrapper: ".wrapper",
|
||||||
|
list: ".item-list"
|
||||||
|
},
|
||||||
|
lang: {
|
||||||
|
create: "Create «{$name}»"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UiSelect extends Webcomponent {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.fetch_template();
|
||||||
|
|
||||||
|
this.wrapper = this.shadowRoot.querySelector(config.content.wrapper);
|
||||||
|
this.list = this.shadowRoot.querySelector(config.content.list);
|
||||||
|
this.input = this.querySelector('input:not([type="hidden"])');
|
||||||
|
this.select = this.querySelector('select');
|
||||||
|
this.active = false;
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.input.setAttribute('readonly', 'readonly');
|
||||||
|
|
||||||
|
if ( this.getAttribute("create") !== null ) {
|
||||||
|
this.item_create = this.add_item("__create__", config.lang.create, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.from(this.select.options).forEach(function(item) {
|
||||||
|
this.add_item(item.value, item.text, item.value === this.select.value);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
[ "mouseover" , "mouseleave" ].forEach(function(ev) {
|
||||||
|
this.list.addEventListener(ev, function(e) {
|
||||||
|
this.active = ( ev === 'mouseover' ) && ( document.activeElement === this.input );
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
[ "focus" , "blur", "keyup", "click" ].forEach(function(ev) {
|
||||||
|
this.input.addEventListener(ev, function(e) {
|
||||||
|
switch(ev) {
|
||||||
|
case "focus":
|
||||||
|
case "blur":
|
||||||
|
setTimeout(function() {
|
||||||
|
this.has_focus = ev === "focus";
|
||||||
|
this[ (this.has_focus ? "show" : "hide" ) + "_list" ].call(this, false);
|
||||||
|
}.bind(this), 100);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "click":
|
||||||
|
if ( ! this.editing && this.has_focus ) {
|
||||||
|
this.editing = true;
|
||||||
|
this.input.removeAttribute('readonly');
|
||||||
|
this.show_list();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "keyup":
|
||||||
|
e.target.value && this.editing ? this.filter(e.target.value) : this.show_list(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
add_item(key, value, selselfected, hide) {
|
||||||
|
let elem = document.createElement('li');
|
||||||
|
|
||||||
|
if (this.list.querySelector(`[data-key="${key}"]`) !== null) {
|
||||||
|
console.error(`Item with key '${key}' is already existing.`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.setAttribute('data-key', key);
|
||||||
|
|
||||||
|
if (! (elem.innerText = value) ) {
|
||||||
|
elem.innerHTML = " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
elem.addEventListener("click", function(e) {
|
||||||
|
self.set_value(this.getAttribute('data-key'), this.innerText);
|
||||||
|
|
||||||
|
let item = self.list.querySelector(".selected");
|
||||||
|
|
||||||
|
if ( item ) {
|
||||||
|
item.classList.remove("selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.classList.add("selected");
|
||||||
|
|
||||||
|
self.hide_list(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
elem.click();
|
||||||
|
// this.input.key_up();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hide) {
|
||||||
|
elem.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.list.appendChild(
|
||||||
|
elem
|
||||||
|
);
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_value(key, value) {
|
||||||
|
this.input.value = value.trim();
|
||||||
|
this.select.value = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide_list(force) {
|
||||||
|
if ( ! this.active || force ) {
|
||||||
|
this.has_focus = this.editing = false;
|
||||||
|
this.list.classList.add('hidden');
|
||||||
|
this.input.setAttribute('readonly', 'readonly');
|
||||||
|
this.show_all_items();
|
||||||
|
this.item_create.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show_list(show_all_items) {
|
||||||
|
this.list.classList.remove('hidden');
|
||||||
|
|
||||||
|
if ( show_all_items ) {
|
||||||
|
this.show_all_items();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.editing && this.input.value && this.filter(this.input.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((! this.querySelectorAll(":not(.hidden)").length || show_all_items ) && this.item_create ) {
|
||||||
|
this.item_create.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flush() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
filter(org_query) {
|
||||||
|
let empty = true,
|
||||||
|
has_exact_result = false,
|
||||||
|
query = org_query.trim().normalize().toLowerCase();
|
||||||
|
|
||||||
|
Array.from( this.list.querySelectorAll('li:not([data-key="__create__"])') ).forEach(function(item) {
|
||||||
|
let text = item.innerText.normalize().toLowerCase(),
|
||||||
|
keep = false;
|
||||||
|
|
||||||
|
if ( text === query ) {
|
||||||
|
keep = true;
|
||||||
|
empty = false;
|
||||||
|
has_exact_result = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
query.split(this._advanced_search ? " " : "\n").filter(item => item.length > 1).forEach(function(query_item) {
|
||||||
|
if ( text.indexOf(query_item) !== -1 ) {
|
||||||
|
keep = true;
|
||||||
|
empty = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( keep ) {;
|
||||||
|
item.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
if ( this.item_create ) {
|
||||||
|
if ( empty && ! has_exact_result ) {
|
||||||
|
this.item_create.classList.remove('hidden');
|
||||||
|
|
||||||
|
this.item_create.innerText = this.replace_vars(config.lang.create, {
|
||||||
|
name: org_query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.item_create.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_vars(content, vars) {
|
||||||
|
return content.replace(/{\$(.*?)}/g, function(match, varname) {
|
||||||
|
return vars[varname];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
show_all_items() {
|
||||||
|
Array.from( this.list.querySelectorAll('li') ).forEach(item => item.classList.remove('hidden'));
|
||||||
|
}
|
||||||
|
|
||||||
|
get content() {
|
||||||
|
return this._content;
|
||||||
|
}
|
||||||
|
|
||||||
|
set content(value) {
|
||||||
|
return this._content = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get list() {
|
||||||
|
return this._list;
|
||||||
|
}
|
||||||
|
|
||||||
|
set list(value) {
|
||||||
|
return this._list = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get wrapper() {
|
||||||
|
return this._wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
set wrapper(value) {
|
||||||
|
return this._wrapper = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get input() {
|
||||||
|
return this._input;
|
||||||
|
}
|
||||||
|
|
||||||
|
set input(value) {
|
||||||
|
return this._input = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get active() {
|
||||||
|
return this._active;
|
||||||
|
}
|
||||||
|
|
||||||
|
set active(value) {
|
||||||
|
return this._active = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get item_create() {
|
||||||
|
return this._item_create;
|
||||||
|
}
|
||||||
|
|
||||||
|
set item_create(value) {
|
||||||
|
return this._item_create = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get has_focus() {
|
||||||
|
return this._has_focus;
|
||||||
|
}
|
||||||
|
|
||||||
|
set has_focus(value) {
|
||||||
|
return this._has_focus = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get editing() {
|
||||||
|
return this._editing;
|
||||||
|
}
|
||||||
|
|
||||||
|
set editing(value) {
|
||||||
|
this.wrapper.classList[value ? "add" : "remove"]('is-editing');
|
||||||
|
return this._editing = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get advanced_search() {
|
||||||
|
return this._advanced_search;
|
||||||
|
}
|
||||||
|
|
||||||
|
set advanced_search(value) {
|
||||||
|
return this._advanced_search = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { UiSelect }
|
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
import { Webcomponent } from "./webcomponent.js";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
item: ".tab-item",
|
||||||
|
content: ".tab-content",
|
||||||
|
activeClass: "ui-tab-active",
|
||||||
|
};
|
||||||
|
|
||||||
|
class UiTabs extends Webcomponent {
|
||||||
|
constructor(parameters) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
window.addEventListener("hashchange", this.hashChange.bind(this), false);
|
||||||
|
|
||||||
|
//this.querySelectorAll(config.item).forEach(ele => ele.addEventListener("click", this.clickTab.bind(this)));
|
||||||
|
|
||||||
|
if (window.location.hash) {
|
||||||
|
let eve = new Event("click");
|
||||||
|
eve.newURL = window.location.hash;
|
||||||
|
this.hashChange(eve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashChange(e) {
|
||||||
|
let anchor = e.newURL.split('#')[1];
|
||||||
|
|
||||||
|
document.querySelectorAll("#" + anchor + config.content).forEach(function(content) {
|
||||||
|
content.parentNode.querySelectorAll(config.content + "." + config.activeClass).forEach(ele => ele.classList.remove(config.activeClass));
|
||||||
|
content.classList.add(config.activeClass);
|
||||||
|
});
|
||||||
|
|
||||||
|
let element = this.querySelector("." + config.activeClass);
|
||||||
|
element ? element.classList.remove(config.activeClass) : null;
|
||||||
|
|
||||||
|
element = this.querySelector(config.item + `[href="#${anchor}"]`);
|
||||||
|
element ? element.closest('div').classList.add(config.activeClass) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickTab(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
history.pushState(null, null, this.getAttribute('href'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { UiTabs }
|
|
@ -0,0 +1,246 @@
|
||||||
|
|
||||||
|
import { Webcomponent } from "./webcomponent.js";
|
||||||
|
|
||||||
|
const baseRows = 1.6;
|
||||||
|
|
||||||
|
const domElements = {
|
||||||
|
input: ".input",
|
||||||
|
toolbar: ".toolbar",
|
||||||
|
wrapper: ".wrapper",
|
||||||
|
boilerplate: ".content"
|
||||||
|
};
|
||||||
|
|
||||||
|
const domReplaceTag = {
|
||||||
|
i: "em",
|
||||||
|
b: "strong",
|
||||||
|
pre: "div"
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyCss = [
|
||||||
|
"font", "font-size", "color", "background", "outline"
|
||||||
|
];
|
||||||
|
|
||||||
|
class UiTextarea extends Webcomponent {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.fetch_template();
|
||||||
|
this.wrapper = this.shadowRoot.querySelector(domElements.wrapper);
|
||||||
|
this.content = this.shadowRoot.querySelector(domElements.boilerplate);
|
||||||
|
this.toolbar = this.shadowRoot.querySelector(domElements.toolbar);
|
||||||
|
|
||||||
|
if ( ! this.textareaInit() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toolbarInit();
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
document.execCommand("DefaultParagraphSeparator", false, "p");
|
||||||
|
|
||||||
|
this.content.innerHTML = this.textarea.value.trim();
|
||||||
|
|
||||||
|
this.content.addEventListener("input", function(e, d) {
|
||||||
|
this.content.querySelectorAll('[style]').forEach(function(element) {
|
||||||
|
element.removeAttribute('style');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.content.querySelectorAll('[class]').forEach(function(element) {
|
||||||
|
element.removeAttribute('class');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.textarea.value = this.content.innerHTML;
|
||||||
|
|
||||||
|
this.content.dispatchEvent(new Event("change"));
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
actionAlign(value) {
|
||||||
|
switch(value) {
|
||||||
|
case "left" :
|
||||||
|
return document.execCommand('justifyLeft',false,null);
|
||||||
|
|
||||||
|
case "center" :
|
||||||
|
return document.execCommand('justifyCenter',false,null);
|
||||||
|
|
||||||
|
case "right" :
|
||||||
|
return document.execCommand('justifyRight',false,null);
|
||||||
|
|
||||||
|
case "justify" :
|
||||||
|
return document.execCommand('justifyFull',false,null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actionBold(value, content) {
|
||||||
|
return document.execCommand('bold', false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionItalic(value, content) {
|
||||||
|
return document.execCommand('italic', false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionUnderline() {
|
||||||
|
return document.execCommand('underline',false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionStrike() {
|
||||||
|
return document.execCommand('strikeThrough',false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionUl() {
|
||||||
|
return document.execCommand('insertUnorderedList',false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionOl() {
|
||||||
|
return document.execCommand('insertOrderedList',false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionUndo() {
|
||||||
|
return document.execCommand('undo', false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionRedo() {
|
||||||
|
return document.execCommand('redo', false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionForecolor(color) {
|
||||||
|
return document.execCommand('forecolor', false, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionLink() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
actionImage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
actionDocument() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
actionTag(tag) {
|
||||||
|
return document.execCommand('formatBlock', false, '<'+tag+'>');
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbarInit() {
|
||||||
|
let action = this.getAttribute('toolbar');
|
||||||
|
|
||||||
|
if ( action !== null ) {
|
||||||
|
this.toolbar.setAttribute('action', action);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toolbar.querySelectorAll('[data-action]:not(.font-color)').forEach(function(item){
|
||||||
|
item.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let action = item.getAttribute('data-action');
|
||||||
|
|
||||||
|
return this["action" + action[0].toUpperCase() + action.slice(1)].call(this, item.getAttribute('data-value'), this.content);
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.toolbar.querySelectorAll('.font-color').forEach(function(item) {
|
||||||
|
item.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.toolbar.querySelector('label[for="colorpicker"]').dispatchEvent(new Event('click'));
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.toolbar.querySelector('input[type="color"]').addEventListener('change', function(e) {
|
||||||
|
return this.actionForecolor.call(this, e.target.value);
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
textareaInit() {
|
||||||
|
if ( ! (this.textarea = this.querySelector("textarea") ) ) {
|
||||||
|
// throw "You should have a <textarea> element within a container with slot='input'";
|
||||||
|
|
||||||
|
this.textarea = document.createElement("textarea");
|
||||||
|
this.textarea.innerHTML = this.innerHTML;
|
||||||
|
this.append(this.textarea);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.textarea.getAttribute('readonly') ) {
|
||||||
|
this.wrapper.classList.add("readonly");
|
||||||
|
this.content.removeAttribute('contenteditable');
|
||||||
|
}
|
||||||
|
|
||||||
|
let rows = this.textarea.getAttribute('rows') ;
|
||||||
|
|
||||||
|
if ( rows ) {
|
||||||
|
rows = parseInt(rows);
|
||||||
|
this.content.style.minHeight = ( rows * baseRows ) + "em";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.textarea.webcomponent = this;
|
||||||
|
|
||||||
|
if ( this.classList.contains("lite") ) {
|
||||||
|
this.wrapper.classList.add("lite");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
html(html) {
|
||||||
|
return html !== undefined ? this.content.innerHTML = html : this.content.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
text(text) {
|
||||||
|
return text !== undefined ? this.content.innerText = text : this.content.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
|
get content() {
|
||||||
|
return this._content;
|
||||||
|
}
|
||||||
|
|
||||||
|
set content(value) {
|
||||||
|
return this._content = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get toolbar() {
|
||||||
|
return this._toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
set toolbar(value) {
|
||||||
|
return this._toolbar = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get textarea() {
|
||||||
|
return this._textarea;
|
||||||
|
}
|
||||||
|
|
||||||
|
set textarea(value) {
|
||||||
|
return this._textarea = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get element() {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
set element(value) {
|
||||||
|
return this._element = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get wrapper() {
|
||||||
|
return this._wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
set wrapper(value) {
|
||||||
|
return this._wrapper = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.content.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
set value(value) {
|
||||||
|
this.content.innerHTML = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { UiTextarea }
|
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
class Webcomponent extends HTMLElement {
|
||||||
|
|
||||||
|
fetch_template() {
|
||||||
|
this.attachShadow({mode: 'open'}).appendChild(
|
||||||
|
document.querySelector("template#" + this.tagName.toLowerCase()).content.cloneNode(true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_name() {
|
||||||
|
return Object.getPrototypeOf(this).constructor.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_slot(name) {
|
||||||
|
return this.querySelector('[slot="' + name + '"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
empty_element(element) {
|
||||||
|
while (element.lastChild) {
|
||||||
|
element.removeChild(element.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
build_url(url, data) {
|
||||||
|
return url + ( Object.entries(data).length ? "?" + Object.keys(data).map(function(key) {
|
||||||
|
return [key, data[key]].map(encodeURIComponent).join("=");
|
||||||
|
}).join("&") : "" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Webcomponent };
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "assets-package/webcomponent",
|
||||||
|
"description": "Web component package",
|
||||||
|
"type": "library",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Dave Mc Nicoll",
|
||||||
|
"email": "info@mcnd.ca"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {},
|
||||||
|
"autoload": {}
|
||||||
|
}
|
Loading…
Reference in New Issue