Files
david/src/core/App.js
T
2026-01-22 03:34:49 +02:00

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();
}
}
}