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