Create strong ui feature improvements

This commit is contained in:
Dennis Eichhorn 2019-01-11 21:13:11 +01:00
parent d9996c1d5a
commit a525f6c881
11 changed files with 343 additions and 66 deletions

View File

@ -36,7 +36,7 @@
this.result[0] = function()
{
jsOMS.Log.Logger.instance.error('Invalid response code.');
jsOMS.Log.Logger.instance.info('Unhandled response');
};
/** global: XMLHttpRequest */

View File

@ -7,7 +7,7 @@
* @since 1.0.0
*/
const timerActionDelay = {};
const timerAction = function (action, callback)
const timerAction = function (action, callback, data)
{
"use strict";
@ -18,6 +18,6 @@ const timerAction = function (action, callback)
timerActionDelay[action.id] = setTimeout(function() {
delete timerActionDelay[action.id];
callback();
callback(data);
}, action.delay);
};

View File

@ -0,0 +1,235 @@
/**
* Advanced input class.
*
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @since 1.0.0
*
* @todo: this class is probably the most stupid thing I've done in a long time. Seriously fix this!
* @todo: Passing self to every MEMBER function is just dumb.
*/
(function (jsOMS)
{
"use strict";
/** @namespace jsOMS.UI */
jsOMS.Autoloader.defineNamespace('jsOMS.UI.Component');
jsOMS.UI.Component.AdvancedInput = class {
/**
* @constructor
*
* @since 1.0.0
*/
constructor (e)
{
this.id = e.id;
this.inputComponent = e;
this.inputField = this.inputComponent.getElementsByClassName('input')[0];
this.dropdownElement = document.getElementById(this.id + '-dropdown');
this.tagElement = document.getElementById(this.id + '-tags');
this.dataList = this.dropdownElement.getElementsByTagName('table')[0];
this.dataListBody = this.dataList.getElementsByTagName('tbody')[0];
this.dataTpl = document.getElementById(this.id + '-rowElement');
this.tagTpl = this.tagElement.getElementsByTagName('template')[0];
this.src = this.inputField.getAttribute('data-src');
const self = this;
this.inputField.addEventListener('keydown', function(e) {
if (e.keyCode === 13 || e.keyCode === 40) {
jsOMS.preventAll(e);
}
if (e.keyCode === 40) {
self.selectOption(self.dataListBody.firstElementChild);
} else {
// handle change delay
self.inputTimeDelay({id: self.id, delay: 300}, self.changeCallback, self, e);
}
});
this.dropdownElement.addEventListener('keydown', function(e) {
jsOMS.preventAll(e);
// todo: consider if it makes sense to have a none element always for phone users only to jump out?
// todo: if not remote then the suggestion dropdown should filter itself based on best match
// todo: dropdown should show/hide or depending on setting be always visible maybe with :focus+table or similar
if (e.keyCode === 27 || e.keyCode === 46 || e.keyCode === 8) {
// handle esc, del to go back to input field
self.inputField.focus();
self.clearDataListSelection(self);
} else if (e.keyCode === 38) {
// handle up-click
if (document.activeElement.previousElementSibling !== null) {
self.clearDataListSelection(self);
self.selectOption(document.activeElement.previousElementSibling);
}
} else if (e.keyCode === 40) {
// handle down-click
if (document.activeElement.nextElementSibling !== null) {
self.clearDataListSelection(self);
self.selectOption(document.activeElement.nextElementSibling);
}
} else if (e.keyCode === 13 || e.keyCode === 9) {
self.clearDataListSelection(self);
self.addToResultList(self);
}
});
this.dropdownElement.addEventListener('focusout', function(e){
self.clearDataListSelection(self);
});
this.dropdownElement.addEventListener('click', function(e) {
if (document.activeElement.tagName.toLowerCase() !== 'tr') {
return;
}
self.clearDataListSelection(self);
self.addToResultList(self);
});
};
remoteCallback(self, data)
{
data = JSON.parse(data.response);
const dataLength = data.length;
console.table(data);
// if dropdown == true
if (self.dropdownElement.getAttribute('data-active') === 'true') {
while (self.dataListBody.firstChild) {
self.dataListBody.removeChild(self.dataListBody.firstChild);
}
for(let i = 0; i < dataLength; ++i) {
// set readable value
const newRow = self.dataTpl.content.cloneNode(true);
let fields = newRow.querySelectorAll('[data-tpl-text]');
let fieldLength = fields.length;
for (let j = 0; j < fieldLength; ++j) {
fields[j].appendChild(
document.createTextNode(
jsOMS.getArray(fields[j].getAttribute('data-tpl-text'), data[i])
)
);
}
// set internal value
fields = newRow.querySelectorAll('[data-tpl-value]');
fieldLength = fields.length;
for (let j = 0; j < fieldLength; ++j) {
fields[j].setAttribute(
'data-value',
jsOMS.getArray(fields[j].getAttribute('data-tpl-value'), data[i])
);
}
self.dataListBody.appendChild(newRow);
}
}
};
changeCallback(self, key)
{
// if remote data
if (typeof self.src !== 'undefined' && self.src !== '') {
const request = new jsOMS.Message.Request.Request(self.src);
request.setSuccess(function (data) { self.remoteCallback(self, data); });
request.send();
}
};
selectOption(e)
{
e.focus();
// todo: change to set style .active
e.setAttribute('style', 'background: #f00');
jsOMS.addClass(e, 'active');
};
clearDataListSelection(self)
{
const list = self.dataListBody.getElementsByTagName('tr'),
length = list.length;
for (let i = 0; i < length; ++i) {
// todo: remove the active class
list[i].setAttribute('style', '');
jsOMS.removeClass(list[i], 'active');
}
};
addToResultList(self) {
if (self.inputField.getAttribute('data-autocomplete') === 'true') {
self.inputField.value = document.activeElement.querySelectorAll('[data-tpl-value="' + self.inputField.getAttribute('data-value') + '"]')[0].getAttribute('data-value');
}
if (self.tagElement.getAttribute('data-active') === 'true') {
// todo: make badges removable
const newTag = self.tagTpl.content.cloneNode(true);
// set internal value
let fields = newTag.querySelectorAll('[data-tpl-value]');
let fieldLength = fields.length;
let uuid = '';
let value = '';
for (let j = 0; j < fieldLength; ++j) {
value = document.activeElement.querySelectorAll('[data-tpl-value="' + newTag.firstElementChild.getAttribute('data-tpl-value') + '"]')[0].getAttribute('data-value');
fields[j].setAttribute('data-value', value);
uuid += value;
}
// don't allow duplicate
if (self.tagElement.querySelectorAll('[data-tpl-uuid="' + uuid + '"').length !== 0) {
return;
}
newTag.firstElementChild.setAttribute('data-tpl-uuid', uuid);
// set readable text
fields = newTag.querySelectorAll('[data-tpl-text]');
fieldLength = fields.length;
for (let j = 0; j < fieldLength; ++j) {
fields[j].appendChild(
document.createTextNode(
document.activeElement.querySelectorAll('[data-tpl-text="' + fields[j].getAttribute('data-tpl-text') + '"]')[0].innerText
)
);
}
// allow limit
if (self.tagElement.childElementCount >= self.tagElement.getAttribute('data-limit')
&& self.tagElement.getAttribute('data-limit') != 0
) {
self.tagElement.removeChild(self.tagElement.firstElementChild);
}
self.tagElement.appendChild(newTag);
}
};
inputTimeDelay(action, callback, self, data)
{
if (jsOMS.UI.Component.AdvancedInput.timerDelay[action.id]) {
clearTimeout(jsOMS.UI.Component.AdvancedInput.timerDelay[action.id]);
delete jsOMS.UI.Component.AdvancedInput.timerDelay[action.id]
}
jsOMS.UI.Component.AdvancedInput.timerDelay[action.id] = setTimeout(function() {
delete jsOMS.UI.Component.AdvancedInput.timerDelay[action.id];
callback(self, data);
}, action.delay);
};
}
jsOMS.UI.Component.AdvancedInput.timerDelay = {};
}(window.jsOMS = window.jsOMS || {}));

View File

@ -1,15 +0,0 @@
/**
* Autocomplete class.
*
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @since 1.0.0
*/
(function (jsOMS)
{
"use strict";
/** @namespace jsOMS.UI */
jsOMS.Autoloader.defineNamespace('jsOMS.UI.Component');
}(window.jsOMS = window.jsOMS || {}));

View File

@ -14,6 +14,16 @@
jsOMS.Autoloader.defineNamespace('jsOMS.UI.Input');
jsOMS.UI.Input = class {
/**
* @constructor
*
* @since 1.0.0
*/
constructor ()
{
this.visObs = null;
};
/**
* Unbind input element
*
@ -49,6 +59,7 @@
input.addEventListener('change', function changeBind(event)
{
console.log('ttttttt');
/* Handle remote datalist/autocomplete input element */
let listId, list;
if (typeof (listId = this.getAttribute('list')) !== 'undefined' && (list = document.getElementById(listId)).getAttribute('data-list-src') !== 'undefined') {

View File

@ -18,8 +18,9 @@
*
* @since 1.0.0
*/
constructor ()
constructor (app)
{
this.app = app;
};
/**
@ -41,7 +42,7 @@
}
} else {
const tabs = document.querySelectorAll('.tabview'),
length = !tabs ? 0 : tabs.length;
length = !tabs ? 0 : tabs.length;
for (let i = 0; i < length; ++i) {
this.bindElement(tabs[i]);
@ -52,7 +53,7 @@
/**
* Bind & rebind UI element.
*
* @param {Object} e Element id
* @param {Object} e Element
*
* @return {void}
*
@ -60,23 +61,55 @@
*/
bindElement (e)
{
const nodes = e.querySelectorAll('.tab-links a');
this.activateTabUri(e);
nodes.addEventListener('click', function (evt)
{
/* Change Tab */
const attr = this.getAttribute('href').substring(1),
cont = this.parentNode.parentNode.parentNode.children[1];
const nodes = e.querySelectorAll('.tab-links li'),
length = nodes.length;
jsOMS.removeClass(jsOMS.getByClass(this.parentNode.parentNode, 'active'), 'active');
jsOMS.addClass(this.parentNode, 'active');
jsOMS.removeClass(jsOMS.getByClass(cont, 'active'), 'active');
jsOMS.addClass(jsOMS.getByClass(cont, attr), 'active');
for (let i = 0; i < length; ++i) {
nodes[i].addEventListener('click', function (evt)
{
let fragmentString = window.location.href.includes('#') ? jsOMS.Uri.Http.parseUrl(window.location.href).fragment : '';
/* Modify url */
/* Change Tab */
/* Remove selected tab */
fragmentString = jsOMS.ltrim(fragmentString.replace(this.parentNode.getElementsByClassName('active')[0].getElementsByTagName('label')[0].getAttribute('for'), ''), ',');
jsOMS.removeClass(this.parentNode.getElementsByClassName('active')[0], 'active');
jsOMS.addClass(this, 'active');
jsOMS.preventAll(evt);
});
/* Add selected tab */
window.history.pushState(null,'',
jsOMS.Uri.UriFactory.build(
'{%}#' + (fragmentString === '' ? '' : fragmentString + ',') + this.getElementsByTagName('label')[0].getAttribute('for')
)
);
});
}
};
/**
* Activates the correct tab based on URI fragment.
*
* This allows to link a specific open tab to a user or make it a bookmark
*
* @param {Object} e Element
*
* @return {void}
*
* @since 1.0.0
*/
activateTabUri(e)
{
const fragmentString = window.location.href.includes('#') ? jsOMS.Uri.Http.parseUrl(window.location.href).fragment : '';
const fragments = fragmentString.split(','),
fragLength = fragments.length;
for (let i = 0; i < fragLength; ++i) {
let label = e.querySelectorAll('label[for="' + fragments[i] + '"]')[0];
if (typeof label !== 'undefined') {
label.click();
}
}
};
}
}(window.jsOMS = window.jsOMS || {}));

View File

@ -1,15 +0,0 @@
/**
* Form manager class.
*
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @since 1.0.0
*/
(function (jsOMS)
{
"use strict";
/** @namespace jsOMS.UI */
jsOMS.Autoloader.defineNamespace('jsOMS.UI.Component');
}(window.jsOMS = window.jsOMS || {}));

View File

@ -1,15 +0,0 @@
/**
* Form manager class.
*
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @since 1.0.0
*/
(function (jsOMS)
{
"use strict";
/** @namespace jsOMS.UI */
jsOMS.Autoloader.defineNamespace('jsOMS.UI.Component');
}(window.jsOMS = window.jsOMS || {}));

View File

@ -36,12 +36,13 @@
bind (id)
{
let e = null;
if (typeof id !== 'undefined') {
if (typeof id !== 'undefined' && id !== null) {
e = document.getElementById(id);
}
this.bindHref(e);
this.bindLazyLoad(e);
this.bindInput(e);
};
/**
@ -102,5 +103,24 @@
}
}
};
/**
* Bind & rebind UI element.
*
* @param {Object} [e] Element id
*
* @return {void}
*
* @since 1.0.0
*/
bindInput (e)
{
e = e !== null ? [e] : document.getElementsByClassName('advancedInput');
const length = e.length;
for (let i = 0; i < length; ++i) {
new jsOMS.UI.Component.AdvancedInput(e[i]);
}
};
}
}(window.jsOMS = window.jsOMS || {}));

View File

@ -25,7 +25,7 @@
{
this.app = app;
this.formManager = new jsOMS.UI.Component.Form(this.app);
this.tabManager = new jsOMS.UI.Component.Tab();
this.tabManager = new jsOMS.UI.Component.Tab(this.app);
this.tableManager = new jsOMS.UI.Component.Table(this.app);
this.actionManager = new jsOMS.UI.ActionManager(this.app);
this.dragNDrop = new jsOMS.UI.DragNDrop(this.app);

View File

@ -123,6 +123,7 @@
*/
static unique (url)
{
// unique queries
const parts = url.replace(/\?/g, '&').split('&'),
full = parts[0];
@ -148,6 +149,28 @@
url = full + '?' + pars.join('&');
}
// unique fragments
const fragments = url.match(/\#[a-zA-Z0-9\-,]+/g),
fragLength = fragments !== null ? fragments.length : 0;
for (let i = 0; i < fragLength; ++i) {
url = url.replace(fragments[i], '');
}
if (fragLength > 0) {
const fragList = fragments[fragLength - 1].split(','),
fragListLength = fragList.length;
let fragListNew = [];
for (let i = 0; i < fragListLength; ++i) {
if (!fragListNew.includes(fragList[i]) && fragList[i] !== '') {
fragListNew.push(fragList[i]);
}
}
url += fragListNew.join(',');
}
return url;
};