774 lines
14 KiB
JavaScript
774 lines
14 KiB
JavaScript
import { Ut } from '../utils/Ut';
|
|
import { Boot } from './Boot';
|
|
import { $, El, EvtOptions } from '../utils/dom';
|
|
import { Render } from './Render';
|
|
/*
|
|
* Loader Class
|
|
*/
|
|
export class App extends Boot {
|
|
|
|
// App Loaded state
|
|
static #started = false;
|
|
|
|
// App registry
|
|
static registry = {};
|
|
|
|
/**
|
|
* App Class Register
|
|
*/
|
|
static register(...classRefList) {
|
|
|
|
if (App.#started) {
|
|
App.log('Register already called!');
|
|
return;
|
|
}
|
|
|
|
App.#started = true;
|
|
|
|
classRefList.forEach( classRef => App.registry[ classRef.name ] = classRef);
|
|
|
|
if (App.registry.Main) {
|
|
Boot.ready(_ => new App.registry.Main);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set User Role
|
|
*/
|
|
static setRole(role) {
|
|
App.role = role;
|
|
}
|
|
|
|
/**
|
|
* Set Main Title
|
|
*/
|
|
static updateMainTitle(title) {
|
|
$('.top-toolbar h1')?.dxHtml(title);
|
|
}
|
|
|
|
/**
|
|
* Get Card Title Wrapper
|
|
*/
|
|
static cardTitleWrapper(card) {
|
|
|
|
let content = card.dxFind('.content-card') || card;
|
|
let topBar = content.dxFind('.top-bar');
|
|
|
|
if (!topBar) {
|
|
topBar = El('div', { className: 'top-bar' });
|
|
content.prepend(topBar);
|
|
}
|
|
|
|
let topBarInner = topBar.dxFind('.top-bar-inner');
|
|
|
|
if (!topBarInner) {
|
|
topBarInner = topBar.appendChild( El('div', { className: 'top-bar-inner' }) );
|
|
}
|
|
|
|
let titleWrapper = topBarInner.dxFind('.title-wrapper');
|
|
|
|
if (!titleWrapper) {
|
|
titleWrapper = topBarInner.appendChild(El('div', { className: 'title-wrapper' }));
|
|
}
|
|
|
|
return titleWrapper;
|
|
}
|
|
|
|
/**
|
|
* Set Card Title
|
|
*/
|
|
static updateCardTitle(cardName, title) {
|
|
|
|
let card = App.viewEl.dxFind('.card-'+ cardName);
|
|
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
let elTitle = card.dxFind('h2.title');
|
|
|
|
if (elTitle) {
|
|
elTitle.dxHtml(title).classList.remove('hide');
|
|
return;
|
|
}
|
|
|
|
elTitle = El('h2', { className: 'title' });
|
|
|
|
if (Ut.isStr(title)) {
|
|
elTitle.dxHtml(title);
|
|
}
|
|
else {
|
|
elTitle.append(title);
|
|
}
|
|
|
|
App.cardTitleWrapper(card).prepend(elTitle);
|
|
}
|
|
|
|
/**
|
|
* Set Card Description
|
|
*/
|
|
static updateCardDescr(cardName, descr) {
|
|
|
|
let card = App.viewEl.dxFind('.card-'+ cardName);
|
|
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
let elDescr = card.dxFind('p.descr');
|
|
|
|
if (elDescr) {
|
|
elDescr.dxHtml(descr).classList.remove('hide');
|
|
return;
|
|
}
|
|
|
|
elDescr = El('p', { className: 'descr' });
|
|
|
|
if (Ut.isStr(title)) {
|
|
elTitle.dxHtml(title);
|
|
}
|
|
else {
|
|
elTitle.append(title);
|
|
}
|
|
|
|
App.getCardTitleWrapper(card).append(elDescr);
|
|
}
|
|
|
|
/**
|
|
* Append Card Toolbar
|
|
*/
|
|
static addCardToolbar(cardName, data) {
|
|
|
|
let card = App.viewEl.dxFind('.card-'+ cardName);
|
|
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
let toolbar = card.dxFind('.top-bar-inner');
|
|
|
|
if (Ut.isStr(data)) {
|
|
toolbar?.insertAdjacentHTML('beforeend', data);
|
|
}
|
|
else {
|
|
toolbar?.append(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set Card Content
|
|
*/
|
|
static updateCardContent(cardName, data) {
|
|
|
|
let card = App.viewEl.dxFind('.card-'+ cardName);
|
|
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
let container = card.dxFind('.container');
|
|
|
|
if (Ut.isStr(data)) {
|
|
container?.dxHtml(data);
|
|
}
|
|
else {
|
|
container?.dxHtml('').append(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append Card Content
|
|
*/
|
|
static addCardContent(cardName, data) {
|
|
|
|
let card = App.viewEl.dxFind('.card-'+ cardName);
|
|
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
let container = card.dxFind('.container');
|
|
|
|
if (Ut.isStr(data)) {
|
|
container?.insertAdjacentHTML('beforeend', data);
|
|
}
|
|
else {
|
|
container?.append(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set Label Info
|
|
*/
|
|
static updateLabel(name, val, isHtml) {
|
|
|
|
let label = App.viewEl.dxFind('.field-info[data-name="'+ name +'"]');
|
|
|
|
if (!label) {
|
|
return;
|
|
}
|
|
|
|
label.classList.toggle('hide', !val);
|
|
|
|
if (isHtml) {
|
|
label.dxFind('.label-value')?.dxHtml(val);
|
|
}
|
|
else {
|
|
label.dxFind('.label-value')?.dxText(val);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render UI Component
|
|
*
|
|
* @param string id Component ID
|
|
* @param object template Template Object Vars
|
|
*
|
|
*/
|
|
static render(id, template) {
|
|
|
|
let token = id.split('.');
|
|
|
|
if (token.length != 2) {
|
|
App.log('Invalid component ID!');
|
|
return null;
|
|
}
|
|
|
|
let nodeObj = null;
|
|
|
|
if (App._section) {
|
|
|
|
let mainNode = App.intData?.section[App._section[0]];
|
|
nodeObj = mainNode?.children?.[token[0]]?.[token[1]];
|
|
}
|
|
else {
|
|
nodeObj = App.intData?.[token[0]]?.[token[1]];
|
|
}
|
|
|
|
if (!nodeObj) {
|
|
App.log('This component ID doesn\'t exist!');
|
|
return null;
|
|
}
|
|
|
|
nodeObj._type = token[0];
|
|
nodeObj.name = token[1];
|
|
nodeObj.template = template || {};
|
|
|
|
return Render.makeElement(nodeObj, null, { vars: App.vars || {} });
|
|
}
|
|
|
|
/**
|
|
* Mount View
|
|
*/
|
|
static mountViewEl(viewEl) {
|
|
|
|
if (!viewEl) {
|
|
return;
|
|
}
|
|
|
|
if (viewEl.classList.contains('sp-card')) {
|
|
App.toolbarEl = viewEl.dxFind('.top-bar-inner');
|
|
App.tabMenuEl = viewEl.dxFind('.tab-menu');
|
|
App.viewEl = viewEl.dxFind('.container');
|
|
}
|
|
else {
|
|
App.viewEl = viewEl;
|
|
}
|
|
|
|
App.contentEl.replaceChildren(viewEl);
|
|
}
|
|
|
|
/**
|
|
* Set Root Main
|
|
*/
|
|
static mountView( component ) {
|
|
App.mountViewEl(App.render(component));
|
|
}
|
|
|
|
/**
|
|
* Call Module Class
|
|
*/
|
|
static callModClass(section, id) {
|
|
|
|
if (section.length == 1) {
|
|
|
|
const regModule = App.registry[ id.charAt(0).toUpperCase() + id.slice(1) ];
|
|
|
|
App.instance = regModule ? new regModule() : null;
|
|
|
|
return;
|
|
}
|
|
|
|
id = id.split('.')[0];
|
|
|
|
const regModule = App.registry[ id.charAt(0).toUpperCase() + id.slice(1) ];
|
|
|
|
if (regModule) {
|
|
App.instance = new regModule;
|
|
App.instance[ section[1] ] ();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Init Module
|
|
*/
|
|
static initModule(id, vars, opts) {
|
|
|
|
vars = vars || {};
|
|
|
|
if (opts === 1 || opts === true) {
|
|
opts = { back: 1 };
|
|
}
|
|
else {
|
|
opts = opts || { back: 0 };
|
|
}
|
|
|
|
if (opts.back === true) {
|
|
opts.back = 1;
|
|
}
|
|
|
|
App._section = id.split('.');
|
|
|
|
let rootNode = App.intData.section[ App._section[0] ];
|
|
|
|
// RootNode is missing .. Just call module class
|
|
if (!Ut.isSet(rootNode)) {
|
|
App.callModClass(App._section, id);
|
|
return;
|
|
}
|
|
|
|
// Call destructor if it's present
|
|
if (App.instance && Ut.isFn(App.instance.destructor)) {
|
|
App.instance.destructor(App.section);
|
|
}
|
|
|
|
// Destroy old global events
|
|
App.destroyOldGlobalEvents();
|
|
|
|
// Set main section title
|
|
let title = rootNode.title || '';
|
|
|
|
// Subsection present
|
|
if (App._section.length == 2) {
|
|
|
|
rootNode = Object.assign({}, rootNode, { that: rootNode.children.section[App._section[1]] });
|
|
|
|
// Set subsection title
|
|
if (rootNode.that.title) {
|
|
title = rootNode.that.title;
|
|
}
|
|
}
|
|
|
|
// Set section root (rootnode children)
|
|
App.r = rootNode.children || {};
|
|
|
|
// Set Stack
|
|
|
|
if (!opts.back) {
|
|
App.stack = null;
|
|
}
|
|
else if (!App.stack) {
|
|
App.stack = { id: App.section, vars: App.vars, back: App.back };
|
|
}
|
|
|
|
// This is the current section
|
|
App.section = id;
|
|
|
|
// Section Main (null at this point)
|
|
App.viewEl = null;
|
|
|
|
// Set section vars
|
|
App.vars = vars || {};
|
|
|
|
// Set section back data
|
|
App.back = opts.back;
|
|
|
|
// Set ref shortcut
|
|
|
|
App.ref = {};
|
|
|
|
if (opts.reference) {
|
|
|
|
for (let name of opts.reference) {
|
|
let section = App.intData.section[ name ];
|
|
App.ref[ name ] = section;
|
|
}
|
|
}
|
|
|
|
// Set listener evt options
|
|
EvtOptions.nsEvent = true;
|
|
EvtOptions.section = id;
|
|
|
|
// Call module class at this point
|
|
App.callModClass(App._section, id);
|
|
|
|
// Fix After Calling
|
|
|
|
if (App.viewEl) {
|
|
|
|
if (title) {
|
|
App.updateMainTitle(title);
|
|
}
|
|
|
|
if (App.menuFlag) {
|
|
App.switchLeftMenu(App._section);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destroy Old Global Events
|
|
*/
|
|
static destroyOldGlobalEvents() {
|
|
|
|
if (App.section) {
|
|
|
|
Ut.each(App.widgetStack, wtList => wtList.forEach(wt => wt.destroy() ) );
|
|
|
|
App.widgetStack = {};
|
|
|
|
Ut.resetKey();
|
|
|
|
window.dxOff(`.${App.section}`);
|
|
document.dxOff(`.${App.section}`);
|
|
document.body.dxOff(`.${App.section}`);
|
|
}
|
|
|
|
// Destroy all TinyMce Instances
|
|
if (Ut.isSet(window.tinymce) && tinymce.editors) {
|
|
for (let i = tinymce.editors.length - 1; i > -1; i--) {
|
|
tinymce.get(tinymce.editors[i].id).destroy();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Switch Left menu
|
|
*/
|
|
static switchLeftMenu = function(rid) {
|
|
|
|
let menuItem = App.leftMenuItems.dxFind('li[data-init="'+ rid.join('.') +'"]');
|
|
|
|
$('.left-menu li.active')?.classList.remove('active');
|
|
$('.left-menu li.active')?.classList.remove('active');
|
|
|
|
if (menuItem) {
|
|
|
|
menuItem?.classList.add('active');
|
|
|
|
if (rid.length == 2) {
|
|
App.leftMenuItems.dxFind('li[data-init="'+ rid[0] +'"]')?.classList.add('active');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update Title or Caption
|
|
*/
|
|
static update_Title(name, props, isCaption) {
|
|
|
|
props = props || {};
|
|
|
|
let target = App.viewEl.dxFind((isCaption ? '.caption' : '.title') + '-' + name);
|
|
|
|
if (!target) {
|
|
return;
|
|
}
|
|
|
|
if (props.label) {
|
|
target.dxFind(isCaption ? 'h3' : 'h2').textContent = props.label;
|
|
}
|
|
|
|
if (props.icon) {
|
|
|
|
let icon = target.dxFind('i');
|
|
|
|
if (icon) {
|
|
icon.className = 'fa' + (props['icon-type'] || 's') + ' fa-'+ props.icon;
|
|
}
|
|
else {
|
|
target.prepend(El('i', { className: 'fa' + (props['icon-type'] || 's') + ' fa-'+ props.icon }) );
|
|
}
|
|
}
|
|
|
|
if (props.color) {
|
|
|
|
target.classList.forEach(cls => {
|
|
if (cls.startsWith('color-')) {
|
|
target.classList.remove(cls);
|
|
}
|
|
});
|
|
|
|
target.className += ' color-' + props.color;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update Title El
|
|
*/
|
|
static updateTitle(name, props) {
|
|
App.update_Title(name, props, false);
|
|
}
|
|
|
|
/**
|
|
* Update Caption El
|
|
*/
|
|
static updateCaption(name, props) {
|
|
App.update_Title(name, props, true);
|
|
}
|
|
|
|
/**
|
|
* Set App Menu events
|
|
*/
|
|
static initMenuEvents() {
|
|
|
|
if (App.menuEventFlag) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Left Logo - Ev Handler
|
|
*/
|
|
$('.left-logo')?.dxOn('click', (_, t) => {
|
|
if (App.section != App.config.home) {
|
|
App.initModule(App.config.home, {}, { eTarget: t });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Dropdown Menu - Ev Handler
|
|
*/
|
|
App.dropdownMenu.dxOn('click.menu', e => {
|
|
e.stopPropagation();
|
|
App.dropdownMenuItems.classList.toggle('hide');
|
|
});
|
|
|
|
App.dropdownMenuItems.dxOn('click.menu', 'li', (_, t) => {
|
|
|
|
if (t.dataset.init) {
|
|
App.initModule(t.dataset.init, {}, { eTarget: t });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Left Menu Trigger - Ev Handlers
|
|
*/
|
|
$('.left-menu-trigger').dxOn('click.menu', (e, t) => {
|
|
|
|
e.stopPropagation();
|
|
|
|
t.classList.toggle('active');
|
|
App.menuSideEl.classList.toggle('toggled');
|
|
});
|
|
|
|
$('.left-menu-handler').dxOn('click.menu', e => {
|
|
|
|
e.stopPropagation();
|
|
|
|
App.menuSideEl.classList.remove('toggled');
|
|
App.menuSideEl.classList.toggle('opened');
|
|
});
|
|
|
|
$('body').dxOn('click.menu', _ => {
|
|
App.dropdownMenuItems.classList.add('hide');
|
|
App.menuSideEl?.classList.remove('opened');
|
|
});
|
|
|
|
/**
|
|
* Left Menu Items - Ev Handler
|
|
*/
|
|
App.leftMenuItems.dxOn('click.menu', 'li', (_, t) => {
|
|
|
|
if (t.dxFilter('.submenu')) {
|
|
|
|
if (!t.dxFind('.active')) {
|
|
t.classList.toggle('opened');
|
|
t.dxFind('ul').dxSlideToggle();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//Overlay.hideModal();
|
|
App.menuSideEl.classList.remove('opened');
|
|
|
|
if (t.dataset.init) {
|
|
App.initModule(t.dataset.init, {}, { eTarget: t });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Top Tooolbar Back btn - Ev Handler
|
|
*/
|
|
$('.top-toolbar').dxOn('click', 'a.back', (_, t) => {
|
|
|
|
let back = t.dataset.back;
|
|
|
|
if (back == 1 && App.stack) { // In stack
|
|
App.initModule(App.stack.id, App.stack.vars, { back: App.stack.back, eTarget: t });
|
|
}
|
|
else { // Explicit
|
|
App.initModule(back, {}, { eTarget: t });
|
|
}
|
|
});
|
|
|
|
App.menuEventFlag = true;
|
|
}
|
|
|
|
/**
|
|
* Set Menu
|
|
*/
|
|
static initMenu() {
|
|
|
|
let role = App.role.toString();
|
|
|
|
// Menu Selectors
|
|
|
|
App.dropdownMenu = $('.dropdown-menu');
|
|
App.dropdownMenuItems = $('.dropdown-menu').dxFind('ul');
|
|
App.leftMenuItems = $('.left-menu ul');
|
|
App.topToolbar = $('.top-toolbar-inner');
|
|
|
|
App.dropdownMenuItems.dxHtml('');
|
|
App.leftMenuItems.dxHtml('');
|
|
|
|
if (App.intData.leftmenu) {
|
|
|
|
Ut.each(Ut.objVal(App.intData.leftmenu)?.children?.item, dataItem => {
|
|
|
|
if (!dataItem.text) {
|
|
return;
|
|
}
|
|
|
|
// Chech role
|
|
if (dataItem.role && dataItem.role.split(',').indexOf(role) == -1) {
|
|
return;
|
|
}
|
|
|
|
let menuItem = App.leftMenuItems.appendChild( El('li', {},
|
|
El('a', {},
|
|
El('span', { className: 'menu-item' }, dataItem.text),
|
|
El('i', { className: 'fa' + (dataItem['icon-type'] || 's') + ' fa-'+ dataItem.icon })
|
|
)
|
|
));
|
|
|
|
if (dataItem.href) {
|
|
|
|
menuItem.firstChild.href = dataItem.href;
|
|
|
|
if (dataItem.target) {
|
|
menuItem.firstChild.target = dataItem.target;
|
|
}
|
|
}
|
|
else if (dataItem.init) {
|
|
menuItem.dataset.init = dataItem.init;
|
|
}
|
|
|
|
if (dataItem?.children?.item) {
|
|
|
|
let submenu = El('ul'),
|
|
len = 0;
|
|
|
|
Ut.each(dataItem.children.item, secDataItem => {
|
|
|
|
if (!secDataItem.text) {
|
|
return;
|
|
}
|
|
|
|
// Chech role
|
|
if (secDataItem.role && secDataItem.role.split(',').indexOf(role) == -1) {
|
|
return;
|
|
}
|
|
|
|
len++;
|
|
|
|
let submenuItem = submenu.appendChild( El('li', {},
|
|
El('a', {},
|
|
El('span', { className: 'menu-item' }, secDataItem.text),
|
|
El('i', { className: 'fa' + (secDataItem['icon-type'] || 's') + ' fa-'+ secDataItem.icon })
|
|
)
|
|
));
|
|
|
|
if (secDataItem.href) {
|
|
|
|
submenuItem.firstChild.href = data.href;
|
|
|
|
if (secDataItem.target) {
|
|
submenuItem.firstChild.target = secDataItem.target;
|
|
}
|
|
}
|
|
else if (secDataItem.init) {
|
|
submenuItem.dataset.init = secDataItem.init;
|
|
}
|
|
});
|
|
|
|
if (len) {
|
|
menuItem.classList.add('submenu')
|
|
menuItem.append(submenu);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if (App.intData.usermenu) {
|
|
|
|
Ut.each(Ut.objVal(App.intData.usermenu)?.children?.item, dataItem => {
|
|
|
|
if (!dataItem.text) {
|
|
return;
|
|
}
|
|
|
|
// Chech role
|
|
if (dataItem.role && dataItem.role.split(',').indexOf(role) == -1) {
|
|
return;
|
|
}
|
|
|
|
let menuItem = App.dropdownMenuItems.appendChild( El('li', {},
|
|
El('a', {},
|
|
El('span', { className: 'menu-item' }, dataItem.text),
|
|
El('i', { className: 'fa' + (dataItem['icon-type'] || 's') + ' fa-'+ dataItem.icon })
|
|
)
|
|
));
|
|
|
|
if (dataItem.href) {
|
|
|
|
menuItem.firstChild.href = dataItem.href;
|
|
|
|
if (dataItem.target) {
|
|
menuItem.firstChild.target = dataItem.target;
|
|
}
|
|
}
|
|
else if (dataItem.init) {
|
|
menuItem.dataset.init = dataItem.init;
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
App.menuFlag = true;
|
|
}
|
|
|
|
/**
|
|
* Select a specific layout
|
|
*/
|
|
static selectLayout(id) {
|
|
|
|
$('.app-layout', layout => layout.classList.add('hide'));
|
|
|
|
const layout = $('.app-layout[data-id="'+ id +'"]');
|
|
|
|
if (!layout) {
|
|
return;
|
|
}
|
|
|
|
layout.classList.remove('hide');
|
|
|
|
App.contentEl = layout.dxFind('.content-wrapper'),
|
|
App.menuSideEl = layout.dxFind('.left-menu-side');
|
|
|
|
if (App.menuSideEl) {
|
|
App.initMenu();
|
|
App.initMenuEvents();
|
|
}
|
|
}
|
|
} |