commit 4af0db8263822c9c0ea4463b757fa3c66b2b062c Author: grainsoft Date: Tue Nov 25 01:21:34 2025 +0200 first commit diff --git a/src/core/App.js b/src/core/App.js new file mode 100644 index 0000000..c4cf152 --- /dev/null +++ b/src/core/App.js @@ -0,0 +1,770 @@ +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', { class: 'top-bar' }); + content.prepend(topBar); + } + + let topBarInner = topBar.dxFind('.top-bar-inner'); + + if (!topBarInner) { + topBarInner = topBar.appendChild( El('div', { class: 'top-bar-inner' }) ); + } + + let titleWrapper = topBarInner.dxFind('.title-wrapper'); + + if (!titleWrapper) { + titleWrapper = topBarInner.appendChild(El('div', { class: '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', { class: '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', { class: '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) { + + console.log( section ) + + 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) { + 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', { class: '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', { class: 'menu-item' }, dataItem.text), + El('i', { class: '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', { class: 'menu-item' }, secDataItem.text), + El('i', { class: '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', { class: 'menu-item' }, dataItem.text), + El('i', { class: '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(); + } + } +} \ No newline at end of file diff --git a/src/core/Boot.js b/src/core/Boot.js new file mode 100644 index 0000000..257189b --- /dev/null +++ b/src/core/Boot.js @@ -0,0 +1,93 @@ +import { Rt } from './Rt'; +import { dxReady } from '../utils/dom'; + +/* +* Loader Class +*/ +export class Boot { + + // Deb Mode + static DEBUG = false; + + // Static App Config + static config = {}; + + // Static Global Data + static data = {}; + + // App Loaded state + static #started = false; + + // Widget Stack + static widgetStack = {}; + + // Root data + static r = {}; + + // Static Section + static section = null; + static _section = null; + + // Static Section Vars + static vars = {}; + + static ready(callback) { + + if (Boot.#started) { + console.log('Register already called!'); + return; + } + + Boot.#started = true; + + dxReady(() => { + + let dirPath = '/'; + const h = document.documentElement; + + if (h.classList.contains('app') && location.pathname != '/') { + dirPath = location.pathname + '/'; + } + + Boot.lang = h.getAttribute('lang') ?? 'en'; + + Rt.setCsrfToken(h.dxFind('meta[name="csrf-token"]')?.content); + + import(dirPath + 'js/interface.js').then((mod) => { + + if (!mod.intData) { + return; + } + + Boot.intData = mod.intData; + + callback(); + + }).catch((e) => {}); + + }); + } + + /** + * Set Config Directive + */ + setConfig(name, value) { + Boot.config[name] = value; + } + + // Clear Global Data + + clearGlobalData() { + Boot.data = {}; + } + + /** + * App Log + */ + log(msg) { + + if (Boot.DEBUG) { + console.log(msg); + } + } +} \ No newline at end of file diff --git a/src/core/Rc.js b/src/core/Rc.js new file mode 100644 index 0000000..6ad4ea2 --- /dev/null +++ b/src/core/Rc.js @@ -0,0 +1,30 @@ +/** + * Standard API Response Codes (Global Object) + */ + export const Rc = { + DONE: 200, // OK + CREATED: 201, // Resource created + ACCEPTED: 202, // Request accepted (async) + NO_CONTENT: 204, // No content to return + + INVALID_PARAMS: 400, // Invalid input parameters + NO_SESSION: 401, // Unauthorized / No session + FORBIDDEN: 403, // Forbidden (e.g. wrong login) + NOT_FOUND: 404, // Resource not found + NOT_ALLOWED: 405, // Method not allowed + NOT_ACCEPTABLE: 406, // Client does not accept the returned format + + ENTRY_EXISTS: 409, // Entry already exists (conflict) + DELETED: 410, // Entry deleted + INVALID_CSRF: 419, // Page Expired (non-standard, CSRF failure) + + NOT_MATCH: 422, // Data mismatch / validation failed + ACCOUNT_LOCKED: 423, // Account locked + EXCEEDED_LIMITS: 429, // Too many requests / rate limit + + NOT_AUTH: 440, // Not authenticated (custom) + + ERROR: 500, // Internal server error + BAD_GATEWAY: 502, // Bad gateway from upstream + SERV_UNAVAILABLE: 503 // Service temporarily unavailable + }; \ No newline at end of file diff --git a/src/core/Render.js b/src/core/Render.js new file mode 100644 index 0000000..5cfa47b --- /dev/null +++ b/src/core/Render.js @@ -0,0 +1,1191 @@ +import './css/app-ui.css'; + +import { Ut } from '../utils/Ut'; +import { El } from '../utils/dom'; +import { Boot } from './Boot'; +import { Cancelable } from '../widgets/cancelable/Cancelable'; +import { DatePicker } from '../widgets/date-picker/DatePicker'; +import { IntervalPicker } from '../widgets/interval-picker/IntervalPicker'; +import { Uploader } from '../widgets/uploader/Uploader'; + +export class Render { + + /** + * Make Card Element + */ + static makeCardElement(node, parent) { + + node.outline = Ut.isSet(node.outline) ? node.outline : 1; + + let card = El('div', { class: 'sp-card' }); + + if (node.name) { + card.className += ' card-' + node.name; + } + + if (node.cls) { + card.dxAddClass(node.cls); + } + + let content = card; + + if (node.outline == 1) { + content = card.appendChild( El('div', { class: 'content-card' }) ); + } + + if (node.children && node.children.tabmenu) { + + let tabMenu = content.appendChild(El('div', { class: 'tab-menu' })); + + let active = false; + + Ut.each(Ut.objVal(node.children.tabmenu)?.children?.item, dataItem => { + + if (!dataItem.text) { + return; + } + + let item = tabMenu.appendChild(El('a', {}, + El('i', { class: 'fa' + (dataItem['icon-type'] || 's') + ' fa-'+ dataItem.icon }), + El('span', {}, dataItem.text) + )); + + if (dataItem.id) { + item.dataset.id = dataItem.id; + } + + if (!active) { + item.classList.add('active'); + } + + active = true; + }); + + tabMenu.dxOn('click', 'a', (_, t) => { + + t.dxSiblings('.active', itm => itm.classList.remove('active') ); + t.classList.add('active'); + + content?.dxFind('[data-group]:not(.hide)')?.dxFadeOut(_ => content?.dxFind('[data-group="'+ t.dataset.id +'"]')?.dxFadeIn()); + }); + } + + if (node['hide-toolbar'] != 1) { + + let topBar = El('div', { class: 'top-bar' }); + + content.append(topBar); + + if (node.title || node.descr || (node.children && node.children.toolbar)) { + + let topBarInner = El('div', { class: 'top-bar-inner' }); + + topBar.append(topBarInner); + + if (node.title || node.descr) { + + let titleWrapper = topBarInner.appendChild(El('div', { class: 'title-wrapper' })); + + if (node.title) { + titleWrapper.append( El('h2', { class: 'title' }, Ut.tplString(node.title, node.template) )); + } + + if (node.descr) { + titleWrapper.append( El('p', { class: 'descr' }, Ut.tplString(node.descr, node.template)) ); + } + } + + if (node.children && node.children.toolbar) { + Render.makeUiElements(Ut.objVal(node.children.toolbar).children, topBarInner.appendChild(El('div', { class: 'ml10 flex v-center no-shrink' })) ); + } + } + } + + let container = (node['hide-content'] == 1) + ? content + : content.appendChild(El('div', { class: 'container' })); + + if (parent) { + parent.append(card); + } + + if (node.children) { + Render.makeUiElements(node.children, container, node.template); + } + + container.dxFind('[data-group]', (el, i) => i && el.classList.add('hide')); + + return card; + } + + /** + * Make Block Element + */ + static makeBlockElement(node, parent) { + + let block = El('div', { class: 'sp-block block-'+ node.name }); + + if (node.cls) { + block.className += ' ' + node.cls; + } + + if (node.id) { + block.id = node.id; + } + + if (node.title) { + block.dataset.title = Ut.tplString(node.title, node.template); + } + + if (node.type) { + block.dataset.type = node.type; + } + + if (node.label) { + block.textContent = Ut.tplString(node.label, node.template); + } + + if (node.group) { + block.dataset.group = node.group; + } + + if (parent) { + parent.append(block); + } + + if (node.children) { + Render.makeUiElements(node.children, block, node.template); + } + + return block; + } + + /** + * Make Form Element + */ + static makeFormElement(node, parent) { + + let form = El('form', { + novalidate: true, + class: 'sp-form form-'+ node.name + (node.cls ? ' ' + node.cls : ''), + autocomplete: 'off', + method: 'post' + }); + + if (node.title) { + form.dataset.title = Ut.tplString(node.title, node.template); + } + + if (node.group) { + form.dataset.group = node.group; + } + + if (parent) { + parent.append(form); + } + + if (node.children) { + Render.makeUiElements(node.children, form, node.template); + } + + return form; + } + + /** + * Make List Element + */ + static makeListElement(node, parent) { + + let list = El('ul', { class: 'sp-list list-'+ node.name + (node.cls ? ' ' + node.cls : ''), + id: node.id || null + }); + + if (node.group) { + list.dataset.group = node.group; + } + + if (node.children && node.children.item) { + + Ut.each (node.children.item, (item, action) => { + + let itm = El('li', { + class: 'item-'+ (item.type || 'text') + (item.type == 'link' ? ' cmd-' : ' item-') + action + (item.cls ? ' ' + item.cls : ''), + 'data-id': item.id || null, + }); + + if (item.icon) { + let icon = item.icon.split(' '); + itm.append(El('i', { class: 'fa-'+ (icon[1] || 'solid') +' fa-'+ icon[0] }) ); + } + + if (item.label) { + itm.append(El('span', { class: 'item-label' }, Ut.tplString(item.label, node.template) )); + } + + list.append(itm); + }); + + if (parent) { + parent.append(list); + } + } + + return list; + } + + /** + * Make Title Element + */ + static makeTitleElement(node, parent) { + + let blockTitle = El('div', { class: 'sp-title mb'+ (node.mb || 30) +' title-'+ node.name }); + + if (node.mt) { + blockTitle.classList.add('mt' + node.mt); + } + + if (node.cls) { + blockTitle.classList.add(node.cls); + } + + if (node.icon) { + blockTitle.appendChild(El('i', { class: 'fa' + (field['icon-type'] || 's') + ' fa-'+ field.icon }) ); + } + + let title = blockTitle.appendChild(El('h2', { class: 'text-semibold fz' + (node.size || 22) })); + + if (node.label) { + title.textContent = Ut.tplString(node.label, node.template); + } + + if (parent) { + parent.append(blockTitle); + } + + return blockTitle; + } + + /** + * Make Caption Element + */ + static makeCaptionElement(node, parent) { + + let blockTitle = El('div', { class: 'sp-caption mb'+ (node.mb || 30) +' caption-'+ node.name }); + + if (node.mt) { + blockTitle.classList.add('mt' + node.mt); + } + + if (node.cls) { + blockTitle.classList.add(node.cls); + } + + if (node.icon) { + blockTitle.appendChild(El('i', { class: 'fa' + (field['icon-type'] || 's') + ' fa-'+ field.icon }) ); + } + + let title = blockTitle.appendChild(El('h3', { class: 'text-medium fz' + (node.size || 18) })); + + if (node.label) { + title.textContent = Ut.tplString(node.label, node.template); + } + + if (parent) { + parent.append(blockTitle); + } + + return blockTitle; + } + + /** + * Make Info Element + */ + static makeInfoElement(field, parent) { + + let labelWrapper = El('div', { class: 'form-field field-info' }); + + if (field.name) { + labelWrapper.dataset.name = field.name; + } + + if (field.color) { + labelWrapper.className += ' color-' + field.color; + } + + if (field.cls) { + labelWrapper.className += ' ' + field.cls; + } + + if (field.icon) { + labelWrapper.append( El('i', { class: 'fa' + (field['icon-type'] || 's') + ' fa-'+ field.icon + ' ic-info fz' + (field['icon-size'] || 32) + ' color-' + (field['icon-color'] || 'black') }) ); + } + + let group = El('div'); + + if (field.label) { + group.append(El('label', { class: 'label-name' }, Ut.tplString(field.label, field.template))); + } + + let labelValue = El('div', { class: 'label-value' }); + + if (field.wrap) { + labelValue.className += ' text-wrap'; + } + + if (field.id) { + labelValue.id = field.id; + } + + if (field.value) { + labelValue.textContent = field.value; + } + + group.append(labelValue); + + labelWrapper.append(group); + + if (parent) { + parent.append(labelWrapper); + } + + return labelWrapper; + } + + /** + * Make Link Element + */ + static makeLinkElement(node, parent) { + + let elem = El('a', { class: 'link' }); + + if (node.name) { + elem.className += ' link-' + node.name; + } + + if (node.href) { + node.href = node.href; + } + + if (node.target) { + node.target = node.target; + } + + if (node.icon) { + elem.prepend(El('i', { class: 'fa' + (node['icon-type'] || 's') + ' fa-'+ node.icon }) ); + } + + if (node.label) { + elem.append(Ut.tplString(node.label, node.template)); + } + else { + elem.className += ' no-text'; + } + + if (node.size) { + elem.className += ' fz' + node.size; + } + + if (node.color) { + elem.className += ' color-' + node.color; + } + + if (node.tooltip) { + + elem.dataset.tooltip = node.tooltip; + + if (node['tooltip-color']) { + elem.className += ' tooltip-color-' + node['tooltip-color']; + } + } + + if (node.cls) { + elem.className += ' ' + node.cls; + } + + if (parent) { + parent.append(elem); + } + + return elem; + } + + /** + * Make Card Menu + */ + static makeCardMenu(node, parent) { + + let cardMenu = El('div', { class: 'sp-card-menu' }); + + if (node?.children?.item) { + + Ut.each (node.children.item, item => { + + if (!item.title) { + return; + } + + const menuItem = cardMenu.appendChild( El('a', { class: 'card-menu-item' }, + El('div', { class: 'card-icon-wrapper' }, + El('i', { class: 'fa' + (item['icon-type'] || 's') + ' fa-'+ (item.icon || 'cog') + ' fz' + (item['icon-size'] || 36) }) + ), + El('div', { class:'card-menu-title' }, item.title) + ) ); + + if (item.id) { + menuItem.dataset.id = item.id; + } + + if (item.init) { + menuItem.dataset.init = item.init; + } + + if (item.descr) { + menuItem.append(El('div', { class: 'card-menu-descr' }, item.descr)); + } + + cardMenu.append(menuItem); + }); + } + + if (parent) { + parent.append(cardMenu); + } + + return cardMenu; + } + + /** + * Make Button Element + */ + static makeButtonElement(node, parent) { + + let elem = El('button', { type: node.type || 'button' }); + + if (node.disabled) { + elem.disabled = true; + } + + elem.className = 'sp-button'; + + if (node.color) { + elem.className += ' ui-color-' + node.color; + } + + if (!node.label && node.icon) { + elem.className += ' icon-only'; + } + + if (node.cls) { + elem.className += ' ' + node.cls; + } + + if (node.id) { + elem.id = node.id; + } + + if (node.init) { + elem.dataset.init = node.init; + } + + if (node.label) { + elem.append( El('span', {}, Ut.tplString(node.label, node.template) ) ); + } + + if (node.icon) { + elem.append( El('i', { class: 'icon-' + (node['icon-align'] || 'left') + ' fa' + (node['icon-type'] || 's') + ' fa-' + node.icon + ' fz' + (node['icon-size'] || 14) + ' color-' + (node['icon-color'] || 'white') }) ); + } + + if (node.tooltip) { + + elem.dataset.tooltip = node.tooltip; + + if (node['tooltip-color']) { + elem.className += ' tooltip-color-' + node['tooltip-color']; + } + } + + if (parent) { + parent.append(elem); + } + + return elem; + } + + /** + * Set Input Default Attrs + */ + static setInputAttr(input, field) { + + input.name = field.name; + + if (field.title) { + input.title = field.title; + } + + if (field.id) { + input.id = field.id; + } + + if (field.dataid) { + input.dataset.id = field.dataid; + } + + if (field.readonly == '1') { + input.readOnly = true; + } + + if (field.rewrite == '1') { + input.dataset.value = ''; + } + + if (field.required == '1') { + input.required = true; + } + } + + /** + * Make Field Wrapper + */ + static makeFieldWrapper(field, parent) { + + let wrapper = El('div', { class: 'form-field field-' + field.type }); + + if (field.name) { + wrapper.className += ' field-' + field.name; + } + + if (field.cls) { + wrapper.className += ' ' + field.cls; + } + + if (field.label) { + wrapper.append( El('label', { class: 'field-label' }, Ut.tplString(field.label, field.template)) ); + } + + let inputWrapper = null; + + if (field.type == 'text' || field.type == 'password' || field.type == 'select') { + + inputWrapper = El('div', { class: 'input-wrapper' }); + + wrapper.append(inputWrapper); + + if (field.icon) { + + let icon; + + if (field['icon-tooltip'] || field['icon-action']) { + + icon = El('a', { class: 'icon-' + (field['icon-align'] || 'left') }, + El('i', { class: 'fa' + (field['icon-type'] || 's') + ' fa-' + field.icon + ' fz' + (field['icon-size'] || 14) + ' color-' + (field['icon-color'] || 'black') }) + ); + } + else { + icon = El('i', { class: 'icon-' + (field['icon-align'] || 'left') + ' fa' + (field['icon-type'] || 's') + ' fa-' + field.icon + ' fz' + (field['icon-size'] || 14) + ' color-' + (field['icon-color'] || 'black') }); + } + + if (field['icon-tooltip']) { + icon.dataset.tooltip = field['icon-tooltip']; + icon.className += ' tooltip-color-' + (field['icon-color'] || black); + } + + if (field['icon-display'] == 0 ) { + icon.className += ' hide'; + } + + if (field['icon-cls']) { + icon.className += ' ' + field['icon-cls']; + } + + inputWrapper.append( icon ); + } + } + + if (parent) { + parent.append(wrapper); + } + + return inputWrapper || wrapper; + } + + /** + * Make Input Element + */ + static makeInputElement(field, parent) { + + let input = El('input', { type: field.type, class: 'sp-form-control' }); + + if (field.value) { + input.value = field.value; + } + + if (field.type != 'hidden' && field.placeholder) { + input.placeholder = field.placeholder; + } + + if (field.type != 'hidden' && field.autocomplete) { + input.setAttribute('autocomplete', field.autocomplete || 'off'); + } + + if (field.disabled == '1') { + input.disabled = true; + } + + Render.setInputAttr(input, field); + + let wrapper = Render.makeFieldWrapper(field, parent); + wrapper.appendChild(input); + + if (field.cancelable == '1') { + new Cancelable(input); + } + + if (field.maxlength) { + input.maxLength = field.maxlength; + } + + if (field['max-chars']) { + input.Counter({ limit: field['max-chars'] }); + } + + return input; + } + + /** + * Make Input Password Element + */ + static makeInputPwdElement(field, parent) { + + let elem = El('input', { type: 'password', class: 'sp-form-control' }); + + if (field.placeholder) { + elem.placeholder = field.placeholder; + } + + Render.setInputAttr(elem, field); + + Render.makeFieldWrapper(field, parent).append(elem); + + return elem; + } + + /** + * Make Textarea Element + */ + static makeTextareaElement(field, parent) { + + let textarea = El('textarea', { class: 'sp-form-control' }); + + if (field.rows) { + textarea.setAttribute('rows', field.rows); + } + + if (field.placeholder) { + textarea.placeholder = field.placeholder; + } + + Render.setInputAttr(textarea, field); + + if (field.autoexpand == '1') { + textarea.AutoExpand({ minRows: field['min-rows'] }); + } + + if (field['max-chars']) { + textarea.Counter({ limit: field['max-chars'] }); + } + + Render.makeFieldWrapper(field, parent).append(textarea); + + return textarea; + } + + /** + * Make DatePicker Element + */ + static makeDatePickerElement(field, parent) { + + let input = Render.makeInputElement({... field, ... { type: 'text' }}, parent); + + new DatePicker(input, { + style: field.style || 'normal', + disabled: field.disabled || false, + multiple: field.multiple || false, + setTime: field['set-time'] || false, + locales: field.locales || 'ro-RO', + dateFormat : field['date-format'] || 'dd-mm-yyyy', + valueFormat : field['value-format'] || 'dd-mm-yyyy', + datesSep : field['dates-sep'] || ' / ', + valuesSep : field['values-sep'] || '&' + }); + + return input; + } + + /** + * Make TwoDatePicker Element + */ + static makeTwoDatePickerElement(field, parent) { + + let input = Render.makeInputElement({... field, ... { type: 'text' }}, parent); + + input.TwoDatePicker({ + style: field.style || 'normal', + disabled: field.disabled || false, + multiple: field.multiple || false, + setTime: field['set-time'] || false, + locales: field.locales || 'ro-RO', + dateFormat : field['date-format'] || 'dd-mm-yyyy', + valueFormat : field['value-format'] || 'dd-mm-yyyy', + datesSep : field['dates-sep'] || ' / ', + valuesSep : field['values-sep'] || '&' + }); + + return input; + } + + /** + * Make IntervalPicker Element + */ + static makeIntervalPickerElement(field, parent) { + + let input = Render.makeInputElement({... field, ... { type: 'text' }}, parent); + + new IntervalPicker(input, { + style : field.style || 'normal', + disabled : field.disabled || false, + locales : field.locales || 'ro-RO', + dateFormat : field['date-format'] || 'dd-mm-yyyy', + valueFormat : field['value-format'] || 'dd-mm-yyyy' + }); + } + + /** + * Make Range Element + */ + static makeInputRangeElement(field, parent) { + + let elem = El('input', { type: 'range' }); + + if (field.min) { + elem.setAttribute('min', field.min); + } + + if (field.max) { + elem.setAttribute('max', field.max); + } + + if (field.step) { + elem.setAttribute('step', field.step); + } + + if (field.value) { + elem.value = field.value; + } + + Render.setInputAttr(elem, field); + + Render.makeFieldWrapper(field, parent).append(elem); + + return elem; + } + + /** + * Make Input Color Element + */ + static makeInputColorElement(field, parent) { + + let input = El('input', { type: 'color' }); + + let elem = El('div', { class: 'input-color-wrapper' }, input); + + Render.setInputAttr(input, field); + + Render.makeFieldWrapper(field, parent).append(elem); + + return input; + } + + /** + * Make Input File + */ + static makeInputFileElement(field, parent, opts) { + + let input = El('input', { type: 'file' }); + + Render.setInputAttr(input, field); + + Render.makeFieldWrapper(field, parent).append(input); + + new Uploader(input, opts?.vars?.id ? { id: opts.vars.id } : {}); + + return input; + } + + /** + * Make Select Element + */ + static makeSelectElement(field, parent) { + + let elem = El('select', { class: 'sp-form-control' }); + + if (field.multiple == '1') { + elem.multiple = true; + } + + if (field.option) { + + Ut.each (field.option, (text, val) => { + elem.append(new Option(text, val, val == field.value)); + }); + + if (field.value) { + elem.value = field.value; + } + } + + Render.setInputAttr(elem, field); + + if (field.cancelable == '1') { + new Cancelable(elem); + } + + Render.makeFieldWrapper(field, parent).append(elem); + + return elem; + } + + /** + * Make Radio Element + */ + static makeRadioElement(field, parent) { + + if (!field.option) { + return false; + } + + let elem = El('div', { class: 'sp-control ' + field.type +'-control' }); + + Ut.each (field.option, (text, val) => { + + let input = El('input', { type: field.type }); + + Render.setInputAttr(input, field); + + if (field.value) { + input.checked = (val == field.value); + } + + input.value = val; + + let label = El('label', {}, input, El('span', { class: 'ctrl-indicator' }), text); + + label.className = 'radio-label'; + + if (field.xcls) { + label.className += ' ' + field.xcls; + } + + elem.append(label); + + if (field.group == '1' && field.type == 'checkbox') { + input.SelectBox(); + } + + }); + + Render.makeFieldWrapper(field, parent).append(elem); + + return elem; + } + + /** + * Make Radio Box Element + */ + static makeRadioBoxElement(field, parent) { + + if (!field.option) { + return false; + } + + let colors = [], + i = 0; + + if (field.color) { + colors = field.color.split(',').map(function(color) { + return color.trim(); + }); + } + + let elem = El('div', { class: 'sp-radio-box' }); + + Ut.each (field.option, (text, val) => { + + let label = El('label'); + + label.className = 'radiobox-label'; + + if (field.xcls) { + label.className += ' ' + field.xcls; + } + + let input = El('input', { type: 'radio' }); + + Render.setInputAttr(input, field); + + input.value = val; + + input.checked = (val == field.value); + + label.append(input); + + let indicator = El('span', { class: 'ctrl-indicator label-color-'+ (colors[i] ? colors[i] : 'default') }); + + indicator.textContent = text; + + label.append(indicator); + + elem.append(label); + + i++; + + }); + + Render.makeFieldWrapper(field, parent).append(elem); + + return elem; + } + + /** + * Make Number Picker Element + */ + static makeNumPickerElement(field, parent) { + + let input = El('input', { name: field.name, type: 'hidden' }); + + if (field.id) { + input.id = field.id; + } + + if (field.value) { + input.value = field.value; + } + + Render.makeFieldWrapper(field, parent).append(input); + + input.NumberPicker({ + minVal: field.min, + maxVal: field.max + }); + + return input; + } + + /** + * Make Radio Box Element + */ + static makePinBoxElement(field, parent) { + + let input = El('input', { + type: 'hidden', + name: field.name + }); + + Render.makeFieldWrapper(field, parent).append(input); + + input.PinBox({ digits: field.digits || 6 }); + + return input; + } + + /** + * Make Hidden Element + */ + static makeHiddenElement(field, parent) { + + if (field.label) { + return Render.makeInputElement(field, parent); + } + + let input = parent.appendChild(El('input', { type: 'hidden' })); + + if (field.name) { + input.name = field.name; + } + + if (field.id) { + input.id = field.id; + } + + if (field.value) { + input.value = field.value; + } + + return input; + } + + /** + * Make Element + */ + static makeElement(props, parent, opts) { + + switch (props._type) { + case 'card': return Render.makeCardElement(props, parent); + case 'block': return Render.makeBlockElement(props, parent); + case 'form': return Render.makeFormElement(props, parent); + case 'field': return Render.makeFormControl(props, parent, opts); + case 'list': return Render.makeListElement(props, parent); + case 'title': return Render.makeTitleElement(props, parent); + case 'caption': return Render.makeCaptionElement(props, parent); + case 'link': return Render.makeLinkElement(props, parent); + case 'button': case 'submit': return Render.makeButtonElement(props, parent); + case 'info': return Render.makeInfoElement(props, parent); + case 'cardmenu': return Render.makeCardMenu(props, parent); + } + } + + /** + * Make Form Control + */ + static makeFormControl(field, parent, opts) { + + if (!field.type || !field.name) { // Field type && field name are required + return false; + } + + switch (field.type) { + case 'text': return Render.makeInputElement(field, parent); + case 'password': return Render.makeInputPwdElement(field, parent); + case 'textarea': return Render.makeTextareaElement(field, parent); + case 'range': return Render.makeInputRangeElement(field, parent); + case 'color': return Render.makeInputColorElement(field, parent); + case 'datepicker': return Render.makeDatePickerElement(field, parent); + case 'twodatepicker': return Render.makeTwoDatePickerElement(field, parent); + case 'intervalpicker': return Render.makeIntervalPickerElement(field, parent); + case 'file': return Render.makeInputFileElement(field, parent, opts); + case 'select': return Render.makeSelectElement(field, parent); + case 'radio': case 'checkbox': return Render.makeRadioElement(field, parent); + case 'radiobox': return Render.makeRadioBoxElement(field, parent); + case 'numpicker': return Render.makeNumPickerElement(field, parent); + case 'pinbox': return Render.makePinBoxElement(field, parent); + case 'hidden': return Render.makeHiddenElement(field, parent); + } + } + + /** + * Make UI Elements + */ + static makeUiElements(rootNode, parent, template) { + + if (!rootNode) { + return; + } + + template = template || {}; + + let nodesList = []; + + Ut.each (rootNode, (node, type) => { + Ut.each(node, (prop, name) => { + if (typeof prop == 'object') { + nodesList[prop.order] = prop; + prop._type = type; + prop.name = name; + prop.template = template; + } + }); + }); + + // Reiterate + nodesList.forEach(props => Render.makeElement(props, parent)); + } + + /** + * Init Module + */ + static loadSection(id, vars) { + + Boot.vars = vars || {}; + + const _section = id.split('.'); + + let rootNode = Boot.intData.section[ _section[0] ]; + + // RootNode is missing + if (!Ut.isSet(rootNode)) { + + if (Boot.DEBUG) { + console.log('Section not found'); + } + + return; + } + + // Set sectionList as global + Boot._section = _section; + + // Subsection present + if (_section.length == 2) { + + // assign (that) object + rootNode = Object.assign({}, rootNode, { that: rootNode.children.section[_section[1]] }); + } + + // Set section root (rootnode children) + Boot.r = rootNode.children || {}; + + // This is the current section + Boot.section = id; + + // Set listener evt options + EvtOptions.nsEvent = true; + EvtOptions.section = id; + } + + /** + * Render UI Component + * + * @param string id Component ID + * @param object template Template Object Vars + * + */ + static make(id, template) { + + const token = id.split('.'); + + if (token.length != 2) { + + if (Boot.DEBUG) { + console.log('Invalid component ID!'); + } + + return null; + } + + let nodeObj = null; + + if (Boot._section) { + + let mainNode = Boot.intData?.section[Boot._section[0]]; + nodeObj = mainNode?.children?.[token[0]]?.[token[1]]; + } + else { + nodeObj = Boot.intData?.[token[0]]?.[token[1]]; + } + + if (!nodeObj) { + + if (Boot.DEBUG) { + console.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: Boot.vars || {} }); + } + +} // End Class \ No newline at end of file diff --git a/src/core/Rt.js b/src/core/Rt.js new file mode 100644 index 0000000..5fd3b02 --- /dev/null +++ b/src/core/Rt.js @@ -0,0 +1,207 @@ + +import { Ut } from '../utils/Ut'; +import { Overlay } from '../widgets/overlay'; + +export class Rt { + + static apiUrl = '/backend'; + static args = null; + static csrfToken = null; + + /** + * Request Handler + */ + static request = { + get: (action, vars, events, opts) => Rt.smartRequest('GET', action, vars, events, opts), + post: (action, vars, events, opts) => Rt.smartRequest('POST', action, vars, events, opts), + put: (action, vars, events, opts) => Rt.smartRequest('PUT', action, vars, events, opts), + delete: (action, vars, events, opts) => Rt.smartRequest('DELETE', action, vars, events, opts), + options: (action, vars, events, opts) => Rt.smartRequest('OPTIONS', action, vars, events, opts), + patch: (action, vars, events, opts) => Rt.smartRequest('PATCH', action, vars, events, opts) + }; + + /** + * Async Request Handler + */ + static call = { + get: (action, vars, events, opts) => Rt.asyncRequest('GET', action, vars, events, opts), + post: (action, vars, events, opts) => Rt.asyncRequest('POST', action, vars, events, opts), + put: (action, vars, events, opts) => Rt.asyncRequest('PUT', action, vars, events, opts), + delete: (action, vars, events, opts) => Rt.asyncRequest('DELETE', action, vars, events, opts), + options: (action, vars, events, opts) => Rt.asyncRequest('OPTIONS', action, vars, events, opts), + patch: (action, vars, events, opts) => Rt.asyncRequest('PATCH', action, vars, events, opts) + }; + + /** + * Set CSRF Token + */ + static setCsrfToken(csrfToken) { + Rt.csrfToken = csrfToken; + } + + /** + * Smart Request + */ + static smartRequest(method, action, vars, events, opts) { + + events = events || {}; + opts = { ... { modal: true, loader: false }, ... opts }; + + let fdata = new FormData(); + + if (vars instanceof FormData) { + fdata = vars; + } + else { + Ut.each(vars, (val, key) => fdata.set(key, val)); + } + + let headers = {}; + + if (Rt.csrfToken) { + headers = { 'X-CSRF-TOKEN': Rt.csrfToken } + } + + if (opts.modal || opts.loader) { + Overlay.showModal(); + } + + if (opts.loader) { + Overlay.showLoader(); + } + + let queryStr = ''; + + if (method == 'GET') { + + if (!fdata.entries().next().done) { + queryStr = '?' + (new URLSearchParams(fdata)).toString(); + } + + fdata = null; + } + + fetch((Rt.apiUrl ?? '') + action + (opts.args || Rt.args || '') + queryStr, { + method: method, + body : fdata, + cache : 'no-cache', + headers: headers, + }) + .then(async response => { + const status = response.status; + const data = await response.json(); + return { status, data }; + }) + .then(jr => { + + if (opts.modal || opts.loader) { + Overlay.hideModal(); + } + + if (opts.loader) { + Overlay.hideLoader(); + } + + //if (j.code == Rc.INVALID_CSRF_TOKEN || j.code == Rc.NO_SESSION) { + // Msg.alert('Session Expired!', () => location.reload()); + // return; + //} + + if (events.done) { + events.done(jr.status, jr.data); + } + }) + .catch(error => { + + if (opts.modal || opts.loader) { + Overlay.hideModal(); + } + + if (opts.loader) { + Overlay.hideLoader(); + } + + if (events.error) { + events.error(error); + } + + console.log(error); + }); + } + + /** + * Async Request + */ + static asyncRequest(method, action, vars, opts) { + + opts = { ... { modal: true, loader: false }, ... opts }; + + let fdata = new FormData(); + let headers = {}; + + if (vars instanceof FormData) { + fdata = vars; + } + else { + Ut.each(vars, (val, key) => fdata.set(key, val)); + } + + if (Rt.csrfToken) { + headers = { 'X-CSRF-TOKEN': Rt.csrfToken } + } + + return async(reqOpts) => { + + const updateVars = !! reqOpts.vars; + + reqOpts = { ... { + update: true, + method: method, + action: action, + vars: vars, + opts: opts + }, ... reqOpts }; + + if (!reqOpts.update) { + fdata = new FormData(); + } + + if (updateVars) { + + if (reqOpts.vars instanceof FormData) { + + for (const [key, value] of reqOpts.vars.entries()) { + fdata.set(key, value); + } + } + else { + Ut.each(reqOpts.vars, (val, key) => fdata.set(key, val)); + } + } + + let queryStr = ''; + + if (reqOpts.method == 'GET') { + + if (!fdata.entries().next().done) { + queryStr = '?' + (new URLSearchParams(fdata)).toString(); + } + } + + const resp = await fetch((Rt.apiUrl ?? '') + reqOpts.action + (reqOpts.opts.args || Rt.args || '') + queryStr, { + method: reqOpts.method, + ...(reqOpts.method != 'GET' && { body: fdata }), + cache : 'no-cache', + headers: headers, + }); + + try { + let data = await resp.json(); + return { code: resp.status, data: data }; + + } catch { + throw new Error("Empty or invalid JSON response"); + } + } + } +} \ No newline at end of file diff --git a/src/core/Stack.js b/src/core/Stack.js new file mode 100644 index 0000000..bd254a4 --- /dev/null +++ b/src/core/Stack.js @@ -0,0 +1,41 @@ +export class Stack { + + static widgets = {}; + + /** + * Registers a widget instance in the global registry. + * + * @param {Object} ref - Widget instance to register. + * @returns {void} + */ + static add(ref) { + + const name = ref.constructor.name; + + Stack.widgets[name] ??= new Map(); + + // ref.ekey = crypto.randomUUID(); + + ref.ekey = Stack.widgets[name].size; + + Stack.widgets[name].set(ref.ekey, ref); + } + + /** + * Removes a widget instance from the global registry. + * + * @param {Object} ref - Widget instance to remove. + * @returns {void} + */ + static delete(ref) { + + const name = ref.constructor.name; + + if (!this.widgets[name]?.has(ref.ekey)) { + return; + } + + this.widgets[name].delete(ref.ekey); + ref.ekey = null; + } +} \ No newline at end of file diff --git a/src/core/css/app-ui.css b/src/core/css/app-ui.css new file mode 100644 index 0000000..fdbb6bc --- /dev/null +++ b/src/core/css/app-ui.css @@ -0,0 +1,345 @@ +/* --------------- + Title +/* -------------*/ + +.sp-title, +.sp-caption { + display: flex; + align-items: center; + flex-basis: 100%; +} + +.sp-title i, +.sp-caption i { + flex-shrink: 0; + margin-right: 10px; + font-size: 20px; +} + +/* --------------- + Links +/* -------------*/ + +.link { + cursor: pointer; + color: var(--main-link-color); + font-size: 1.18rem; + transition: opacity 0.2s; +} + +.link:hover * { + opacity: 0.8; +} + +.link.disabled { + opacity: 0.6; + pointer-events: none; +} + +.link i { + margin-right: 7px; +} + +.link.no-text i { + margin-right: 0; +} + +/* --------------- + List +/* -------------*/ + +.sp-list { + display: block; + width: 100%; + -webkit-user-select: none; + user-select: none; +} + +.sp-list li { + padding: 0 5px; + border-bottom: 1px solid #dee2e6; + display: flex; + align-items: center; + flex-wrap: nowrap; + width: 100%; + font-size: 16px; + height: 50px; +} + +.sp-list li:last-child { + border: 0; +} + +.sp-list li.item-link { + cursor: pointer; + transition: all 0.2s; +} + +.sp-list li.item-link:hover { + color: var(--theme-widget-color); +} + +.sp-list i { + margin-right: 15px; + flex-shrink: 0; + font-size: 18px; +} + +/* --------------- + ChipBox +/* -------------*/ + +.sp-chip { + font-size: 12px; + background: #e4e9f0; + border-radius: 50px; + padding: 5px 10px; + margin: 4px; + display: inline-flex; + align-items: center; + white-space: nowrap; +} + +.sp-chip.big { + padding: 16px; +} + +.sp-chip i { + margin-right: 8px; +} + +.sp-chip i.close { + cursor: pointer; + font-size: 16px; + line-height: 32px; + padding-left: 8px; + transition: all .1s linear; + font-size: 12px; + margin-right: 0; +} + +/* ---------------- + Status Point +/* ---------------*/ + +.sp-status { + border-radius: 50%; + width: 10px; + height: 10px; + margin-right: 8px; + display: inline-block; +} + +.sp-status.st-0, +.sp-status.status-0 { + background-color: #CCC; +} + +.sp-status.st-1, +.sp-status.status-1 { + background-color: #2ECC40; +} + +.sp-status.status-0, +.sp-status.status-1 { + margin-right: 2em; +} + +/* --------------- + Block +/* --------------*/ + +.sp-block { + display: flex; + flex-wrap: wrap; + align-items: self-start; + align-content: start; + width: 100%; +} + +.sp-block .sp-card { + padding: 16px; +} + +.sp-block.separator { + width: 100%; + height: 1px; + margin: 0 0 18px 0; + background: rgba(116, 112, 141, 0.4); + background: linear-gradient(to right, rgba(116, 112, 141, 0) 0%, rgba(116, 112, 141, 0.4) 50%, rgba(116, 112, 141, 0) 100%); +} + +/* --------------- + Cards +/* --------------*/ + +.sp-card { + width: 100%; + display: flex; + flex-wrap: wrap; + align-items: start; + position: relative; +} + +.sp-card .content-card { + width: 100%; + height: 100%; + background-color: #fff; + display: flex; + flex-direction: column; + border-radius: 10px; + /* + overflow: hidden; + */ +} + +.sp-card .container { + width: 100%; + display: flex; + flex-wrap: wrap; + flex: 1; + padding: 5px 24px 24px 24px; +} + +.sp-card .content-main { + padding-bottom: 48px; +} + +.sp-card .top-bar { + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + padding: 18px 24px 18px 24px; +} + +.sp-card .top-bar .top-bar-inner { + display: flex; + flex-wrap: nowrap; + align-items: center; + justify-content: space-between; + position: relative; +} + +.sp-card .top-bar .title-wrapper { + width: 100%; +} + +.sp-card h2.title { + font-size: 22px; + font-weight: 600; +} + +.sp-card p.descr { + color: #7a7a7a; + font-size: 14px; + margin-top: 3px; +} + +.sp-card .sp-table { + margin-bottom: 32px; +} + +.sp-card .ic-edit { + cursor: pointer; + font-size: 24px; +} + +.sp-card .ic-edit:hover { + opacity: 0.75; +} + +.sp-card .tab-menu { + display: flex; + -webkit-user-select: none; + user-select: none; + padding: 0 24px 0 24px; +} + +.sp-card .tab-menu a { + display: flex; + flex: 1; + justify-content: center; + align-items: center; + font-size: 22px; + padding: 20px 0; + color: #4b4b4b; + transition: all 0.2s; + border-bottom: 3px solid #ededed; +} + +.sp-card .tab-menu a.active { + border-bottom: 3px solid var(--theme-widget-color); + pointer-events: none; + color: var(--theme-widget-color); +} + +.sp-card .tab-menu span { + margin-left: 20px; +} + +/* --------------- + Card Menu +/* --------------*/ + +.sp-card-menu { + display: flex; + text-align: center; + gap: 20px; + -webkit-user-select: none; + user-select: none; +} + +.sp-card-menu .card-menu-item { + width: 200px; + box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px 0px, rgba(0, 0, 0, 0.1) 0px 0px 1px 0px; + border-radius: 20px; + transition: all 0.2s; + padding: 0 15px 15px 15px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.sp-card-menu .card-menu-item:hover { + box-shadow: rgba(16, 82, 137, 0.4) 0px 0px 5px 0px, rgba(16, 82, 137, 0.4) 0px 0px 1px 0px; +} + +.sp-card-menu .card-menu-item .card-icon-wrapper { + height: 80px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.sp-card-menu .card-menu-item .card-icon-wrapper i { + color: #a5a5a5; + transition: all 0.2s; +} + +.sp-card-menu .card-menu-item .card-menu-title { + font-size: 14px; + font-weight: 600; + color: #4b4b4b; + white-space: nowrap; + transition: all 0.2s; +} + +.sp-card-menu .card-menu-item .card-menu-descr { + margin-top: 10px; + font-size: 13px; + line-height: 1.6; + color: #666; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + transition: all 0.2s; +} + +.sp-card-menu .card-menu-item:hover .card-icon-wrapper i, +.sp-card-menu .card-menu-item:hover .card-menu-title, +.sp-card-menu .card-menu-item:hover .card-menu-descr { + color: #105289; +} \ No newline at end of file diff --git a/src/core/css/grid.css b/src/core/css/grid.css new file mode 100644 index 0000000..bb9798d --- /dev/null +++ b/src/core/css/grid.css @@ -0,0 +1,5252 @@ +/* Wrapper */ + +.wrapper { + width: var(--main-wrapper-width); + margin: 0 auto; +} + +.wrapper.fluid { + width: 100%; + max-width: 100%; +} + +/* Flex > Row & Column */ + +.row, .flex { + display: flex; + flex-wrap: wrap; + justify-content: start; +} + +.row { flex-direction: row; } +.inline-row, .inline-flex { display: inline-flex; } +.grid { display: grid; } +.inline-grid { display: inline-grid; } + +.column { + display: flex; + flex-direction: column; +} + +.wrap { flex-wrap: wrap; } +.nowrap { flex-wrap: nowrap; } +.no-shrink { flex-shrink: 0; } + +.fill { flex-grow: 1; min-width: 33.33333%; } + +.x-start { justify-content: start; } +.x-end { justify-content: end; } +.x-center { justify-content: center; } +.x-between { justify-content: space-between; } +.x-around { justify-content: space-around; } +.x-evenly { justify-content: space-evenly; } + +.g-start { justify-items: start; } +.g-end { justify-items: end; } +.g-center { justify-items: center; } +.g-stretch { justify-items: stretch; } + +.v-normal { align-items: normal; } +.v-start { align-items: start; } +.v-end { align-items: end; } +.v-center { align-items: center; } +.v-stretch { align-items: stretch; } +.v-baseline { align-items: baseline; } + +.y-start { align-content: start; } +.y-end { align-content: end; } +.y-center { align-content: center; } +.y-stretch { align-content: stretch; } +.y-between { align-content: space-between; } +.y-around { align-content: space-around; } + +/* Self Alignment */ + +.start { align-self: start; } +.end { align-self: end; } +.center { align-self: center; } +.baseline { align-self: baseline; } +.stretch { align-self: stretch; } + +/* Flex - display */ + +.hide { display: none !important; } +.not-display { display: none; } +.block { display: block !important; } +.flex { display: flex !important; align-self: auto; } + +/* Grid Sizes (as 12 cols) */ + +.s1 { width: 8.33333%; } +.s2 { width: 16.66666%; } +.s3 { width: 24.99999%; } +.s4 { width: 33.33333%; } +.s5 { width: 41.66666%; } +.s6 { width: 49.99999%; } +.s7 { width: 58.33333%; } +.s8 { width: 66.66666%; } +.s9 { width: 74.99999%; } +.s10{ width: 83.33333%; } +.s11{ width: 91.66666%; } +.s12 { width: 99.99999%; } + +/* Grid Sizes (as percent) */ + +.s-5 { width: 5%; } +.s-10 { width: 10%; } +.s-15 { width: 15%; } +.s-20 { width: 20%; } +.s-25 { width: 25%; } +.s-30 { width: 30%; } +.s-35 { width: 35%; } +.s-40 { width: 40%; } +.s-45 { width: 45%; } +.s-50 { width: 50%; } +.s-55 { width: 55%; } +.s-65 { width: 65%; } +.s-70 { width: 70%; } +.s-75 { width: 75%; } +.s-80 { width: 80%; } +.s-85 { width: 85%; } +.s-90 { width: 90%; } +.s-95 { width: 90%; } +.s-100{ width: 100%; } +.s-a { width: auto; } + +/* Grid Template Columns */ + +.tc1 { grid-template-columns: 1fr; } +.tc2 { grid-template-columns: repeat(2, 1fr); } +.tc3 { grid-template-columns: repeat(3, 1fr); } +.tc4 { grid-template-columns: repeat(4, 1fr); } +.tc5 { grid-template-columns: repeat(5, 1fr); } +.tc6 { grid-template-columns: repeat(6, 1fr); } +.tc7 { grid-template-columns: repeat(7, 1fr); } +.tc8 { grid-template-columns: repeat(8, 1fr); } +.tc9 { grid-template-columns: repeat(9, 1fr); } +.tc10 { grid-template-columns: repeat(10, 1fr); } +.tc11 { grid-template-columns: repeat(11, 1fr); } +.tc12 { grid-template-columns: repeat(12, 1fr); } + +.tc-1 { grid-template-columns: auto; } +.tc-2 { grid-template-columns: repeat(2, auto); } +.tc-3 { grid-template-columns: repeat(3, auto); } +.tc-4 { grid-template-columns: repeat(4, auto); } +.tc-5 { grid-template-columns: repeat(5, auto); } +.tc-6 { grid-template-columns: repeat(6, auto); } +.tc-7 { grid-template-columns: repeat(7, auto); } +.tc-8 { grid-template-columns: repeat(8, auto); } +.tc-9 { grid-template-columns: repeat(9, auto); } +.tc-10 { grid-template-columns: repeat(10, auto); } +.tc-11 { grid-template-columns: repeat(11, auto); } +.tc-12 { grid-template-columns: repeat(12, auto); } + +/* Grid spanning */ + +.no-span { grid-column: auto; } +.span2 { grid-column: span 2; } +.span3 { grid-column: span 3; } +.span4 { grid-column: span 4; } +.span5 { grid-column: span 5; } +.span6 { grid-column: span 6; } +.span7 { grid-column: span 7; } +.span8 { grid-column: span 8; } +.span9 { grid-column: span 9; } +.span10 { grid-column: span 10;} +.span11 { grid-column: span 11; } +.span12 { grid-column: span 12; } + +/* Grid gap */ + +.gap0 { gap: 0 } +.gap5 { gap: 5px; } +.gap10 { gap:10px; } +.gap15 { gap: 15px; } +.gap20 { gap: 20px; } +.gap25 { gap: 25px; } +.gap30 { gap: 30px; } +.gap35 { gap: 35px; } +.gap40 { gap: 40px; } +.gap45 { gap: 45px; } +.gap50 { gap: 50px; } +.gap55 { gap: 55px; } +.gap60 { gap: 60px; } +.gap65 { gap: 65px; } +.gap70 { gap: 70px; } +.gap75 { gap: 75px; } +.gap80 { gap: 80px; } +.gap85 { gap: 85px; } +.gap90 { gap: 90px; } +.gap95 { gap: 95px; } +.gap100 { gap: 100px; } +.gap105 { gap: 105px; } +.gap110 { gap: 110px; } +.gap115 { gap: 115px; } +.gap120 { gap: 120px; } +.gap125 { gap: 125px; } +.gap130 { gap: 130px; } +.gap135 { gap: 135px; } +.gap140 { gap: 140px; } +.gap145 { gap: 145px; } +.gap150 { gap: 150px; } +.gap155 { gap: 155px; } +.gap160 { gap: 160px; } +.gap165 { gap: 165px; } +.gap170 { gap: 170px; } +.gap175 { gap: 175px; } +.gap180 { gap: 180px; } +.gap185 { gap: 185px; } +.gap190 { gap: 190px; } +.gap195 { gap: 195px; } +.gap200 { gap: 200px; } + +.x-gap0 { column-gap: 0 } +.x-gap5 { column-gap: 5px; } +.x-gap10 { column-gap:10px; } +.x-gap15 { column-gap: 15px; } +.x-gap20 { column-gap: 20px; } +.x-gap25 { column-gap: 25px; } +.x-gap30 { column-gap: 30px; } +.x-gap35 { column-gap: 35px; } +.x-gap40 { column-gap: 40px; } +.x-gap45 { column-gap: 45px; } +.x-gap50 { column-gap: 50px; } +.x-gap55 { column-gap: 55px; } +.x-gap60 { column-gap: 60px; } +.x-gap65 { column-gap: 65px; } +.x-gap70 { column-gap: 70px; } +.x-gap75 { column-gap: 75px; } +.x-gap80 { column-gap: 80px; } +.x-gap85 { column-gap: 85px; } +.x-gap90 { column-gap: 90px; } +.x-gap95 { column-gap: 95px; } +.x-gap100 { column-gap: 100px; } +.x-gap105 { column-gap: 105px; } +.x-gap110 { column-gap: 110px; } +.x-gap115 { column-gap: 115px; } +.x-gap120 { column-gap: 120px; } +.x-gap125 { column-gap: 125px; } +.x-gap130 { column-gap: 130px; } +.x-gap135 { column-gap: 135px; } +.x-gap140 { column-gap: 140px; } +.x-gap145 { column-gap: 145px; } +.x-gap150 { column-gap: 150px; } +.x-gap155 { column-gap: 155px; } +.x-gap160 { column-gap: 160px; } +.x-gap165 { column-gap: 165px; } +.x-gap170 { column-gap: 170px; } +.x-gap175 { column-gap: 175px; } +.x-gap180 { column-gap: 180px; } +.x-gap185 { column-gap: 185px; } +.x-gap190 { column-gap: 190px; } +.x-gap195 { column-gap: 195px; } +.x-gap200 { column-gap: 200px; } + +.y-gap0 { row-gap: 0 } +.y-gap5 { row-gap: 5px; } +.y-gap10 { row-gap:10px; } +.y-gap15 { row-gap: 15px; } +.y-gap20 { row-gap: 20px; } +.y-gap25 { row-gap: 25px; } +.y-gap30 { row-gap: 30px; } +.y-gap35 { row-gap: 35px; } +.y-gap40 { row-gap: 40px; } +.y-gap45 { row-gap: 45px; } +.y-gap50 { row-gap: 50px; } +.y-gap55 { row-gap: 55px; } +.y-gap60 { row-gap: 60px; } +.y-gap65 { row-gap: 65px; } +.y-gap70 { row-gap: 70px; } +.y-gap75 { row-gap: 75px; } +.y-gap80 { row-gap: 80px; } +.y-gap85 { row-gap: 85px; } +.y-gap90 { row-gap: 90px; } +.y-gap95 { row-gap: 95px; } +.y-gap100 { row-gap: 100px; } +.y-gap105 { row-gap: 105px; } +.y-gap110 { row-gap: 110px; } +.y-gap115 { row-gap: 115px; } +.y-gap120 { row-gap: 120px; } +.y-gap125 { row-gap: 125px; } +.y-gap130 { row-gap: 130px; } +.y-gap135 { row-gap: 135px; } +.y-gap140 { row-gap: 140px; } +.y-gap145 { row-gap: 145px; } +.y-gap150 { row-gap: 150px; } +.y-gap155 { row-gap: 155px; } +.y-gap160 { row-gap: 160px; } +.y-gap165 { row-gap: 165px; } +.y-gap170 { row-gap: 170px; } +.y-gap175 { row-gap: 175px; } +.y-gap180 { row-gap: 180px; } +.y-gap185 { row-gap: 185px; } +.y-gap190 { row-gap: 190px; } +.y-gap195 { row-gap: 195px; } +.y-gap200 { row-gap: 200px; } + +/* Standard grid gutter */ +.grid.gutters { grid-gap: var(--gutters-sizes); } + +/* Margins - General */ + +.mga { margin: auto; } +.mg0 { margin: 0 } +.mg5 { margin: 5px; } +.mg10 { margin:10px; } +.mg15 { margin: 15px; } +.mg20 { margin: 20px; } +.mg25 { margin: 25px; } +.mg30 { margin: 30px; } +.mg35 { margin: 35px; } +.mg40 { margin: 40px; } +.mg45 { margin: 45px; } +.mg50 { margin: 50px; } +.mg55 { margin: 55px; } +.mg60 { margin: 60px; } +.mg65 { margin: 65px; } +.mg70 { margin: 70px; } +.mg75 { margin: 75px; } +.mg80 { margin: 80px; } +.mg85 { margin: 85px; } +.mg90 { margin: 90px; } +.mg95 { margin: 95px; } +.mg100 { margin: 100px; } +.mg105 { margin: 105px; } +.mg110 { margin: 110px; } +.mg115 { margin: 115px; } +.mg120 { margin: 120px; } +.mg125 { margin: 125px; } +.mg130 { margin: 130px; } +.mg135 { margin: 135px; } +.mg140 { margin: 140px; } +.mg145 { margin: 145px; } +.mg150 { margin: 150px; } +.mg155 { margin: 155px; } +.mg160 { margin: 160px; } +.mg165 { margin: 165px; } +.mg170 { margin: 170px; } +.mg175 { margin: 175px; } +.mg180 { margin: 180px; } +.mg185 { margin: 185px; } +.mg190 { margin: 190px; } +.mg195 { margin: 195px; } +.mg200 { margin: 200px; } + +/* Margins - Top */ + +.mta { margin-top: auto; } +.mt0 { margin-top: 0; } +.mt5 { margin-top: 5px; } +.mt10 { margin-top:10px; } +.mt15 { margin-top: 15px; } +.mt20 { margin-top: 20px; } +.mt25 { margin-top: 25px; } +.mt30 { margin-top: 30px; } +.mt35 { margin-top: 35px; } +.mt40 { margin-top: 40px; } +.mt45 { margin-top: 45px; } +.mt50 { margin-top: 50px; } +.mt55 { margin-top: 55px; } +.mt60 { margin-top: 60px; } +.mt65 { margin-top: 65px; } +.mt70 { margin-top: 70px; } +.mt75 { margin-top: 75px; } +.mt80 { margin-top: 80px; } +.mt85 { margin-top: 85px; } +.mt90 { margin-top: 90px; } +.mt95 { margin-top: 95px; } +.mt100 { margin-top: 100px; } +.mt105 { margin-top: 105px; } +.mt110 { margin-top: 110px; } +.mt115 { margin-top: 115px; } +.mt120 { margin-top: 120px; } +.mt125 { margin-top: 125px; } +.mt130 { margin-top: 130px; } +.mt135 { margin-top: 135px; } +.mt140 { margin-top: 140px; } +.mt145 { margin-top: 145px; } +.mt150 { margin-top: 150px; } +.mt155 { margin-top: 155px; } +.mt160 { margin-top: 160px; } +.mt165 { margin-top: 165px; } +.mt170 { margin-top: 170px; } +.mt175 { margin-top: 175px; } +.mt180 { margin-top: 180px; } +.mt185 { margin-top: 185px; } +.mt190 { margin-top: 190px; } +.mt195 { margin-top: 195px; } +.mt200 { margin-top: 200px; } + +/* Negative Margins - Top */ + +.mt-5 { margin-top: -5px; } +.mt-10 { margin-top: -10px; } +.mt-15 { margin-top: -15px; } +.mt-20 { margin-top: -20px; } +.mt-25 { margin-top: -25px; } +.mt-30 { margin-top: -30px; } +.mt-35 { margin-top: -35px; } +.mt-40 { margin-top: -40px; } +.mt-45 { margin-top: -45px; } +.mt-50 { margin-top: -50px; } +.mt-55 { margin-top: -55px; } +.mt-60 { margin-top: -60px; } +.mt-65 { margin-top: -65px; } +.mt-70 { margin-top: -70px; } +.mt-75 { margin-top: -75px; } +.mt-80 { margin-top: -80px; } +.mt-85 { margin-top: -85px; } +.mt-90 { margin-top: -90px; } +.mt-95 { margin-top: -95px; } +.mt-100 { margin-top: -100px; } +.mt-105 { margin-top: -105px; } +.mt-110 { margin-top: -110px; } +.mt-115 { margin-top: -115px; } +.mt-120 { margin-top: -120px; } +.mt-125 { margin-top: -125px; } +.mt-130 { margin-top: -130px; } +.mt-135 { margin-top: -135px; } +.mt-140 { margin-top: -140px; } +.mt-145 { margin-top: -145px; } +.mt-150 { margin-top: -150px; } +.mt-155 { margin-top: -155px; } +.mt-160 { margin-top: -160px; } +.mt-165 { margin-top: -165px; } +.mt-170 { margin-top: -170px; } +.mt-175 { margin-top: -175px; } +.mt-180 { margin-top: -180px; } +.mt-185 { margin-top: -185px; } +.mt-190 { margin-top: -190px; } +.mt-195 { margin-top: -195px; } +.mt-200 { margin-top: -200px; } + +/* Margins - Bottom */ + +.mba { margin-bottom: auto; } +.mb0 { margin-bottom: 0 } +.mb5 { margin-bottom: 5px; } +.mb10 { margin-bottom:10px; } +.mb15 { margin-bottom: 15px; } +.mb20 { margin-bottom: 20px; } +.mb25 { margin-bottom: 25px; } +.mb30 { margin-bottom: 30px; } +.mb35 { margin-bottom: 35px; } +.mb40 { margin-bottom: 40px; } +.mb45 { margin-bottom: 45px; } +.mb50 { margin-bottom: 50px; } +.mb55 { margin-bottom: 55px; } +.mb60 { margin-bottom: 60px; } +.mb65 { margin-bottom: 65px; } +.mb70 { margin-bottom: 70px; } +.mb75 { margin-bottom: 75px; } +.mb80 { margin-bottom: 80px; } +.mb85 { margin-bottom: 85px; } +.mb90 { margin-bottom: 90px; } +.mb95 { margin-bottom: 95px; } +.mb100 { margin-bottom: 100px; } +.mb105 { margin-bottom: 105px; } +.mb110 { margin-bottom: 110px; } +.mb115 { margin-bottom: 115px; } +.mb120 { margin-bottom: 120px; } +.mb125 { margin-bottom: 125px; } +.mb130 { margin-bottom: 130px; } +.mb135 { margin-bottom: 135px; } +.mb140 { margin-bottom: 140px; } +.mb145 { margin-bottom: 145px; } +.mb150 { margin-bottom: 150px; } +.mb155 { margin-bottom: 155px; } +.mb160 { margin-bottom: 160px; } +.mb165 { margin-bottom: 165px; } +.mb170 { margin-bottom: 170px; } +.mb175 { margin-bottom: 175px; } +.mb180 { margin-bottom: 180px; } +.mb185 { margin-bottom: 185px; } +.mb190 { margin-bottom: 190px; } +.mb195 { margin-bottom: 195px; } +.mb200 { margin-bottom: 200px; } + +/* Margins - Left */ + +.mla { margin-left: auto; } +.ml0 { margin-left: 0; } +.ml5 { margin-left: 5px; } +.ml10 { margin-left:10px; } +.ml15 { margin-left: 15px; } +.ml20 { margin-left: 20px; } +.ml25 { margin-left: 25px; } +.ml30 { margin-left: 30px; } +.ml35 { margin-left: 35px; } +.ml40 { margin-left: 40px; } +.ml45 { margin-left: 45px; } +.ml50 { margin-left: 50px; } +.ml55 { margin-left: 55px; } +.ml60 { margin-left: 60px; } +.ml65 { margin-left: 65px; } +.ml70 { margin-left: 70px; } +.ml75 { margin-left: 75px; } +.ml80 { margin-left: 80px; } +.ml85 { margin-left: 85px; } +.ml90 { margin-left: 90px; } +.ml95 { margin-left: 95px; } +.ml100 { margin-left: 100px; } +.ml105 { margin-left: 105px; } +.ml110 { margin-left: 110px; } +.ml115 { margin-left: 115px; } +.ml120 { margin-left: 120px; } +.ml125 { margin-left: 125px; } +.ml130 { margin-left: 130px; } +.ml135 { margin-left: 135px; } +.ml140 { margin-left: 140px; } +.ml145 { margin-left: 145px; } +.ml150 { margin-left: 150px; } +.ml155 { margin-left: 155px; } +.ml160 { margin-left: 160px; } +.ml165 { margin-left: 165px; } +.ml170 { margin-left: 170px; } +.ml175 { margin-left: 175px; } +.ml180 { margin-left: 180px; } +.ml185 { margin-left: 185px; } +.ml190 { margin-left: 190px; } +.ml195 { margin-left: 195px; } +.ml200 { margin-left: 200px; } + +/* Margins - Right */ + +.mra { margin-right: auto; } +.mr0 { margin-right: 0; } +.mr5 { margin-right: 5px; } +.mr10 { margin-right:10px; } +.mr15 { margin-right: 15px; } +.mr20 { margin-right: 20px; } +.mr25 { margin-right: 25px; } +.mr30 { margin-right: 30px; } +.mr35 { margin-right: 35px; } +.mr40 { margin-right: 40px; } +.mr45 { margin-right: 45px; } +.mr50 { margin-right: 50px; } +.mr55 { margin-right: 55px; } +.mr60 { margin-right: 60px; } +.mr65 { margin-right: 65px; } +.mr70 { margin-right: 70px; } +.mr75 { margin-right: 75px; } +.mr80 { margin-right: 80px; } +.mr85 { margin-right: 85px; } +.mr90 { margin-right: 90px; } +.mr95 { margin-right: 95px; } +.mr100 { margin-right: 100px; } +.mr105 { margin-right: 105px; } +.mr110 { margin-right: 110px; } +.mr115 { margin-right: 115px; } +.mr120 { margin-right: 120px; } +.mr125 { margin-right: 125px; } +.mr130 { margin-right: 130px; } +.mr135 { margin-right: 135px; } +.mr140 { margin-right: 140px; } +.mr145 { margin-right: 145px; } +.mr150 { margin-right: 150px; } +.mr155 { margin-right: 155px; } +.mr160 { margin-right: 160px; } +.mr165 { margin-right: 165px; } +.mr170 { margin-right: 170px; } +.mr175 { margin-right: 175px; } +.mr180 { margin-right: 180px; } +.mr185 { margin-right: 185px; } +.mr190 { margin-right: 190px; } +.mr195 { margin-right: 195px; } +.mr200 { margin-right: 200px; } + +/* Margins - Top & Bottom */ + +.mtba { margin-top: auto; margin-bottom: auto; } +.mtb0 { margin-top: 0; margin-bottom: 0; } +.mtb5 { margin-top: 5px; margin-bottom: 5px; } +.mtb10 { margin-top:10px; margin-bottom: 10px; } +.mtb15 { margin-top: 15px; margin-bottom: 15px; } +.mtb20 { margin-top: 20px; margin-bottom: 20px; } +.mtb25 { margin-top: 25px; margin-bottom: 25px; } +.mtb30 { margin-top: 30px; margin-bottom: 30px; } +.mtb35 { margin-top: 35px; margin-bottom: 35px; } +.mtb40 { margin-top: 40px; margin-bottom: 40px; } +.mtb45 { margin-top: 45px; margin-bottom: 45px; } +.mtb50 { margin-top: 50px; margin-bottom: 50px; } +.mtb55 { margin-top: 55px; margin-bottom: 55px; } +.mtb60 { margin-top: 60px; margin-bottom: 60px; } +.mtb65 { margin-top: 65px; margin-bottom: 65px; } +.mtb70 { margin-top: 70px; margin-bottom: 70px; } +.mtb75 { margin-top: 75px; margin-bottom: 75px; } +.mtb80 { margin-top: 80px; margin-bottom: 80px; } +.mtb85 { margin-top: 85px; margin-bottom: 85px; } +.mtb90 { margin-top: 90px; margin-bottom: 90px; } +.mtb95 { margin-top: 95px; margin-bottom: 95px; } +.mtb100 { margin-top: 100px; margin-bottom: 100px; } +.mtb105 { margin-top: 105px; margin-bottom: 105px; } +.mtb110 { margin-top:110px; margin-bottom: 110px; } +.mtb115 { margin-top: 115px; margin-bottom: 115px; } +.mtb120 { margin-top: 120px; margin-bottom: 120px; } +.mtb125 { margin-top: 125px; margin-bottom: 125px; } +.mtb130 { margin-top: 130px; margin-bottom: 130px; } +.mtb135 { margin-top: 135px; margin-bottom: 135px; } +.mtb140 { margin-top: 140px; margin-bottom: 140px; } +.mtb145 { margin-top: 145px; margin-bottom: 145px; } +.mtb150 { margin-top: 150px; margin-bottom: 150px; } +.mtb155 { margin-top: 155px; margin-bottom: 155px; } +.mtb160 { margin-top: 160px; margin-bottom: 160px; } +.mtb165 { margin-top: 165px; margin-bottom: 165px; } +.mtb170 { margin-top: 170px; margin-bottom: 170px; } +.mtb175 { margin-top: 175px; margin-bottom: 175px; } +.mtb180 { margin-top: 180px; margin-bottom: 180px; } +.mtb185 { margin-top: 185px; margin-bottom: 185px; } +.mtb190 { margin-top: 190px; margin-bottom: 190px; } +.mtb195 { margin-top: 195px; margin-bottom: 195px; } +.mtb200 { margin-top: 200px; margin-bottom: 200px; } + +.centered { margin-left: auto; margin-right: auto; } +.filled { margin-left: 0; margin-right: 0; } + +.mlr5 { margin-left: 5px; margin-right: 5px; } +.mlr10 { margin-left: 10px; margin-right:10px; } +.mlr15 { margin-left: 15px; margin-right: 15px; } +.mlr20 { margin-left: 20px; margin-right: 20px; } +.mlr25 { margin-left: 25px; margin-right: 25px; } +.mlr30 { margin-left: 30px; margin-right: 30px; } +.mlr35 { margin-left: 35px; margin-right: 35px; } +.mlr40 { margin-left: 40px; margin-right: 40px; } +.mlr45 { margin-left: 45px; margin-right: 45px; } +.mlr50 { margin-left: 50px; margin-right: 50px; } +.mlr55 { margin-left: 55px; margin-right: 55px; } +.mlr60 { margin-left: 60px; margin-right: 60px; } +.mlr65 { margin-left: 65px; margin-right: 65px; } +.mlr70 { margin-left: 70px; margin-right: 70px; } +.mlr75 { margin-left: 75px; margin-right: 75px; } +.mlr80 { margin-left: 80px; margin-right: 80px; } +.mlr85 { margin-left: 85px; margin-right: 85px; } +.mlr90 { margin-left: 90px; margin-right: 90px; } +.mlr95 { margin-left: 95px; margin-right: 95px; } +.mlr100 { margin-left: 100px; margin-right: 100px; } +.mlr105 { margin-left: 105px; margin-right: 105px; } +.mlr110 { margin-left:110px; margin-right: 110px; } +.mlr115 { margin-left: 115px; margin-right: 115px; } +.mlr120 { margin-left: 120px; margin-right: 120px; } +.mlr125 { margin-left: 125px; margin-right: 125px; } +.mlr130 { margin-left: 130px; margin-right: 130px; } +.mlr135 { margin-left: 135px; margin-right: 135px; } +.mlr140 { margin-left: 140px; margin-right: 140px; } +.mlr145 { margin-left: 145px; margin-right: 145px; } +.mlr150 { margin-left: 150px; margin-right: 150px; } +.mlr155 { margin-left: 155px; margin-right: 155px; } +.mlr160 { margin-left: 160px; margin-right: 160px; } +.mlr165 { margin-left: 165px; margin-right: 165px; } +.mlr170 { margin-left: 170px; margin-right: 170px; } +.mlr175 { margin-left: 175px; margin-right: 175px; } +.mlr180 { margin-left: 180px; margin-right: 180px; } +.mlr185 { margin-left: 185px; margin-right: 185px; } +.mlr190 { margin-left: 190px; margin-right: 190px; } +.mlr195 { margin-left: 195px; margin-right: 195px; } +.mlr200 { margin-left: 200px; margin-right: 200px; } + +/* Paddings */ + +.pda {padding: auto; } +.pd0 { padding: 0 } +.pd5 { padding: 5px; } +.pd10 { padding:10px; } +.pd15 { padding: 15px; } +.pd20 { padding: 20px; } +.pd25 { padding: 25px; } +.pd30 { padding: 30px; } +.pd35 { padding: 35px; } +.pd40 { padding: 40px; } +.pd45 { padding: 45px; } +.pd50 { padding: 50px; } +.pd55 { padding: 55px; } +.pd60 { padding: 60px; } +.pd65 { padding: 65px; } +.pd70 { padding: 70px; } +.pd75 { padding: 75px; } +.pd80 { padding: 80px; } +.pd85 { padding: 85px; } +.pd90 { padding: 90px; } +.pd95 { padding: 95px; } +.pd100 { padding: 100px; } +.pd105 { padding: 105px; } +.pd110 { padding: 110px; } +.pd115 { padding: 115px; } +.pd120 { padding: 120px; } +.pd125 { padding: 125px; } +.pd130 { padding: 130px; } +.pd135 { padding: 135px; } +.pd140 { padding: 140px; } +.pd145 { padding: 145px; } +.pd150 { padding: 150px; } +.pd155 { padding: 155px; } +.pd160 { padding: 160px; } +.pd165 { padding: 165px; } +.pd170 { padding: 170px; } +.pd175 { padding: 175px; } +.pd180 { padding: 180px; } +.pd185 { padding: 185px; } +.pd190 { padding: 190px; } +.pd195 { padding: 195px; } +.pd200 { padding: 200px; } + +/* Paddings - Top */ + +.pta { padding-top: auto; } +.pt0 { padding-top: 0; } +.pt5 { padding-top: 5px; } +.pt10 { padding-top:10px; } +.pt15 { padding-top: 15px; } +.pt20 { padding-top: 20px; } +.pt25 { padding-top: 25px; } +.pt30 { padding-top: 30px; } +.pt35 { padding-top: 35px; } +.pt40 { padding-top: 40px; } +.pt45 { padding-top: 45px; } +.pt50 { padding-top: 50px; } +.pt55 { padding-top: 55px; } +.pt60 { padding-top: 60px; } +.pt65 { padding-top: 65px; } +.pt70 { padding-top: 70px; } +.pt75 { padding-top: 75px; } +.pt80 { padding-top: 80px; } +.pt85 { padding-top: 85px; } +.pt90 { padding-top: 90px; } +.pt95 { padding-top: 95px; } +.pt100 { padding-top: 100px; } +.pt105 { padding-top: 105px; } +.pt110 { padding-top: 110px; } +.pt115 { padding-top: 115px; } +.pt120 { padding-top: 120px; } +.pt125 { padding-top: 125px; } +.pt130 { padding-top: 130px; } +.pt135 { padding-top: 135px; } +.pt140 { padding-top: 140px; } +.pt145 { padding-top: 145px; } +.pt150 { padding-top: 150px; } +.pt155 { padding-top: 155px; } +.pt160 { padding-top: 160px; } +.pt165 { padding-top: 165px; } +.pt170 { padding-top: 170px; } +.pt175 { padding-top: 175px; } +.pt180 { padding-top: 180px; } +.pt185 { padding-top: 185px; } +.pt190 { padding-top: 190px; } +.pt195 { padding-top: 195px; } +.pt200 { padding-top: 200px; } + +/* Paddings - Bottom */ + +.pba { padding-bottom: auto; } +.pb0 { padding-bottom: 0 } +.pb5 { padding-bottom: 5px; } +.pb10 { padding-bottom:10px; } +.pb15 { padding-bottom: 15px; } +.pb20 { padding-bottom: 20px; } +.pb25 { padding-bottom: 25px; } +.pb30 { padding-bottom: 30px; } +.pb35 { padding-bottom: 35px; } +.pb40 { padding-bottom: 40px; } +.pb45 { padding-bottom: 45px; } +.pb50 { padding-bottom: 50px; } +.pb55 { padding-bottom: 55px; } +.pb60 { padding-bottom: 60px; } +.pb65 { padding-bottom: 65px; } +.pb70 { padding-bottom: 70px; } +.pb75 { padding-bottom: 75px; } +.pb80 { padding-bottom: 80px; } +.pb85 { padding-bottom: 85px; } +.pb90 { padding-bottom: 90px; } +.pb95 { padding-bottom: 95px; } +.pb100 { padding-bottom: 100px; } +.pb105 { padding-bottom: 105px; } +.pb110 { padding-bottom: 110px; } +.pb115 { padding-bottom: 115px; } +.pb120 { padding-bottom: 120px; } +.pb125 { padding-bottom: 125px; } +.pb130 { padding-bottom: 130px; } +.pb135 { padding-bottom: 135px; } +.pb140 { padding-bottom: 140px; } +.pb145 { padding-bottom: 145px; } +.pb150 { padding-bottom: 150px; } +.pb155 { padding-bottom: 155px; } +.pb160 { padding-bottom: 160px; } +.pb165 { padding-bottom: 165px; } +.pb170 { padding-bottom: 170px; } +.pb175 { padding-bottom: 175px; } +.pb180 { padding-bottom: 180px; } +.pb185 { padding-bottom: 185px; } +.pb190 { padding-bottom: 190px; } +.pb195 { padding-bottom: 195px; } +.pb200 { padding-bottom: 200px; } + +/* Paddings - Left */ + +.pla { padding-left: auto; } +.pl0 { padding-left: 0; } +.pl5 { padding-left: 5px; } +.pl10 { padding-left:10px; } +.pl15 { padding-left: 15px; } +.pl20 { padding-left: 20px; } +.pl25 { padding-left: 25px; } +.pl30 { padding-left: 30px; } +.pl35 { padding-left: 35px; } +.pl40 { padding-left: 40px; } +.pl45 { padding-left: 45px; } +.pl50 { padding-left: 50px; } +.pl55 { padding-left: 55px; } +.pl60 { padding-left: 60px; } +.pl65 { padding-left: 65px; } +.pl70 { padding-left: 70px; } +.pl75 { padding-left: 75px; } +.pl80 { padding-left: 80px; } +.pl85 { padding-left: 85px; } +.pl90 { padding-left: 90px; } +.pl95 { padding-left: 95px; } +.pl100 { padding-left: 100px; } +.pl105 { padding-left: 105px; } +.pl110 { padding-left: 110px; } +.pl115 { padding-left: 115px; } +.pl120 { padding-left: 120px; } +.pl125 { padding-left: 125px; } +.pl130 { padding-left: 130px; } +.pl135 { padding-left: 135px; } +.pl140 { padding-left: 140px; } +.pl145 { padding-left: 145px; } +.pl150 { padding-left: 150px; } +.pl155 { padding-left: 155px; } +.pl160 { padding-left: 160px; } +.pl165 { padding-left: 165px; } +.pl170 { padding-left: 170px; } +.pl175 { padding-left: 175px; } +.pl180 { padding-left: 180px; } +.pl185 { padding-left: 185px; } +.pl190 { padding-left: 190px; } +.pl195 { padding-left: 195px; } +.pl200 { padding-left: 200px; } + +/* Paddings - Right */ + +.pra { padding-right: auto; } +.pr0 { padding-right: 0; } +.pr5 { padding-right: 5px; } +.pr10 { padding-right:10px; } +.pr15 { padding-right: 15px; } +.pr20 { padding-right: 20px; } +.pr25 { padding-right: 25px; } +.pr30 { padding-right: 30px; } +.pr35 { padding-right: 35px; } +.pr40 { padding-right: 40px; } +.pr45 { padding-right: 45px; } +.pr50 { padding-right: 50px; } +.pr55 { padding-right: 55px; } +.pr60 { padding-right: 60px; } +.pr65 { padding-right: 65px; } +.pr70 { padding-right: 70px; } +.pr75 { padding-right: 75px; } +.pr80 { padding-right: 80px; } +.pr85 { padding-right: 85px; } +.pr90 { padding-right: 90px; } +.pr95 { padding-right: 95px; } +.pr100 { padding-right: 100px; } +.pr105 { padding-right: 105px; } +.pr110 { padding-right: 110px; } +.pr115 { padding-right: 115px; } +.pr120 { padding-right: 120px; } +.pr125 { padding-right: 125px; } +.pr130 { padding-right: 130px; } +.pr135 { padding-right: 135px; } +.pr140 { padding-right: 140px; } +.pr145 { padding-right: 145px; } +.pr150 { padding-right: 150px; } +.pr155 { padding-right: 155px; } +.pr160 { padding-right: 160px; } +.pr165 { padding-right: 165px; } +.pr170 { padding-right: 170px; } +.pr175 { padding-right: 175px; } +.pr180 { padding-right: 180px; } +.pr185 { padding-right: 185px; } +.pr190 { padding-right: 190px; } +.pr195 { padding-right: 195px; } +.pr200 { padding-right: 200px; } + +/* Paddings - Top & Bottom */ + +.ptb-a { padding-top: auto; padding-bottom: auto; } +.ptb0 { padding-top: 0; padding-bottom: 0; } +.ptb5 { padding-top: 5px; padding-bottom: 5px; } +.ptb10 { padding-top:10px; padding-bottom: 10px; } +.ptb15 { padding-top: 15px; padding-bottom: 15px; } +.ptb20 { padding-top: 20px; padding-bottom: 20px; } +.ptb25 { padding-top: 25px; padding-bottom: 25px; } +.ptb30 { padding-top: 30px; padding-bottom: 30px; } +.ptb35 { padding-top: 35px; padding-bottom: 35px; } +.ptb40 { padding-top: 40px; padding-bottom: 40px; } +.ptb45 { padding-top: 45px; padding-bottom: 45px; } +.ptb50 { padding-top: 50px; padding-bottom: 50px; } +.ptb55 { padding-top: 55px; padding-bottom: 55px; } +.ptb60 { padding-top: 60px; padding-bottom: 60px; } +.ptb65 { padding-top: 65px; padding-bottom: 65px; } +.ptb70 { padding-top: 70px; padding-bottom: 70px; } +.ptb75 { padding-top: 75px; padding-bottom: 75px; } +.ptb80 { padding-top: 80px; padding-bottom: 80px; } +.ptb85 { padding-top: 85px; padding-bottom: 85px; } +.ptb90 { padding-top: 90px; padding-bottom: 90px; } +.ptb95 { padding-top: 95px; padding-bottom: 95px; } +.ptb100 { padding-top: 100px; padding-bottom: 100px; } +.ptb105 { padding-top: 105px; padding-bottom: 105px; } +.ptb110 { padding-top:110px; padding-bottom: 110px; } +.ptb115 { padding-top: 115px; padding-bottom: 115px; } +.ptb120 { padding-top: 120px; padding-bottom: 120px; } +.ptb125 { padding-top: 125px; padding-bottom: 125px; } +.ptb130 { padding-top: 130px; padding-bottom: 130px; } +.ptb135 { padding-top: 135px; padding-bottom: 135px; } +.ptb140 { padding-top: 140px; padding-bottom: 140px; } +.ptb145 { padding-top: 145px; padding-bottom: 145px; } +.ptb150 { padding-top: 150px; padding-bottom: 150px; } +.ptb155 { padding-top: 155px; padding-bottom: 155px; } +.ptb160 { padding-top: 160px; padding-bottom: 160px; } +.ptb165 { padding-top: 165px; padding-bottom: 165px; } +.ptb170 { padding-top: 170px; padding-bottom: 170px; } +.ptb175 { padding-top: 175px; padding-bottom: 175px; } +.ptb180 { padding-top: 180px; padding-bottom: 180px; } +.ptb185 { padding-top: 185px; padding-bottom: 185px; } +.ptb190 { padding-top: 190px; padding-bottom: 190px; } +.ptb195 { padding-top: 195px; padding-bottom: 195px; } +.ptb200 { padding-top: 200px; padding-bottom: 200px; } + +.plra { padding-left: auto; padding-right: auto; } +.plr0 { padding-left: 0; padding-right: 0; } +.plr5 { padding-left: 5px; padding-right: 5px; } +.plr10 { padding-left: 10px; padding-right:10px; } +.plr15 { padding-left: 15px; padding-right: 15px; } +.plr20 { padding-left: 20px; padding-right: 20px; } +.plr25 { padding-left: 25px; padding-right: 25px; } +.plr30 { padding-left: 30px; padding-right: 30px; } +.plr35 { padding-left: 35px; padding-right: 35px; } +.plr40 { padding-left: 40px; padding-right: 40px; } +.plr45 { padding-left: 45px; padding-right: 45px; } +.plr50 { padding-left: 50px; padding-right: 50px; } +.plr55 { padding-left: 55px; padding-right: 55px; } +.plr60 { padding-left: 60px; padding-right: 60px; } +.plr65 { padding-left: 65px; padding-right: 65px; } +.plr70 { padding-left: 70px; padding-right: 70px; } +.plr75 { padding-left: 75px; padding-right: 75px; } +.plr80 { padding-left: 80px; padding-right: 80px; } +.plr85 { padding-left: 85px; padding-right: 85px; } +.plr90 { padding-left: 90px; padding-right: 90px; } +.plr95 { padding-left: 95px; padding-right: 95px; } +.plr100 { padding-left: 100px; padding-right: 100px; } +.plr105 { padding-left: 105px; padding-right: 105px; } +.plr110 { padding-left:110px; padding-right: 110px; } +.plr115 { padding-left: 115px; padding-right: 115px; } +.plr120 { padding-left: 120px; padding-right: 120px; } +.plr125 { padding-left: 125px; padding-right: 125px; } +.plr130 { padding-left: 130px; padding-right: 130px; } +.plr135 { padding-left: 135px; padding-right: 135px; } +.plr140 { padding-left: 140px; padding-right: 140px; } +.plr145 { padding-left: 145px; padding-right: 145px; } +.plr150 { padding-left: 150px; padding-right: 150px; } +.plr155 { padding-left: 155px; padding-right: 155px; } +.plr160 { padding-left: 160px; padding-right: 160px; } +.plr165 { padding-left: 165px; padding-right: 165px; } +.plr170 { padding-left: 170px; padding-right: 170px; } +.plr175 { padding-left: 175px; padding-right: 175px; } +.plr180 { padding-left: 180px; padding-right: 180px; } +.plr185 { padding-left: 185px; padding-right: 185px; } +.plr190 { padding-left: 190px; padding-right: 190px; } +.plr195 { padding-left: 195px; padding-right: 195px; } +.plr200 { padding-left: 200px; padding-right: 200px; } + +/* TYPOGRAPHY */ + +/* Font Sizes */ + +.fz8 { font-size: 8px; } +.fz10 { font-size: 10px; } +.fz11 { font-size: 11px; } +.fz12 { font-size: 12px; } +.fz13 { font-size: 13px; } +.fz14 { font-size: 14px; } +.fz16 { font-size: 16px; } +.fz18 { font-size: 18px; } +.fz20 { font-size: 20px; } +.fz22 { font-size: 22px; } +.fz24 { font-size: 24px; } +.fz26 { font-size: 26px; } +.fz28 { font-size: 28px; } +.fz30 { font-size: 30px; } +.fz32 { font-size: 32px; } +.fz34 { font-size: 34px; } +.fz36 { font-size: 36px; } +.fz38 { font-size: 38px; } +.fz40 { font-size: 40px; } +.fz42 { font-size: 42px; } +.fz44 { font-size: 44px; } +.fz46 { font-size: 46px; } +.fz48 { font-size: 48px; } +.fz50 { font-size: 50px; } +.fz52 { font-size: 52px; } +.fz54 { font-size: 54px; } +.fz56 { font-size: 56px; } +.fz58 { font-size: 58px; } +.fz60 { font-size: 60px; } + +/* Custom font sizes breakpoints */ + +.f1 { font-size: var(--f1); } +.f2 { font-size: var(--f2); } +.f3 { font-size: var(--f3); } +.f4 { font-size: var(--f4); } +.f5 { font-size: var(--f5); } +.f6 { font-size: var(--f6); } +.f7 { font-size: var(--f7); } +.f8 { font-size: var(--f8); } +.f9 { font-size: var(--f9); } + +/* Typography > Default heading styles */ + +p, h1, h2, h3, h4, h5, h6 { line-height: 1.6; } + +h1, h2, h3 { flex-basis: 100%; } +h1 { font-size: 24px; } +h2 { font-size: 16px; } +h3 { font-size: 14px; } + +h2.block-title, +h3.block-title { + margin-bottom: 15px; +} + +h2.block-title i, +h2.block-title i { + margin-right: 10px; +} + +/* Typography > Text Utils */ + +.text-normal { font-weight: normal; } +.text-light { font-weight: 200; } +.text-medium { font-weight: 500; } +.text-semibold { font-weight: 600; } +.text-bold { font-weight: bold; } +.text-italic { font-style: italic; } +.text-through { text-decoration: line-through; } +.text-small { font-size: 14px; } +.text-center{ text-align:center; } +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-justify { text-align: justify; } + +/* Helpers */ + +.expand { height: 100%; } +.ovh { overflow:hidden; } + +.round{ + border-radius: 50%; + overflow: hidden; +} + +.no-wrap { white-space: nowrap; } +.text-wrap { white-space: normal; } + +.hidden { + position: absolute !important; + top: -9999px !important; + left: -9999px !important; +} + +.visible { + opacity: 1; +} + +.no-select { + -webkit-user-select: none; + user-select: none; +} + +.end:after { + display: block; + content: ''; + flex-basis: 1; +} + +.input-field { position: relative; } + +/* Text Colors */ + +.color-white { color: var(--white); } +.color-red { color: var(--red); } +.color-blue { color: var(--blue); } +.color-green { color: var(--green); } +.color-yellow { color: var(--yellow); } +.color-gray { color: var(--gray); } +.color-black { color: var(--black); } +.color-carbon { color: var(--carbon); } +.color-orange { color: var(--orange); } +.color-navy { color: var(--navy); } +.color-purple { color: var(--purple); } +.color-lime { color: var(--lime); } + +/* Background Colors */ + +.bg-white { background-color: var(--white); } +.bg-red { background-color: var(--red); } +.bg-blue { background-color: var(--blue); } +.bg-green { background-color: var(--green); } +.bg-yellow { background-color: var(--yellow); } +.bg-gray { background-color: var(--gray); } +.bg-black { background-color: var(--black); } +.bg-carbon { background-color: var(--carbon); } +.bg-orange { background-color: var(--orange); } +.bg-navy { background-color: var(--navy); } +.bg-purple { background-color: var(--purple); } +.bg-lime { background-color: var(--lime); } + +/* --------------------- + Custom UI Colors +/* -------------------*/ + +.ui-color-blue { + background-color: var(--blue); + color: var(--white); +} + +.ui-color-blue.transparent { + background-color: transparent; + border: 1px solid var(--blue); + color: var(--blue); +} + +.ui-color-blue:after { + border-top-color: var(--blue); +} + +.ui-color-red { + background-color: var(--red); + color: var(--white); +} + +.ui-color-red.transparent { + background-color: transparent; + border: 1px solid var(--red); + color: var(--red); +} + +.ui-color-red:after { + border-top-color: var(--red); +} + +.ui-color-orange { + background-color: var(--orange); + color: var(--white); +} + +.ui-color-orange.transparent { + background-color: transparent; + border: 1px solid var(--orange); + color: var(--orange); +} + +.ui-color-orange:after { + border-top-color: var(--orange); +} + +.ui-color-green { + background-color: var(--green); + color: var(--white); +} + +.ui-color-green.transparent { + background-color: transparent; + border: 1px solid var(--green); + color: var(--green); +} + +.ui-color-green:after { + border-top-color: var(--green); +} + +.ui-color-yellow { + background-color: var(--yellow); + color: var(--carbon); +} +.ui-color-yellow.transparent { + background-color: transparent; + border: 1px solid var(--yellow); + color: var(--yellow); +} + +.ui-color-yellow:after { + border-top-color: var(--yellow); +} + +.ui-color-lime { + background-color: var(--lime); + color: var(--carbon); +} +.ui-color-lime.transparent { + background-color: transparent; + border: 1px solid var(--lime); + color: var(--lime); +} + +.ui-color-lime:after { + border-top-color: var(--lime); +} + +.ui-color-navy { + background-color: var(--navy); + color: var(--white); +} + +.ui-color-navy.transparent { + background-color: transparent; + border: 1px solid var(--navy); + color: var(--navy); +} + +.ui-color-navy:after { + border-top-color: var(--navy); +} + +.ui-color-purple { + background-color: var(--purple); + color: var(--white); +} + +.ui-color-purple.transparent { + background-color: transparent; + border: 1px solid var(--purple); + color: var(--navy); +} + +.ui-color-purple:after { + border-top-color: var(--purple); +} + +.ui-color-black { + background-color: var(--black); + color: var(--white); +} + +.ui-color-black.transparent { + background-color: transparent; + border: 1px solid var(--black); + color: var(--black); +} + +.ui-color-black:after { + border-top-color: var(--black); +} + +.ui-color-carbon { + background-color: var(--carbon); + color: var(--white); +} + +.ui-color-carbon:after { + border-top-color: var(--carbon); +} + +.ui-color-carbon.transparent { + background-color: transparent; + border: 1px solid var(--carbon); + color: var(--carbon); +} + +.ui-color-gray { + background-color: var(--gray); + color: var(--white); +} + +.ui-color-gray.transparent { + background-color: transparent; + border: 1px solid var(--gray); + color: var(--gray); +} + +.ui-color-gray:after { + border-top-color: var(--gray); +} + +.ui-color-white { + background-color: var(--white); + color: var(--black); +} + +.ui-color-white.transparent { + background-color: transparent; + border: 1px solid var(--white); + color: var(--white); +} + +.ui-color-white:after { + border-top-color: var(--white); +} + +/* Flex Grid - (Medium) */ + +@media only screen and (min-width : 768px) { + + /* Grid Sizes (as 12 cols) */ + + .m1 { width: 8.33333%; } + .m2 { width: 16.66666%; } + .m3 { width: 24.99999%; } + .m4 { width: 33.33333%; } + .m5 { width: 41.66666%; } + .m6 { width: 49.99999%; } + .m7 { width: 58.33333%; } + .m8 { width: 66.66666%; } + .m9 { width: 74.99999%; } + .m10{ width: 83.33333%; } + .m11{ width: 91.66666%; } + .m12 { width: 99.99999%; } + + /* Grid Sizes (as percent) */ + + .m-5 { width: 5%; } + .m-10 { width: 10%; } + .m-15 { width: 15%; } + .m-20 { width: 20%; } + .m-25 { width: 25%; } + .m-30 { width: 30%; } + .m-35 { width: 35%; } + .m-40 { width: 40%; } + .m-45 { width: 45%; } + .m-50 { width: 50%; } + .m-55 { width: 55%; } + .m-65 { width: 65%; } + .m-70 { width: 70%; } + .m-75 { width: 75%; } + .m-80 { width: 80%; } + .m-85 { width: 85%; } + .m-90 { width: 90%; } + .m-95 { width: 90%; } + .m-100{ width: 100%; } + .m-a { width: auto; } + + /* Grid Template Columns */ + + .m-tc1 { grid-template-columns: 1fr; } + .m-tc2 { grid-template-columns: repeat(2, 1fr); } + .m-tc3 { grid-template-columns: repeat(3, 1fr); } + .m-tc4 { grid-template-columns: repeat(4, 1fr); } + .m-tc5 { grid-template-columns: repeat(5, 1fr); } + .m-tc6 { grid-template-columns: repeat(6, 1fr); } + .m-tc7 { grid-template-columns: repeat(7, 1fr); } + .m-tc8 { grid-template-columns: repeat(8, 1fr); } + .m-tc9 { grid-template-columns: repeat(9, 1fr); } + .m-tc10 { grid-template-columns: repeat(10, 1fr); } + .m-tc11 { grid-template-columns: repeat(11, 1fr); } + .m-tc12 { grid-template-columns: repeat(12, 1fr); } + + .m-tc-1 { grid-template-columns: auto; } + .m-tc-2 { grid-template-columns: repeat(2, auto); } + .m-tc-3 { grid-template-columns: repeat(3, auto); } + .m-tc-4 { grid-template-columns: repeat(4, auto); } + .m-tc-5 { grid-template-columns: repeat(5, auto); } + .m-tc-6 { grid-template-columns: repeat(6, auto); } + .m-tc-7 { grid-template-columns: repeat(7, auto); } + .m-tc-8 { grid-template-columns: repeat(8, auto); } + .m-tc-9 { grid-template-columns: repeat(9, auto); } + .m-tc-10 { grid-template-columns: repeat(10, auto); } + .m-tc-11 { grid-template-columns: repeat(11, auto); } + .m-tc-12 { grid-template-columns: repeat(12, auto); } + + /* Grid spanning */ + + .m-no-span { grid-column: auto; } + .m-span2 { grid-column: span 2; } + .m-span3 { grid-column: span 3; } + .m-span4 { grid-column: span 4; } + .m-span5 { grid-column: span 5; } + .m-span6 { grid-column: span 6; } + .m-span7 { grid-column: span 7; } + .m-span8 { grid-column: span 8; } + .m-span9 { grid-column: span 9; } + .m-span10 { grid-column: span 10;} + .m-span11 { grid-column: span 11; } + .m-span12 { grid-column: span 12; } + + /* Grid gap */ + + .m-gap0 { gap: 0 } + .m-gap5 { gap: 5px; } + .m-gap10 { gap:10px; } + .m-gap15 { gap: 15px; } + .m-gap20 { gap: 20px; } + .m-gap25 { gap: 25px; } + .m-gap30 { gap: 30px; } + .m-gap35 { gap: 35px; } + .m-gap40 { gap: 40px; } + .m-gap45 { gap: 45px; } + .m-gap50 { gap: 50px; } + .m-gap55 { gap: 55px; } + .m-gap60 { gap: 60px; } + .m-gap65 { gap: 65px; } + .m-gap70 { gap: 70px; } + .m-gap75 { gap: 75px; } + .m-gap80 { gap: 80px; } + .m-gap85 { gap: 85px; } + .m-gap90 { gap: 90px; } + .m-gap95 { gap: 95px; } + .m-gap100 { gap: 100px; } + .m-gap105 { gap: 105px; } + .m-gap110 { gap: 110px; } + .m-gap115 { gap: 115px; } + .m-gap120 { gap: 120px; } + .m-gap125 { gap: 125px; } + .m-gap130 { gap: 130px; } + .m-gap135 { gap: 135px; } + .m-gap140 { gap: 140px; } + .m-gap145 { gap: 145px; } + .m-gap150 { gap: 150px; } + .m-gap155 { gap: 155px; } + .m-gap160 { gap: 160px; } + .m-gap165 { gap: 165px; } + .m-gap170 { gap: 170px; } + .m-gap175 { gap: 175px; } + .m-gap180 { gap: 180px; } + .m-gap185 { gap: 185px; } + .m-gap190 { gap: 190px; } + .m-gap195 { gap: 195px; } + .m-gap200 { gap: 200px; } + + .m-x-gap0 { column-gap: 0 } + .m-x-gap5 { column-gap: 5px; } + .m-x-gap10 { column-gap:10px; } + .m-x-gap15 { column-gap: 15px; } + .m-x-gap20 { column-gap: 20px; } + .m-x-gap25 { column-gap: 25px; } + .m-x-gap30 { column-gap: 30px; } + .m-x-gap35 { column-gap: 35px; } + .m-x-gap40 { column-gap: 40px; } + .m-x-gap45 { column-gap: 45px; } + .m-x-gap50 { column-gap: 50px; } + .m-x-gap55 { column-gap: 55px; } + .m-x-gap60 { column-gap: 60px; } + .m-x-gap65 { column-gap: 65px; } + .m-x-gap70 { column-gap: 70px; } + .m-x-gap75 { column-gap: 75px; } + .m-x-gap80 { column-gap: 80px; } + .m-x-gap85 { column-gap: 85px; } + .m-x-gap90 { column-gap: 90px; } + .m-x-gap95 { column-gap: 95px; } + .m-x-gap100 { column-gap: 100px; } + .m-x-gap105 { column-gap: 105px; } + .m-x-gap110 { column-gap: 110px; } + .m-x-gap115 { column-gap: 115px; } + .m-x-gap120 { column-gap: 120px; } + .m-x-gap125 { column-gap: 125px; } + .m-x-gap130 { column-gap: 130px; } + .m-x-gap135 { column-gap: 135px; } + .m-x-gap140 { column-gap: 140px; } + .m-x-gap145 { column-gap: 145px; } + .m-x-gap150 { column-gap: 150px; } + .m-x-gap155 { column-gap: 155px; } + .m-x-gap160 { column-gap: 160px; } + .m-x-gap165 { column-gap: 165px; } + .m-x-gap170 { column-gap: 170px; } + .m-x-gap175 { column-gap: 175px; } + .m-x-gap180 { column-gap: 180px; } + .m-x-gap185 { column-gap: 185px; } + .m-x-gap190 { column-gap: 190px; } + .m-x-gap195 { column-gap: 195px; } + .m-x-gap200 { column-gap: 200px; } + + .m-y-gap0 { row-gap: 0 } + .m-y-gap5 { row-gap: 5px; } + .m-y-gap10 { row-gap:10px; } + .m-y-gap15 { row-gap: 15px; } + .m-y-gap20 { row-gap: 20px; } + .m-y-gap25 { row-gap: 25px; } + .m-y-gap30 { row-gap: 30px; } + .m-y-gap35 { row-gap: 35px; } + .m-y-gap40 { row-gap: 40px; } + .m-y-gap45 { row-gap: 45px; } + .m-y-gap50 { row-gap: 50px; } + .m-y-gap55 { row-gap: 55px; } + .m-y-gap60 { row-gap: 60px; } + .m-y-gap65 { row-gap: 65px; } + .m-y-gap70 { row-gap: 70px; } + .m-y-gap75 { row-gap: 75px; } + .m-y-gap80 { row-gap: 80px; } + .m-y-gap85 { row-gap: 85px; } + .m-y-gap90 { row-gap: 90px; } + .m-y-gap95 { row-gap: 95px; } + .m-y-gap100 { row-gap: 100px; } + .m-y-gap105 { row-gap: 105px; } + .m-y-gap110 { row-gap: 110px; } + .m-y-gap115 { row-gap: 115px; } + .m-y-gap120 { row-gap: 120px; } + .m-y-gap125 { row-gap: 125px; } + .m-y-gap130 { row-gap: 130px; } + .m-y-gap135 { row-gap: 135px; } + .m-y-gap140 { row-gap: 140px; } + .m-y-gap145 { row-gap: 145px; } + .m-y-gap150 { row-gap: 150px; } + .m-y-gap155 { row-gap: 155px; } + .m-y-gap160 { row-gap: 160px; } + .m-y-gap165 { row-gap: 165px; } + .m-y-gap170 { row-gap: 170px; } + .m-y-gap175 { row-gap: 175px; } + .m-y-gap180 { row-gap: 180px; } + .m-y-gap185 { row-gap: 185px; } + .m-y-gap190 { row-gap: 190px; } + .m-y-gap195 { row-gap: 195px; } + .m-y-gap200 { row-gap: 200px; } + + /* Box display */ + + .m-hide { display:none !important; } + .m-block { display: block !important; } + .m-flex { display: flex !important; align-self: auto; } + .m-grid { display: grid; } + .m-inline-grid { display: inline-grid; } + + /* Flex / Grid Align */ + + .m-x-start { justify-content: start; } + .m-x-end { justify-content: end; } + .m-x-center { justify-content: center; } + .m-x-between { justify-content: space-between; } + .m-x-around { justify-content: space-around; } + .m-x-evenly { justify-content: space-evenly; } + + .m-g-start { justify-items: start; } + .m-g-end { justify-items: end; } + .m-g-center { justify-items: center; } + .m-g-stretch { justify-items: stretch; } + + .m-v-normal { align-items: normal; } + .m-v-start { align-items: start; } + .m-v-end { align-items: end; } + .m-v-center { align-items: center; } + .m-v-stretch { align-items: stretch; } + .m-v-baseline { align-items: baseline; } + + .m-y-start { align-content: start; } + .m-y-end { align-content: end; } + .m-y-center { align-content: center; } + .m-y-stretch { align-content: stretch; } + .m-y-between { align-content: space-between; } + .m-y-around { align-content: space-around; } + + .m-start { align-self: start; } + .m-end { align-self: end; } + .m-center { align-self: center; } + .m-baseline { align-self: baseline; } + .m-stretch { align-self: stretch; } + + /* Margins - General */ + + .m-mga { margin: auto; } + .m-mg0 { margin: 0 } + .m-mg5 { margin: 5px; } + .m-mg10 { margin:10px; } + .m-mg15 { margin: 15px; } + .m-mg20 { margin: 20px; } + .m-mg25 { margin: 25px; } + .m-mg30 { margin: 30px; } + .m-mg35 { margin: 35px; } + .m-mg40 { margin: 40px; } + .m-mg45 { margin: 45px; } + .m-mg50 { margin: 50px; } + .m-mg55 { margin: 55px; } + .m-mg60 { margin: 60px; } + .m-mg65 { margin: 65px; } + .m-mg70 { margin: 70px; } + .m-mg75 { margin: 75px; } + .m-mg80 { margin: 80px; } + .m-mg85 { margin: 85px; } + .m-mg90 { margin: 90px; } + .m-mg95 { margin: 95px; } + .m-mg100 { margin: 100px; } + .m-mg105 { margin: 105px; } + .m-mg110 { margin: 110px; } + .m-mg115 { margin: 115px; } + .m-mg120 { margin: 120px; } + .m-mg125 { margin: 125px; } + .m-mg130 { margin: 130px; } + .m-mg135 { margin: 135px; } + .m-mg140 { margin: 140px; } + .m-mg145 { margin: 145px; } + .m-mg150 { margin: 150px; } + .m-mg155 { margin: 155px; } + .m-mg160 { margin: 160px; } + .m-mg165 { margin: 165px; } + .m-mg170 { margin: 170px; } + .m-mg175 { margin: 175px; } + .m-mg180 { margin: 180px; } + .m-mg185 { margin: 185px; } + .m-mg190 { margin: 190px; } + .m-mg195 { margin: 195px; } + .m-mg200 { margin: 200px; } + + /* Margins - Top */ + + .m-mta { margin-top: auto; } + .m-mt0 { margin-top: 0; } + .m-mt5 { margin-top: 5px; } + .m-mt10 { margin-top:10px; } + .m-mt15 { margin-top: 15px; } + .m-mt20 { margin-top: 20px; } + .m-mt25 { margin-top: 25px; } + .m-mt30 { margin-top: 30px; } + .m-mt35 { margin-top: 35px; } + .m-mt40 { margin-top: 40px; } + .m-mt45 { margin-top: 45px; } + .m-mt50 { margin-top: 50px; } + .m-mt55 { margin-top: 55px; } + .m-mt60 { margin-top: 60px; } + .m-mt65 { margin-top: 65px; } + .m-mt70 { margin-top: 70px; } + .m-mt75 { margin-top: 75px; } + .m-mt80 { margin-top: 80px; } + .m-mt85 { margin-top: 85px; } + .m-mt90 { margin-top: 90px; } + .m-mt95 { margin-top: 95px; } + .m-mt100 { margin-top: 100px; } + .m-mt105 { margin-top: 105px; } + .m-mt110 { margin-top: 110px; } + .m-mt115 { margin-top: 115px; } + .m-mt120 { margin-top: 120px; } + .m-mt125 { margin-top: 125px; } + .m-mt130 { margin-top: 130px; } + .m-mt135 { margin-top: 135px; } + .m-mt140 { margin-top: 140px; } + .m-mt145 { margin-top: 145px; } + .m-mt150 { margin-top: 150px; } + .m-mt155 { margin-top: 155px; } + .m-mt160 { margin-top: 160px; } + .m-mt165 { margin-top: 165px; } + .m-mt170 { margin-top: 170px; } + .m-mt175 { margin-top: 175px; } + .m-mt180 { margin-top: 180px; } + .m-mt185 { margin-top: 185px; } + .m-mt190 { margin-top: 190px; } + .m-mt195 { margin-top: 195px; } + .m-mt200 { margin-top: 200px; } + + /* Negative Margins - Top */ + + .m-mt-5 { margin-top: -5px; } + .m-mt-10 { margin-top: -10px; } + .m-mt-15 { margin-top: -15px; } + .m-mt-20 { margin-top: -20px; } + .m-mt-25 { margin-top: -25px; } + .m-mt-30 { margin-top: -30px; } + .m-mt-35 { margin-top: -35px; } + .m-mt-40 { margin-top: -40px; } + .m-mt-45 { margin-top: -45px; } + .m-mt-50 { margin-top: -50px; } + .m-mt-55 { margin-top: -55px; } + .m-mt-60 { margin-top: -60px; } + .m-mt-65 { margin-top: -65px; } + .m-mt-70 { margin-top: -70px; } + .m-mt-75 { margin-top: -75px; } + .m-mt-80 { margin-top: -80px; } + .m-mt-85 { margin-top: -85px; } + .m-mt-90 { margin-top: -90px; } + .m-mt-95 { margin-top: -95px; } + .m-mt-100 { margin-top: -100px; } + .m-mt-105 { margin-top: -105px; } + .m-mt-110 { margin-top: -110px; } + .m-mt-115 { margin-top: -115px; } + .m-mt-120 { margin-top: -120px; } + .m-mt-125 { margin-top: -125px; } + .m-mt-130 { margin-top: -130px; } + .m-mt-135 { margin-top: -135px; } + .m-mt-140 { margin-top: -140px; } + .m-mt-145 { margin-top: -145px; } + .m-mt-150 { margin-top: -150px; } + .m-mt-155 { margin-top: -155px; } + .m-mt-160 { margin-top: -160px; } + .m-mt-165 { margin-top: -165px; } + .m-mt-170 { margin-top: -170px; } + .m-mt-175 { margin-top: -175px; } + .m-mt-180 { margin-top: -180px; } + .m-mt-185 { margin-top: -185px; } + .m-mt-190 { margin-top: -190px; } + .m-mt-195 { margin-top: -195px; } + .m-mt-200 { margin-top: -200px; } + + /* Margins - Bottom */ + + .m-mba { margin-bottom: auto; } + .m-mb0 { margin-bottom: 0 } + .m-mb5 { margin-bottom: 5px; } + .m-mb10 { margin-bottom:10px; } + .m-mb15 { margin-bottom: 15px; } + .m-mb20 { margin-bottom: 20px; } + .m-mb25 { margin-bottom: 25px; } + .m-mb30 { margin-bottom: 30px; } + .m-mb35 { margin-bottom: 35px; } + .m-mb40 { margin-bottom: 40px; } + .m-mb45 { margin-bottom: 45px; } + .m-mb50 { margin-bottom: 50px; } + .m-mb55 { margin-bottom: 55px; } + .m-mb60 { margin-bottom: 60px; } + .m-mb65 { margin-bottom: 65px; } + .m-mb70 { margin-bottom: 70px; } + .m-mb75 { margin-bottom: 75px; } + .m-mb80 { margin-bottom: 80px; } + .m-mb85 { margin-bottom: 85px; } + .m-mb90 { margin-bottom: 90px; } + .m-mb95 { margin-bottom: 95px; } + .m-mb100 { margin-bottom: 100px; } + .m-mb105 { margin-bottom: 105px; } + .m-mb110 { margin-bottom: 110px; } + .m-mb115 { margin-bottom: 115px; } + .m-mb120 { margin-bottom: 120px; } + .m-mb125 { margin-bottom: 125px; } + .m-mb130 { margin-bottom: 130px; } + .m-mb135 { margin-bottom: 135px; } + .m-mb140 { margin-bottom: 140px; } + .m-mb145 { margin-bottom: 145px; } + .m-mb150 { margin-bottom: 150px; } + .m-mb155 { margin-bottom: 155px; } + .m-mb160 { margin-bottom: 160px; } + .m-mb165 { margin-bottom: 165px; } + .m-mb170 { margin-bottom: 170px; } + .m-mb175 { margin-bottom: 175px; } + .m-mb180 { margin-bottom: 180px; } + .m-mb185 { margin-bottom: 185px; } + .m-mb190 { margin-bottom: 190px; } + .m-mb195 { margin-bottom: 195px; } + .m-mb200 { margin-bottom: 200px; } + + /* Margins - Left */ + + .m-mla { margin-left: auto; } + .m-ml0 { margin-left: 0; } + .m-ml5 { margin-left: 5px; } + .m-ml10 { margin-left:10px; } + .m-ml15 { margin-left: 15px; } + .m-ml20 { margin-left: 20px; } + .m-ml25 { margin-left: 25px; } + .m-ml30 { margin-left: 30px; } + .m-ml35 { margin-left: 35px; } + .m-ml40 { margin-left: 40px; } + .m-ml45 { margin-left: 45px; } + .m-ml50 { margin-left: 50px; } + .m-ml55 { margin-left: 55px; } + .m-ml60 { margin-left: 60px; } + .m-ml65 { margin-left: 65px; } + .m-ml70 { margin-left: 70px; } + .m-ml75 { margin-left: 75px; } + .m-ml80 { margin-left: 80px; } + .m-ml85 { margin-left: 85px; } + .m-ml90 { margin-left: 90px; } + .m-ml95 { margin-left: 95px; } + .m-ml100 { margin-left: 100px; } + .m-ml105 { margin-left: 105px; } + .m-ml110 { margin-left: 110px; } + .m-ml115 { margin-left: 115px; } + .m-ml120 { margin-left: 120px; } + .m-ml125 { margin-left: 125px; } + .m-ml130 { margin-left: 130px; } + .m-ml135 { margin-left: 135px; } + .m-ml140 { margin-left: 140px; } + .m-ml145 { margin-left: 145px; } + .m-ml150 { margin-left: 150px; } + .m-ml155 { margin-left: 155px; } + .m-ml160 { margin-left: 160px; } + .m-ml165 { margin-left: 165px; } + .m-ml170 { margin-left: 170px; } + .m-ml175 { margin-left: 175px; } + .m-ml180 { margin-left: 180px; } + .m-ml185 { margin-left: 185px; } + .m-ml190 { margin-left: 190px; } + .m-ml195 { margin-left: 195px; } + .m-ml200 { margin-left: 200px; } + + /* Margins - Right */ + + .m-mra { margin-right: auto; } + .m-mr0 { margin-right: 0; } + .m-mr5 { margin-right: 5px; } + .m-mr10 { margin-right:10px; } + .m-mr15 { margin-right: 15px; } + .m-mr20 { margin-right: 20px; } + .m-mr25 { margin-right: 25px; } + .m-mr30 { margin-right: 30px; } + .m-mr35 { margin-right: 35px; } + .m-mr40 { margin-right: 40px; } + .m-mr45 { margin-right: 45px; } + .m-mr50 { margin-right: 50px; } + .m-mr55 { margin-right: 55px; } + .m-mr60 { margin-right: 60px; } + .m-mr65 { margin-right: 65px; } + .m-mr70 { margin-right: 70px; } + .m-mr75 { margin-right: 75px; } + .m-mr80 { margin-right: 80px; } + .m-mr85 { margin-right: 85px; } + .m-mr90 { margin-right: 90px; } + .m-mr95 { margin-right: 95px; } + .m-mr100 { margin-right: 100px; } + .m-mr105 { margin-right: 105px; } + .m-mr110 { margin-right: 110px; } + .m-mr115 { margin-right: 115px; } + .m-mr120 { margin-right: 120px; } + .m-mr125 { margin-right: 125px; } + .m-mr130 { margin-right: 130px; } + .m-mr135 { margin-right: 135px; } + .m-mr140 { margin-right: 140px; } + .m-mr145 { margin-right: 145px; } + .m-mr150 { margin-right: 150px; } + .m-mr155 { margin-right: 155px; } + .m-mr160 { margin-right: 160px; } + .m-mr165 { margin-right: 165px; } + .m-mr170 { margin-right: 170px; } + .m-mr175 { margin-right: 175px; } + .m-mr180 { margin-right: 180px; } + .m-mr185 { margin-right: 185px; } + .m-mr190 { margin-right: 190px; } + .m-mr195 { margin-right: 195px; } + .m-mr200 { margin-right: 200px; } + + /* Margins - Top & Bottom */ + + .m-mtba { margin-top: auto; margin-bottom: auto; } + .m-mtb0 { margin-top: 0; margin-bottom: 0; } + .m-mtb5 { margin-top: 5px; margin-bottom: 5px; } + .m-mtb10 { margin-top:10px; margin-bottom: 10px; } + .m-mtb15 { margin-top: 15px; margin-bottom: 15px; } + .m-mtb20 { margin-top: 20px; margin-bottom: 20px; } + .m-mtb25 { margin-top: 25px; margin-bottom: 25px; } + .m-mtb30 { margin-top: 30px; margin-bottom: 30px; } + .m-mtb35 { margin-top: 35px; margin-bottom: 35px; } + .m-mtb40 { margin-top: 40px; margin-bottom: 40px; } + .m-mtb45 { margin-top: 45px; margin-bottom: 45px; } + .m-mtb50 { margin-top: 50px; margin-bottom: 50px; } + .m-mtb55 { margin-top: 55px; margin-bottom: 55px; } + .m-mtb60 { margin-top: 60px; margin-bottom: 60px; } + .m-mtb65 { margin-top: 65px; margin-bottom: 65px; } + .m-mtb70 { margin-top: 70px; margin-bottom: 70px; } + .m-mtb75 { margin-top: 75px; margin-bottom: 75px; } + .m-mtb80 { margin-top: 80px; margin-bottom: 80px; } + .m-mtb85 { margin-top: 85px; margin-bottom: 85px; } + .m-mtb90 { margin-top: 90px; margin-bottom: 90px; } + .m-mtb95 { margin-top: 95px; margin-bottom: 95px; } + .m-mtb100 { margin-top: 100px; margin-bottom: 100px; } + .m-mtb105 { margin-top: 105px; margin-bottom: 105px; } + .m-mtb110 { margin-top:110px; margin-bottom: 110px; } + .m-mtb115 { margin-top: 115px; margin-bottom: 115px; } + .m-mtb120 { margin-top: 120px; margin-bottom: 120px; } + .m-mtb125 { margin-top: 125px; margin-bottom: 125px; } + .m-mtb130 { margin-top: 130px; margin-bottom: 130px; } + .m-mtb135 { margin-top: 135px; margin-bottom: 135px; } + .m-mtb140 { margin-top: 140px; margin-bottom: 140px; } + .m-mtb145 { margin-top: 145px; margin-bottom: 145px; } + .m-mtb150 { margin-top: 150px; margin-bottom: 150px; } + .m-mtb155 { margin-top: 155px; margin-bottom: 155px; } + .m-mtb160 { margin-top: 160px; margin-bottom: 160px; } + .m-mtb165 { margin-top: 165px; margin-bottom: 165px; } + .m-mtb170 { margin-top: 170px; margin-bottom: 170px; } + .m-mtb175 { margin-top: 175px; margin-bottom: 175px; } + .m-mtb180 { margin-top: 180px; margin-bottom: 180px; } + .m-mtb185 { margin-top: 185px; margin-bottom: 185px; } + .m-mtb190 { margin-top: 190px; margin-bottom: 190px; } + .m-mtb195 { margin-top: 195px; margin-bottom: 195px; } + .m-mtb200 { margin-top: 200px; margin-bottom: 200px; } + + .m-centered { margin-left: auto; margin-right: auto; } + .m-filled { margin-left: 0; margin-right: 0; } + + .m-mlr5 { margin-left: 5px; margin-right: 5px; } + .m-mlr10 { margin-left: 10px; margin-right:10px; } + .m-mlr15 { margin-left: 15px; margin-right: 15px; } + .m-mlr20 { margin-left: 20px; margin-right: 20px; } + .m-mlr25 { margin-left: 25px; margin-right: 25px; } + .m-mlr30 { margin-left: 30px; margin-right: 30px; } + .m-mlr35 { margin-left: 35px; margin-right: 35px; } + .m-mlr40 { margin-left: 40px; margin-right: 40px; } + .m-mlr45 { margin-left: 45px; margin-right: 45px; } + .m-mlr50 { margin-left: 50px; margin-right: 50px; } + .m-mlr55 { margin-left: 55px; margin-right: 55px; } + .m-mlr60 { margin-left: 60px; margin-right: 60px; } + .m-mlr65 { margin-left: 65px; margin-right: 65px; } + .m-mlr70 { margin-left: 70px; margin-right: 70px; } + .m-mlr75 { margin-left: 75px; margin-right: 75px; } + .m-mlr80 { margin-left: 80px; margin-right: 80px; } + .m-mlr85 { margin-left: 85px; margin-right: 85px; } + .m-mlr90 { margin-left: 90px; margin-right: 90px; } + .m-mlr95 { margin-left: 95px; margin-right: 95px; } + .m-mlr100 { margin-left: 100px; margin-right: 100px; } + .m-mlr105 { margin-left: 105px; margin-right: 105px; } + .m-mlr110 { margin-left:110px; margin-right: 110px; } + .m-mlr115 { margin-left: 115px; margin-right: 115px; } + .m-mlr120 { margin-left: 120px; margin-right: 120px; } + .m-mlr125 { margin-left: 125px; margin-right: 125px; } + .m-mlr130 { margin-left: 130px; margin-right: 130px; } + .m-mlr135 { margin-left: 135px; margin-right: 135px; } + .m-mlr140 { margin-left: 140px; margin-right: 140px; } + .m-mlr145 { margin-left: 145px; margin-right: 145px; } + .m-mlr150 { margin-left: 150px; margin-right: 150px; } + .m-mlr155 { margin-left: 155px; margin-right: 155px; } + .m-mlr160 { margin-left: 160px; margin-right: 160px; } + .m-mlr165 { margin-left: 165px; margin-right: 165px; } + .m-mlr170 { margin-left: 170px; margin-right: 170px; } + .m-mlr175 { margin-left: 175px; margin-right: 175px; } + .m-mlr180 { margin-left: 180px; margin-right: 180px; } + .m-mlr185 { margin-left: 185px; margin-right: 185px; } + .m-mlr190 { margin-left: 190px; margin-right: 190px; } + .m-mlr195 { margin-left: 195px; margin-right: 195px; } + .m-mlr200 { margin-left: 200px; margin-right: 200px; } + + /* Paddings */ + + .m-pda {padding: auto; } + .m-pd0 { padding: 0 } + .m-pd5 { padding: 5px; } + .m-pd10 { padding:10px; } + .m-pd15 { padding: 15px; } + .m-pd20 { padding: 20px; } + .m-pd25 { padding: 25px; } + .m-pd30 { padding: 30px; } + .m-pd35 { padding: 35px; } + .m-pd40 { padding: 40px; } + .m-pd45 { padding: 45px; } + .m-pd50 { padding: 50px; } + .m-pd55 { padding: 55px; } + .m-pd60 { padding: 60px; } + .m-pd65 { padding: 65px; } + .m-pd70 { padding: 70px; } + .m-pd75 { padding: 75px; } + .m-pd80 { padding: 80px; } + .m-pd85 { padding: 85px; } + .m-pd90 { padding: 90px; } + .m-pd95 { padding: 95px; } + .m-pd100 { padding: 100px; } + .m-pd105 { padding: 105px; } + .m-pd110 { padding: 110px; } + .m-pd115 { padding: 115px; } + .m-pd120 { padding: 120px; } + .m-pd125 { padding: 125px; } + .m-pd130 { padding: 130px; } + .m-pd135 { padding: 135px; } + .m-pd140 { padding: 140px; } + .m-pd145 { padding: 145px; } + .m-pd150 { padding: 150px; } + .m-pd155 { padding: 155px; } + .m-pd160 { padding: 160px; } + .m-pd165 { padding: 165px; } + .m-pd170 { padding: 170px; } + .m-pd175 { padding: 175px; } + .m-pd180 { padding: 180px; } + .m-pd185 { padding: 185px; } + .m-pd190 { padding: 190px; } + .m-pd195 { padding: 195px; } + .m-pd200 { padding: 200px; } + + /* Paddings - Top */ + + .m-pta { padding-top: auto; } + .m-pt0 { padding-top: 0; } + .m-pt5 { padding-top: 5px; } + .m-pt10 { padding-top:10px; } + .m-pt15 { padding-top: 15px; } + .m-pt20 { padding-top: 20px; } + .m-pt25 { padding-top: 25px; } + .m-pt30 { padding-top: 30px; } + .m-pt35 { padding-top: 35px; } + .m-pt40 { padding-top: 40px; } + .m-pt45 { padding-top: 45px; } + .m-pt50 { padding-top: 50px; } + .m-pt55 { padding-top: 55px; } + .m-pt60 { padding-top: 60px; } + .m-pt65 { padding-top: 65px; } + .m-pt70 { padding-top: 70px; } + .m-pt75 { padding-top: 75px; } + .m-pt80 { padding-top: 80px; } + .m-pt85 { padding-top: 85px; } + .m-pt90 { padding-top: 90px; } + .m-pt95 { padding-top: 95px; } + .m-pt100 { padding-top: 100px; } + .m-pt105 { padding-top: 105px; } + .m-pt110 { padding-top: 110px; } + .m-pt115 { padding-top: 115px; } + .m-pt120 { padding-top: 120px; } + .m-pt125 { padding-top: 125px; } + .m-pt130 { padding-top: 130px; } + .m-pt135 { padding-top: 135px; } + .m-pt140 { padding-top: 140px; } + .m-pt145 { padding-top: 145px; } + .m-pt150 { padding-top: 150px; } + .m-pt155 { padding-top: 155px; } + .m-pt160 { padding-top: 160px; } + .m-pt165 { padding-top: 165px; } + .m-pt170 { padding-top: 170px; } + .m-pt175 { padding-top: 175px; } + .m-pt180 { padding-top: 180px; } + .m-pt185 { padding-top: 185px; } + .m-pt190 { padding-top: 190px; } + .m-pt195 { padding-top: 195px; } + .m-pt200 { padding-top: 200px; } + + /* Paddings - Bottom */ + + .m-pba { padding-bottom: auto; } + .m-pb0 { padding-bottom: 0 } + .m-pb5 { padding-bottom: 5px; } + .m-pb10 { padding-bottom:10px; } + .m-pb15 { padding-bottom: 15px; } + .m-pb20 { padding-bottom: 20px; } + .m-pb25 { padding-bottom: 25px; } + .m-pb30 { padding-bottom: 30px; } + .m-pb35 { padding-bottom: 35px; } + .m-pb40 { padding-bottom: 40px; } + .m-pb45 { padding-bottom: 45px; } + .m-pb50 { padding-bottom: 50px; } + .m-pb55 { padding-bottom: 55px; } + .m-pb60 { padding-bottom: 60px; } + .m-pb65 { padding-bottom: 65px; } + .m-pb70 { padding-bottom: 70px; } + .m-pb75 { padding-bottom: 75px; } + .m-pb80 { padding-bottom: 80px; } + .m-pb85 { padding-bottom: 85px; } + .m-pb90 { padding-bottom: 90px; } + .m-pb95 { padding-bottom: 95px; } + .m-pb100 { padding-bottom: 100px; } + .m-pb105 { padding-bottom: 105px; } + .m-pb110 { padding-bottom: 110px; } + .m-pb115 { padding-bottom: 115px; } + .m-pb120 { padding-bottom: 120px; } + .m-pb125 { padding-bottom: 125px; } + .m-pb130 { padding-bottom: 130px; } + .m-pb135 { padding-bottom: 135px; } + .m-pb140 { padding-bottom: 140px; } + .m-pb145 { padding-bottom: 145px; } + .m-pb150 { padding-bottom: 150px; } + .m-pb155 { padding-bottom: 155px; } + .m-pb160 { padding-bottom: 160px; } + .m-pb165 { padding-bottom: 165px; } + .m-pb170 { padding-bottom: 170px; } + .m-pb175 { padding-bottom: 175px; } + .m-pb180 { padding-bottom: 180px; } + .m-pb185 { padding-bottom: 185px; } + .m-pb190 { padding-bottom: 190px; } + .m-pb195 { padding-bottom: 195px; } + .m-pb200 { padding-bottom: 200px; } + + /* Paddings - Left */ + + .m-pla { padding-left: auto; } + .m-pl0 { padding-left: 0; } + .m-pl5 { padding-left: 5px; } + .m-pl10 { padding-left:10px; } + .m-pl15 { padding-left: 15px; } + .m-pl20 { padding-left: 20px; } + .m-pl25 { padding-left: 25px; } + .m-pl30 { padding-left: 30px; } + .m-pl35 { padding-left: 35px; } + .m-pl40 { padding-left: 40px; } + .m-pl45 { padding-left: 45px; } + .m-pl50 { padding-left: 50px; } + .m-pl55 { padding-left: 55px; } + .m-pl60 { padding-left: 60px; } + .m-pl65 { padding-left: 65px; } + .m-pl70 { padding-left: 70px; } + .m-pl75 { padding-left: 75px; } + .m-pl80 { padding-left: 80px; } + .m-pl85 { padding-left: 85px; } + .m-pl90 { padding-left: 90px; } + .m-pl95 { padding-left: 95px; } + .m-pl100 { padding-left: 100px; } + .m-pl105 { padding-left: 105px; } + .m-pl110 { padding-left: 110px; } + .m-pl115 { padding-left: 115px; } + .m-pl120 { padding-left: 120px; } + .m-pl125 { padding-left: 125px; } + .m-pl130 { padding-left: 130px; } + .m-pl135 { padding-left: 135px; } + .m-pl140 { padding-left: 140px; } + .m-pl145 { padding-left: 145px; } + .m-pl150 { padding-left: 150px; } + .m-pl155 { padding-left: 155px; } + .m-pl160 { padding-left: 160px; } + .m-pl165 { padding-left: 165px; } + .m-pl170 { padding-left: 170px; } + .m-pl175 { padding-left: 175px; } + .m-pl180 { padding-left: 180px; } + .m-pl185 { padding-left: 185px; } + .m-pl190 { padding-left: 190px; } + .m-pl195 { padding-left: 195px; } + .m-pl200 { padding-left: 200px; } + + /* Paddings - Right */ + + .m-pra { padding-right: auto; } + .m-pr0 { padding-right: 0; } + .m-pr5 { padding-right: 5px; } + .m-pr10 { padding-right:10px; } + .m-pr15 { padding-right: 15px; } + .m-pr20 { padding-right: 20px; } + .m-pr25 { padding-right: 25px; } + .m-pr30 { padding-right: 30px; } + .m-pr35 { padding-right: 35px; } + .m-pr40 { padding-right: 40px; } + .m-pr45 { padding-right: 45px; } + .m-pr50 { padding-right: 50px; } + .m-pr55 { padding-right: 55px; } + .m-pr60 { padding-right: 60px; } + .m-pr65 { padding-right: 65px; } + .m-pr70 { padding-right: 70px; } + .m-pr75 { padding-right: 75px; } + .m-pr80 { padding-right: 80px; } + .m-pr85 { padding-right: 85px; } + .m-pr90 { padding-right: 90px; } + .m-pr95 { padding-right: 95px; } + .m-pr100 { padding-right: 100px; } + .m-pr105 { padding-right: 105px; } + .m-pr110 { padding-right: 110px; } + .m-pr115 { padding-right: 115px; } + .m-pr120 { padding-right: 120px; } + .m-pr125 { padding-right: 125px; } + .m-pr130 { padding-right: 130px; } + .m-pr135 { padding-right: 135px; } + .m-pr140 { padding-right: 140px; } + .m-pr145 { padding-right: 145px; } + .m-pr150 { padding-right: 150px; } + .m-pr155 { padding-right: 155px; } + .m-pr160 { padding-right: 160px; } + .m-pr165 { padding-right: 165px; } + .m-pr170 { padding-right: 170px; } + .m-pr175 { padding-right: 175px; } + .m-pr180 { padding-right: 180px; } + .m-pr185 { padding-right: 185px; } + .m-pr190 { padding-right: 190px; } + .m-pr195 { padding-right: 195px; } + .m-pr200 { padding-right: 200px; } + + /* Paddings - Top & Bottom */ + + .m-ptb-a { padding-top: auto; padding-bottom: auto; } + .m-ptb0 { padding-top: 0; padding-bottom: 0; } + .m-ptb5 { padding-top: 5px; padding-bottom: 5px; } + .m-ptb10 { padding-top:10px; padding-bottom: 10px; } + .m-ptb15 { padding-top: 15px; padding-bottom: 15px; } + .m-ptb20 { padding-top: 20px; padding-bottom: 20px; } + .m-ptb25 { padding-top: 25px; padding-bottom: 25px; } + .m-ptb30 { padding-top: 30px; padding-bottom: 30px; } + .m-ptb35 { padding-top: 35px; padding-bottom: 35px; } + .m-ptb40 { padding-top: 40px; padding-bottom: 40px; } + .m-ptb45 { padding-top: 45px; padding-bottom: 45px; } + .m-ptb50 { padding-top: 50px; padding-bottom: 50px; } + .m-ptb55 { padding-top: 55px; padding-bottom: 55px; } + .m-ptb60 { padding-top: 60px; padding-bottom: 60px; } + .m-ptb65 { padding-top: 65px; padding-bottom: 65px; } + .m-ptb70 { padding-top: 70px; padding-bottom: 70px; } + .m-ptb75 { padding-top: 75px; padding-bottom: 75px; } + .m-ptb80 { padding-top: 80px; padding-bottom: 80px; } + .m-ptb85 { padding-top: 85px; padding-bottom: 85px; } + .m-ptb90 { padding-top: 90px; padding-bottom: 90px; } + .m-ptb95 { padding-top: 95px; padding-bottom: 95px; } + .m-ptb100 { padding-top: 100px; padding-bottom: 100px; } + .m-ptb105 { padding-top: 105px; padding-bottom: 105px; } + .m-ptb110 { padding-top:110px; padding-bottom: 110px; } + .m-ptb115 { padding-top: 115px; padding-bottom: 115px; } + .m-ptb120 { padding-top: 120px; padding-bottom: 120px; } + .m-ptb125 { padding-top: 125px; padding-bottom: 125px; } + .m-ptb130 { padding-top: 130px; padding-bottom: 130px; } + .m-ptb135 { padding-top: 135px; padding-bottom: 135px; } + .m-ptb140 { padding-top: 140px; padding-bottom: 140px; } + .m-ptb145 { padding-top: 145px; padding-bottom: 145px; } + .m-ptb150 { padding-top: 150px; padding-bottom: 150px; } + .m-ptb155 { padding-top: 155px; padding-bottom: 155px; } + .m-ptb160 { padding-top: 160px; padding-bottom: 160px; } + .m-ptb165 { padding-top: 165px; padding-bottom: 165px; } + .m-ptb170 { padding-top: 170px; padding-bottom: 170px; } + .m-ptb175 { padding-top: 175px; padding-bottom: 175px; } + .m-ptb180 { padding-top: 180px; padding-bottom: 180px; } + .m-ptb185 { padding-top: 185px; padding-bottom: 185px; } + .m-ptb190 { padding-top: 190px; padding-bottom: 190px; } + .m-ptb195 { padding-top: 195px; padding-bottom: 195px; } + .m-ptb200 { padding-top: 200px; padding-bottom: 200px; } + + .m-plra { padding-left: auto; padding-right: auto; } + .m-plr0 { padding-left: 0; padding-right: 0; } + .m-plr5 { padding-left: 5px; padding-right: 5px; } + .m-plr10 { padding-left: 10px; padding-right:10px; } + .m-plr15 { padding-left: 15px; padding-right: 15px; } + .m-plr20 { padding-left: 20px; padding-right: 20px; } + .m-plr25 { padding-left: 25px; padding-right: 25px; } + .m-plr30 { padding-left: 30px; padding-right: 30px; } + .m-plr35 { padding-left: 35px; padding-right: 35px; } + .m-plr40 { padding-left: 40px; padding-right: 40px; } + .m-plr45 { padding-left: 45px; padding-right: 45px; } + .m-plr50 { padding-left: 50px; padding-right: 50px; } + .m-plr55 { padding-left: 55px; padding-right: 55px; } + .m-plr60 { padding-left: 60px; padding-right: 60px; } + .m-plr65 { padding-left: 65px; padding-right: 65px; } + .m-plr70 { padding-left: 70px; padding-right: 70px; } + .m-plr75 { padding-left: 75px; padding-right: 75px; } + .m-plr80 { padding-left: 80px; padding-right: 80px; } + .m-plr85 { padding-left: 85px; padding-right: 85px; } + .m-plr90 { padding-left: 90px; padding-right: 90px; } + .m-plr95 { padding-left: 95px; padding-right: 95px; } + .m-plr100 { padding-left: 100px; padding-right: 100px; } + .m-plr105 { padding-left: 105px; padding-right: 105px; } + .m-plr110 { padding-left:110px; padding-right: 110px; } + .m-plr115 { padding-left: 115px; padding-right: 115px; } + .m-plr120 { padding-left: 120px; padding-right: 120px; } + .m-plr125 { padding-left: 125px; padding-right: 125px; } + .m-plr130 { padding-left: 130px; padding-right: 130px; } + .m-plr135 { padding-left: 135px; padding-right: 135px; } + .m-plr140 { padding-left: 140px; padding-right: 140px; } + .m-plr145 { padding-left: 145px; padding-right: 145px; } + .m-plr150 { padding-left: 150px; padding-right: 150px; } + .m-plr155 { padding-left: 155px; padding-right: 155px; } + .m-plr160 { padding-left: 160px; padding-right: 160px; } + .m-plr165 { padding-left: 165px; padding-right: 165px; } + .m-plr170 { padding-left: 170px; padding-right: 170px; } + .m-plr175 { padding-left: 175px; padding-right: 175px; } + .m-plr180 { padding-left: 180px; padding-right: 180px; } + .m-plr185 { padding-left: 185px; padding-right: 185px; } + .m-plr190 { padding-left: 190px; padding-right: 190px; } + .m-plr195 { padding-left: 195px; padding-right: 195px; } + .m-plr200 { padding-left: 200px; padding-right: 200px; } + + /* Font Sizes */ + + .m-fz8 { font-size: 8px; } + .m-fz10 { font-size: 10px; } + .m-fz11 { font-size: 11px; } + .m-fz12 { font-size: 12px; } + .m-fz13 { font-size: 13px; } + .m-fz14 { font-size: 14px; } + .m-fz16 { font-size: 16px; } + .m-fz18 { font-size: 18px; } + .m-fz20 { font-size: 20px; } + .m-fz22 { font-size: 22px; } + .m-fz24 { font-size: 24px; } + .m-fz26 { font-size: 26px; } + .m-fz28 { font-size: 28px; } + .m-fz30 { font-size: 30px; } + .m-fz32 { font-size: 32px; } + .m-fz34 { font-size: 34px; } + .m-fz36 { font-size: 36px; } + .m-fz38 { font-size: 38px; } + .m-fz40 { font-size: 40px; } + .m-fz42 { font-size: 42px; } + .m-fz44 { font-size: 44px; } + .m-fz46 { font-size: 46px; } + .m-fz48 { font-size: 48px; } + .m-fz50 { font-size: 50px; } + .m-fz52 { font-size: 52px; } + .m-fz54 { font-size: 54px; } + .m-fz56 { font-size: 56px; } + .m-fz58 { font-size: 58px; } + .m-fz60 { font-size: 60px; } + + /* Custom font sizes breakpoints */ + + .f1 { font-size: var(--m-f1); } + .f2 { font-size: var(--m-f2); } + .f3 { font-size: var(--m-f3); } + .f4 { font-size: var(--m-f4); } + .f5 { font-size: var(--m-f5); } + .f6 { font-size: var(--m-f6); } + .f7 { font-size: var(--m-f7); } + .f8 { font-size: var(--m-f8); } + .f9 { font-size: var(--m-f9); } + + /* Typography > Text Utils */ + + .m-text-normal { font-weight: normal; } + .m-text-light { font-weight: 200; } + .m-text-medium { font-weight: 500; } + .m-text-semibold { font-weight: 600; } + .m-text-bold { font-weight: bold; } + .m-text-italic { font-style: italic; } + .m-text-through { text-decoration: line-through; } + .m-text-small { font-size: 14px; } + .m-text-center{ text-align:center; } + .m-text-left { text-align: left; } + .m-text-right { text-align: right; } + .m-text-justify { text-align: justify; } +} + +/* Flex Grid (Large) */ + +@media only screen and (min-width : 992px) { + + /* Grid Sizes (as 12 cols) */ + + .l1 { width: 8.33333%; } + .l2 { width: 16.66666%; } + .l3 { width: 24.99999%; } + .l4 { width: 33.33333%; } + .l5 { width: 41.66666%; } + .l6 { width: 49.99999%; } + .l7 { width: 58.33333%; } + .l8 { width: 66.66666%; } + .l9 { width: 74.99999%; } + .l10{ width: 83.33333%; } + .l11{ width: 91.66666%; } + .l12 { width: 99.99999%; } + + /* Grid Sizes (as percent) */ + + .l-5 { width: 5%; } + .l-10 { width: 10%; } + .l-15 { width: 15%; } + .l-20 { width: 20%; } + .l-25 { width: 25%; } + .l-30 { width: 30%; } + .l-35 { width: 35%; } + .l-40 { width: 40%; } + .l-45 { width: 45%; } + .l-50 { width: 50%; } + .l-55 { width: 55%; } + .l-65 { width: 65%; } + .l-70 { width: 70%; } + .l-75 { width: 75%; } + .l-80 { width: 80%; } + .l-85 { width: 85%; } + .l-90 { width: 90%; } + .l-95 { width: 90%; } + .l-100{ width: 100%; } + .l-a { width: auto; } + + /* Grid Temlate Columns */ + + .l-tc1 { grid-template-columns: 1fr; } + .l-tc2 { grid-template-columns: repeat(2, 1fr); } + .l-tc3 { grid-template-columns: repeat(3, 1fr); } + .l-tc4 { grid-template-columns: repeat(4, 1fr); } + .l-tc5 { grid-template-columns: repeat(5, 1fr); } + .l-tc6 { grid-template-columns: repeat(6, 1fr); } + .l-tc7 { grid-template-columns: repeat(7, 1fr); } + .l-tc8 { grid-template-columns: repeat(8, 1fr); } + .l-tc9 { grid-template-columns: repeat(9, 1fr); } + .l-tc10 { grid-template-columns: repeat(10, 1fr); } + .l-tc11 { grid-template-columns: repeat(11, 1fr); } + .l-tc12 { grid-template-columns: repeat(12, 1fr); } + + .l-tc-1 { grid-template-columns: auto; } + .l-tc-2 { grid-template-columns: repeat(2, auto); } + .l-tc-3 { grid-template-columns: repeat(3, auto); } + .l-tc-4 { grid-template-columns: repeat(4, auto); } + .l-tc-5 { grid-template-columns: repeat(5, auto); } + .l-tc-6 { grid-template-columns: repeat(6, auto); } + .l-tc-7 { grid-template-columns: repeat(7, auto); } + .l-tc-8 { grid-template-columns: repeat(8, auto); } + .l-tc-9 { grid-template-columns: repeat(9, auto); } + .l-tc-10 { grid-template-columns: repeat(10, auto); } + .l-tc-11 { grid-template-columns: repeat(11, auto); } + .l-tc-12 { grid-template-columns: repeat(12, auto); } + + /* Grid spanning */ + + .l-no-span { grid-column: auto; } + .l-span2 { grid-column: span 2; } + .l-span3 { grid-column: span 3; } + .l-span4 { grid-column: span 4; } + .l-span5 { grid-column: span 5; } + .l-span6 { grid-column: span 6; } + .l-span7 { grid-column: span 7; } + .l-span8 { grid-column: span 8; } + .l-span9 { grid-column: span 9; } + .l-span10 { grid-column: span 10;} + .l-span11 { grid-column: span 11; } + .l-span12 { grid-column: span 12; } + + /* Grid gap */ + + .l-gap0 { gap: 0 } + .l-gap5 { gap: 5px; } + .l-gap10 { gap:10px; } + .l-gap15 { gap: 15px; } + .l-gap20 { gap: 20px; } + .l-gap25 { gap: 25px; } + .l-gap30 { gap: 30px; } + .l-gap35 { gap: 35px; } + .l-gap40 { gap: 40px; } + .l-gap45 { gap: 45px; } + .l-gap50 { gap: 50px; } + .l-gap55 { gap: 55px; } + .l-gap60 { gap: 60px; } + .l-gap65 { gap: 65px; } + .l-gap70 { gap: 70px; } + .l-gap75 { gap: 75px; } + .l-gap80 { gap: 80px; } + .l-gap85 { gap: 85px; } + .l-gap90 { gap: 90px; } + .l-gap95 { gap: 95px; } + .l-gap100 { gap: 100px; } + .l-gap105 { gap: 105px; } + .l-gap110 { gap: 110px; } + .l-gap115 { gap: 115px; } + .l-gap120 { gap: 120px; } + .l-gap125 { gap: 125px; } + .l-gap130 { gap: 130px; } + .l-gap135 { gap: 135px; } + .l-gap140 { gap: 140px; } + .l-gap145 { gap: 145px; } + .l-gap150 { gap: 150px; } + .l-gap155 { gap: 155px; } + .l-gap160 { gap: 160px; } + .l-gap165 { gap: 165px; } + .l-gap170 { gap: 170px; } + .l-gap175 { gap: 175px; } + .l-gap180 { gap: 180px; } + .l-gap185 { gap: 185px; } + .l-gap190 { gap: 190px; } + .l-gap195 { gap: 195px; } + .l-gap200 { gap: 200px; } + + .l-x-gap0 { column-gap: 0 } + .l-x-gap5 { column-gap: 5px; } + .l-x-gap10 { column-gap:10px; } + .l-x-gap15 { column-gap: 15px; } + .l-x-gap20 { column-gap: 20px; } + .l-x-gap25 { column-gap: 25px; } + .l-x-gap30 { column-gap: 30px; } + .l-x-gap35 { column-gap: 35px; } + .l-x-gap40 { column-gap: 40px; } + .l-x-gap45 { column-gap: 45px; } + .l-x-gap50 { column-gap: 50px; } + .l-x-gap55 { column-gap: 55px; } + .l-x-gap60 { column-gap: 60px; } + .l-x-gap65 { column-gap: 65px; } + .l-x-gap70 { column-gap: 70px; } + .l-x-gap75 { column-gap: 75px; } + .l-x-gap80 { column-gap: 80px; } + .l-x-gap85 { column-gap: 85px; } + .l-x-gap90 { column-gap: 90px; } + .l-x-gap95 { column-gap: 95px; } + .l-x-gap100 { column-gap: 100px; } + .l-x-gap105 { column-gap: 105px; } + .l-x-gap110 { column-gap: 110px; } + .l-x-gap115 { column-gap: 115px; } + .l-x-gap120 { column-gap: 120px; } + .l-x-gap125 { column-gap: 125px; } + .l-x-gap130 { column-gap: 130px; } + .l-x-gap135 { column-gap: 135px; } + .l-x-gap140 { column-gap: 140px; } + .l-x-gap145 { column-gap: 145px; } + .l-x-gap150 { column-gap: 150px; } + .l-x-gap155 { column-gap: 155px; } + .l-x-gap160 { column-gap: 160px; } + .l-x-gap165 { column-gap: 165px; } + .l-x-gap170 { column-gap: 170px; } + .l-x-gap175 { column-gap: 175px; } + .l-x-gap180 { column-gap: 180px; } + .l-x-gap185 { column-gap: 185px; } + .l-x-gap190 { column-gap: 190px; } + .l-x-gap195 { column-gap: 195px; } + .l-x-gap200 { column-gap: 200px; } + + .l-y-gap0 { row-gap: 0 } + .l-y-gap5 { row-gap: 5px; } + .l-y-gap10 { row-gap:10px; } + .l-y-gap15 { row-gap: 15px; } + .l-y-gap20 { row-gap: 20px; } + .l-y-gap25 { row-gap: 25px; } + .l-y-gap30 { row-gap: 30px; } + .l-y-gap35 { row-gap: 35px; } + .l-y-gap40 { row-gap: 40px; } + .l-y-gap45 { row-gap: 45px; } + .l-y-gap50 { row-gap: 50px; } + .l-y-gap55 { row-gap: 55px; } + .l-y-gap60 { row-gap: 60px; } + .l-y-gap65 { row-gap: 65px; } + .l-y-gap70 { row-gap: 70px; } + .l-y-gap75 { row-gap: 75px; } + .l-y-gap80 { row-gap: 80px; } + .l-y-gap85 { row-gap: 85px; } + .l-y-gap90 { row-gap: 90px; } + .l-y-gap95 { row-gap: 95px; } + .l-y-gap100 { row-gap: 100px; } + .l-y-gap105 { row-gap: 105px; } + .l-y-gap110 { row-gap: 110px; } + .l-y-gap115 { row-gap: 115px; } + .l-y-gap120 { row-gap: 120px; } + .l-y-gap125 { row-gap: 125px; } + .l-y-gap130 { row-gap: 130px; } + .l-y-gap135 { row-gap: 135px; } + .l-y-gap140 { row-gap: 140px; } + .l-y-gap145 { row-gap: 145px; } + .l-y-gap150 { row-gap: 150px; } + .l-y-gap155 { row-gap: 155px; } + .l-y-gap160 { row-gap: 160px; } + .l-y-gap165 { row-gap: 165px; } + .l-y-gap170 { row-gap: 170px; } + .l-y-gap175 { row-gap: 175px; } + .l-y-gap180 { row-gap: 180px; } + .l-y-gap185 { row-gap: 185px; } + .l-y-gap190 { row-gap: 190px; } + .l-y-gap195 { row-gap: 195px; } + .l-y-gap200 { row-gap: 200px; } + + /* Box display */ + + .l-hide { display:none !important; } + .l-flex { display: flex !important; align-self: auto; } + .l-grid { display: grid; } + .l-inline-grid { display: inline-grid; } + .l-block { display: block !important; } + + /* Flex / Grid Align */ + + .l-x-start { justify-content: start; } + .l-x-end { justify-content: end; } + .l-x-center { justify-content: center; } + .l-x-between { justify-content: space-between; } + .l-x-around { justify-content: space-around; } + .l-x-evenly { justify-content: space-evenly; } + + .l-g-start { justify-items: start; } + .l-g-end { justify-items: end; } + .l-g-center { justify-items: center; } + .l-g-stretch { justify-items: stretch; } + + .l-v-normal { align-items: normal; } + .l-v-start { align-items: start; } + .l-v-end { align-items: end; } + .l-v-center { align-items: center; } + .l-v-stretch { align-items: stretch; } + .l-v-baseline { align-items: baseline; } + + .l-y-start { align-content: start; } + .l-y-end { align-content: end; } + .l-y-center { align-content: center; } + .l-y-stretch { align-content: stretch; } + .l-y-between { align-content: space-between; } + .l-y-around { align-content: space-around; } + + .l-start { align-self: start; } + .l-end { align-self: end; } + .l-center { align-self: center; } + .l-baseline { align-self: baseline; } + .l-stretch { align-self: stretch; } + + /* Margins - General */ + + .l-mga { margin: auto; } + .l-mg0 { margin: 0 } + .l-mg5 { margin: 5px; } + .l-mg10 { margin:10px; } + .l-mg15 { margin: 15px; } + .l-mg20 { margin: 20px; } + .l-mg25 { margin: 25px; } + .l-mg30 { margin: 30px; } + .l-mg35 { margin: 35px; } + .l-mg40 { margin: 40px; } + .l-mg45 { margin: 45px; } + .l-mg50 { margin: 50px; } + .l-mg55 { margin: 55px; } + .l-mg60 { margin: 60px; } + .l-mg65 { margin: 65px; } + .l-mg70 { margin: 70px; } + .l-mg75 { margin: 75px; } + .l-mg80 { margin: 80px; } + .l-mg85 { margin: 85px; } + .l-mg90 { margin: 90px; } + .l-mg95 { margin: 95px; } + .l-mg100 { margin: 100px; } + .l-mg105 { margin: 105px; } + .l-mg110 { margin: 110px; } + .l-mg115 { margin: 115px; } + .l-mg120 { margin: 120px; } + .l-mg125 { margin: 125px; } + .l-mg130 { margin: 130px; } + .l-mg135 { margin: 135px; } + .l-mg140 { margin: 140px; } + .l-mg145 { margin: 145px; } + .l-mg150 { margin: 150px; } + .l-mg155 { margin: 155px; } + .l-mg160 { margin: 160px; } + .l-mg165 { margin: 165px; } + .l-mg170 { margin: 170px; } + .l-mg175 { margin: 175px; } + .l-mg180 { margin: 180px; } + .l-mg185 { margin: 185px; } + .l-mg190 { margin: 190px; } + .l-mg195 { margin: 195px; } + .l-mg200 { margin: 200px; } + + /* Margins - Top */ + + .l-mta { margin-top: auto; } + .l-mt0 { margin-top: 0; } + .l-mt5 { margin-top: 5px; } + .l-mt10 { margin-top:10px; } + .l-mt15 { margin-top: 15px; } + .l-mt20 { margin-top: 20px; } + .l-mt25 { margin-top: 25px; } + .l-mt30 { margin-top: 30px; } + .l-mt35 { margin-top: 35px; } + .l-mt40 { margin-top: 40px; } + .l-mt45 { margin-top: 45px; } + .l-mt50 { margin-top: 50px; } + .l-mt55 { margin-top: 55px; } + .l-mt60 { margin-top: 60px; } + .l-mt65 { margin-top: 65px; } + .l-mt70 { margin-top: 70px; } + .l-mt75 { margin-top: 75px; } + .l-mt80 { margin-top: 80px; } + .l-mt85 { margin-top: 85px; } + .l-mt90 { margin-top: 90px; } + .l-mt95 { margin-top: 95px; } + .l-mt100 { margin-top: 100px; } + .l-mt105 { margin-top: 105px; } + .l-mt110 { margin-top: 110px; } + .l-mt115 { margin-top: 115px; } + .l-mt120 { margin-top: 120px; } + .l-mt125 { margin-top: 125px; } + .l-mt130 { margin-top: 130px; } + .l-mt135 { margin-top: 135px; } + .l-mt140 { margin-top: 140px; } + .l-mt145 { margin-top: 145px; } + .l-mt150 { margin-top: 150px; } + .l-mt155 { margin-top: 155px; } + .l-mt160 { margin-top: 160px; } + .l-mt165 { margin-top: 165px; } + .l-mt170 { margin-top: 170px; } + .l-mt175 { margin-top: 175px; } + .l-mt180 { margin-top: 180px; } + .l-mt185 { margin-top: 185px; } + .l-mt190 { margin-top: 190px; } + .l-mt195 { margin-top: 195px; } + .l-mt200 { margin-top: 200px; } + + /* Negative Margins - Top */ + + .l-mt-5 { margin-top: -5px; } + .l-mt-10 { margin-top: -10px; } + .l-mt-15 { margin-top: -15px; } + .l-mt-20 { margin-top: -20px; } + .l-mt-25 { margin-top: -25px; } + .l-mt-30 { margin-top: -30px; } + .l-mt-35 { margin-top: -35px; } + .l-mt-40 { margin-top: -40px; } + .l-mt-45 { margin-top: -45px; } + .l-mt-50 { margin-top: -50px; } + .l-mt-55 { margin-top: -55px; } + .l-mt-60 { margin-top: -60px; } + .l-mt-65 { margin-top: -65px; } + .l-mt-70 { margin-top: -70px; } + .l-mt-75 { margin-top: -75px; } + .l-mt-80 { margin-top: -80px; } + .l-mt-85 { margin-top: -85px; } + .l-mt-90 { margin-top: -90px; } + .l-mt-95 { margin-top: -95px; } + .l-mt-100 { margin-top: -100px; } + .l-mt-105 { margin-top: -105px; } + .l-mt-110 { margin-top: -110px; } + .l-mt-115 { margin-top: -115px; } + .l-mt-120 { margin-top: -120px; } + .l-mt-125 { margin-top: -125px; } + .l-mt-130 { margin-top: -130px; } + .l-mt-135 { margin-top: -135px; } + .l-mt-140 { margin-top: -140px; } + .l-mt-145 { margin-top: -145px; } + .l-mt-150 { margin-top: -150px; } + .l-mt-155 { margin-top: -155px; } + .l-mt-160 { margin-top: -160px; } + .l-mt-165 { margin-top: -165px; } + .l-mt-170 { margin-top: -170px; } + .l-mt-175 { margin-top: -175px; } + .l-mt-180 { margin-top: -180px; } + .l-mt-185 { margin-top: -185px; } + .l-mt-190 { margin-top: -190px; } + .l-mt-195 { margin-top: -195px; } + .l-mt-200 { margin-top: -200px; } + + /* Margins - Bottom */ + + .l-mba { margin-bottom: auto; } + .l-mb0 { margin-bottom: 0 } + .l-mb5 { margin-bottom: 5px; } + .l-mb10 { margin-bottom:10px; } + .l-mb15 { margin-bottom: 15px; } + .l-mb20 { margin-bottom: 20px; } + .l-mb25 { margin-bottom: 25px; } + .l-mb30 { margin-bottom: 30px; } + .l-mb35 { margin-bottom: 35px; } + .l-mb40 { margin-bottom: 40px; } + .l-mb45 { margin-bottom: 45px; } + .l-mb50 { margin-bottom: 50px; } + .l-mb55 { margin-bottom: 55px; } + .l-mb60 { margin-bottom: 60px; } + .l-mb65 { margin-bottom: 65px; } + .l-mb70 { margin-bottom: 70px; } + .l-mb75 { margin-bottom: 75px; } + .l-mb80 { margin-bottom: 80px; } + .l-mb85 { margin-bottom: 85px; } + .l-mb90 { margin-bottom: 90px; } + .l-mb95 { margin-bottom: 95px; } + .l-mb100 { margin-bottom: 100px; } + .l-mb105 { margin-bottom: 105px; } + .l-mb110 { margin-bottom: 110px; } + .l-mb115 { margin-bottom: 115px; } + .l-mb120 { margin-bottom: 120px; } + .l-mb125 { margin-bottom: 125px; } + .l-mb130 { margin-bottom: 130px; } + .l-mb135 { margin-bottom: 135px; } + .l-mb140 { margin-bottom: 140px; } + .l-mb145 { margin-bottom: 145px; } + .l-mb150 { margin-bottom: 150px; } + .l-mb155 { margin-bottom: 155px; } + .l-mb160 { margin-bottom: 160px; } + .l-mb165 { margin-bottom: 165px; } + .l-mb170 { margin-bottom: 170px; } + .l-mb175 { margin-bottom: 175px; } + .l-mb180 { margin-bottom: 180px; } + .l-mb185 { margin-bottom: 185px; } + .l-mb190 { margin-bottom: 190px; } + .l-mb195 { margin-bottom: 195px; } + .l-mb200 { margin-bottom: 200px; } + + /* Margins - Left */ + + .l-mla { margin-left: auto; } + .l-ml0 { margin-left: 0; } + .l-ml5 { margin-left: 5px; } + .l-ml10 { margin-left:10px; } + .l-ml15 { margin-left: 15px; } + .l-ml20 { margin-left: 20px; } + .l-ml25 { margin-left: 25px; } + .l-ml30 { margin-left: 30px; } + .l-ml35 { margin-left: 35px; } + .l-ml40 { margin-left: 40px; } + .l-ml45 { margin-left: 45px; } + .l-ml50 { margin-left: 50px; } + .l-ml55 { margin-left: 55px; } + .l-ml60 { margin-left: 60px; } + .l-ml65 { margin-left: 65px; } + .l-ml70 { margin-left: 70px; } + .l-ml75 { margin-left: 75px; } + .l-ml80 { margin-left: 80px; } + .l-ml85 { margin-left: 85px; } + .l-ml90 { margin-left: 90px; } + .l-ml95 { margin-left: 95px; } + .l-ml100 { margin-left: 100px; } + .l-ml105 { margin-left: 105px; } + .l-ml110 { margin-left: 110px; } + .l-ml115 { margin-left: 115px; } + .l-ml120 { margin-left: 120px; } + .l-ml125 { margin-left: 125px; } + .l-ml130 { margin-left: 130px; } + .l-ml135 { margin-left: 135px; } + .l-ml140 { margin-left: 140px; } + .l-ml145 { margin-left: 145px; } + .l-ml150 { margin-left: 150px; } + .l-ml155 { margin-left: 155px; } + .l-ml160 { margin-left: 160px; } + .l-ml165 { margin-left: 165px; } + .l-ml170 { margin-left: 170px; } + .l-ml175 { margin-left: 175px; } + .l-ml180 { margin-left: 180px; } + .l-ml185 { margin-left: 185px; } + .l-ml190 { margin-left: 190px; } + .l-ml195 { margin-left: 195px; } + .l-ml200 { margin-left: 200px; } + + /* Margins - Right */ + + .l-mra { margin-right: auto; } + .l-mr0 { margin-right: 0; } + .l-mr5 { margin-right: 5px; } + .l-mr10 { margin-right:10px; } + .l-mr15 { margin-right: 15px; } + .l-mr20 { margin-right: 20px; } + .l-mr25 { margin-right: 25px; } + .l-mr30 { margin-right: 30px; } + .l-mr35 { margin-right: 35px; } + .l-mr40 { margin-right: 40px; } + .l-mr45 { margin-right: 45px; } + .l-mr50 { margin-right: 50px; } + .l-mr55 { margin-right: 55px; } + .l-mr60 { margin-right: 60px; } + .l-mr65 { margin-right: 65px; } + .l-mr70 { margin-right: 70px; } + .l-mr75 { margin-right: 75px; } + .l-mr80 { margin-right: 80px; } + .l-mr85 { margin-right: 85px; } + .l-mr90 { margin-right: 90px; } + .l-mr95 { margin-right: 95px; } + .l-mr100 { margin-right: 100px; } + .l-mr105 { margin-right: 105px; } + .l-mr110 { margin-right: 110px; } + .l-mr115 { margin-right: 115px; } + .l-mr120 { margin-right: 120px; } + .l-mr125 { margin-right: 125px; } + .l-mr130 { margin-right: 130px; } + .l-mr135 { margin-right: 135px; } + .l-mr140 { margin-right: 140px; } + .l-mr145 { margin-right: 145px; } + .l-mr150 { margin-right: 150px; } + .l-mr155 { margin-right: 155px; } + .l-mr160 { margin-right: 160px; } + .l-mr165 { margin-right: 165px; } + .l-mr170 { margin-right: 170px; } + .l-mr175 { margin-right: 175px; } + .l-mr180 { margin-right: 180px; } + .l-mr185 { margin-right: 185px; } + .l-mr190 { margin-right: 190px; } + .l-mr195 { margin-right: 195px; } + .l-mr200 { margin-right: 200px; } + + /* Margins - Top & Bottom */ + + .l-mtba { margin-top: auto; margin-bottom: auto; } + .l-mtb0 { margin-top: 0; margin-bottom: 0; } + .l-mtb5 { margin-top: 5px; margin-bottom: 5px; } + .l-mtb10 { margin-top:10px; margin-bottom: 10px; } + .l-mtb15 { margin-top: 15px; margin-bottom: 15px; } + .l-mtb20 { margin-top: 20px; margin-bottom: 20px; } + .l-mtb25 { margin-top: 25px; margin-bottom: 25px; } + .l-mtb30 { margin-top: 30px; margin-bottom: 30px; } + .l-mtb35 { margin-top: 35px; margin-bottom: 35px; } + .l-mtb40 { margin-top: 40px; margin-bottom: 40px; } + .l-mtb45 { margin-top: 45px; margin-bottom: 45px; } + .l-mtb50 { margin-top: 50px; margin-bottom: 50px; } + .l-mtb55 { margin-top: 55px; margin-bottom: 55px; } + .l-mtb60 { margin-top: 60px; margin-bottom: 60px; } + .l-mtb65 { margin-top: 65px; margin-bottom: 65px; } + .l-mtb70 { margin-top: 70px; margin-bottom: 70px; } + .l-mtb75 { margin-top: 75px; margin-bottom: 75px; } + .l-mtb80 { margin-top: 80px; margin-bottom: 80px; } + .l-mtb85 { margin-top: 85px; margin-bottom: 85px; } + .l-mtb90 { margin-top: 90px; margin-bottom: 90px; } + .l-mtb95 { margin-top: 95px; margin-bottom: 95px; } + .l-mtb100 { margin-top: 100px; margin-bottom: 100px; } + .l-mtb105 { margin-top: 105px; margin-bottom: 105px; } + .l-mtb110 { margin-top:110px; margin-bottom: 110px; } + .l-mtb115 { margin-top: 115px; margin-bottom: 115px; } + .l-mtb120 { margin-top: 120px; margin-bottom: 120px; } + .l-mtb125 { margin-top: 125px; margin-bottom: 125px; } + .l-mtb130 { margin-top: 130px; margin-bottom: 130px; } + .l-mtb135 { margin-top: 135px; margin-bottom: 135px; } + .l-mtb140 { margin-top: 140px; margin-bottom: 140px; } + .l-mtb145 { margin-top: 145px; margin-bottom: 145px; } + .l-mtb150 { margin-top: 150px; margin-bottom: 150px; } + .l-mtb155 { margin-top: 155px; margin-bottom: 155px; } + .l-mtb160 { margin-top: 160px; margin-bottom: 160px; } + .l-mtb165 { margin-top: 165px; margin-bottom: 165px; } + .l-mtb170 { margin-top: 170px; margin-bottom: 170px; } + .l-mtb175 { margin-top: 175px; margin-bottom: 175px; } + .l-mtb180 { margin-top: 180px; margin-bottom: 180px; } + .l-mtb185 { margin-top: 185px; margin-bottom: 185px; } + .l-mtb190 { margin-top: 190px; margin-bottom: 190px; } + .l-mtb195 { margin-top: 195px; margin-bottom: 195px; } + .l-mtb200 { margin-top: 200px; margin-bottom: 200px; } + + .l-centered { margin-left: auto; margin-right: auto; } + .l-filled { margin-left: 0; margin-right: 0; } + + .l-mlr5 { margin-left: 5px; margin-right: 5px; } + .l-mlr10 { margin-left: 10px; margin-right:10px; } + .l-mlr15 { margin-left: 15px; margin-right: 15px; } + .l-mlr20 { margin-left: 20px; margin-right: 20px; } + .l-mlr25 { margin-left: 25px; margin-right: 25px; } + .l-mlr30 { margin-left: 30px; margin-right: 30px; } + .l-mlr35 { margin-left: 35px; margin-right: 35px; } + .l-mlr40 { margin-left: 40px; margin-right: 40px; } + .l-mlr45 { margin-left: 45px; margin-right: 45px; } + .l-mlr50 { margin-left: 50px; margin-right: 50px; } + .l-mlr55 { margin-left: 55px; margin-right: 55px; } + .l-mlr60 { margin-left: 60px; margin-right: 60px; } + .l-mlr65 { margin-left: 65px; margin-right: 65px; } + .l-mlr70 { margin-left: 70px; margin-right: 70px; } + .l-mlr75 { margin-left: 75px; margin-right: 75px; } + .l-mlr80 { margin-left: 80px; margin-right: 80px; } + .l-mlr85 { margin-left: 85px; margin-right: 85px; } + .l-mlr90 { margin-left: 90px; margin-right: 90px; } + .l-mlr95 { margin-left: 95px; margin-right: 95px; } + .l-mlr100 { margin-left: 100px; margin-right: 100px; } + .l-mlr105 { margin-left: 105px; margin-right: 105px; } + .l-mlr110 { margin-left:110px; margin-right: 110px; } + .l-mlr115 { margin-left: 115px; margin-right: 115px; } + .l-mlr120 { margin-left: 120px; margin-right: 120px; } + .l-mlr125 { margin-left: 125px; margin-right: 125px; } + .l-mlr130 { margin-left: 130px; margin-right: 130px; } + .l-mlr135 { margin-left: 135px; margin-right: 135px; } + .l-mlr140 { margin-left: 140px; margin-right: 140px; } + .l-mlr145 { margin-left: 145px; margin-right: 145px; } + .l-mlr150 { margin-left: 150px; margin-right: 150px; } + .l-mlr155 { margin-left: 155px; margin-right: 155px; } + .l-mlr160 { margin-left: 160px; margin-right: 160px; } + .l-mlr165 { margin-left: 165px; margin-right: 165px; } + .l-mlr170 { margin-left: 170px; margin-right: 170px; } + .l-mlr175 { margin-left: 175px; margin-right: 175px; } + .l-mlr180 { margin-left: 180px; margin-right: 180px; } + .l-mlr185 { margin-left: 185px; margin-right: 185px; } + .l-mlr190 { margin-left: 190px; margin-right: 190px; } + .l-mlr195 { margin-left: 195px; margin-right: 195px; } + .l-mlr200 { margin-left: 200px; margin-right: 200px; } + + /* Paddings */ + + .l-pda {padding: auto; } + .l-pd0 { padding: 0 } + .l-pd5 { padding: 5px; } + .l-pd10 { padding:10px; } + .l-pd15 { padding: 15px; } + .l-pd20 { padding: 20px; } + .l-pd25 { padding: 25px; } + .l-pd30 { padding: 30px; } + .l-pd35 { padding: 35px; } + .l-pd40 { padding: 40px; } + .l-pd45 { padding: 45px; } + .l-pd50 { padding: 50px; } + .l-pd55 { padding: 55px; } + .l-pd60 { padding: 60px; } + .l-pd65 { padding: 65px; } + .l-pd70 { padding: 70px; } + .l-pd75 { padding: 75px; } + .l-pd80 { padding: 80px; } + .l-pd85 { padding: 85px; } + .l-pd90 { padding: 90px; } + .l-pd95 { padding: 95px; } + .l-pd100 { padding: 100px; } + .l-pd105 { padding: 105px; } + .l-pd110 { padding: 110px; } + .l-pd115 { padding: 115px; } + .l-pd120 { padding: 120px; } + .l-pd125 { padding: 125px; } + .l-pd130 { padding: 130px; } + .l-pd135 { padding: 135px; } + .l-pd140 { padding: 140px; } + .l-pd145 { padding: 145px; } + .l-pd150 { padding: 150px; } + .l-pd155 { padding: 155px; } + .l-pd160 { padding: 160px; } + .l-pd165 { padding: 165px; } + .l-pd170 { padding: 170px; } + .l-pd175 { padding: 175px; } + .l-pd180 { padding: 180px; } + .l-pd185 { padding: 185px; } + .l-pd190 { padding: 190px; } + .l-pd195 { padding: 195px; } + .l-pd200 { padding: 200px; } + + /* Paddings - Top */ + + .l-pta { padding-top: auto; } + .l-pt0 { padding-top: 0; } + .l-pt5 { padding-top: 5px; } + .l-pt10 { padding-top:10px; } + .l-pt15 { padding-top: 15px; } + .l-pt20 { padding-top: 20px; } + .l-pt25 { padding-top: 25px; } + .l-pt30 { padding-top: 30px; } + .l-pt35 { padding-top: 35px; } + .l-pt40 { padding-top: 40px; } + .l-pt45 { padding-top: 45px; } + .l-pt50 { padding-top: 50px; } + .l-pt55 { padding-top: 55px; } + .l-pt60 { padding-top: 60px; } + .l-pt65 { padding-top: 65px; } + .l-pt70 { padding-top: 70px; } + .l-pt75 { padding-top: 75px; } + .l-pt80 { padding-top: 80px; } + .l-pt85 { padding-top: 85px; } + .l-pt90 { padding-top: 90px; } + .l-pt95 { padding-top: 95px; } + .l-pt100 { padding-top: 100px; } + .l-pt105 { padding-top: 105px; } + .l-pt110 { padding-top: 110px; } + .l-pt115 { padding-top: 115px; } + .l-pt120 { padding-top: 120px; } + .l-pt125 { padding-top: 125px; } + .l-pt130 { padding-top: 130px; } + .l-pt135 { padding-top: 135px; } + .l-pt140 { padding-top: 140px; } + .l-pt145 { padding-top: 145px; } + .l-pt150 { padding-top: 150px; } + .l-pt155 { padding-top: 155px; } + .l-pt160 { padding-top: 160px; } + .l-pt165 { padding-top: 165px; } + .l-pt170 { padding-top: 170px; } + .l-pt175 { padding-top: 175px; } + .l-pt180 { padding-top: 180px; } + .l-pt185 { padding-top: 185px; } + .l-pt190 { padding-top: 190px; } + .l-pt195 { padding-top: 195px; } + .l-pt200 { padding-top: 200px; } + + /* Paddings - Bottom */ + + .l-pba { padding-bottom: auto; } + .l-pb0 { padding-bottom: 0 } + .l-pb5 { padding-bottom: 5px; } + .l-pb10 { padding-bottom:10px; } + .l-pb15 { padding-bottom: 15px; } + .l-pb20 { padding-bottom: 20px; } + .l-pb25 { padding-bottom: 25px; } + .l-pb30 { padding-bottom: 30px; } + .l-pb35 { padding-bottom: 35px; } + .l-pb40 { padding-bottom: 40px; } + .l-pb45 { padding-bottom: 45px; } + .l-pb50 { padding-bottom: 50px; } + .l-pb55 { padding-bottom: 55px; } + .l-pb60 { padding-bottom: 60px; } + .l-pb65 { padding-bottom: 65px; } + .l-pb70 { padding-bottom: 70px; } + .l-pb75 { padding-bottom: 75px; } + .l-pb80 { padding-bottom: 80px; } + .l-pb85 { padding-bottom: 85px; } + .l-pb90 { padding-bottom: 90px; } + .l-pb95 { padding-bottom: 95px; } + .l-pb100 { padding-bottom: 100px; } + .l-pb105 { padding-bottom: 105px; } + .l-pb110 { padding-bottom: 110px; } + .l-pb115 { padding-bottom: 115px; } + .l-pb120 { padding-bottom: 120px; } + .l-pb125 { padding-bottom: 125px; } + .l-pb130 { padding-bottom: 130px; } + .l-pb135 { padding-bottom: 135px; } + .l-pb140 { padding-bottom: 140px; } + .l-pb145 { padding-bottom: 145px; } + .l-pb150 { padding-bottom: 150px; } + .l-pb155 { padding-bottom: 155px; } + .l-pb160 { padding-bottom: 160px; } + .l-pb165 { padding-bottom: 165px; } + .l-pb170 { padding-bottom: 170px; } + .l-pb175 { padding-bottom: 175px; } + .l-pb180 { padding-bottom: 180px; } + .l-pb185 { padding-bottom: 185px; } + .l-pb190 { padding-bottom: 190px; } + .l-pb195 { padding-bottom: 195px; } + .l-pb200 { padding-bottom: 200px; } + + /* Paddings - Left */ + + .l-pla { padding-left: auto; } + .l-pl0 { padding-left: 0; } + .l-pl5 { padding-left: 5px; } + .l-pl10 { padding-left:10px; } + .l-pl15 { padding-left: 15px; } + .l-pl20 { padding-left: 20px; } + .l-pl25 { padding-left: 25px; } + .l-pl30 { padding-left: 30px; } + .l-pl35 { padding-left: 35px; } + .l-pl40 { padding-left: 40px; } + .l-pl45 { padding-left: 45px; } + .l-pl50 { padding-left: 50px; } + .l-pl55 { padding-left: 55px; } + .l-pl60 { padding-left: 60px; } + .l-pl65 { padding-left: 65px; } + .l-pl70 { padding-left: 70px; } + .l-pl75 { padding-left: 75px; } + .l-pl80 { padding-left: 80px; } + .l-pl85 { padding-left: 85px; } + .l-pl90 { padding-left: 90px; } + .l-pl95 { padding-left: 95px; } + .l-pl100 { padding-left: 100px; } + .l-pl105 { padding-left: 105px; } + .l-pl110 { padding-left: 110px; } + .l-pl115 { padding-left: 115px; } + .l-pl120 { padding-left: 120px; } + .l-pl125 { padding-left: 125px; } + .l-pl130 { padding-left: 130px; } + .l-pl135 { padding-left: 135px; } + .l-pl140 { padding-left: 140px; } + .l-pl145 { padding-left: 145px; } + .l-pl150 { padding-left: 150px; } + .l-pl155 { padding-left: 155px; } + .l-pl160 { padding-left: 160px; } + .l-pl165 { padding-left: 165px; } + .l-pl170 { padding-left: 170px; } + .l-pl175 { padding-left: 175px; } + .l-pl180 { padding-left: 180px; } + .l-pl185 { padding-left: 185px; } + .l-pl190 { padding-left: 190px; } + .l-pl195 { padding-left: 195px; } + .l-pl200 { padding-left: 200px; } + + /* Paddings - Right */ + + .l-pra { padding-right: auto; } + .l-pr0 { padding-right: 0; } + .l-pr5 { padding-right: 5px; } + .l-pr10 { padding-right:10px; } + .l-pr15 { padding-right: 15px; } + .l-pr20 { padding-right: 20px; } + .l-pr25 { padding-right: 25px; } + .l-pr30 { padding-right: 30px; } + .l-pr35 { padding-right: 35px; } + .l-pr40 { padding-right: 40px; } + .l-pr45 { padding-right: 45px; } + .l-pr50 { padding-right: 50px; } + .l-pr55 { padding-right: 55px; } + .l-pr60 { padding-right: 60px; } + .l-pr65 { padding-right: 65px; } + .l-pr70 { padding-right: 70px; } + .l-pr75 { padding-right: 75px; } + .l-pr80 { padding-right: 80px; } + .l-pr85 { padding-right: 85px; } + .l-pr90 { padding-right: 90px; } + .l-pr95 { padding-right: 95px; } + .l-pr100 { padding-right: 100px; } + .l-pr105 { padding-right: 105px; } + .l-pr110 { padding-right: 110px; } + .l-pr115 { padding-right: 115px; } + .l-pr120 { padding-right: 120px; } + .l-pr125 { padding-right: 125px; } + .l-pr130 { padding-right: 130px; } + .l-pr135 { padding-right: 135px; } + .l-pr140 { padding-right: 140px; } + .l-pr145 { padding-right: 145px; } + .l-pr150 { padding-right: 150px; } + .l-pr155 { padding-right: 155px; } + .l-pr160 { padding-right: 160px; } + .l-pr165 { padding-right: 165px; } + .l-pr170 { padding-right: 170px; } + .l-pr175 { padding-right: 175px; } + .l-pr180 { padding-right: 180px; } + .l-pr185 { padding-right: 185px; } + .l-pr190 { padding-right: 190px; } + .l-pr195 { padding-right: 195px; } + .l-pr200 { padding-right: 200px; } + + /* Paddings - Top & Bottom */ + + .l-ptb-a { padding-top: auto; padding-bottom: auto; } + .l-ptb0 { padding-top: 0; padding-bottom: 0; } + .l-ptb5 { padding-top: 5px; padding-bottom: 5px; } + .l-ptb10 { padding-top:10px; padding-bottom: 10px; } + .l-ptb15 { padding-top: 15px; padding-bottom: 15px; } + .l-ptb20 { padding-top: 20px; padding-bottom: 20px; } + .l-ptb25 { padding-top: 25px; padding-bottom: 25px; } + .l-ptb30 { padding-top: 30px; padding-bottom: 30px; } + .l-ptb35 { padding-top: 35px; padding-bottom: 35px; } + .l-ptb40 { padding-top: 40px; padding-bottom: 40px; } + .l-ptb45 { padding-top: 45px; padding-bottom: 45px; } + .l-ptb50 { padding-top: 50px; padding-bottom: 50px; } + .l-ptb55 { padding-top: 55px; padding-bottom: 55px; } + .l-ptb60 { padding-top: 60px; padding-bottom: 60px; } + .l-ptb65 { padding-top: 65px; padding-bottom: 65px; } + .l-ptb70 { padding-top: 70px; padding-bottom: 70px; } + .l-ptb75 { padding-top: 75px; padding-bottom: 75px; } + .l-ptb80 { padding-top: 80px; padding-bottom: 80px; } + .l-ptb85 { padding-top: 85px; padding-bottom: 85px; } + .l-ptb90 { padding-top: 90px; padding-bottom: 90px; } + .l-ptb95 { padding-top: 95px; padding-bottom: 95px; } + .l-ptb100 { padding-top: 100px; padding-bottom: 100px; } + .l-ptb105 { padding-top: 105px; padding-bottom: 105px; } + .l-ptb110 { padding-top:110px; padding-bottom: 110px; } + .l-ptb115 { padding-top: 115px; padding-bottom: 115px; } + .l-ptb120 { padding-top: 120px; padding-bottom: 120px; } + .l-ptb125 { padding-top: 125px; padding-bottom: 125px; } + .l-ptb130 { padding-top: 130px; padding-bottom: 130px; } + .l-ptb135 { padding-top: 135px; padding-bottom: 135px; } + .l-ptb140 { padding-top: 140px; padding-bottom: 140px; } + .l-ptb145 { padding-top: 145px; padding-bottom: 145px; } + .l-ptb150 { padding-top: 150px; padding-bottom: 150px; } + .l-ptb155 { padding-top: 155px; padding-bottom: 155px; } + .l-ptb160 { padding-top: 160px; padding-bottom: 160px; } + .l-ptb165 { padding-top: 165px; padding-bottom: 165px; } + .l-ptb170 { padding-top: 170px; padding-bottom: 170px; } + .l-ptb175 { padding-top: 175px; padding-bottom: 175px; } + .l-ptb180 { padding-top: 180px; padding-bottom: 180px; } + .l-ptb185 { padding-top: 185px; padding-bottom: 185px; } + .l-ptb190 { padding-top: 190px; padding-bottom: 190px; } + .l-ptb195 { padding-top: 195px; padding-bottom: 195px; } + .l-ptb200 { padding-top: 200px; padding-bottom: 200px; } + + .l-plra { padding-left: auto; padding-right: auto; } + .l-plr0 { padding-left: 0; padding-right: 0; } + .l-plr5 { padding-left: 5px; padding-right: 5px; } + .l-plr10 { padding-left: 10px; padding-right:10px; } + .l-plr15 { padding-left: 15px; padding-right: 15px; } + .l-plr20 { padding-left: 20px; padding-right: 20px; } + .l-plr25 { padding-left: 25px; padding-right: 25px; } + .l-plr30 { padding-left: 30px; padding-right: 30px; } + .l-plr35 { padding-left: 35px; padding-right: 35px; } + .l-plr40 { padding-left: 40px; padding-right: 40px; } + .l-plr45 { padding-left: 45px; padding-right: 45px; } + .l-plr50 { padding-left: 50px; padding-right: 50px; } + .l-plr55 { padding-left: 55px; padding-right: 55px; } + .l-plr60 { padding-left: 60px; padding-right: 60px; } + .l-plr65 { padding-left: 65px; padding-right: 65px; } + .l-plr70 { padding-left: 70px; padding-right: 70px; } + .l-plr75 { padding-left: 75px; padding-right: 75px; } + .l-plr80 { padding-left: 80px; padding-right: 80px; } + .l-plr85 { padding-left: 85px; padding-right: 85px; } + .l-plr90 { padding-left: 90px; padding-right: 90px; } + .l-plr95 { padding-left: 95px; padding-right: 95px; } + .l-plr100 { padding-left: 100px; padding-right: 100px; } + .l-plr105 { padding-left: 105px; padding-right: 105px; } + .l-plr110 { padding-left:110px; padding-right: 110px; } + .l-plr115 { padding-left: 115px; padding-right: 115px; } + .l-plr120 { padding-left: 120px; padding-right: 120px; } + .l-plr125 { padding-left: 125px; padding-right: 125px; } + .l-plr130 { padding-left: 130px; padding-right: 130px; } + .l-plr135 { padding-left: 135px; padding-right: 135px; } + .l-plr140 { padding-left: 140px; padding-right: 140px; } + .l-plr145 { padding-left: 145px; padding-right: 145px; } + .l-plr150 { padding-left: 150px; padding-right: 150px; } + .l-plr155 { padding-left: 155px; padding-right: 155px; } + .l-plr160 { padding-left: 160px; padding-right: 160px; } + .l-plr165 { padding-left: 165px; padding-right: 165px; } + .l-plr170 { padding-left: 170px; padding-right: 170px; } + .l-plr175 { padding-left: 175px; padding-right: 175px; } + .l-plr180 { padding-left: 180px; padding-right: 180px; } + .l-plr185 { padding-left: 185px; padding-right: 185px; } + .l-plr190 { padding-left: 190px; padding-right: 190px; } + .l-plr195 { padding-left: 195px; padding-right: 195px; } + .l-plr200 { padding-left: 200px; padding-right: 200px; } + + /* Font Sizes */ + + .l-fz8 { font-size: 8px; } + .l-fz10 { font-size: 10px; } + .l-fz11 { font-size: 11px; } + .l-fz12 { font-size: 12px; } + .l-fz13 { font-size: 13px; } + .l-fz14 { font-size: 14px; } + .l-fz16 { font-size: 16px; } + .l-fz18 { font-size: 18px; } + .l-fz20 { font-size: 20px; } + .l-fz22 { font-size: 22px; } + .l-fz24 { font-size: 24px; } + .l-fz26 { font-size: 26px; } + .l-fz28 { font-size: 28px; } + .l-fz30 { font-size: 30px; } + .l-fz32 { font-size: 32px; } + .l-fz34 { font-size: 34px; } + .l-fz36 { font-size: 36px; } + .l-fz38 { font-size: 38px; } + .l-fz40 { font-size: 40px; } + .l-fz42 { font-size: 42px; } + .l-fz44 { font-size: 44px; } + .l-fz46 { font-size: 46px; } + .l-fz48 { font-size: 48px; } + .l-fz50 { font-size: 50px; } + .l-fz52 { font-size: 52px; } + .l-fz54 { font-size: 54px; } + .l-fz56 { font-size: 56px; } + .l-fz58 { font-size: 58px; } + .l-fz60 { font-size: 60px; } + + /* Custom font sizes breakpoints */ + + .f1 { font-size: var(--l-f1); } + .f2 { font-size: var(--l-f2); } + .f3 { font-size: var(--l-f3); } + .f4 { font-size: var(--l-f4); } + .f5 { font-size: var(--l-f5); } + .f6 { font-size: var(--l-f6); } + .f7 { font-size: var(--l-f7); } + .f8 { font-size: var(--l-f8); } + .f9 { font-size: var(--l-f9); } + + /* Typography > text utils */ + + .l-text-normal { font-weight: normal; } + .l-text-light { font-weight: 200; } + .l-text-medium { font-weight: 500; } + .l-text-semibold { font-weight: 600; } + .l-text-bold { font-weight: bold; } + .l-text-italic { font-style: italic; } + .l-text-through { text-decoration: line-through; } + .l-text-small { font-size: 14px; } + .l-text-center{ text-align:center; } + .l-text-left { text-align: left; } + .l-text-right { text-align: right; } + .l-text-justify { text-align: justify; } + + /* Typography > Default font sizes */ + + h1 { font-size: 26px; } + h2 { font-size: 18px; } + h3 { font-size: 16px; } +} + +/* Extra Large Breakpoint */ + +@media only screen and (min-width : 1200px) { + + .wrapper{ max-width: 1200px; } + + /* Grid Sizes (as 12 cols) */ + + .xl1 { width: 8.33333%; } + .xl2 { width: 16.66666%; } + .xl3 { width: 24.99999%; } + .xl4 { width: 33.33333%; } + .xl5 { width: 41.66666%; } + .xl6 { width: 49.99999%; } + .xl7 { width: 58.33333%; } + .xl8 { width: 66.66666%; } + .xl9 { width: 74.99999%; } + .xl10{ width: 83.33333%; } + .xl11{ width: 91.66666%; } + .xl12 { width: 99.99999%; } + + /* Grid Sizes (as percent) */ + + .xl-5 { width: 5%; } + .xl-10 { width: 10%; } + .xl-15 { width: 15%; } + .xl-20 { width: 20%; } + .xl-25 { width: 25%; } + .xl-30 { width: 30%; } + .xl-35 { width: 35%; } + .xl-40 { width: 40%; } + .xl-45 { width: 45%; } + .xl-50 { width: 50%; } + .xl-55 { width: 55%; } + .xl-65 { width: 65%; } + .xl-70 { width: 70%; } + .xl-75 { width: 75%; } + .xl-80 { width: 80%; } + .xl-85 { width: 85%; } + .xl-90 { width: 90%; } + .xl-95 { width: 90%; } + .xl-100{ width: 100%; } + .xl-a { width: auto; } + + /* Grid Temaplte Columns */ + + .xl-tc1 { grid-template-columns: 1fr; } + .xl-tc2 { grid-template-columns: repeat(2, 1fr); } + .xl-tc3 { grid-template-columns: repeat(3, 1fr); } + .xl-tc4 { grid-template-columns: repeat(4, 1fr); } + .xl-tc5 { grid-template-columns: repeat(5, 1fr); } + .xl-tc6 { grid-template-columns: repeat(6, 1fr); } + .xl-tc7 { grid-template-columns: repeat(7, 1fr); } + .xl-tc8 { grid-template-columns: repeat(8, 1fr); } + .xl-tc9 { grid-template-columns: repeat(9, 1fr); } + .xl-tc10 { grid-template-columns: repeat(10, 1fr); } + .xl-tc11 { grid-template-columns: repeat(11, 1fr); } + .xl-tc12 { grid-template-columns: repeat(12, 1fr); } + + .xl-tc-1 { grid-template-columns: auto; } + .xl-tc-2 { grid-template-columns: repeat(2, auto); } + .xl-tc-3 { grid-template-columns: repeat(3, auto); } + .xl-tc-4 { grid-template-columns: repeat(4, auto); } + .xl-tc-5 { grid-template-columns: repeat(5, auto); } + .xl-tc-6 { grid-template-columns: repeat(6, auto); } + .xl-tc-7 { grid-template-columns: repeat(7, auto); } + .xl-tc-8 { grid-template-columns: repeat(8, auto); } + .xl-tc-9 { grid-template-columns: repeat(9, auto); } + .xl-tc-10 { grid-template-columns: repeat(10, auto); } + .xl-tc-11 { grid-template-columns: repeat(11, auto); } + .xl-tc-12 { grid-template-columns: repeat(12, auto); } + + /* Grid spanning */ + + .xl-no-span { grid-column: auto; } + .xl-span2 { grid-column: span 2; } + .xl-span3 { grid-column: span 3; } + .xl-span4 { grid-column: span 4; } + .xl-span5 { grid-column: span 5; } + .xl-span6 { grid-column: span 6; } + .xl-span7 { grid-column: span 7; } + .xl-span8 { grid-column: span 8; } + .xl-span9 { grid-column: span 9; } + .xl-span10 { grid-column: span 10;} + .xl-span11 { grid-column: span 11; } + .xl-span12 { grid-column: span 12; } + + /* Grid gap */ + + .xl-gap0 { gap: 0 } + .xl-gap5 { gap: 5px; } + .xl-gap10 { gap:10px; } + .xl-gap15 { gap: 15px; } + .xl-gap20 { gap: 20px; } + .xl-gap25 { gap: 25px; } + .xl-gap30 { gap: 30px; } + .xl-gap35 { gap: 35px; } + .xl-gap40 { gap: 40px; } + .xl-gap45 { gap: 45px; } + .xl-gap50 { gap: 50px; } + .xl-gap55 { gap: 55px; } + .xl-gap60 { gap: 60px; } + .xl-gap65 { gap: 65px; } + .xl-gap70 { gap: 70px; } + .xl-gap75 { gap: 75px; } + .xl-gap80 { gap: 80px; } + .xl-gap85 { gap: 85px; } + .xl-gap90 { gap: 90px; } + .xl-gap95 { gap: 95px; } + .xl-gap100 { gap: 100px; } + .xl-gap105 { gap: 105px; } + .xl-gap110 { gap: 110px; } + .xl-gap115 { gap: 115px; } + .xl-gap120 { gap: 120px; } + .xl-gap125 { gap: 125px; } + .xl-gap130 { gap: 130px; } + .xl-gap135 { gap: 135px; } + .xl-gap140 { gap: 140px; } + .xl-gap145 { gap: 145px; } + .xl-gap150 { gap: 150px; } + .xl-gap155 { gap: 155px; } + .xl-gap160 { gap: 160px; } + .xl-gap165 { gap: 165px; } + .xl-gap170 { gap: 170px; } + .xl-gap175 { gap: 175px; } + .xl-gap180 { gap: 180px; } + .xl-gap185 { gap: 185px; } + .xl-gap190 { gap: 190px; } + .xl-gap195 { gap: 195px; } + .xl-gap200 { gap: 200px; } + + .xl-x-gap0 { column-gap: 0 } + .xl-x-gap5 { column-gap: 5px; } + .xl-x-gap10 { column-gap:10px; } + .xl-x-gap15 { column-gap: 15px; } + .xl-x-gap20 { column-gap: 20px; } + .xl-x-gap25 { column-gap: 25px; } + .xl-x-gap30 { column-gap: 30px; } + .xl-x-gap35 { column-gap: 35px; } + .xl-x-gap40 { column-gap: 40px; } + .xl-x-gap45 { column-gap: 45px; } + .xl-x-gap50 { column-gap: 50px; } + .xl-x-gap55 { column-gap: 55px; } + .xl-x-gap60 { column-gap: 60px; } + .xl-x-gap65 { column-gap: 65px; } + .xl-x-gap70 { column-gap: 70px; } + .xl-x-gap75 { column-gap: 75px; } + .xl-x-gap80 { column-gap: 80px; } + .xl-x-gap85 { column-gap: 85px; } + .xl-x-gap90 { column-gap: 90px; } + .xl-x-gap95 { column-gap: 95px; } + .xl-x-gap100 { column-gap: 100px; } + .xl-x-gap105 { column-gap: 105px; } + .xl-x-gap110 { column-gap: 110px; } + .xl-x-gap115 { column-gap: 115px; } + .xl-x-gap120 { column-gap: 120px; } + .xl-x-gap125 { column-gap: 125px; } + .xl-x-gap130 { column-gap: 130px; } + .xl-x-gap135 { column-gap: 135px; } + .xl-x-gap140 { column-gap: 140px; } + .xl-x-gap145 { column-gap: 145px; } + .xl-x-gap150 { column-gap: 150px; } + .xl-x-gap155 { column-gap: 155px; } + .xl-x-gap160 { column-gap: 160px; } + .xl-x-gap165 { column-gap: 165px; } + .xl-x-gap170 { column-gap: 170px; } + .xl-x-gap175 { column-gap: 175px; } + .xl-x-gap180 { column-gap: 180px; } + .xl-x-gap185 { column-gap: 185px; } + .xl-x-gap190 { column-gap: 190px; } + .xl-x-gap195 { column-gap: 195px; } + .xl-x-gap200 { column-gap: 200px; } + + .xl-y-gap0 { row-gap: 0 } + .xl-y-gap5 { row-gap: 5px; } + .xl-y-gap10 { row-gap:10px; } + .xl-y-gap15 { row-gap: 15px; } + .xl-y-gap20 { row-gap: 20px; } + .xl-y-gap25 { row-gap: 25px; } + .xl-y-gap30 { row-gap: 30px; } + .xl-y-gap35 { row-gap: 35px; } + .xl-y-gap40 { row-gap: 40px; } + .xl-y-gap45 { row-gap: 45px; } + .xl-y-gap50 { row-gap: 50px; } + .xl-y-gap55 { row-gap: 55px; } + .xl-y-gap60 { row-gap: 60px; } + .xl-y-gap65 { row-gap: 65px; } + .xl-y-gap70 { row-gap: 70px; } + .xl-y-gap75 { row-gap: 75px; } + .xl-y-gap80 { row-gap: 80px; } + .xl-y-gap85 { row-gap: 85px; } + .xl-y-gap90 { row-gap: 90px; } + .xl-y-gap95 { row-gap: 95px; } + .xl-y-gap100 { row-gap: 100px; } + .xl-y-gap105 { row-gap: 105px; } + .xl-y-gap110 { row-gap: 110px; } + .xl-y-gap115 { row-gap: 115px; } + .xl-y-gap120 { row-gap: 120px; } + .xl-y-gap125 { row-gap: 125px; } + .xl-y-gap130 { row-gap: 130px; } + .xl-y-gap135 { row-gap: 135px; } + .xl-y-gap140 { row-gap: 140px; } + .xl-y-gap145 { row-gap: 145px; } + .xl-y-gap150 { row-gap: 150px; } + .xl-y-gap155 { row-gap: 155px; } + .xl-y-gap160 { row-gap: 160px; } + .xl-y-gap165 { row-gap: 165px; } + .xl-y-gap170 { row-gap: 170px; } + .xl-y-gap175 { row-gap: 175px; } + .xl-y-gap180 { row-gap: 180px; } + .xl-y-gap185 { row-gap: 185px; } + .xl-y-gap190 { row-gap: 190px; } + .xl-y-gap195 { row-gap: 195px; } + .xl-y-gap200 { row-gap: 200px; } + + /* Box display */ + + .xl-hide { display:none !important; } + .xl-flex { display: flex !important; align-self: auto; } + .xl-block { display: block !important; } + .xl-grid { display: grid; } + .xl-inline-grid { display: inline-grid; } + + /* Flex / Grid Align */ + + /* Alignment along the X axis (H Align) */ + + .xl-x-start { justify-content: start; } + .xl-x-end { justify-content: end; } + .xl-x-center { justify-content: center; } + .xl-x-between { justify-content: space-between; } + .xl-x-around { justify-content: space-around; } + .xl-x-evenly { justify-content: space-evenly; } + + .xl-g-start { justify-items: start; } + .xl-g-end { justify-items: end; } + .xl-g-center { justify-items: center; } + .xl-g-stretch { justify-items: stretch; } + + /* Alignment laid out along the Y axis */ + + .xl-v-normal { align-items: normal; } + .xl-v-start { align-items: start; } + .xl-v-end { align-items: end; } + .xl-v-center { align-items: center; } + .xl-v-stretch { align-items: stretch; } + .xl-v-baseline { align-items: baseline; } + + /* Alignment along the X axis (H Align) */ + + .xl-y-start { align-content: start; } + .xl-y-end { align-content: end; } + .xl-y-center { align-content: center; } + .xl-y-stretch { align-content: stretch; } + .xl-y-between { align-content: space-between; } + .xl-y-around { align-content: space-around; } + + /* Self alignment */ + + .xl-start { align-self: start; } + .xl-end { align-self: end; } + .xl-center { align-self: center; } + .xl-baseline { align-self: baseline; } + .xl-stretch { align-self: stretch; } + + /* Margins - General */ + + .xl-mga { margin: auto; } + .xl-mg0 { margin: 0 } + .xl-mg5 { margin: 5px; } + .xl-mg10 { margin:10px; } + .xl-mg15 { margin: 15px; } + .xl-mg20 { margin: 20px; } + .xl-mg25 { margin: 25px; } + .xl-mg30 { margin: 30px; } + .xl-mg35 { margin: 35px; } + .xl-mg40 { margin: 40px; } + .xl-mg45 { margin: 45px; } + .xl-mg50 { margin: 50px; } + .xl-mg55 { margin: 55px; } + .xl-mg60 { margin: 60px; } + .xl-mg65 { margin: 65px; } + .xl-mg70 { margin: 70px; } + .xl-mg75 { margin: 75px; } + .xl-mg80 { margin: 80px; } + .xl-mg85 { margin: 85px; } + .xl-mg90 { margin: 90px; } + .xl-mg95 { margin: 95px; } + .xl-mg100 { margin: 100px; } + .xl-mg105 { margin: 105px; } + .xl-mg110 { margin: 110px; } + .xl-mg115 { margin: 115px; } + .xl-mg120 { margin: 120px; } + .xl-mg125 { margin: 125px; } + .xl-mg130 { margin: 130px; } + .xl-mg135 { margin: 135px; } + .xl-mg140 { margin: 140px; } + .xl-mg145 { margin: 145px; } + .xl-mg150 { margin: 150px; } + .xl-mg155 { margin: 155px; } + .xl-mg160 { margin: 160px; } + .xl-mg165 { margin: 165px; } + .xl-mg170 { margin: 170px; } + .xl-mg175 { margin: 175px; } + .xl-mg180 { margin: 180px; } + .xl-mg185 { margin: 185px; } + .xl-mg190 { margin: 190px; } + .xl-mg195 { margin: 195px; } + .xl-mg200 { margin: 200px; } + + /* Margins - Top */ + + .xl-mta { margin-top: auto; } + .xl-mt0 { margin-top: 0; } + .xl-mt5 { margin-top: 5px; } + .xl-mt10 { margin-top:10px; } + .xl-mt15 { margin-top: 15px; } + .xl-mt20 { margin-top: 20px; } + .xl-mt25 { margin-top: 25px; } + .xl-mt30 { margin-top: 30px; } + .xl-mt35 { margin-top: 35px; } + .xl-mt40 { margin-top: 40px; } + .xl-mt45 { margin-top: 45px; } + .xl-mt50 { margin-top: 50px; } + .xl-mt55 { margin-top: 55px; } + .xl-mt60 { margin-top: 60px; } + .xl-mt65 { margin-top: 65px; } + .xl-mt70 { margin-top: 70px; } + .xl-mt75 { margin-top: 75px; } + .xl-mt80 { margin-top: 80px; } + .xl-mt85 { margin-top: 85px; } + .xl-mt90 { margin-top: 90px; } + .xl-mt95 { margin-top: 95px; } + .xl-mt100 { margin-top: 100px; } + .xl-mt105 { margin-top: 105px; } + .xl-mt110 { margin-top: 110px; } + .xl-mt115 { margin-top: 115px; } + .xl-mt120 { margin-top: 120px; } + .xl-mt125 { margin-top: 125px; } + .xl-mt130 { margin-top: 130px; } + .xl-mt135 { margin-top: 135px; } + .xl-mt140 { margin-top: 140px; } + .xl-mt145 { margin-top: 145px; } + .xl-mt150 { margin-top: 150px; } + .xl-mt155 { margin-top: 155px; } + .xl-mt160 { margin-top: 160px; } + .xl-mt165 { margin-top: 165px; } + .xl-mt170 { margin-top: 170px; } + .xl-mt175 { margin-top: 175px; } + .xl-mt180 { margin-top: 180px; } + .xl-mt185 { margin-top: 185px; } + .xl-mt190 { margin-top: 190px; } + .xl-mt195 { margin-top: 195px; } + .xl-mt200 { margin-top: 200px; } + + /* Negative Margins - Top */ + + .xl-mt-5 { margin-top: -5px; } + .xl-mt-10 { margin-top: -10px; } + .xl-mt-15 { margin-top: -15px; } + .xl-mt-20 { margin-top: -20px; } + .xl-mt-25 { margin-top: -25px; } + .xl-mt-30 { margin-top: -30px; } + .xl-mt-35 { margin-top: -35px; } + .xl-mt-40 { margin-top: -40px; } + .xl-mt-45 { margin-top: -45px; } + .xl-mt-50 { margin-top: -50px; } + .xl-mt-55 { margin-top: -55px; } + .xl-mt-60 { margin-top: -60px; } + .xl-mt-65 { margin-top: -65px; } + .xl-mt-70 { margin-top: -70px; } + .xl-mt-75 { margin-top: -75px; } + .xl-mt-80 { margin-top: -80px; } + .xl-mt-85 { margin-top: -85px; } + .xl-mt-90 { margin-top: -90px; } + .xl-mt-95 { margin-top: -95px; } + .xl-mt-100 { margin-top: -100px; } + .xl-mt-105 { margin-top: -105px; } + .xl-mt-110 { margin-top: -110px; } + .xl-mt-115 { margin-top: -115px; } + .xl-mt-120 { margin-top: -120px; } + .xl-mt-125 { margin-top: -125px; } + .xl-mt-130 { margin-top: -130px; } + .xl-mt-135 { margin-top: -135px; } + .xl-mt-140 { margin-top: -140px; } + .xl-mt-145 { margin-top: -145px; } + .xl-mt-150 { margin-top: -150px; } + .xl-mt-155 { margin-top: -155px; } + .xl-mt-160 { margin-top: -160px; } + .xl-mt-165 { margin-top: -165px; } + .xl-mt-170 { margin-top: -170px; } + .xl-mt-175 { margin-top: -175px; } + .xl-mt-180 { margin-top: -180px; } + .xl-mt-185 { margin-top: -185px; } + .xl-mt-190 { margin-top: -190px; } + .xl-mt-195 { margin-top: -195px; } + .xl-mt-200 { margin-top: -200px; } + + /* Margins - Bottom */ + + .xl-mba { margin-bottom: auto; } + .xl-mb0 { margin-bottom: 0 } + .xl-mb5 { margin-bottom: 5px; } + .xl-mb10 { margin-bottom:10px; } + .xl-mb15 { margin-bottom: 15px; } + .xl-mb20 { margin-bottom: 20px; } + .xl-mb25 { margin-bottom: 25px; } + .xl-mb30 { margin-bottom: 30px; } + .xl-mb35 { margin-bottom: 35px; } + .xl-mb40 { margin-bottom: 40px; } + .xl-mb45 { margin-bottom: 45px; } + .xl-mb50 { margin-bottom: 50px; } + .xl-mb55 { margin-bottom: 55px; } + .xl-mb60 { margin-bottom: 60px; } + .xl-mb65 { margin-bottom: 65px; } + .xl-mb70 { margin-bottom: 70px; } + .xl-mb75 { margin-bottom: 75px; } + .xl-mb80 { margin-bottom: 80px; } + .xl-mb85 { margin-bottom: 85px; } + .xl-mb90 { margin-bottom: 90px; } + .xl-mb95 { margin-bottom: 95px; } + .xl-mb100 { margin-bottom: 100px; } + .xl-mb105 { margin-bottom: 105px; } + .xl-mb110 { margin-bottom: 110px; } + .xl-mb115 { margin-bottom: 115px; } + .xl-mb120 { margin-bottom: 120px; } + .xl-mb125 { margin-bottom: 125px; } + .xl-mb130 { margin-bottom: 130px; } + .xl-mb135 { margin-bottom: 135px; } + .xl-mb140 { margin-bottom: 140px; } + .xl-mb145 { margin-bottom: 145px; } + .xl-mb150 { margin-bottom: 150px; } + .xl-mb155 { margin-bottom: 155px; } + .xl-mb160 { margin-bottom: 160px; } + .xl-mb165 { margin-bottom: 165px; } + .xl-mb170 { margin-bottom: 170px; } + .xl-mb175 { margin-bottom: 175px; } + .xl-mb180 { margin-bottom: 180px; } + .xl-mb185 { margin-bottom: 185px; } + .xl-mb190 { margin-bottom: 190px; } + .xl-mb195 { margin-bottom: 195px; } + .xl-mb200 { margin-bottom: 200px; } + + /* Margins - Left */ + + .xl-mla { margin-left: auto; } + .xl-ml0 { margin-left: 0; } + .xl-ml5 { margin-left: 5px; } + .xl-ml10 { margin-left:10px; } + .xl-ml15 { margin-left: 15px; } + .xl-ml20 { margin-left: 20px; } + .xl-ml25 { margin-left: 25px; } + .xl-ml30 { margin-left: 30px; } + .xl-ml35 { margin-left: 35px; } + .xl-ml40 { margin-left: 40px; } + .xl-ml45 { margin-left: 45px; } + .xl-ml50 { margin-left: 50px; } + .xl-ml55 { margin-left: 55px; } + .xl-ml60 { margin-left: 60px; } + .xl-ml65 { margin-left: 65px; } + .xl-ml70 { margin-left: 70px; } + .xl-ml75 { margin-left: 75px; } + .xl-ml80 { margin-left: 80px; } + .xl-ml85 { margin-left: 85px; } + .xl-ml90 { margin-left: 90px; } + .xl-ml95 { margin-left: 95px; } + .xl-ml100 { margin-left: 100px; } + .xl-ml105 { margin-left: 105px; } + .xl-ml110 { margin-left: 110px; } + .xl-ml115 { margin-left: 115px; } + .xl-ml120 { margin-left: 120px; } + .xl-ml125 { margin-left: 125px; } + .xl-ml130 { margin-left: 130px; } + .xl-ml135 { margin-left: 135px; } + .xl-ml140 { margin-left: 140px; } + .xl-ml145 { margin-left: 145px; } + .xl-ml150 { margin-left: 150px; } + .xl-ml155 { margin-left: 155px; } + .xl-ml160 { margin-left: 160px; } + .xl-ml165 { margin-left: 165px; } + .xl-ml170 { margin-left: 170px; } + .xl-ml175 { margin-left: 175px; } + .xl-ml180 { margin-left: 180px; } + .xl-ml185 { margin-left: 185px; } + .xl-ml190 { margin-left: 190px; } + .xl-ml195 { margin-left: 195px; } + .xl-ml200 { margin-left: 200px; } + + /* Margins - Right */ + + .xl-mra { margin-right: auto; } + .xl-mr0 { margin-right: 0; } + .xl-mr5 { margin-right: 5px; } + .xl-mr10 { margin-right:10px; } + .xl-mr15 { margin-right: 15px; } + .xl-mr20 { margin-right: 20px; } + .xl-mr25 { margin-right: 25px; } + .xl-mr30 { margin-right: 30px; } + .xl-mr35 { margin-right: 35px; } + .xl-mr40 { margin-right: 40px; } + .xl-mr45 { margin-right: 45px; } + .xl-mr50 { margin-right: 50px; } + .xl-mr55 { margin-right: 55px; } + .xl-mr60 { margin-right: 60px; } + .xl-mr65 { margin-right: 65px; } + .xl-mr70 { margin-right: 70px; } + .xl-mr75 { margin-right: 75px; } + .xl-mr80 { margin-right: 80px; } + .xl-mr85 { margin-right: 85px; } + .xl-mr90 { margin-right: 90px; } + .xl-mr95 { margin-right: 95px; } + .xl-mr100 { margin-right: 100px; } + .xl-mr105 { margin-right: 105px; } + .xl-mr110 { margin-right: 110px; } + .xl-mr115 { margin-right: 115px; } + .xl-mr120 { margin-right: 120px; } + .xl-mr125 { margin-right: 125px; } + .xl-mr130 { margin-right: 130px; } + .xl-mr135 { margin-right: 135px; } + .xl-mr140 { margin-right: 140px; } + .xl-mr145 { margin-right: 145px; } + .xl-mr150 { margin-right: 150px; } + .xl-mr155 { margin-right: 155px; } + .xl-mr160 { margin-right: 160px; } + .xl-mr165 { margin-right: 165px; } + .xl-mr170 { margin-right: 170px; } + .xl-mr175 { margin-right: 175px; } + .xl-mr180 { margin-right: 180px; } + .xl-mr185 { margin-right: 185px; } + .xl-mr190 { margin-right: 190px; } + .xl-mr195 { margin-right: 195px; } + .xl-mr200 { margin-right: 200px; } + + /* Margins - Top & Bottom */ + + .xl-mtba { margin-top: auto; margin-bottom: auto; } + .xl-mtb0 { margin-top: 0; margin-bottom: 0; } + .xl-mtb5 { margin-top: 5px; margin-bottom: 5px; } + .xl-mtb10 { margin-top:10px; margin-bottom: 10px; } + .xl-mtb15 { margin-top: 15px; margin-bottom: 15px; } + .xl-mtb20 { margin-top: 20px; margin-bottom: 20px; } + .xl-mtb25 { margin-top: 25px; margin-bottom: 25px; } + .xl-mtb30 { margin-top: 30px; margin-bottom: 30px; } + .xl-mtb35 { margin-top: 35px; margin-bottom: 35px; } + .xl-mtb40 { margin-top: 40px; margin-bottom: 40px; } + .xl-mtb45 { margin-top: 45px; margin-bottom: 45px; } + .xl-mtb50 { margin-top: 50px; margin-bottom: 50px; } + .xl-mtb55 { margin-top: 55px; margin-bottom: 55px; } + .xl-mtb60 { margin-top: 60px; margin-bottom: 60px; } + .xl-mtb65 { margin-top: 65px; margin-bottom: 65px; } + .xl-mtb70 { margin-top: 70px; margin-bottom: 70px; } + .xl-mtb75 { margin-top: 75px; margin-bottom: 75px; } + .xl-mtb80 { margin-top: 80px; margin-bottom: 80px; } + .xl-mtb85 { margin-top: 85px; margin-bottom: 85px; } + .xl-mtb90 { margin-top: 90px; margin-bottom: 90px; } + .xl-mtb95 { margin-top: 95px; margin-bottom: 95px; } + .xl-mtb100 { margin-top: 100px; margin-bottom: 100px; } + .xl-mtb105 { margin-top: 105px; margin-bottom: 105px; } + .xl-mtb110 { margin-top:110px; margin-bottom: 110px; } + .xl-mtb115 { margin-top: 115px; margin-bottom: 115px; } + .xl-mtb120 { margin-top: 120px; margin-bottom: 120px; } + .xl-mtb125 { margin-top: 125px; margin-bottom: 125px; } + .xl-mtb130 { margin-top: 130px; margin-bottom: 130px; } + .xl-mtb135 { margin-top: 135px; margin-bottom: 135px; } + .xl-mtb140 { margin-top: 140px; margin-bottom: 140px; } + .xl-mtb145 { margin-top: 145px; margin-bottom: 145px; } + .xl-mtb150 { margin-top: 150px; margin-bottom: 150px; } + .xl-mtb155 { margin-top: 155px; margin-bottom: 155px; } + .xl-mtb160 { margin-top: 160px; margin-bottom: 160px; } + .xl-mtb165 { margin-top: 165px; margin-bottom: 165px; } + .xl-mtb170 { margin-top: 170px; margin-bottom: 170px; } + .xl-mtb175 { margin-top: 175px; margin-bottom: 175px; } + .xl-mtb180 { margin-top: 180px; margin-bottom: 180px; } + .xl-mtb185 { margin-top: 185px; margin-bottom: 185px; } + .xl-mtb190 { margin-top: 190px; margin-bottom: 190px; } + .xl-mtb195 { margin-top: 195px; margin-bottom: 195px; } + .xl-mtb200 { margin-top: 200px; margin-bottom: 200px; } + + .xl-centered { margin-left: auto; margin-right: auto; } + .xl-filled { margin-left: 0; margin-right: 0; } + + .xl-mlr5 { margin-left: 5px; margin-right: 5px; } + .xl-mlr10 { margin-left: 10px; margin-right:10px; } + .xl-mlr15 { margin-left: 15px; margin-right: 15px; } + .xl-mlr20 { margin-left: 20px; margin-right: 20px; } + .xl-mlr25 { margin-left: 25px; margin-right: 25px; } + .xl-mlr30 { margin-left: 30px; margin-right: 30px; } + .xl-mlr35 { margin-left: 35px; margin-right: 35px; } + .xl-mlr40 { margin-left: 40px; margin-right: 40px; } + .xl-mlr45 { margin-left: 45px; margin-right: 45px; } + .xl-mlr50 { margin-left: 50px; margin-right: 50px; } + .xl-mlr55 { margin-left: 55px; margin-right: 55px; } + .xl-mlr60 { margin-left: 60px; margin-right: 60px; } + .xl-mlr65 { margin-left: 65px; margin-right: 65px; } + .xl-mlr70 { margin-left: 70px; margin-right: 70px; } + .xl-mlr75 { margin-left: 75px; margin-right: 75px; } + .xl-mlr80 { margin-left: 80px; margin-right: 80px; } + .xl-mlr85 { margin-left: 85px; margin-right: 85px; } + .xl-mlr90 { margin-left: 90px; margin-right: 90px; } + .xl-mlr95 { margin-left: 95px; margin-right: 95px; } + .xl-mlr100 { margin-left: 100px; margin-right: 100px; } + .xl-mlr105 { margin-left: 105px; margin-right: 105px; } + .xl-mlr110 { margin-left:110px; margin-right: 110px; } + .xl-mlr115 { margin-left: 115px; margin-right: 115px; } + .xl-mlr120 { margin-left: 120px; margin-right: 120px; } + .xl-mlr125 { margin-left: 125px; margin-right: 125px; } + .xl-mlr130 { margin-left: 130px; margin-right: 130px; } + .xl-mlr135 { margin-left: 135px; margin-right: 135px; } + .xl-mlr140 { margin-left: 140px; margin-right: 140px; } + .xl-mlr145 { margin-left: 145px; margin-right: 145px; } + .xl-mlr150 { margin-left: 150px; margin-right: 150px; } + .xl-mlr155 { margin-left: 155px; margin-right: 155px; } + .xl-mlr160 { margin-left: 160px; margin-right: 160px; } + .xl-mlr165 { margin-left: 165px; margin-right: 165px; } + .xl-mlr170 { margin-left: 170px; margin-right: 170px; } + .xl-mlr175 { margin-left: 175px; margin-right: 175px; } + .xl-mlr180 { margin-left: 180px; margin-right: 180px; } + .xl-mlr185 { margin-left: 185px; margin-right: 185px; } + .xl-mlr190 { margin-left: 190px; margin-right: 190px; } + .xl-mlr195 { margin-left: 195px; margin-right: 195px; } + .xl-mlr200 { margin-left: 200px; margin-right: 200px; } + + /* Paddings */ + + .xl-pda {padding: auto; } + .xl-pd0 { padding: 0 } + .xl-pd5 { padding: 5px; } + .xl-pd10 { padding:10px; } + .xl-pd15 { padding: 15px; } + .xl-pd20 { padding: 20px; } + .xl-pd25 { padding: 25px; } + .xl-pd30 { padding: 30px; } + .xl-pd35 { padding: 35px; } + .xl-pd40 { padding: 40px; } + .xl-pd45 { padding: 45px; } + .xl-pd50 { padding: 50px; } + .xl-pd55 { padding: 55px; } + .xl-pd60 { padding: 60px; } + .xl-pd65 { padding: 65px; } + .xl-pd70 { padding: 70px; } + .xl-pd75 { padding: 75px; } + .xl-pd80 { padding: 80px; } + .xl-pd85 { padding: 85px; } + .xl-pd90 { padding: 90px; } + .xl-pd95 { padding: 95px; } + .xl-pd100 { padding: 100px; } + .xl-pd105 { padding: 105px; } + .xl-pd110 { padding: 110px; } + .xl-pd115 { padding: 115px; } + .xl-pd120 { padding: 120px; } + .xl-pd125 { padding: 125px; } + .xl-pd130 { padding: 130px; } + .xl-pd135 { padding: 135px; } + .xl-pd140 { padding: 140px; } + .xl-pd145 { padding: 145px; } + .xl-pd150 { padding: 150px; } + .xl-pd155 { padding: 155px; } + .xl-pd160 { padding: 160px; } + .xl-pd165 { padding: 165px; } + .xl-pd170 { padding: 170px; } + .xl-pd175 { padding: 175px; } + .xl-pd180 { padding: 180px; } + .xl-pd185 { padding: 185px; } + .xl-pd190 { padding: 190px; } + .xl-pd195 { padding: 195px; } + .xl-pd200 { padding: 200px; } + + /* Paddings - Top */ + + .xl-pta { padding-top: auto; } + .xl-pt0 { padding-top: 0; } + .xl-pt5 { padding-top: 5px; } + .xl-pt10 { padding-top:10px; } + .xl-pt15 { padding-top: 15px; } + .xl-pt20 { padding-top: 20px; } + .xl-pt25 { padding-top: 25px; } + .xl-pt30 { padding-top: 30px; } + .xl-pt35 { padding-top: 35px; } + .xl-pt40 { padding-top: 40px; } + .xl-pt45 { padding-top: 45px; } + .xl-pt50 { padding-top: 50px; } + .xl-pt55 { padding-top: 55px; } + .xl-pt60 { padding-top: 60px; } + .xl-pt65 { padding-top: 65px; } + .xl-pt70 { padding-top: 70px; } + .xl-pt75 { padding-top: 75px; } + .xl-pt80 { padding-top: 80px; } + .xl-pt85 { padding-top: 85px; } + .xl-pt90 { padding-top: 90px; } + .xl-pt95 { padding-top: 95px; } + .xl-pt100 { padding-top: 100px; } + .xl-pt105 { padding-top: 105px; } + .xl-pt110 { padding-top: 110px; } + .xl-pt115 { padding-top: 115px; } + .xl-pt120 { padding-top: 120px; } + .xl-pt125 { padding-top: 125px; } + .xl-pt130 { padding-top: 130px; } + .xl-pt135 { padding-top: 135px; } + .xl-pt140 { padding-top: 140px; } + .xl-pt145 { padding-top: 145px; } + .xl-pt150 { padding-top: 150px; } + .xl-pt155 { padding-top: 155px; } + .xl-pt160 { padding-top: 160px; } + .xl-pt165 { padding-top: 165px; } + .xl-pt170 { padding-top: 170px; } + .xl-pt175 { padding-top: 175px; } + .xl-pt180 { padding-top: 180px; } + .xl-pt185 { padding-top: 185px; } + .xl-pt190 { padding-top: 190px; } + .xl-pt195 { padding-top: 195px; } + .xl-pt200 { padding-top: 200px; } + + /* Paddings - Bottom */ + + .xl-pba { padding-bottom: auto; } + .xl-pb0 { padding-bottom: 0 } + .xl-pb5 { padding-bottom: 5px; } + .xl-pb10 { padding-bottom:10px; } + .xl-pb15 { padding-bottom: 15px; } + .xl-pb20 { padding-bottom: 20px; } + .xl-pb25 { padding-bottom: 25px; } + .xl-pb30 { padding-bottom: 30px; } + .xl-pb35 { padding-bottom: 35px; } + .xl-pb40 { padding-bottom: 40px; } + .xl-pb45 { padding-bottom: 45px; } + .xl-pb50 { padding-bottom: 50px; } + .xl-pb55 { padding-bottom: 55px; } + .xl-pb60 { padding-bottom: 60px; } + .xl-pb65 { padding-bottom: 65px; } + .xl-pb70 { padding-bottom: 70px; } + .xl-pb75 { padding-bottom: 75px; } + .xl-pb80 { padding-bottom: 80px; } + .xl-pb85 { padding-bottom: 85px; } + .xl-pb90 { padding-bottom: 90px; } + .xl-pb95 { padding-bottom: 95px; } + .xl-pb100 { padding-bottom: 100px; } + .xl-pb105 { padding-bottom: 105px; } + .xl-pb110 { padding-bottom: 110px; } + .xl-pb115 { padding-bottom: 115px; } + .xl-pb120 { padding-bottom: 120px; } + .xl-pb125 { padding-bottom: 125px; } + .xl-pb130 { padding-bottom: 130px; } + .xl-pb135 { padding-bottom: 135px; } + .xl-pb140 { padding-bottom: 140px; } + .xl-pb145 { padding-bottom: 145px; } + .xl-pb150 { padding-bottom: 150px; } + .xl-pb155 { padding-bottom: 155px; } + .xl-pb160 { padding-bottom: 160px; } + .xl-pb165 { padding-bottom: 165px; } + .xl-pb170 { padding-bottom: 170px; } + .xl-pb175 { padding-bottom: 175px; } + .xl-pb180 { padding-bottom: 180px; } + .xl-pb185 { padding-bottom: 185px; } + .xl-pb190 { padding-bottom: 190px; } + .xl-pb195 { padding-bottom: 195px; } + .xl-pb200 { padding-bottom: 200px; } + + /* Paddings - Left */ + + .xl-pla { padding-left: auto; } + .xl-pl0 { padding-left: 0; } + .xl-pl5 { padding-left: 5px; } + .xl-pl10 { padding-left:10px; } + .xl-pl15 { padding-left: 15px; } + .xl-pl20 { padding-left: 20px; } + .xl-pl25 { padding-left: 25px; } + .xl-pl30 { padding-left: 30px; } + .xl-pl35 { padding-left: 35px; } + .xl-pl40 { padding-left: 40px; } + .xl-pl45 { padding-left: 45px; } + .xl-pl50 { padding-left: 50px; } + .xl-pl55 { padding-left: 55px; } + .xl-pl60 { padding-left: 60px; } + .xl-pl65 { padding-left: 65px; } + .xl-pl70 { padding-left: 70px; } + .xl-pl75 { padding-left: 75px; } + .xl-pl80 { padding-left: 80px; } + .xl-pl85 { padding-left: 85px; } + .xl-pl90 { padding-left: 90px; } + .xl-pl95 { padding-left: 95px; } + .xl-pl100 { padding-left: 100px; } + .xl-pl105 { padding-left: 105px; } + .xl-pl110 { padding-left: 110px; } + .xl-pl115 { padding-left: 115px; } + .xl-pl120 { padding-left: 120px; } + .xl-pl125 { padding-left: 125px; } + .xl-pl130 { padding-left: 130px; } + .xl-pl135 { padding-left: 135px; } + .xl-pl140 { padding-left: 140px; } + .xl-pl145 { padding-left: 145px; } + .xl-pl150 { padding-left: 150px; } + .xl-pl155 { padding-left: 155px; } + .xl-pl160 { padding-left: 160px; } + .xl-pl165 { padding-left: 165px; } + .xl-pl170 { padding-left: 170px; } + .xl-pl175 { padding-left: 175px; } + .xl-pl180 { padding-left: 180px; } + .xl-pl185 { padding-left: 185px; } + .xl-pl190 { padding-left: 190px; } + .xl-pl195 { padding-left: 195px; } + .xl-pl200 { padding-left: 200px; } + + /* Paddings - Right */ + + .xl-pra { padding-right: auto; } + .xl-pr0 { padding-right: 0; } + .xl-pr5 { padding-right: 5px; } + .xl-pr10 { padding-right:10px; } + .xl-pr15 { padding-right: 15px; } + .xl-pr20 { padding-right: 20px; } + .xl-pr25 { padding-right: 25px; } + .xl-pr30 { padding-right: 30px; } + .xl-pr35 { padding-right: 35px; } + .xl-pr40 { padding-right: 40px; } + .xl-pr45 { padding-right: 45px; } + .xl-pr50 { padding-right: 50px; } + .xl-pr55 { padding-right: 55px; } + .xl-pr60 { padding-right: 60px; } + .xl-pr65 { padding-right: 65px; } + .xl-pr70 { padding-right: 70px; } + .xl-pr75 { padding-right: 75px; } + .xl-pr80 { padding-right: 80px; } + .xl-pr85 { padding-right: 85px; } + .xl-pr90 { padding-right: 90px; } + .xl-pr95 { padding-right: 95px; } + .xl-pr100 { padding-right: 100px; } + .xl-pr105 { padding-right: 105px; } + .xl-pr110 { padding-right: 110px; } + .xl-pr115 { padding-right: 115px; } + .xl-pr120 { padding-right: 120px; } + .xl-pr125 { padding-right: 125px; } + .xl-pr130 { padding-right: 130px; } + .xl-pr135 { padding-right: 135px; } + .xl-pr140 { padding-right: 140px; } + .xl-pr145 { padding-right: 145px; } + .xl-pr150 { padding-right: 150px; } + .xl-pr155 { padding-right: 155px; } + .xl-pr160 { padding-right: 160px; } + .xl-pr165 { padding-right: 165px; } + .xl-pr170 { padding-right: 170px; } + .xl-pr175 { padding-right: 175px; } + .xl-pr180 { padding-right: 180px; } + .xl-pr185 { padding-right: 185px; } + .xl-pr190 { padding-right: 190px; } + .xl-pr195 { padding-right: 195px; } + .xl-pr200 { padding-right: 200px; } + + /* Paddings - Top & Bottom */ + + .xl-ptb-a { padding-top: auto; padding-bottom: auto; } + .xl-ptb0 { padding-top: 0; padding-bottom: 0; } + .xl-ptb5 { padding-top: 5px; padding-bottom: 5px; } + .xl-ptb10 { padding-top:10px; padding-bottom: 10px; } + .xl-ptb15 { padding-top: 15px; padding-bottom: 15px; } + .xl-ptb20 { padding-top: 20px; padding-bottom: 20px; } + .xl-ptb25 { padding-top: 25px; padding-bottom: 25px; } + .xl-ptb30 { padding-top: 30px; padding-bottom: 30px; } + .xl-ptb35 { padding-top: 35px; padding-bottom: 35px; } + .xl-ptb40 { padding-top: 40px; padding-bottom: 40px; } + .xl-ptb45 { padding-top: 45px; padding-bottom: 45px; } + .xl-ptb50 { padding-top: 50px; padding-bottom: 50px; } + .xl-ptb55 { padding-top: 55px; padding-bottom: 55px; } + .xl-ptb60 { padding-top: 60px; padding-bottom: 60px; } + .xl-ptb65 { padding-top: 65px; padding-bottom: 65px; } + .xl-ptb70 { padding-top: 70px; padding-bottom: 70px; } + .xl-ptb75 { padding-top: 75px; padding-bottom: 75px; } + .xl-ptb80 { padding-top: 80px; padding-bottom: 80px; } + .xl-ptb85 { padding-top: 85px; padding-bottom: 85px; } + .xl-ptb90 { padding-top: 90px; padding-bottom: 90px; } + .xl-ptb95 { padding-top: 95px; padding-bottom: 95px; } + .xl-ptb100 { padding-top: 100px; padding-bottom: 100px; } + .xl-ptb105 { padding-top: 105px; padding-bottom: 105px; } + .xl-ptb110 { padding-top:110px; padding-bottom: 110px; } + .xl-ptb115 { padding-top: 115px; padding-bottom: 115px; } + .xl-ptb120 { padding-top: 120px; padding-bottom: 120px; } + .xl-ptb125 { padding-top: 125px; padding-bottom: 125px; } + .xl-ptb130 { padding-top: 130px; padding-bottom: 130px; } + .xl-ptb135 { padding-top: 135px; padding-bottom: 135px; } + .xl-ptb140 { padding-top: 140px; padding-bottom: 140px; } + .xl-ptb145 { padding-top: 145px; padding-bottom: 145px; } + .xl-ptb150 { padding-top: 150px; padding-bottom: 150px; } + .xl-ptb155 { padding-top: 155px; padding-bottom: 155px; } + .xl-ptb160 { padding-top: 160px; padding-bottom: 160px; } + .xl-ptb165 { padding-top: 165px; padding-bottom: 165px; } + .xl-ptb170 { padding-top: 170px; padding-bottom: 170px; } + .xl-ptb175 { padding-top: 175px; padding-bottom: 175px; } + .xl-ptb180 { padding-top: 180px; padding-bottom: 180px; } + .xl-ptb185 { padding-top: 185px; padding-bottom: 185px; } + .xl-ptb190 { padding-top: 190px; padding-bottom: 190px; } + .xl-ptb195 { padding-top: 195px; padding-bottom: 195px; } + .xl-ptb200 { padding-top: 200px; padding-bottom: 200px; } + + .xl-plra { padding-left: auto; padding-right: auto; } + .xl-plr0 { padding-left: 0; padding-right: 0; } + .xl-plr5 { padding-left: 5px; padding-right: 5px; } + .xl-plr10 { padding-left: 10px; padding-right:10px; } + .xl-plr15 { padding-left: 15px; padding-right: 15px; } + .xl-plr20 { padding-left: 20px; padding-right: 20px; } + .xl-plr25 { padding-left: 25px; padding-right: 25px; } + .xl-plr30 { padding-left: 30px; padding-right: 30px; } + .xl-plr35 { padding-left: 35px; padding-right: 35px; } + .xl-plr40 { padding-left: 40px; padding-right: 40px; } + .xl-plr45 { padding-left: 45px; padding-right: 45px; } + .xl-plr50 { padding-left: 50px; padding-right: 50px; } + .xl-plr55 { padding-left: 55px; padding-right: 55px; } + .xl-plr60 { padding-left: 60px; padding-right: 60px; } + .xl-plr65 { padding-left: 65px; padding-right: 65px; } + .xl-plr70 { padding-left: 70px; padding-right: 70px; } + .xl-plr75 { padding-left: 75px; padding-right: 75px; } + .xl-plr80 { padding-left: 80px; padding-right: 80px; } + .xl-plr85 { padding-left: 85px; padding-right: 85px; } + .xl-plr90 { padding-left: 90px; padding-right: 90px; } + .xl-plr95 { padding-left: 95px; padding-right: 95px; } + .xl-plr100 { padding-left: 100px; padding-right: 100px; } + .xl-plr105 { padding-left: 105px; padding-right: 105px; } + .xl-plr110 { padding-left:110px; padding-right: 110px; } + .xl-plr115 { padding-left: 115px; padding-right: 115px; } + .xl-plr120 { padding-left: 120px; padding-right: 120px; } + .xl-plr125 { padding-left: 125px; padding-right: 125px; } + .xl-plr130 { padding-left: 130px; padding-right: 130px; } + .xl-plr135 { padding-left: 135px; padding-right: 135px; } + .xl-plr140 { padding-left: 140px; padding-right: 140px; } + .xl-plr145 { padding-left: 145px; padding-right: 145px; } + .xl-plr150 { padding-left: 150px; padding-right: 150px; } + .xl-plr155 { padding-left: 155px; padding-right: 155px; } + .xl-plr160 { padding-left: 160px; padding-right: 160px; } + .xl-plr165 { padding-left: 165px; padding-right: 165px; } + .xl-plr170 { padding-left: 170px; padding-right: 170px; } + .xl-plr175 { padding-left: 175px; padding-right: 175px; } + .xl-plr180 { padding-left: 180px; padding-right: 180px; } + .xl-plr185 { padding-left: 185px; padding-right: 185px; } + .xl-plr190 { padding-left: 190px; padding-right: 190px; } + .xl-plr195 { padding-left: 195px; padding-right: 195px; } + .xl-plr200 { padding-left: 200px; padding-right: 200px; } + + /* Font Sizes */ + + .xl-fz8 { font-size: 8px; } + .xl-fz10 { font-size: 10px; } + .xl-fz11 { font-size: 11px; } + .xl-fz12 { font-size: 12px; } + .xl-fz13 { font-size: 13px; } + .xl-fz14 { font-size: 14px; } + .xl-fz16 { font-size: 16px; } + .xl-fz18 { font-size: 18px; } + .xl-fz20 { font-size: 20px; } + .xl-fz22 { font-size: 22px; } + .xl-fz24 { font-size: 24px; } + .xl-fz26 { font-size: 26px; } + .xl-fz28 { font-size: 28px; } + .xl-fz30 { font-size: 30px; } + .xl-fz32 { font-size: 32px; } + .xl-fz34 { font-size: 34px; } + .xl-fz36 { font-size: 36px; } + .xl-fz38 { font-size: 38px; } + .xl-fz40 { font-size: 40px; } + .xl-fz42 { font-size: 42px; } + .xl-fz44 { font-size: 44px; } + .xl-fz46 { font-size: 46px; } + .xl-fz48 { font-size: 48px; } + .xl-fz50 { font-size: 50px; } + .xl-fz52 { font-size: 52px; } + .xl-fz54 { font-size: 54px; } + .xl-fz56 { font-size: 56px; } + .xl-fz58 { font-size: 58px; } + .xl-fz60 { font-size: 60px; } + + /* Custom font sizes breakpoints */ + + .f1 { font-size: var(--xl-f1); } + .f2 { font-size: var(--xl-f2); } + .f3 { font-size: var(--xl-f3); } + .f4 { font-size: var(--xl-f4); } + .f5 { font-size: var(--xl-f5); } + .f6 { font-size: var(--xl-f6); } + .f7 { font-size: var(--xl-f7); } + .f8 { font-size: var(--xl-f8); } + .f9 { font-size: var(--xl-f9); } + + /* Typography > Text Utils */ + + .xl-text-normal { font-weight: normal; } + .xl-text-light { font-weight: 200; } + .xl-text-medium { font-weight: 500; } + .xl-text-semibold { font-weight: 600; } + .xl-text-bold { font-weight: bold; } + .xl-text-italic { font-style: italic; } + .xl-text-through { text-decoration: line-through; } + .xl-text-small { font-size: 14px; } + .xl-text-center{ text-align:center; } + .xl-text-left { text-align: left; } + .xl-text-right { text-align: right; } + .xl-text-justify { text-align: justify; } +} + +/* Extra extra large breakpoint */ + +@media only screen and (min-width : 1400px) { + + .wrapper{ max-width: 1400px; } + + /* Grid Sizes (as 12 cols) */ + + .xxl1 { width: 8.33333%; } + .xxl2 { width: 16.66666%; } + .xxl3 { width: 24.99999%; } + .xxl4 { width: 33.33333%; } + .xxl5 { width: 41.66666%; } + .xxl6 { width: 49.99999%; } + .xxl7 { width: 58.33333%; } + .xxl8 { width: 66.66666%; } + .xxl9 { width: 74.99999%; } + .xxl10{ width: 83.33333%; } + .xxl11{ width: 91.66666%; } + .xxl12 { width: 99.99999%; } + + /* Grid Sizes (as percent) */ + + .xxl-5 { width: 5%; } + .xxl-10 { width: 10%; } + .xxl-15 { width: 15%; } + .xxl-20 { width: 20%; } + .xxl-25 { width: 25%; } + .xxl-30 { width: 30%; } + .xxl-35 { width: 35%; } + .xxl-40 { width: 40%; } + .xxl-45 { width: 45%; } + .xxl-50 { width: 50%; } + .xxl-55 { width: 55%; } + .xxl-65 { width: 65%; } + .xxl-70 { width: 70%; } + .xxl-75 { width: 75%; } + .xxl-80 { width: 80%; } + .xxl-85 { width: 85%; } + .xxl-90 { width: 90%; } + .xxl-95 { width: 90%; } + .xxl-100{ width: 100%; } + .xxl-a { width: auto; } + + /* Grid Temaplte Columns */ + + .xxl-tc1 { grid-template-columns: 1fr; } + .xxl-tc2 { grid-template-columns: repeat(2, 1fr); } + .xxl-tc3 { grid-template-columns: repeat(3, 1fr); } + .xxl-tc4 { grid-template-columns: repeat(4, 1fr); } + .xxl-tc5 { grid-template-columns: repeat(5, 1fr); } + .xxl-tc6 { grid-template-columns: repeat(6, 1fr); } + .xxl-tc7 { grid-template-columns: repeat(7, 1fr); } + .xxl-tc8 { grid-template-columns: repeat(8, 1fr); } + .xxl-tc9 { grid-template-columns: repeat(9, 1fr); } + .xxl-tc10 { grid-template-columns: repeat(10, 1fr); } + .xxl-tc11 { grid-template-columns: repeat(11, 1fr); } + .xxl-tc12 { grid-template-columns: repeat(12, 1fr); } + + .xxl-tc-1 { grid-template-columns: auto; } + .xxl-tc-2 { grid-template-columns: repeat(2, auto); } + .xxl-tc-3 { grid-template-columns: repeat(3, auto); } + .xxl-tc-4 { grid-template-columns: repeat(4, auto); } + .xxl-tc-5 { grid-template-columns: repeat(5, auto); } + .xxl-tc-6 { grid-template-columns: repeat(6, auto); } + .xxl-tc-7 { grid-template-columns: repeat(7, auto); } + .xxl-tc-8 { grid-template-columns: repeat(8, auto); } + .xxl-tc-9 { grid-template-columns: repeat(9, auto); } + .xxl-tc-10 { grid-template-columns: repeat(10, auto); } + .xxl-tc-11 { grid-template-columns: repeat(11, auto); } + .xxl-tc-12 { grid-template-columns: repeat(12, auto); } + + /* Grid spanning */ + + .xxl-no-span { grid-column: auto; } + .xxl-span2 { grid-column: span 2; } + .xxl-span3 { grid-column: span 3; } + .xxl-span4 { grid-column: span 4; } + .xxl-span5 { grid-column: span 5; } + .xxl-span6 { grid-column: span 6; } + .xxl-span7 { grid-column: span 7; } + .xxl-span8 { grid-column: span 8; } + .xxl-span9 { grid-column: span 9; } + .xxl-span10 { grid-column: span 10;} + .xxl-span11 { grid-column: span 11; } + .xxl-span12 { grid-column: span 12; } + + /* Grid gap */ + + .xxl-gap0 { gap: 0 } + .xxl-gap5 { gap: 5px; } + .xxl-gap10 { gap:10px; } + .xxl-gap15 { gap: 15px; } + .xxl-gap20 { gap: 20px; } + .xxl-gap25 { gap: 25px; } + .xxl-gap30 { gap: 30px; } + .xxl-gap35 { gap: 35px; } + .xxl-gap40 { gap: 40px; } + .xxl-gap45 { gap: 45px; } + .xxl-gap50 { gap: 50px; } + .xxl-gap55 { gap: 55px; } + .xxl-gap60 { gap: 60px; } + .xxl-gap65 { gap: 65px; } + .xxl-gap70 { gap: 70px; } + .xxl-gap75 { gap: 75px; } + .xxl-gap80 { gap: 80px; } + .xxl-gap85 { gap: 85px; } + .xxl-gap90 { gap: 90px; } + .xxl-gap95 { gap: 95px; } + .xxl-gap100 { gap: 100px; } + .xxl-gap105 { gap: 105px; } + .xxl-gap110 { gap: 110px; } + .xxl-gap115 { gap: 115px; } + .xxl-gap120 { gap: 120px; } + .xxl-gap125 { gap: 125px; } + .xxl-gap130 { gap: 130px; } + .xxl-gap135 { gap: 135px; } + .xxl-gap140 { gap: 140px; } + .xxl-gap145 { gap: 145px; } + .xxl-gap150 { gap: 150px; } + .xxl-gap155 { gap: 155px; } + .xxl-gap160 { gap: 160px; } + .xxl-gap165 { gap: 165px; } + .xxl-gap170 { gap: 170px; } + .xxl-gap175 { gap: 175px; } + .xxl-gap180 { gap: 180px; } + .xxl-gap185 { gap: 185px; } + .xxl-gap190 { gap: 190px; } + .xxl-gap195 { gap: 195px; } + .xxl-gap200 { gap: 200px; } + + .xxl-x-gap0 { column-gap: 0 } + .xxl-x-gap5 { column-gap: 5px; } + .xxl-x-gap10 { column-gap:10px; } + .xxl-x-gap15 { column-gap: 15px; } + .xxl-x-gap20 { column-gap: 20px; } + .xxl-x-gap25 { column-gap: 25px; } + .xxl-x-gap30 { column-gap: 30px; } + .xxl-x-gap35 { column-gap: 35px; } + .xxl-x-gap40 { column-gap: 40px; } + .xxl-x-gap45 { column-gap: 45px; } + .xxl-x-gap50 { column-gap: 50px; } + .xxl-x-gap55 { column-gap: 55px; } + .xxl-x-gap60 { column-gap: 60px; } + .xxl-x-gap65 { column-gap: 65px; } + .xxl-x-gap70 { column-gap: 70px; } + .xxl-x-gap75 { column-gap: 75px; } + .xxl-x-gap80 { column-gap: 80px; } + .xxl-x-gap85 { column-gap: 85px; } + .xxl-x-gap90 { column-gap: 90px; } + .xxl-x-gap95 { column-gap: 95px; } + .xxl-x-gap100 { column-gap: 100px; } + .xxl-x-gap105 { column-gap: 105px; } + .xxl-x-gap110 { column-gap: 110px; } + .xxl-x-gap115 { column-gap: 115px; } + .xxl-x-gap120 { column-gap: 120px; } + .xxl-x-gap125 { column-gap: 125px; } + .xxl-x-gap130 { column-gap: 130px; } + .xxl-x-gap135 { column-gap: 135px; } + .xxl-x-gap140 { column-gap: 140px; } + .xxl-x-gap145 { column-gap: 145px; } + .xxl-x-gap150 { column-gap: 150px; } + .xxl-x-gap155 { column-gap: 155px; } + .xxl-x-gap160 { column-gap: 160px; } + .xxl-x-gap165 { column-gap: 165px; } + .xxl-x-gap170 { column-gap: 170px; } + .xxl-x-gap175 { column-gap: 175px; } + .xxl-x-gap180 { column-gap: 180px; } + .xxl-x-gap185 { column-gap: 185px; } + .xxl-x-gap190 { column-gap: 190px; } + .xxl-x-gap195 { column-gap: 195px; } + .xxl-x-gap200 { column-gap: 200px; } + + .xxl-y-gap0 { row-gap: 0 } + .xxl-y-gap5 { row-gap: 5px; } + .xxl-y-gap10 { row-gap:10px; } + .xxl-y-gap15 { row-gap: 15px; } + .xxl-y-gap20 { row-gap: 20px; } + .xxl-y-gap25 { row-gap: 25px; } + .xxl-y-gap30 { row-gap: 30px; } + .xxl-y-gap35 { row-gap: 35px; } + .xxl-y-gap40 { row-gap: 40px; } + .xxl-y-gap45 { row-gap: 45px; } + .xxl-y-gap50 { row-gap: 50px; } + .xxl-y-gap55 { row-gap: 55px; } + .xxl-y-gap60 { row-gap: 60px; } + .xxl-y-gap65 { row-gap: 65px; } + .xxl-y-gap70 { row-gap: 70px; } + .xxl-y-gap75 { row-gap: 75px; } + .xxl-y-gap80 { row-gap: 80px; } + .xxl-y-gap85 { row-gap: 85px; } + .xxl-y-gap90 { row-gap: 90px; } + .xxl-y-gap95 { row-gap: 95px; } + .xxl-y-gap100 { row-gap: 100px; } + .xxl-y-gap105 { row-gap: 105px; } + .xxl-y-gap110 { row-gap: 110px; } + .xxl-y-gap115 { row-gap: 115px; } + .xxl-y-gap120 { row-gap: 120px; } + .xxl-y-gap125 { row-gap: 125px; } + .xxl-y-gap130 { row-gap: 130px; } + .xxl-y-gap135 { row-gap: 135px; } + .xxl-y-gap140 { row-gap: 140px; } + .xxl-y-gap145 { row-gap: 145px; } + .xxl-y-gap150 { row-gap: 150px; } + .xxl-y-gap155 { row-gap: 155px; } + .xxl-y-gap160 { row-gap: 160px; } + .xxl-y-gap165 { row-gap: 165px; } + .xxl-y-gap170 { row-gap: 170px; } + .xxl-y-gap175 { row-gap: 175px; } + .xxl-y-gap180 { row-gap: 180px; } + .xxl-y-gap185 { row-gap: 185px; } + .xxl-y-gap190 { row-gap: 190px; } + .xxl-y-gap195 { row-gap: 195px; } + .xxl-y-gap200 { row-gap: 200px; } + + /* Box display */ + + .xxl-hide { display:none !important; } + .xxl-flex { display: flex !important; align-self: auto; } + .xxl-block { display: block !important; } + .xxl-grid { display: grid; } + .xxl-inline-grid { display: inline-grid; } + + /* Flex / Grid Align */ + + /* Alignment along the X axis (H Align) */ + + .xxl-x-start { justify-content: start; } + .xxl-x-end { justify-content: end; } + .xxl-x-center { justify-content: center; } + .xxl-x-between { justify-content: space-between; } + .xxl-x-around { justify-content: space-around; } + .xxl-x-evenly { justify-content: space-evenly; } + + .xxl-g-start { justify-items: start; } + .xxl-g-end { justify-items: end; } + .xxl-g-center { justify-items: center; } + .xxl-g-stretch { justify-items: stretch; } + + /* Alignment laid out along the Y axis */ + + .xxl-v-normal { align-items: normal; } + .xxl-v-start { align-items: start; } + .xxl-v-end { align-items: end; } + .xxl-v-center { align-items: center; } + .xxl-v-stretch { align-items: stretch; } + .xxl-v-baseline { align-items: baseline; } + + /* Alignment along the X axis (H Align) */ + + .xxl-y-start { align-content: start; } + .xxl-y-end { align-content: end; } + .xxl-y-center { align-content: center; } + .xxl-y-stretch { align-content: stretch; } + .xxl-y-between { align-content: space-between; } + .xxl-y-around { align-content: space-around; } + + /* Self alignment */ + + .xxl-start { align-self: start; } + .xxl-end { align-self: end; } + .xxl-center { align-self: center; } + .xxl-baseline { align-self: baseline; } + .xxl-stretch { align-self: stretch; } + + /* Margins - General */ + + .xxl-mga { margin: auto; } + .xxl-mg0 { margin: 0 } + .xxl-mg5 { margin: 5px; } + .xxl-mg10 { margin:10px; } + .xxl-mg15 { margin: 15px; } + .xxl-mg20 { margin: 20px; } + .xxl-mg25 { margin: 25px; } + .xxl-mg30 { margin: 30px; } + .xxl-mg35 { margin: 35px; } + .xxl-mg40 { margin: 40px; } + .xxl-mg45 { margin: 45px; } + .xxl-mg50 { margin: 50px; } + .xxl-mg55 { margin: 55px; } + .xxl-mg60 { margin: 60px; } + .xxl-mg65 { margin: 65px; } + .xxl-mg70 { margin: 70px; } + .xxl-mg75 { margin: 75px; } + .xxl-mg80 { margin: 80px; } + .xxl-mg85 { margin: 85px; } + .xxl-mg90 { margin: 90px; } + .xxl-mg95 { margin: 95px; } + .xxl-mg100 { margin: 100px; } + .xxl-mg105 { margin: 105px; } + .xxl-mg110 { margin: 110px; } + .xxl-mg115 { margin: 115px; } + .xxl-mg120 { margin: 120px; } + .xxl-mg125 { margin: 125px; } + .xxl-mg130 { margin: 130px; } + .xxl-mg135 { margin: 135px; } + .xxl-mg140 { margin: 140px; } + .xxl-mg145 { margin: 145px; } + .xxl-mg150 { margin: 150px; } + .xxl-mg155 { margin: 155px; } + .xxl-mg160 { margin: 160px; } + .xxl-mg165 { margin: 165px; } + .xxl-mg170 { margin: 170px; } + .xxl-mg175 { margin: 175px; } + .xxl-mg180 { margin: 180px; } + .xxl-mg185 { margin: 185px; } + .xxl-mg190 { margin: 190px; } + .xxl-mg195 { margin: 195px; } + .xxl-mg200 { margin: 200px; } + + /* Margins - Top */ + + .xxl-mta { margin-top: auto; } + .xxl-mt0 { margin-top: 0; } + .xxl-mt5 { margin-top: 5px; } + .xxl-mt10 { margin-top:10px; } + .xxl-mt15 { margin-top: 15px; } + .xxl-mt20 { margin-top: 20px; } + .xxl-mt25 { margin-top: 25px; } + .xxl-mt30 { margin-top: 30px; } + .xxl-mt35 { margin-top: 35px; } + .xxl-mt40 { margin-top: 40px; } + .xxl-mt45 { margin-top: 45px; } + .xxl-mt50 { margin-top: 50px; } + .xxl-mt55 { margin-top: 55px; } + .xxl-mt60 { margin-top: 60px; } + .xxl-mt65 { margin-top: 65px; } + .xxl-mt70 { margin-top: 70px; } + .xxl-mt75 { margin-top: 75px; } + .xxl-mt80 { margin-top: 80px; } + .xxl-mt85 { margin-top: 85px; } + .xxl-mt90 { margin-top: 90px; } + .xxl-mt95 { margin-top: 95px; } + .xxl-mt100 { margin-top: 100px; } + .xxl-mt105 { margin-top: 105px; } + .xxl-mt110 { margin-top: 110px; } + .xxl-mt115 { margin-top: 115px; } + .xxl-mt120 { margin-top: 120px; } + .xxl-mt125 { margin-top: 125px; } + .xxl-mt130 { margin-top: 130px; } + .xxl-mt135 { margin-top: 135px; } + .xxl-mt140 { margin-top: 140px; } + .xxl-mt145 { margin-top: 145px; } + .xxl-mt150 { margin-top: 150px; } + .xxl-mt155 { margin-top: 155px; } + .xxl-mt160 { margin-top: 160px; } + .xxl-mt165 { margin-top: 165px; } + .xxl-mt170 { margin-top: 170px; } + .xxl-mt175 { margin-top: 175px; } + .xxl-mt180 { margin-top: 180px; } + .xxl-mt185 { margin-top: 185px; } + .xxl-mt190 { margin-top: 190px; } + .xxl-mt195 { margin-top: 195px; } + .xxl-mt200 { margin-top: 200px; } + + /* Negative Margins - Top */ + + .xxl-mt-5 { margin-top: -5px; } + .xxl-mt-10 { margin-top: -10px; } + .xxl-mt-15 { margin-top: -15px; } + .xxl-mt-20 { margin-top: -20px; } + .xxl-mt-25 { margin-top: -25px; } + .xxl-mt-30 { margin-top: -30px; } + .xxl-mt-35 { margin-top: -35px; } + .xxl-mt-40 { margin-top: -40px; } + .xxl-mt-45 { margin-top: -45px; } + .xxl-mt-50 { margin-top: -50px; } + .xxl-mt-55 { margin-top: -55px; } + .xxl-mt-60 { margin-top: -60px; } + .xxl-mt-65 { margin-top: -65px; } + .xxl-mt-70 { margin-top: -70px; } + .xxl-mt-75 { margin-top: -75px; } + .xxl-mt-80 { margin-top: -80px; } + .xxl-mt-85 { margin-top: -85px; } + .xxl-mt-90 { margin-top: -90px; } + .xxl-mt-95 { margin-top: -95px; } + .xxl-mt-100 { margin-top: -100px; } + .xxl-mt-105 { margin-top: -105px; } + .xxl-mt-110 { margin-top: -110px; } + .xxl-mt-115 { margin-top: -115px; } + .xxl-mt-120 { margin-top: -120px; } + .xxl-mt-125 { margin-top: -125px; } + .xxl-mt-130 { margin-top: -130px; } + .xxl-mt-135 { margin-top: -135px; } + .xxl-mt-140 { margin-top: -140px; } + .xxl-mt-145 { margin-top: -145px; } + .xxl-mt-150 { margin-top: -150px; } + .xxl-mt-155 { margin-top: -155px; } + .xxl-mt-160 { margin-top: -160px; } + .xxl-mt-165 { margin-top: -165px; } + .xxl-mt-170 { margin-top: -170px; } + .xxl-mt-175 { margin-top: -175px; } + .xxl-mt-180 { margin-top: -180px; } + .xxl-mt-185 { margin-top: -185px; } + .xxl-mt-190 { margin-top: -190px; } + .xxl-mt-195 { margin-top: -195px; } + .xxl-mt-200 { margin-top: -200px; } + + /* Margins - Bottom */ + + .xxl-mba { margin-bottom: auto; } + .xxl-mb0 { margin-bottom: 0 } + .xxl-mb5 { margin-bottom: 5px; } + .xxl-mb10 { margin-bottom:10px; } + .xxl-mb15 { margin-bottom: 15px; } + .xxl-mb20 { margin-bottom: 20px; } + .xxl-mb25 { margin-bottom: 25px; } + .xxl-mb30 { margin-bottom: 30px; } + .xxl-mb35 { margin-bottom: 35px; } + .xxl-mb40 { margin-bottom: 40px; } + .xxl-mb45 { margin-bottom: 45px; } + .xxl-mb50 { margin-bottom: 50px; } + .xxl-mb55 { margin-bottom: 55px; } + .xxl-mb60 { margin-bottom: 60px; } + .xxl-mb65 { margin-bottom: 65px; } + .xxl-mb70 { margin-bottom: 70px; } + .xxl-mb75 { margin-bottom: 75px; } + .xxl-mb80 { margin-bottom: 80px; } + .xxl-mb85 { margin-bottom: 85px; } + .xxl-mb90 { margin-bottom: 90px; } + .xxl-mb95 { margin-bottom: 95px; } + .xxl-mb100 { margin-bottom: 100px; } + .xxl-mb105 { margin-bottom: 105px; } + .xxl-mb110 { margin-bottom: 110px; } + .xxl-mb115 { margin-bottom: 115px; } + .xxl-mb120 { margin-bottom: 120px; } + .xxl-mb125 { margin-bottom: 125px; } + .xxl-mb130 { margin-bottom: 130px; } + .xxl-mb135 { margin-bottom: 135px; } + .xxl-mb140 { margin-bottom: 140px; } + .xxl-mb145 { margin-bottom: 145px; } + .xxl-mb150 { margin-bottom: 150px; } + .xxl-mb155 { margin-bottom: 155px; } + .xxl-mb160 { margin-bottom: 160px; } + .xxl-mb165 { margin-bottom: 165px; } + .xxl-mb170 { margin-bottom: 170px; } + .xxl-mb175 { margin-bottom: 175px; } + .xxl-mb180 { margin-bottom: 180px; } + .xxl-mb185 { margin-bottom: 185px; } + .xxl-mb190 { margin-bottom: 190px; } + .xxl-mb195 { margin-bottom: 195px; } + .xxl-mb200 { margin-bottom: 200px; } + + /* Margins - Left */ + + .xxl-mla { margin-left: auto; } + .xxl-ml0 { margin-left: 0; } + .xxl-ml5 { margin-left: 5px; } + .xxl-ml10 { margin-left:10px; } + .xxl-ml15 { margin-left: 15px; } + .xxl-ml20 { margin-left: 20px; } + .xxl-ml25 { margin-left: 25px; } + .xxl-ml30 { margin-left: 30px; } + .xxl-ml35 { margin-left: 35px; } + .xxl-ml40 { margin-left: 40px; } + .xxl-ml45 { margin-left: 45px; } + .xxl-ml50 { margin-left: 50px; } + .xxl-ml55 { margin-left: 55px; } + .xxl-ml60 { margin-left: 60px; } + .xxl-ml65 { margin-left: 65px; } + .xxl-ml70 { margin-left: 70px; } + .xxl-ml75 { margin-left: 75px; } + .xxl-ml80 { margin-left: 80px; } + .xxl-ml85 { margin-left: 85px; } + .xxl-ml90 { margin-left: 90px; } + .xxl-ml95 { margin-left: 95px; } + .xxl-ml100 { margin-left: 100px; } + .xxl-ml105 { margin-left: 105px; } + .xxl-ml110 { margin-left: 110px; } + .xxl-ml115 { margin-left: 115px; } + .xxl-ml120 { margin-left: 120px; } + .xxl-ml125 { margin-left: 125px; } + .xxl-ml130 { margin-left: 130px; } + .xxl-ml135 { margin-left: 135px; } + .xxl-ml140 { margin-left: 140px; } + .xxl-ml145 { margin-left: 145px; } + .xxl-ml150 { margin-left: 150px; } + .xxl-ml155 { margin-left: 155px; } + .xxl-ml160 { margin-left: 160px; } + .xxl-ml165 { margin-left: 165px; } + .xxl-ml170 { margin-left: 170px; } + .xxl-ml175 { margin-left: 175px; } + .xxl-ml180 { margin-left: 180px; } + .xxl-ml185 { margin-left: 185px; } + .xxl-ml190 { margin-left: 190px; } + .xxl-ml195 { margin-left: 195px; } + .xxl-ml200 { margin-left: 200px; } + + /* Margins - Right */ + + .xxl-mra { margin-right: auto; } + .xxl-mr0 { margin-right: 0; } + .xxl-mr5 { margin-right: 5px; } + .xxl-mr10 { margin-right:10px; } + .xxl-mr15 { margin-right: 15px; } + .xxl-mr20 { margin-right: 20px; } + .xxl-mr25 { margin-right: 25px; } + .xxl-mr30 { margin-right: 30px; } + .xxl-mr35 { margin-right: 35px; } + .xxl-mr40 { margin-right: 40px; } + .xxl-mr45 { margin-right: 45px; } + .xxl-mr50 { margin-right: 50px; } + .xxl-mr55 { margin-right: 55px; } + .xxl-mr60 { margin-right: 60px; } + .xxl-mr65 { margin-right: 65px; } + .xxl-mr70 { margin-right: 70px; } + .xxl-mr75 { margin-right: 75px; } + .xxl-mr80 { margin-right: 80px; } + .xxl-mr85 { margin-right: 85px; } + .xxl-mr90 { margin-right: 90px; } + .xxl-mr95 { margin-right: 95px; } + .xxl-mr100 { margin-right: 100px; } + .xxl-mr105 { margin-right: 105px; } + .xxl-mr110 { margin-right: 110px; } + .xxl-mr115 { margin-right: 115px; } + .xxl-mr120 { margin-right: 120px; } + .xxl-mr125 { margin-right: 125px; } + .xxl-mr130 { margin-right: 130px; } + .xxl-mr135 { margin-right: 135px; } + .xxl-mr140 { margin-right: 140px; } + .xxl-mr145 { margin-right: 145px; } + .xxl-mr150 { margin-right: 150px; } + .xxl-mr155 { margin-right: 155px; } + .xxl-mr160 { margin-right: 160px; } + .xxl-mr165 { margin-right: 165px; } + .xxl-mr170 { margin-right: 170px; } + .xxl-mr175 { margin-right: 175px; } + .xxl-mr180 { margin-right: 180px; } + .xxl-mr185 { margin-right: 185px; } + .xxl-mr190 { margin-right: 190px; } + .xxl-mr195 { margin-right: 195px; } + .xxl-mr200 { margin-right: 200px; } + + /* Margins - Top & Bottom */ + + .xxl-mtba { margin-top: auto; margin-bottom: auto; } + .xxl-mtb0 { margin-top: 0; margin-bottom: 0; } + .xxl-mtb5 { margin-top: 5px; margin-bottom: 5px; } + .xxl-mtb10 { margin-top:10px; margin-bottom: 10px; } + .xxl-mtb15 { margin-top: 15px; margin-bottom: 15px; } + .xxl-mtb20 { margin-top: 20px; margin-bottom: 20px; } + .xxl-mtb25 { margin-top: 25px; margin-bottom: 25px; } + .xxl-mtb30 { margin-top: 30px; margin-bottom: 30px; } + .xxl-mtb35 { margin-top: 35px; margin-bottom: 35px; } + .xxl-mtb40 { margin-top: 40px; margin-bottom: 40px; } + .xxl-mtb45 { margin-top: 45px; margin-bottom: 45px; } + .xxl-mtb50 { margin-top: 50px; margin-bottom: 50px; } + .xxl-mtb55 { margin-top: 55px; margin-bottom: 55px; } + .xxl-mtb60 { margin-top: 60px; margin-bottom: 60px; } + .xxl-mtb65 { margin-top: 65px; margin-bottom: 65px; } + .xxl-mtb70 { margin-top: 70px; margin-bottom: 70px; } + .xxl-mtb75 { margin-top: 75px; margin-bottom: 75px; } + .xxl-mtb80 { margin-top: 80px; margin-bottom: 80px; } + .xxl-mtb85 { margin-top: 85px; margin-bottom: 85px; } + .xxl-mtb90 { margin-top: 90px; margin-bottom: 90px; } + .xxl-mtb95 { margin-top: 95px; margin-bottom: 95px; } + .xxl-mtb100 { margin-top: 100px; margin-bottom: 100px; } + .xxl-mtb105 { margin-top: 105px; margin-bottom: 105px; } + .xxl-mtb110 { margin-top:110px; margin-bottom: 110px; } + .xxl-mtb115 { margin-top: 115px; margin-bottom: 115px; } + .xxl-mtb120 { margin-top: 120px; margin-bottom: 120px; } + .xxl-mtb125 { margin-top: 125px; margin-bottom: 125px; } + .xxl-mtb130 { margin-top: 130px; margin-bottom: 130px; } + .xxl-mtb135 { margin-top: 135px; margin-bottom: 135px; } + .xxl-mtb140 { margin-top: 140px; margin-bottom: 140px; } + .xxl-mtb145 { margin-top: 145px; margin-bottom: 145px; } + .xxl-mtb150 { margin-top: 150px; margin-bottom: 150px; } + .xxl-mtb155 { margin-top: 155px; margin-bottom: 155px; } + .xxl-mtb160 { margin-top: 160px; margin-bottom: 160px; } + .xxl-mtb165 { margin-top: 165px; margin-bottom: 165px; } + .xxl-mtb170 { margin-top: 170px; margin-bottom: 170px; } + .xxl-mtb175 { margin-top: 175px; margin-bottom: 175px; } + .xxl-mtb180 { margin-top: 180px; margin-bottom: 180px; } + .xxl-mtb185 { margin-top: 185px; margin-bottom: 185px; } + .xxl-mtb190 { margin-top: 190px; margin-bottom: 190px; } + .xxl-mtb195 { margin-top: 195px; margin-bottom: 195px; } + .xxl-mtb200 { margin-top: 200px; margin-bottom: 200px; } + + .xxl-centered { margin-left: auto; margin-right: auto; } + .xxl-filled { margin-left: 0; margin-right: 0; } + + .xxl-mlr5 { margin-left: 5px; margin-right: 5px; } + .xxl-mlr10 { margin-left: 10px; margin-right:10px; } + .xxl-mlr15 { margin-left: 15px; margin-right: 15px; } + .xxl-mlr20 { margin-left: 20px; margin-right: 20px; } + .xxl-mlr25 { margin-left: 25px; margin-right: 25px; } + .xxl-mlr30 { margin-left: 30px; margin-right: 30px; } + .xxl-mlr35 { margin-left: 35px; margin-right: 35px; } + .xxl-mlr40 { margin-left: 40px; margin-right: 40px; } + .xxl-mlr45 { margin-left: 45px; margin-right: 45px; } + .xxl-mlr50 { margin-left: 50px; margin-right: 50px; } + .xxl-mlr55 { margin-left: 55px; margin-right: 55px; } + .xxl-mlr60 { margin-left: 60px; margin-right: 60px; } + .xxl-mlr65 { margin-left: 65px; margin-right: 65px; } + .xxl-mlr70 { margin-left: 70px; margin-right: 70px; } + .xxl-mlr75 { margin-left: 75px; margin-right: 75px; } + .xxl-mlr80 { margin-left: 80px; margin-right: 80px; } + .xxl-mlr85 { margin-left: 85px; margin-right: 85px; } + .xxl-mlr90 { margin-left: 90px; margin-right: 90px; } + .xxl-mlr95 { margin-left: 95px; margin-right: 95px; } + .xxl-mlr100 { margin-left: 100px; margin-right: 100px; } + .xxl-mlr105 { margin-left: 105px; margin-right: 105px; } + .xxl-mlr110 { margin-left:110px; margin-right: 110px; } + .xxl-mlr115 { margin-left: 115px; margin-right: 115px; } + .xxl-mlr120 { margin-left: 120px; margin-right: 120px; } + .xxl-mlr125 { margin-left: 125px; margin-right: 125px; } + .xxl-mlr130 { margin-left: 130px; margin-right: 130px; } + .xxl-mlr135 { margin-left: 135px; margin-right: 135px; } + .xxl-mlr140 { margin-left: 140px; margin-right: 140px; } + .xxl-mlr145 { margin-left: 145px; margin-right: 145px; } + .xxl-mlr150 { margin-left: 150px; margin-right: 150px; } + .xxl-mlr155 { margin-left: 155px; margin-right: 155px; } + .xxl-mlr160 { margin-left: 160px; margin-right: 160px; } + .xxl-mlr165 { margin-left: 165px; margin-right: 165px; } + .xxl-mlr170 { margin-left: 170px; margin-right: 170px; } + .xxl-mlr175 { margin-left: 175px; margin-right: 175px; } + .xxl-mlr180 { margin-left: 180px; margin-right: 180px; } + .xxl-mlr185 { margin-left: 185px; margin-right: 185px; } + .xxl-mlr190 { margin-left: 190px; margin-right: 190px; } + .xxl-mlr195 { margin-left: 195px; margin-right: 195px; } + .xxl-mlr200 { margin-left: 200px; margin-right: 200px; } + + /* Paddings */ + + .xxl-pda {padding: auto; } + .xxl-pd0 { padding: 0 } + .xxl-pd5 { padding: 5px; } + .xxl-pd10 { padding:10px; } + .xxl-pd15 { padding: 15px; } + .xxl-pd20 { padding: 20px; } + .xxl-pd25 { padding: 25px; } + .xxl-pd30 { padding: 30px; } + .xxl-pd35 { padding: 35px; } + .xxl-pd40 { padding: 40px; } + .xxl-pd45 { padding: 45px; } + .xxl-pd50 { padding: 50px; } + .xxl-pd55 { padding: 55px; } + .xxl-pd60 { padding: 60px; } + .xxl-pd65 { padding: 65px; } + .xxl-pd70 { padding: 70px; } + .xxl-pd75 { padding: 75px; } + .xxl-pd80 { padding: 80px; } + .xxl-pd85 { padding: 85px; } + .xxl-pd90 { padding: 90px; } + .xxl-pd95 { padding: 95px; } + .xxl-pd100 { padding: 100px; } + .xxl-pd105 { padding: 105px; } + .xxl-pd110 { padding: 110px; } + .xxl-pd115 { padding: 115px; } + .xxl-pd120 { padding: 120px; } + .xxl-pd125 { padding: 125px; } + .xxl-pd130 { padding: 130px; } + .xxl-pd135 { padding: 135px; } + .xxl-pd140 { padding: 140px; } + .xxl-pd145 { padding: 145px; } + .xxl-pd150 { padding: 150px; } + .xxl-pd155 { padding: 155px; } + .xxl-pd160 { padding: 160px; } + .xxl-pd165 { padding: 165px; } + .xxl-pd170 { padding: 170px; } + .xxl-pd175 { padding: 175px; } + .xxl-pd180 { padding: 180px; } + .xxl-pd185 { padding: 185px; } + .xxl-pd190 { padding: 190px; } + .xxl-pd195 { padding: 195px; } + .xxl-pd200 { padding: 200px; } + + /* Paddings - Top */ + + .xxl-pta { padding-top: auto; } + .xxl-pt0 { padding-top: 0; } + .xxl-pt5 { padding-top: 5px; } + .xxl-pt10 { padding-top:10px; } + .xxl-pt15 { padding-top: 15px; } + .xxl-pt20 { padding-top: 20px; } + .xxl-pt25 { padding-top: 25px; } + .xxl-pt30 { padding-top: 30px; } + .xxl-pt35 { padding-top: 35px; } + .xxl-pt40 { padding-top: 40px; } + .xxl-pt45 { padding-top: 45px; } + .xxl-pt50 { padding-top: 50px; } + .xxl-pt55 { padding-top: 55px; } + .xxl-pt60 { padding-top: 60px; } + .xxl-pt65 { padding-top: 65px; } + .xxl-pt70 { padding-top: 70px; } + .xxl-pt75 { padding-top: 75px; } + .xxl-pt80 { padding-top: 80px; } + .xxl-pt85 { padding-top: 85px; } + .xxl-pt90 { padding-top: 90px; } + .xxl-pt95 { padding-top: 95px; } + .xxl-pt100 { padding-top: 100px; } + .xxl-pt105 { padding-top: 105px; } + .xxl-pt110 { padding-top: 110px; } + .xxl-pt115 { padding-top: 115px; } + .xxl-pt120 { padding-top: 120px; } + .xxl-pt125 { padding-top: 125px; } + .xxl-pt130 { padding-top: 130px; } + .xxl-pt135 { padding-top: 135px; } + .xxl-pt140 { padding-top: 140px; } + .xxl-pt145 { padding-top: 145px; } + .xxl-pt150 { padding-top: 150px; } + .xxl-pt155 { padding-top: 155px; } + .xxl-pt160 { padding-top: 160px; } + .xxl-pt165 { padding-top: 165px; } + .xxl-pt170 { padding-top: 170px; } + .xxl-pt175 { padding-top: 175px; } + .xxl-pt180 { padding-top: 180px; } + .xxl-pt185 { padding-top: 185px; } + .xxl-pt190 { padding-top: 190px; } + .xxl-pt195 { padding-top: 195px; } + .xxl-pt200 { padding-top: 200px; } + + /* Paddings - Bottom */ + + .xxl-pba { padding-bottom: auto; } + .xxl-pb0 { padding-bottom: 0 } + .xxl-pb5 { padding-bottom: 5px; } + .xxl-pb10 { padding-bottom:10px; } + .xxl-pb15 { padding-bottom: 15px; } + .xxl-pb20 { padding-bottom: 20px; } + .xxl-pb25 { padding-bottom: 25px; } + .xxl-pb30 { padding-bottom: 30px; } + .xxl-pb35 { padding-bottom: 35px; } + .xxl-pb40 { padding-bottom: 40px; } + .xxl-pb45 { padding-bottom: 45px; } + .xxl-pb50 { padding-bottom: 50px; } + .xxl-pb55 { padding-bottom: 55px; } + .xxl-pb60 { padding-bottom: 60px; } + .xxl-pb65 { padding-bottom: 65px; } + .xxl-pb70 { padding-bottom: 70px; } + .xxl-pb75 { padding-bottom: 75px; } + .xxl-pb80 { padding-bottom: 80px; } + .xxl-pb85 { padding-bottom: 85px; } + .xxl-pb90 { padding-bottom: 90px; } + .xxl-pb95 { padding-bottom: 95px; } + .xxl-pb100 { padding-bottom: 100px; } + .xxl-pb105 { padding-bottom: 105px; } + .xxl-pb110 { padding-bottom: 110px; } + .xxl-pb115 { padding-bottom: 115px; } + .xxl-pb120 { padding-bottom: 120px; } + .xxl-pb125 { padding-bottom: 125px; } + .xxl-pb130 { padding-bottom: 130px; } + .xxl-pb135 { padding-bottom: 135px; } + .xxl-pb140 { padding-bottom: 140px; } + .xxl-pb145 { padding-bottom: 145px; } + .xxl-pb150 { padding-bottom: 150px; } + .xxl-pb155 { padding-bottom: 155px; } + .xxl-pb160 { padding-bottom: 160px; } + .xxl-pb165 { padding-bottom: 165px; } + .xxl-pb170 { padding-bottom: 170px; } + .xxl-pb175 { padding-bottom: 175px; } + .xxl-pb180 { padding-bottom: 180px; } + .xxl-pb185 { padding-bottom: 185px; } + .xxl-pb190 { padding-bottom: 190px; } + .xxl-pb195 { padding-bottom: 195px; } + .xxl-pb200 { padding-bottom: 200px; } + + /* Paddings - Left */ + + .xxl-pla { padding-left: auto; } + .xxl-pl0 { padding-left: 0; } + .xxl-pl5 { padding-left: 5px; } + .xxl-pl10 { padding-left:10px; } + .xxl-pl15 { padding-left: 15px; } + .xxl-pl20 { padding-left: 20px; } + .xxl-pl25 { padding-left: 25px; } + .xxl-pl30 { padding-left: 30px; } + .xxl-pl35 { padding-left: 35px; } + .xxl-pl40 { padding-left: 40px; } + .xxl-pl45 { padding-left: 45px; } + .xxl-pl50 { padding-left: 50px; } + .xxl-pl55 { padding-left: 55px; } + .xxl-pl60 { padding-left: 60px; } + .xxl-pl65 { padding-left: 65px; } + .xxl-pl70 { padding-left: 70px; } + .xxl-pl75 { padding-left: 75px; } + .xxl-pl80 { padding-left: 80px; } + .xxl-pl85 { padding-left: 85px; } + .xxl-pl90 { padding-left: 90px; } + .xxl-pl95 { padding-left: 95px; } + .xxl-pl100 { padding-left: 100px; } + .xxl-pl105 { padding-left: 105px; } + .xxl-pl110 { padding-left: 110px; } + .xxl-pl115 { padding-left: 115px; } + .xxl-pl120 { padding-left: 120px; } + .xxl-pl125 { padding-left: 125px; } + .xxl-pl130 { padding-left: 130px; } + .xxl-pl135 { padding-left: 135px; } + .xxl-pl140 { padding-left: 140px; } + .xxl-pl145 { padding-left: 145px; } + .xxl-pl150 { padding-left: 150px; } + .xxl-pl155 { padding-left: 155px; } + .xxl-pl160 { padding-left: 160px; } + .xxl-pl165 { padding-left: 165px; } + .xxl-pl170 { padding-left: 170px; } + .xxl-pl175 { padding-left: 175px; } + .xxl-pl180 { padding-left: 180px; } + .xxl-pl185 { padding-left: 185px; } + .xxl-pl190 { padding-left: 190px; } + .xxl-pl195 { padding-left: 195px; } + .xxl-pl200 { padding-left: 200px; } + + /* Paddings - Right */ + + .xxl-pra { padding-right: auto; } + .xxl-pr0 { padding-right: 0; } + .xxl-pr5 { padding-right: 5px; } + .xxl-pr10 { padding-right:10px; } + .xxl-pr15 { padding-right: 15px; } + .xxl-pr20 { padding-right: 20px; } + .xxl-pr25 { padding-right: 25px; } + .xxl-pr30 { padding-right: 30px; } + .xxl-pr35 { padding-right: 35px; } + .xxl-pr40 { padding-right: 40px; } + .xxl-pr45 { padding-right: 45px; } + .xxl-pr50 { padding-right: 50px; } + .xxl-pr55 { padding-right: 55px; } + .xxl-pr60 { padding-right: 60px; } + .xxl-pr65 { padding-right: 65px; } + .xxl-pr70 { padding-right: 70px; } + .xxl-pr75 { padding-right: 75px; } + .xxl-pr80 { padding-right: 80px; } + .xxl-pr85 { padding-right: 85px; } + .xxl-pr90 { padding-right: 90px; } + .xxl-pr95 { padding-right: 95px; } + .xxl-pr100 { padding-right: 100px; } + .xxl-pr105 { padding-right: 105px; } + .xxl-pr110 { padding-right: 110px; } + .xxl-pr115 { padding-right: 115px; } + .xxl-pr120 { padding-right: 120px; } + .xxl-pr125 { padding-right: 125px; } + .xxl-pr130 { padding-right: 130px; } + .xxl-pr135 { padding-right: 135px; } + .xxl-pr140 { padding-right: 140px; } + .xxl-pr145 { padding-right: 145px; } + .xxl-pr150 { padding-right: 150px; } + .xxl-pr155 { padding-right: 155px; } + .xxl-pr160 { padding-right: 160px; } + .xxl-pr165 { padding-right: 165px; } + .xxl-pr170 { padding-right: 170px; } + .xxl-pr175 { padding-right: 175px; } + .xxl-pr180 { padding-right: 180px; } + .xxl-pr185 { padding-right: 185px; } + .xxl-pr190 { padding-right: 190px; } + .xxl-pr195 { padding-right: 195px; } + .xxl-pr200 { padding-right: 200px; } + + /* Paddings - Top & Bottom */ + + .xxl-ptb-a { padding-top: auto; padding-bottom: auto; } + .xxl-ptb0 { padding-top: 0; padding-bottom: 0; } + .xxl-ptb5 { padding-top: 5px; padding-bottom: 5px; } + .xxl-ptb10 { padding-top:10px; padding-bottom: 10px; } + .xxl-ptb15 { padding-top: 15px; padding-bottom: 15px; } + .xxl-ptb20 { padding-top: 20px; padding-bottom: 20px; } + .xxl-ptb25 { padding-top: 25px; padding-bottom: 25px; } + .xxl-ptb30 { padding-top: 30px; padding-bottom: 30px; } + .xxl-ptb35 { padding-top: 35px; padding-bottom: 35px; } + .xxl-ptb40 { padding-top: 40px; padding-bottom: 40px; } + .xxl-ptb45 { padding-top: 45px; padding-bottom: 45px; } + .xxl-ptb50 { padding-top: 50px; padding-bottom: 50px; } + .xxl-ptb55 { padding-top: 55px; padding-bottom: 55px; } + .xxl-ptb60 { padding-top: 60px; padding-bottom: 60px; } + .xxl-ptb65 { padding-top: 65px; padding-bottom: 65px; } + .xxl-ptb70 { padding-top: 70px; padding-bottom: 70px; } + .xxl-ptb75 { padding-top: 75px; padding-bottom: 75px; } + .xxl-ptb80 { padding-top: 80px; padding-bottom: 80px; } + .xxl-ptb85 { padding-top: 85px; padding-bottom: 85px; } + .xxl-ptb90 { padding-top: 90px; padding-bottom: 90px; } + .xxl-ptb95 { padding-top: 95px; padding-bottom: 95px; } + .xxl-ptb100 { padding-top: 100px; padding-bottom: 100px; } + .xxl-ptb105 { padding-top: 105px; padding-bottom: 105px; } + .xxl-ptb110 { padding-top:110px; padding-bottom: 110px; } + .xxl-ptb115 { padding-top: 115px; padding-bottom: 115px; } + .xxl-ptb120 { padding-top: 120px; padding-bottom: 120px; } + .xxl-ptb125 { padding-top: 125px; padding-bottom: 125px; } + .xxl-ptb130 { padding-top: 130px; padding-bottom: 130px; } + .xxl-ptb135 { padding-top: 135px; padding-bottom: 135px; } + .xxl-ptb140 { padding-top: 140px; padding-bottom: 140px; } + .xxl-ptb145 { padding-top: 145px; padding-bottom: 145px; } + .xxl-ptb150 { padding-top: 150px; padding-bottom: 150px; } + .xxl-ptb155 { padding-top: 155px; padding-bottom: 155px; } + .xxl-ptb160 { padding-top: 160px; padding-bottom: 160px; } + .xxl-ptb165 { padding-top: 165px; padding-bottom: 165px; } + .xxl-ptb170 { padding-top: 170px; padding-bottom: 170px; } + .xxl-ptb175 { padding-top: 175px; padding-bottom: 175px; } + .xxl-ptb180 { padding-top: 180px; padding-bottom: 180px; } + .xxl-ptb185 { padding-top: 185px; padding-bottom: 185px; } + .xxl-ptb190 { padding-top: 190px; padding-bottom: 190px; } + .xxl-ptb195 { padding-top: 195px; padding-bottom: 195px; } + .xxl-ptb200 { padding-top: 200px; padding-bottom: 200px; } + + .xxl-plra { padding-left: auto; padding-right: auto; } + .xxl-plr0 { padding-left: 0; padding-right: 0; } + .xxl-plr5 { padding-left: 5px; padding-right: 5px; } + .xxl-plr10 { padding-left: 10px; padding-right:10px; } + .xxl-plr15 { padding-left: 15px; padding-right: 15px; } + .xxl-plr20 { padding-left: 20px; padding-right: 20px; } + .xxl-plr25 { padding-left: 25px; padding-right: 25px; } + .xxl-plr30 { padding-left: 30px; padding-right: 30px; } + .xxl-plr35 { padding-left: 35px; padding-right: 35px; } + .xxl-plr40 { padding-left: 40px; padding-right: 40px; } + .xxl-plr45 { padding-left: 45px; padding-right: 45px; } + .xxl-plr50 { padding-left: 50px; padding-right: 50px; } + .xxl-plr55 { padding-left: 55px; padding-right: 55px; } + .xxl-plr60 { padding-left: 60px; padding-right: 60px; } + .xxl-plr65 { padding-left: 65px; padding-right: 65px; } + .xxl-plr70 { padding-left: 70px; padding-right: 70px; } + .xxl-plr75 { padding-left: 75px; padding-right: 75px; } + .xxl-plr80 { padding-left: 80px; padding-right: 80px; } + .xxl-plr85 { padding-left: 85px; padding-right: 85px; } + .xxl-plr90 { padding-left: 90px; padding-right: 90px; } + .xxl-plr95 { padding-left: 95px; padding-right: 95px; } + .xxl-plr100 { padding-left: 100px; padding-right: 100px; } + .xxl-plr105 { padding-left: 105px; padding-right: 105px; } + .xxl-plr110 { padding-left:110px; padding-right: 110px; } + .xxl-plr115 { padding-left: 115px; padding-right: 115px; } + .xxl-plr120 { padding-left: 120px; padding-right: 120px; } + .xxl-plr125 { padding-left: 125px; padding-right: 125px; } + .xxl-plr130 { padding-left: 130px; padding-right: 130px; } + .xxl-plr135 { padding-left: 135px; padding-right: 135px; } + .xxl-plr140 { padding-left: 140px; padding-right: 140px; } + .xxl-plr145 { padding-left: 145px; padding-right: 145px; } + .xxl-plr150 { padding-left: 150px; padding-right: 150px; } + .xxl-plr155 { padding-left: 155px; padding-right: 155px; } + .xxl-plr160 { padding-left: 160px; padding-right: 160px; } + .xxl-plr165 { padding-left: 165px; padding-right: 165px; } + .xxl-plr170 { padding-left: 170px; padding-right: 170px; } + .xxl-plr175 { padding-left: 175px; padding-right: 175px; } + .xxl-plr180 { padding-left: 180px; padding-right: 180px; } + .xxl-plr185 { padding-left: 185px; padding-right: 185px; } + .xxl-plr190 { padding-left: 190px; padding-right: 190px; } + .xxl-plr195 { padding-left: 195px; padding-right: 195px; } + .xxl-plr200 { padding-left: 200px; padding-right: 200px; } + + /* Font Sizes */ + + .xxl-fz8 { font-size: 8px; } + .xxl-fz10 { font-size: 10px; } + .xxl-fz11 { font-size: 11px; } + .xxl-fz12 { font-size: 12px; } + .xxl-fz13 { font-size: 13px; } + .xxl-fz14 { font-size: 14px; } + .xxl-fz16 { font-size: 16px; } + .xxl-fz18 { font-size: 18px; } + .xxl-fz20 { font-size: 20px; } + .xxl-fz22 { font-size: 22px; } + .xxl-fz24 { font-size: 24px; } + .xxl-fz26 { font-size: 26px; } + .xxl-fz28 { font-size: 28px; } + .xxl-fz30 { font-size: 30px; } + .xxl-fz32 { font-size: 32px; } + .xxl-fz34 { font-size: 34px; } + .xxl-fz36 { font-size: 36px; } + .xxl-fz38 { font-size: 38px; } + .xxl-fz40 { font-size: 40px; } + .xxl-fz42 { font-size: 42px; } + .xxl-fz44 { font-size: 44px; } + .xxl-fz46 { font-size: 46px; } + .xxl-fz48 { font-size: 48px; } + .xxl-fz50 { font-size: 50px; } + .xxl-fz52 { font-size: 52px; } + .xxl-fz54 { font-size: 54px; } + .xxl-fz56 { font-size: 56px; } + .xxl-fz58 { font-size: 58px; } + .xxl-fz60 { font-size: 60px; } + + /* Custom font sizes breakpoints */ + + .f1 { font-size: var(--xxl-f1); } + .f2 { font-size: var(--xxl-f2); } + .f3 { font-size: var(--xxl-f3); } + .f4 { font-size: var(--xxl-f4); } + .f5 { font-size: var(--xxl-f5); } + .f6 { font-size: var(--xxl-f6); } + .f7 { font-size: var(--xxl-f7); } + .f8 { font-size: var(--xxl-f8); } + .f9 { font-size: var(--xxl-f9); } + + /* Typography > Text Utils */ + + .xxl-text-normal { font-weight: normal; } + .xxl-text-light { font-weight: 200; } + .xxl-text-medium { font-weight: 500; } + .xxl-text-semibold { font-weight: 600; } + .xxl-text-bold { font-weight: bold; } + .xxl-text-italic { font-style: italic; } + .xxl-text-through { text-decoration: line-through; } + .xxl-text-small { font-size: 14px; } + .xxl-text-center{ text-align:center; } + .xxl-text-left { text-align: left; } + .xxl-text-right { text-align: right; } + .xxl-text-justify { text-align: justify; } +} \ No newline at end of file diff --git a/src/core/css/root.css b/src/core/css/root.css new file mode 100644 index 0000000..facce1b --- /dev/null +++ b/src/core/css/root.css @@ -0,0 +1,991 @@ +:root { + +/* -------------------------- + Flex Grid standard sizes +/* -------------------------*/ + + --main-wrapper-width: 92%; + --gutters-sizes: 10px; + + /* System colors */ + + --white: #FFFFFF; + --red: #FF4136; + --blue: #0074D9; + --green: #228B22; + --yellow: #FFDC00; + --gray: #AAAAAA; + --black: #111111; + --carbon: #666666; + --orange: #FF851B; + --navy: #001f3f; + --purple: #B10DC9; + --lime: #01FF70; + + /* -------------------------- + Theme Colors + /* -------------------------*/ + + --main-font-color: #333333; + --main-link-color: #303436; + + --theme-widget-color: #333; + + /* -------------------------- + Typography Standard + /* -------------------------*/ + + --font-size-unit: 16px; + --main-font-family: Arial, Helvetica, sans-serif; + --main-font-size: 0.8125rem; + + --m-f1: var(--f1); + --l-f1: var(--m-f1); + --xl-f1: var(--l-f1); + --xxl-f1: var(--xl-f1); + + --m-f2: var(--f2); + --l-f2: var(--m-f2); + --xl-f2: var(--l-f2); + --xxl-f2: var(--xl-f2); + + --m-f3: var(--f3); + --l-f3: var(--m-f3); + --xl-f3: var(--l-f3); + --xxl-f3: var(--xl-f3); + + --m-f4: var(--f4); + --l-f4: var(--m-f4); + --xl-f4: var(--l-f4); + --xxl-f4: var(--xl-f4); + + --m-f5: var(--f5); + --l-f5: var(--m-f5); + --xl-f5: var(--l-f5); + --xxl-f5: var(--xl-f5); + + --m-f6: var(--f6); + --l-f6: var(--m-f6); + --xl-f6: var(--l-f6); + --xxl-f6: var(--xl-f6); + + --m-f7: var(--f7); + --l-f7: var(--m-f7); + --xl-f7: var(--l-f7); + --xxl-f7: var(--xl-f7); + + --m-f8: var(--f8); + --l-f8: var(--m-f8); + --xl-f8: var(--l-f8); + --xxl-f8: var(--xl-f8); + + --m-f9: var(--f9); + --l-f9: var(--m-f9); + --xl-f9: var(--l-f9); + --xxl-f9: var(--xl-f9); +} + +/* ---------------- + Reset +/* ---------------*/ + +* { + outline: none; + box-sizing: border-box; +} + +html { + font-family: var(--main-font-family); + font-size: var(--font-size-unit); + scroll-behavior: smooth; +} + +body, p, ul, ol, li, h1, h2, h3, h4, h5, h6, form { + padding: 0; + margin: 0; + vertical-align: baseline; + border: 0; + font-weight: normal; +} + +body { + font-size: var(--main-font-size); + color: var(--main-font-color); +} + +article, aside, details, figcaption, figure, footer, header, nav, section, main, summary { + display: block; +} + +ul { list-style-type: none; } + +textarea{ resize: none; } + +a { + text-decoration :none; + color: var(--main-link-color); + cursor: pointer; +} + +img { + display: block; + max-width: 100%; + height: auto; +} + +[class*='sp-'] { + font-family: var(--main-font-family); +} + +input::-webkit-calendar-picker-indicator { + display: none !important; +} + +/* ---------------- + ScrollBar +/* ---------------*/ + +.sp-scrollbar { + scrollbar-width: thin; + scrollbar-color: transparent transparent; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.sp-scrollbar:hover { + scrollbar-color: #ccc transparent; +} + +.sp-scrollbar::-webkit-scrollbar { + width: 5px; + background: transparent; +} + +.sp-scrollbar::-webkit-scrollbar-thumb { + background: transparent; +} + +.sp-scrollbar:hover::-webkit-scrollbar-thumb { + background: #ccc; +} + +/* ----------------- + ToolTip +/* ----------------*/ + +[data-tooltip] { + position: relative; +} + +[data-tooltip]:hover::before, +[data-tooltip-left]:hover::before, +[data-tooltip-right]:hover::before, +[data-tooltip-bottom]:hover::before { + position: relative; + justify-content: center; + flex-wrap: nowrap; + align-items: center; + content: attr(data-tooltip); + background: #fff; + color: #212121; + padding: 0 10px; + height: 25px; + border-radius: 5px; + font-weight: 600; + font-family:'raleway', Arial, sans-serif; + position: absolute; + z-index: 100; + font-size: 13px; + top: -30px; + left: -10px; + white-space: nowrap; + display: flex; +} + +[data-tooltip-left]:hover::before { + content: attr(data-tooltip-left); + top: 0; + left: 0; + bottom: 0; + margin: auto; + transform: translateX(-102%); +} + +[data-tooltip-right]:hover::before { + content: attr(data-tooltip-right); + top: 0; + left: auto; + right: 0; + bottom: 0; + margin: auto; + transform: translateX(102%); +} + +[data-tooltip-bottom]:hover::before { + content: attr(data-tooltip-bottom); + top: auto; + bottom: -30px; +} + +[data-tooltip].tooltip-color-blue::before, +[data-tooltip-left].tooltip-color-blue::before, +[data-tooltip-right].tooltip-color-blue::before, +[data-tooltip-bottom].tooltip-color-blue::before { + background-color: var(--blue); + color: var(--white); +} + +[data-tooltip].tooltip-color-blue.transparent::before, +[data-tooltip-left].tooltip-color-blue.transparent::before, +[data-tooltip-right].tooltip-color-blue.transparent::before, +[data-tooltip-bottom].tooltip-color-blue.transparent::before { + background-color: transparent; + border: 1px solid var(--blue); + color: var(--blue); +} + +[data-tooltip].tooltip-color-red::before, +[data-tooltip-left].tooltip-color-red::before, +[data-tooltip-right].tooltip-color-red::before, +[data-tooltip-bottom].tooltip-color-red::before { + background-color: var(--red); + color: var(--white); +} + +[data-tooltip].tooltip-color-red.transparent::before, +[data-tooltip-left].tooltip-color-red.transparent::before, +[data-tooltip-right].tooltip-color-red.transparent::before, +[data-tooltip-bottom].tooltip-color-red.transparent::before { + background-color: transparent; + border: 1px solid var(--red); + color: var(--red); +} + +[data-tooltip].tooltip-color-orange::before, +[data-tooltip-left].tooltip-color-orange::before, +[data-tooltip-right].tooltip-color-orange::before, +[data-tooltip-bottom].tooltip-color-orange::before { + background-color: var(--orange); + color: var(--white); +} + +[data-tooltip].tooltip-color-orange.transparent::before, +[data-tooltip-left].tooltip-color-orange.transparent::before, +[data-tooltip-right].tooltip-color-orange.transparent::before, +[data-tooltip-bottom].tooltip-color-orange.transparent::before { + background-color: transparent; + border: 1px solid var(--orange); + color: var(--orange); +} + +[data-tooltip].tooltip-color-green::before, +[data-tooltip-left].tooltip-color-green::before, +[data-tooltip-right].tooltip-color-green::before, +[data-tooltip-bottom].tooltip-color-green::before { + background-color: var(--green); + color: var(--white); +} + +[data-tooltip].tooltip-color-green.transparent::before, +[data-tooltip-left].tooltip-color-green.transparent::before, +[data-tooltip-right].tooltip-color-green.transparent::before, +[data-tooltip-bottom].tooltip-color-green.transparent::before { + background-color: transparent; + border: 1px solid var(--green); + color: var(--green); +} + +[data-tooltip].tooltip-color-navy::before, +[data-tooltip-left].tooltip-color-navy::before, +[data-tooltip-right].tooltip-color-navy::before, +[data-tooltip-bottom].tooltip-color-navy::before { + background-color: var(--navy); + color: var(--white); +} + +[data-tooltip].tooltip-color-navy.transparent::before, +[data-tooltip-left].tooltip-color-navy.transparent::before, +[data-tooltip-right].tooltip-color-navy.transparent::before, +[data-tooltip-bottom].tooltip-color-navy.transparent::before { + background-color: transparent; + border: 1px solid var(--navy); + color: var(--navy); +} + +[data-tooltip].tooltip-color-black::before, +[data-tooltip-left].tooltip-color-black::before, +[data-tooltip-right].tooltip-color-black::before, +[data-tooltip-bottom].tooltip-color-black::before { + background-color: var(--black); + color: var(--white); +} + +[data-tooltip].tooltip-color-black.transparent::before, +[data-tooltip-left].tooltip-color-black.transparent::before, +[data-tooltip-right].tooltip-color-black.transparent::before, +[data-tooltip-bottom].tooltip-color-black.transparent::before { + background-color: transparent; + border: 1px solid var(--black); + color: var(--black); +} + +[data-tooltip].tooltip-color-carbon::before, +[data-tooltip-left].tooltip-color-carbon::before, +[data-tooltip-right].tooltip-color-carbon::before, +[data-tooltip-bottom].tooltip-color-carbon::before { + background-color: var(--carbon); + color: var(--white); +} + +[data-tooltip].tooltip-color-carbon.transparent::before, +[data-tooltip-left].tooltip-color-carbon.transparent::before, +[data-tooltip-right].tooltip-color-carbon.transparent::before, +[data-tooltip-bottom].tooltip-color-carbon.transparent::before { + background-color: transparent; + border: 1px solid var(--carbon); + color: var(--carbon); +} + +[data-tooltip].tooltip-color-gray::before, +[data-tooltip-left].tooltip-color-gray::before, +[data-tooltip-right].tooltip-color-gray::before, +[data-tooltip-bottom].tooltip-color-gray::before { + background-color: var(--gray); + color: var(--white); +} + +[data-tooltip].tooltip-color-gray.transparent::before, +[data-tooltip-left].tooltip-color-gray.transparent::before, +[data-tooltip-right].tooltip-color-gray.transparent::before, +[data-tooltip-bottom].tooltip-color-gray.transparent::before { + background-color: transparent; + border: 1px solid var(--gray); + color: var(--gray); +} + +[data-tooltip].tooltip-color-white::before, +[data-tooltip-left].tooltip-color-white::before, +[data-tooltip-right].tooltip-color-white::before, +[data-tooltip-bottom].tooltip-color-white::before { + background-color: var(--white); + color: var(--black); +} + +[data-tooltip].tooltip-color-white.transparent::before, +[data-tooltip-left].tooltip-color-white.transparent::before, +[data-tooltip-right].tooltip-color-white.transparent::before, +[data-tooltip-bottom].tooltip-color-white.transparent::before { + background-color: transparent; + border: 1px solid var(--white); + color: var(--white); +} + +/* --------------- + Buttons +/* --------------*/ + +.sp-button { + display: flex; + align-items: center; + justify-content: center; + border: 0; + background-color: var(--theme-widget-color); + cursor: pointer; + color: #fff; + -webkit-user-select: none; + user-select: none; + transition: opacity 0.2s ease-in-out; + font-weight: 400; + letter-spacing: 0.0285rem; + font-size: 14px; + border-radius: 4px; + position: relative; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + height: 40px; + padding: 0 12px; + color: #fff; +} + +.sp-button.small { + height: 24px; + padding: 0 8px; + font-size: 12px; +} + +.sp-button.transparent { + background-color: transparent; + border: 0; +} + +.sp-button.ui-color-white { + background-color: var(--white); + color: var(--carbon); + border: 1px solid #e4e9f0; +} + +.sp-button:disabled, +.sp-button.disabled, +.sp-button.disabled:hover { + opacity: 0.5; + pointer-events: none; +} + +.sp-button:hover { + opacity: 0.8; +} + +.sp-button i.icon-left { + margin-right: 8px; + order: 0; +} + +.sp-button i.icon-right { + margin-left: 8px; + order: 1; +} + +.sp-button.icon-only i.icon-left { + margin-right: 0; +} + +.sp-button.icon-only i.icon-right { + margin-left: 0; +} + +.sp-button span { + order: 1; +} + +/* ---------------------- + Forms And Block +/* ---------------------*/ + +.sp-form { + display: flex; + flex-wrap: wrap; + align-items: self-start; + align-content: start; + width: 100%; +} + +.form-field { + padding: 0 4px; + margin-bottom: 20px; + position: relative; +} + +.form-field.inline { + margin-bottom: 0; +} + +.form-field .field-label { + margin-bottom: 8px; + display: block; + white-space: nowrap; +} + +.form-field.field-radio .field-label, +.form-field.field-checkbox .field-label { + margin-bottom: 12px; +} + +.checkbox-control label { + white-space: normal; +} + +.form-field .input-wrapper { + display: flex; + flex-wrap: nowrap; + align-items: center; + overflow: hidden; +} + +.form-field.field-text .input-wrapper, +.form-field.field-password .input-wrapper, +.form-field.field-select .input-wrapper { + border: 1px solid #e4e9f0; + border-radius: 4px; + height: 40px; + transition: border-color 0.15s ease-in-out; +} + +.form-field.field-text .input-wrapper:not(.form-field-error):focus-within, +.form-field.field-password .input-wrapper:not(.form-field-error):focus-within, +.form-field.field-select .input-wrapper:not(.form-field-error):focus-within { + border-color: rgba(16, 82, 137, 0.4); +} + +.form-field.field-text i, +.form-field.field-password i, +.form-field.field-select i { + flex-shrink: 0; + padding: 0 15px; + display: flex; + align-items: center; + height: 100%; +} + +.form-field.form-field-error .input-wrapper { + border-bottom-color: red; +} + +.form-field.form-field-error textarea { + border-bottom-color: red; +} + +.form-field.field-info { + display: flex; + align-items: center; + flex-wrap: nowrap; + flex-shrink: 0; +} + +.form-field.field-info .label-name { + font-size: 13px; +} + +.form-field.field-info .label-value { + font-size: 14px; + font-weight: 600; + margin-top: 5px; +} + +.form-field.field-info .label-value.text-wrap { + line-height: 1.4; +} + +.form-field.field-info i { + margin-right: 10px; +} + +.form-field.field-separator { + width: 100%; + margin-bottom: 0; +} + +.sp-form .sp-button.bottom { + align-self: end; + margin-bottom: 20px; +} + +.block-buttons { + padding-top: 20px; + padding-left: 20px; +} + +.form-field .icon-left { + order: 0; +} + +.form-field .icon-right { + order: 1; +} + +.form-field .icon-clear { + cursor: pointer; +} + +.form-field .icon-left.disabled, +.form-field .icon-right.disabled { + opacity: 0.5; + pointer-events: none; +} + +.form-field i.icon-clear:after { + display: block; + content: ''; + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2aWV3Qm94PSIwIDAgNDkyIDQ5MiI+PGcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMzAwLjIgMjQ2TDQ4NC4xIDYyYzUuMS01LjEgNy45LTExLjggNy45LTE5IDAtNy4yLTIuOC0xNC03LjktMTlMNDY4IDcuOWMtNS4xLTUuMS0xMS44LTcuOS0xOS03LjkgLTcuMiAwLTE0IDIuOC0xOSA3LjlMMjQ2IDE5MS44IDYyIDcuOWMtNS4xLTUuMS0xMS44LTcuOS0xOS03LjkgLTcuMiAwLTE0IDIuOC0xOSA3LjlMNy45IDI0Yy0xMC41IDEwLjUtMTAuNSAyNy42IDAgMzguMUwxOTEuOCAyNDYgNy45IDQzMGMtNS4xIDUuMS03LjkgMTEuOC03LjkgMTkgMCA3LjIgMi44IDE0IDcuOSAxOWwxNi4xIDE2LjFjNS4xIDUuMSAxMS44IDcuOSAxOSA3LjkgNy4yIDAgMTQtMi44IDE5LTcuOWwxODQtMTg0IDE4NCAxODRjNS4xIDUuMSAxMS44IDcuOSAxOSA3LjloMGM3LjIgMCAxNC0yLjggMTktNy45bDE2LjEtMTYuMWM1LjEtNS4xIDcuOS0xMS44IDcuOS0xOSAwLTcuMi0yLjgtMTQtNy45LTE5TDMwMC4yIDI0NnoiIGRhdGEtb3JpZ2luYWw9IiMwMDAwMDAiIGZpbGw9IiM3ODc0NzQiLz48L2c+PC9zdmc+) no-repeat 96% 50%; + width: 10px; + height: 10px; + background-size: 10px 10px; +} + +.form-field .input-bar { + position: relative; + display: block; + width: 100%; +} + +.form-field .input-bar:before { + content: ""; + height: 1px; + width: 0; + bottom: 0; + position: absolute; + background: var(--theme-widget-color); + transition: 300ms ease all; + left: 0%; +} + +.sp-button.btn-cancel { + background-color: transparent; + color: #333; +} + +.label-value { + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.label-value p { + font-weight: bold; + line-height: 1.8; +} + +/* ----------------- + Checkbox Ui +/* ----------------*/ + +.sp-control { + -webkit-user-select: none; + user-select: none; + display: flex; + flex-wrap: wrap; + width: 100%; +} + +.sp-control label { + display: block; + flex-basis: auto; + padding-right: 20px; + line-height: 18px; + display: flex; + align-items: center; +} + +.sp-control input{ + display: none; +} + +.sp-control .ctrl-indicator{ + height: 18px; + width: 18px; + background: #d2d9e5; + border-radius: 3px; + position: relative; + margin-right: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.sp-control .ctrl-indicator::after { + display: block; + content: ''; +} + +.sp-control label:hover input ~ .ctrl-indicator, +.sp-control input:focus ~ .ctrl-indicator { + background: #b8beca; +} + +.sp-control input:checked ~ .ctrl-indicator { + background: var(--theme-widget-color); +} + +.sp-control label:hover input:not([disabled]):checked ~ .ctrl-indicator, +.sp-control input:checked:focus ~ .ctrl-indicator { + background: var(--theme-widget-color); + opacity: 0.8; +} + +.sp-control input:disabled ~ .ctrl-indicator { + pointer-events: none; + opacity: 0.6; + background: #eef0f4; +} + +.sp-control input:checked ~ .ctrl-indicator:after { + display: block; +} + +.sp-control.checkbox-control input:checked ~ .ctrl-indicator:after { + width: 6px; + height: 10px; + transform: rotate(45deg); + border: solid #fff; + border-width: 0 2px 2px 0; + margin-top: -4px; +} + +.sp-control input:disabled ~ .ctrl-indicator:after { + border-color: #7b7b7b; +} + +.sp-control.radio-control .ctrl-indicator { + border-radius: 50% !important; +} + +.sp-control.radio-control .ctrl-indicator:after { + width: 6px; + height: 6px; + border-radius: 50% !important; + background: #fff; +} + +.sp-control.radio-control input:disabled ~ .ctrl-indicator:after { + background: #7b7b7b; +} + +/* ---------------------- + Input Form Control +/* ---------------------*/ + +.sp-form-control { + flex: 1; + padding: 0 12px; + font-weight: 400; + color: #74708d; + background-color: #fff; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + -webkit-appearance: none; + appearance: none; + border: 0; + width: 100%; + height: 100%; + font-size: 13px; +} + +.no-fit .sp-form-control { + width: auto; + min-width: 40%; +} + +.max-fit .sp-form-control { + max-width: 220px; +} + +.sp-form-control[data-cancelable="1"] { + padding-right: 40px; +} + +.sp-form-control::placeholder { + color: #a9a9a9; +} + +select.sp-form-control { + padding-right: 24px; + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2aWV3Qm94PSIwIDAgNDkyIDQ5MiI+PGcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNDg0LjEgMTI1bC0xNi4xLTE2LjJjLTUuMS01LjEtMTEuOC03LjktMTktNy45IC03LjIgMC0xNCAyLjgtMTkgNy45bC0xODMuOCAxODMuOEw2Mi4xIDEwOC42Yy01LjEtNS4xLTExLjgtNy45LTE5LTcuOXMtMTQgMi44LTE5IDcuOWwtMTYuMSAxNi4xYy0xMC41IDEwLjUtMTAuNSAyNy42IDAgMzguMWwyMTkuMSAyMTkuOWM1LjEgNS4xIDExLjggOC42IDE5LjEgOC42aDAuMWM3LjIgMCAxNC0zLjYgMTktOC42bDIxOC45LTIxOS4zYzUuMS01LjEgNy45LTEyIDcuOS0xOS4yQzQ5MiAxMzYuOSA0ODkuMiAxMzAgNDg0LjEgMTI1eiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgZmlsbD0iIzc0NzQ3NCIvPjwvZz48L3N2Zz4=) no-repeat; + background-position: calc(100% - 10px) 50%; + background-size: 10px 10px; +} + + .field-color .input-color-wrapper { + width: 50px; + height: 25px; + border-radius: 5px; + overflow: hidden; + border: 1px solid var(--gray); + } + + .field-color input { + border: 0; + padding: 0; + width: 200%; + height: 200%; + cursor: pointer; + transform: translate(-25%, -25%); +} + +.field-range input { + width: 100%; +} + +.field-group { + display: flex; + align-items: center; +} + +textarea.sp-form-control { + height: 130px; + padding: 12px; + font-family: var(--main-font-family); + border: 1px solid #e4e9f0; + border-radius: 4px; + line-height: 1.8; +} + +textarea.sp-form-control[rows] { + height: auto; +} + +textarea.sp-form-control[data-expand] { + overflow: hidden; +} + +.sp-form-control:disabled { + background: rgba(0, 0, 0, 0.03); +} + +/* ----------------- + RadioBox +/* ----------------*/ + +.sp-radio-box { + -webkit-user-select: none; + user-select: none; + display: inline-flex; +} + +.sp-radio-box input { + display: none; +} + +.sp-radio-box label { + margin-bottom: 0; +} + +.sp-radio-box .ctrl-indicator { + height: 38px; + justify-content: center; + display: flex; + align-items: center; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + padding: 0 16px; + cursor: pointer; + border: 1px solid #e4e9f0; + color: var(--black); + border-right: 0; +} + +.sp-radio-box label:first-child .ctrl-indicator { + border-radius: 4px 0 0 4px; +} + +.sp-radio-box label:last-child .ctrl-indicator { + border-radius: 0 4px 4px 0; + border-right: 1px solid #e4e9f0; +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-gray { + background-color: var(--gray); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-green { + background-color: var(--green); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-red { + background-color: var(--red); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-blue { + background-color: var(--blue); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-orange { + background-color: var(--orange); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-yellow { + background-color: var(--yellow); + color: var(--black); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-navy { + background-color: var(--navy); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-purple { + background-color: var(--purple); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-lime { + background-color: var(--lime); + color: var(--black); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-black { + background-color: var(--black); + color: var(--white); +} + +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-default, +.sp-radio-box input:checked ~ .ctrl-indicator.label-color-white { + background-color: #eee; + color: var(--black); +} + +.sp-radio-box input:checked ~ .ctrl-indicator { + cursor: default; +} + +/* --------------------- + Switch Buttons +/* -------------------*/ + +.sp-switch { + position: relative; + width: 52px; + height: 30px; + cursor: pointer; + position: relative; + display: flex; +} + +.sp-switch.inline-block { + display: inline-flex; +} + +.sp-switch .s-slider { + width: 100%; + display: flex; + background-color: #dee2e6; + align-items: center; + padding: 3px; +} + +.sp-switch.round-switch .s-slider { + border-radius: 32px; + overflow: hidden; +} + +.sp-switch .s-slider:before { + width: 24px; + height: 100%; + background-color: #fff; + display: block; + content: ''; + transition: .3s; +} + +.sp-switch.round-switch .s-slider::before { + border-radius: 50%; +} + +.sp-switch input { + display: none; +} + +.sp-switch input:checked + .s-slider { + background-color:var(--theme-widget-color); +} + +.sp-switch input:checked + .s-slider::before { + transform: translateX(21px); +} + +/* ------------------------- + Zoom input on safari +/* -----------------------*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + + select, textarea, input { + font-size: 16px; + } +} \ No newline at end of file diff --git a/src/utils/AppendOptions.js b/src/utils/AppendOptions.js new file mode 100644 index 0000000..231a780 --- /dev/null +++ b/src/utils/AppendOptions.js @@ -0,0 +1,37 @@ +//import { Ut } from "."; + +export class AppendOptions { + + constructor(target, data, opts) { + + opts = opts || {}; + + if (opts.clear) { + this.dxHtml(''); + } + + if (typeof data === 'string') { + + if (data == '{}') { + return this; + } + + data.slice(1).slice(0, -1).split(',').forEach(function(token) { + + let option = token.split(':').map(function(a) { + return a.slice(1).slice(0, -1); + }); + + target.append(new Option(option[1], option[0])); + }); + } + else { + + for (let id in data) { + target.append(new Option(data[id], id)); + } + } + } +} + +// Ut.extendNodeUx(AppendOptions); \ No newline at end of file diff --git a/src/utils/Form.js b/src/utils/Form.js new file mode 100644 index 0000000..0cb0b8c --- /dev/null +++ b/src/utils/Form.js @@ -0,0 +1,60 @@ +import { Ut } from "./Ut"; +import { FormValidator } from './form-validator/FormValidator'; + +//////////////////// +// Class Form +//////////////////// + +export class Form extends FormValidator { + + pendingRequest = false; + + constructor(target, caller, opts) { + + super(target, opts); + + const that = this; + opts = opts || {}; + this.reqOpts = {}; + + this.opts.onSubmit = () => { + + if (Ut.isFn(opts.onSubmit) && !opts.onSubmit(this.entry, this)) { + return; + } + + let btnIcon = null; + let btnIconCls = null; + + if (opts.btnLoader) { + + btnIcon = target.dxFind('button[type="submit"] i'); + + if (btnIcon) { + btnIconCls = btnIcon.className; + btnIcon.className = 'icon-left fas fa-rotate-right fa-spin fz14'; + } + } + + that.pendingRequest = true; + this.reqOpts.vars = this.entry; + + caller(this.reqOpts).then(jr => { + + that.pendingRequest = false; + + if (btnIcon) { + btnIcon.className = btnIconCls; + } + + Ut.trigger(opts.done, jr.code, jr.data, that.target.elements); + + }).catch(error => { + that.pendingRequest = false; + console.error(error); + }); + }; + } +} + +//Ut.extendNodeUx(Form); \ No newline at end of file diff --git a/src/utils/Options.js b/src/utils/Options.js new file mode 100644 index 0000000..26f1a6b --- /dev/null +++ b/src/utils/Options.js @@ -0,0 +1,127 @@ +import { Ut } from './Ut'; + +export class Options { + + /** + * Options, Contructor + */ + constructor(target, caller, events, opts) { + + this.target = target; + + //if (this.target.dxData('options')) { + // return; + //} + + //this.target.dxData('options', this); + + this.caller = caller; + this.events = events || {}; + this.opts = { ... { clear: false, cache: false }, ... opts }; + this.size = 0; + + this.targetName = this.target.name; + + Resort.data.options = Resort.data.options || {}; + + this.create(); + } + + /** + * Options, Create + */ + create() { + + let that = this; + + if (this.opts.clear) { + this.clear(); + } + + if (this.opts.cache && Resort.data.options[ this.targetName ]) { + this.appendOptions(Resort.data.options[ this.targetName ]); + } + else { + + this.request(data => { + + that.appendOptions(data); + + if (that.opts.cache) { + Resort.data.options[ that.targetName ] = data; + } + }); + } + } + + /** + * Options, Append Options + */ + appendOptions(data) { + + if (!data) { + return; + } + + let that = this; + + if (typeof data === 'object') { + + each(data, (val, id) => { + that.size++; + that.target.append(new Option(val, id)); + }); + } + else if (typeof data === 'string' && data != '{}') { + + data.slice(1).slice(0, -1).split(',').forEach(function(token) { + + let opt = token.split(':').map(function(a) { + that.size++; + return a.slice(1).slice(0, -1); + }); + + that.target.append(new Option(opt[1], opt[0])); + }); + } + + Ut.trigger(this.events.update, this); + + let val = this.target.dataset.val; + + if (val) { + this.target.dxVal(val).dxTrigger('change'); + } + } + + /** + * Options, Request + */ + request(callback) { + + this.caller().then(jr => { + + if (jr.code == Rc.DONE) { + callback(data); + } + + }).catch(error => console.error(error)); + } + + /** + * Options, Clear + */ + clear() { + + let options = this.target.options; + + if (options.length && (options[0].value == '' || options[0].value == 0)) { + this.target.dxFind('option', (option, i) => i && option.remove()); + } + else { + this.target.dxHtml(''); + } + } +} + +//Ut.extendNodeUx(Options); \ No newline at end of file diff --git a/src/utils/Password.js b/src/utils/Password.js new file mode 100644 index 0000000..916eee4 --- /dev/null +++ b/src/utils/Password.js @@ -0,0 +1,71 @@ +export class Password { + + // Random Pwd Utils + + static ALPHA_LOWER_CHARS = "abcdefghijklmnopqrstuvwxyz"; + static ALPHA_UPPER_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + static NUMERIC_CHARS = "0123456789" + static SPECIAL_CHARS = "~!@#$%^&*()_-=+{}[];<,.>?" + static HASH_CHARS = "abcdef0123456789" + + static ALPHA_LOWER = 0x01; + static ALPHA_UPPER = 0x02; + static NUMERIC = 0x08; + static SPECIAL = 0x10; + static HASH = 0x20; + + /** + * Randowm Password Generator + */ + generate(flags, length) { + + let chList = []; + let n = 0; + + if (flags & Password.ALPHA_LOWER) { + chList.push(Password.ALPHA_LOWER_CHARS); + n++; + } + + if (flags & Password.ALPHA_UPPER) { + chList.push(Password.ALPHA_UPPER_CHARS); + n++; + } + + if (flags & Password.NUMERIC) { + chList.push(Password.NUMERIC_CHARS); + n++; + } + + if (flags & Password.SPECIAL) { + chList.push(Password.SPECIAL_CHARS); + n++; + } + + if (flags & Password.HASH) { + chList.push(Password.HASH_CHARS); + n++; + } + + let div = Math.floor(length / n); + let rem = length % n; + + let chars = []; + + for (let i = 0; i < chList.length; i++) { + + let chSet = chList[i]; + + if (i + 1 == chList.length) { + div += rem; + } + + for (let j = 0; j < div; j++) { + chars.push(chSet.charAt( Math.floor(Math.random() * chSet.length) )); + } + } + + return chars.sort(() => Math.random()-.5).join(''); + } + +} \ No newline at end of file diff --git a/src/utils/Print.js b/src/utils/Print.js new file mode 100644 index 0000000..ad6668c --- /dev/null +++ b/src/utils/Print.js @@ -0,0 +1,35 @@ +import { Ut } from "./Ut"; + +export class Print{ + + constructor(opts) { + + opts = { ... { width: 1200, height: 800, lang: 'en' }, ... opts }; + + let win = window.open('', 'print', 'width=' + opts.width + ',height=' + opts.height); + + let doc = win.document; + + doc.write('Print'); + + if (opts.style) { + doc.write(''); + } + + doc.write(''); + doc.write(this.cloneNode(true).innerHTML); + doc.write(''); + + doc.close(); + + setTimeout(() => win.print(), 500); + + win.onafterprint = function() { + win.close(); + }; + + return this; + } +} + +Ut.extendNodeUx(Print); \ No newline at end of file diff --git a/src/utils/SwipeEvents.js b/src/utils/SwipeEvents.js new file mode 100644 index 0000000..290e783 --- /dev/null +++ b/src/utils/SwipeEvents.js @@ -0,0 +1,199 @@ + export class SwipeEvents { + + constructor(currentTarget, target, opts) { + + this.currentTarget = currentTarget; + target = target || null; + this.opts = { desktopSwipe: false, ... opts }; + + this.uiDev = { + touchCapable : ('ontouchstart' in window), // Is Mobile Device Flag + isMobile : /Mobi/.test(navigator.userAgent), + swipe_h_threshold : 50, + swipe_v_threshold : 50, + startevent : ('ontouchstart' in window) ? 'touchstart' : 'mousedown', + endevent : ('ontouchstart' in window) ? 'touchend' : 'mouseup', + moveevent : ('ontouchstart' in window) ? 'touchmove' : 'mousemove' + }; + + if (!opts.desktopSwipe && !uiDev.isMobile) { + return; + } + + let duplicate = this.dxData('swipe') || 0; + + this.dxData('swipe', ++duplicate); + + if (duplicate > 1) { + return; + } + + this.started = false, + this.hasSwiped = false, + this.originalCoord = { + x: 0, + y: 0 + }; + + this.finalCoord = { + x: 0, + y: 0 + }; + + this.startEvnt = {}; + + this.dxOn(this.uiDev.startevent + '.' + 'swipe', e => this.touchStart(e), target, { passive: true } ); + this.dxOn(this.uiDev.moveevent + '.' + 'swipe', e => this.touchMove(e), target, { passive: true }); + this.dxOn(this.uiDev.endevent + '.' + 'swipe', e => this.touchEnd(e), target, { passive: true }); + } + + // Screen touched, store the original coordinate + touchStart(e) { + + this.originalCoord.x = (e.targetTouches) ? e.targetTouches[0].pageX : e.pageX; + this.originalCoord.y = (e.targetTouches) ? e.targetTouches[0].pageY : e.pageY; + this.finalCoord.x = originalCoord.x; + this.finalCoord.y = originalCoord.y; + this.started = true; + + let offset = this.currentTarget.dxOffset(); + + // Read event data into our startEvt: + this.startEvnt = { + 'position': { + 'x': (this.uiDev.touchCapable) ? e.touches[0].pageX : e.pageX, + 'y': (this.uiDev.touchCapable) ? e.touches[0].pageY : e.pageY + }, + 'offset': { + 'x': (this.uiDev.touchCapable) ? Math.round(e.changedTouches[0].pageX - offset.left) : Math.round(e.pageX - offset.left), + 'y': (this.uiDev.touchCapable) ? Math.round(e.changedTouches[0].pageY - offset.top) : Math.round(e.pageY - offset.top) + }, + 'time': Date.now(), + 'target': e.target + }; + } + + // Store coordinates as finger is swiping + touchMove(e) { + + this.finalCoord.x = (e.targetTouches) ? e.targetTouches[0].pageX : e.pageX; + this.finalCoord.y = (e.targetTouches) ? e.targetTouches[0].pageY : e.pageY; + + let swipedir; + + // We need to check if the element to which the event was bound contains a data-xthreshold | data-vthreshold: + let ele_x_threshold = (this.currentTarget.parentNode.dxData('xthreshold')) ? this.currentTarget.parentNode.dxData('xthreshold') : this.currentTarget.dxData('xthreshold'), + ele_y_threshold = (this.currentTarget.parentNode.dxData('ythreshold')) ? this.currentTarget.parentNode.dxData('ythreshold') : this.currentTarget.dxData('ythreshold'), + h_threshold = (typeof ele_x_threshold !== 'undefined' && ele_x_threshold !== false && parseInt(ele_x_threshold)) ? parseInt(ele_x_threshold) : this.uiDev.swipe_h_threshold, + v_threshold = (typeof ele_y_threshold !== 'undefined' && ele_y_threshold !== false && parseInt(ele_y_threshold)) ? parseInt(ele_y_threshold) : this.uiDev.swipe_v_threshold; + + if (this.originalCoord.y > this.finalCoord.y && (this.originalCoord.y - this.finalCoord.y > v_threshold)) { + swipedir = 'swipeup'; + } + + if (originalCoord.x < finalCoord.x && (finalCoord.x - originalCoord.x > h_threshold)) { + swipedir = 'swiperight'; + } + + if (originalCoord.y < finalCoord.y && (finalCoord.y - originalCoord.y > v_threshold)) { + swipedir = 'swipedown'; + } + + if (this.originalCoord.x > this.finalCoord.x && (this.originalCoord.x - this.finalCoord.x > h_threshold)) { + swipedir = 'swipeleft'; + } + + if (swipedir != undefined && this.started) { + + originalCoord.x = 0; + originalCoord.y = 0; + finalCoord.x = 0; + finalCoord.y = 0; + this.started = false; + + let offset = this.currentTarget.dxOffset(); + + let endEvnt = { + 'position': { + 'x': (this.uiDev.touchCapable) ? e.touches[0].pageX : e.pageX, + 'y': (this.uiDev.touchCapable) ? e.touches[0].pageY : e.pageY + }, + 'offset': { + 'x': (this.uiDev.touchCapable) ? Math.round(e.changedTouches[0].pageX - offset.left) : Math.round(e.pageX - offset.left), + 'y': (this.uiDev.touchCapable) ? Math.round(e.changedTouches[0].pageY - offset.top) : Math.round(e.pageY - offset.top) + }, + 'time': Date.now(), + 'target': e.target + }; + + // Calculate the swipe amount (normalized): + let xAmount = Math.abs(this.startEvnt.position.x - endEvnt.position.x), + yAmount = Math.abs(this.startEvnt.position.y - endEvnt.position.y); + + const touchData = { + 'startEvnt' : this.startEvnt, + 'endEvnt' : endEvnt, + 'direction' : swipedir.replace('swipe', ''), + 'xAmount' : xAmount, + 'yAmount' : yAmount, + 'duration' : endEvnt.time - this.startEvnt.time + }; + + this.hasSwiped = true; + + this.currentTarget.dxTrigger('swipe', touchData).dxTrigger(swipedir, null, touchData); + } + } + + /** + * Touch End + */ + touchEnd(e) { + this.started = false; + this.hasSwiped = false; + } + + destroy(target) { + + if (!this.opts.desktopSwipe && !this.uiDev.isMobile) { + return; + } + + let duplicate = this.dxData('swipe') || 0; + + this.dxData('swipe', --duplicate); + + if (!duplicate) { + + if (target) { + this.dxOff(this.uiDev.startevent + '.' + 'swipe', target); + this.dxOff(this.uiDev.moveevent + '.' + 'swipe', target); + this.dxOff(this.uiDev.endevent + '.' + 'swipe', target); + } + else { + this.dxOff(this.uiDev.startevent + '.' + 'swipe'); + this.dxOff(this.uiDev.moveevent + '.' + 'swipe'); + this.dxOff(this.uiDev.endevent + '.' + 'swipe'); + } + } + } + } + + + /** + * Custom Swipe Events + */ + export const customEvents = { + + swipe: { + + + + }, + + }; + + customEvents.swipeup = customEvents.swipe; + customEvents.swiperight = customEvents.swipe; + customEvents.swipedown = customEvents.swipe; + customEvents.swipeleft = customEvents.swipe; diff --git a/src/utils/Ut.js b/src/utils/Ut.js new file mode 100644 index 0000000..26b3023 --- /dev/null +++ b/src/utils/Ut.js @@ -0,0 +1,148 @@ +const stamp = Date.now(); + +export class Ut { + + static EventsUID = 'Events' + stamp; + static DataUID = 'Data' + stamp; + + static isSet = (val) => typeof val !== 'undefined'; + static isFn = (val) => typeof val === 'function'; + static isStr = (val) => typeof val === 'string'; + static ucFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1); + static lcFirst = (str) => str.charAt(0).toLowerCase() + str.slice(1); + static isEmptyObj = obj => !Object.keys(obj).length; + static objVal = obj => Object.values(obj)[0]; + + static trigger = (handler, ...args) => typeof handler === 'function' ? handler.call(this, ...args) : null; + + static each(obj, callback, context) { + for (let key in obj) { + callback.call(context || this, obj[key], key); + } + } + + static tplString = (str, context) => new Function('return `' + str + '`;').call(context || {}); + + static extendNode(name, value) { + + if (!Object.getOwnPropertyDescriptor(Node.prototype, name)) { + Object.defineProperty(Node.prototype, name, { + value: value, + enumerable: false, + writable: true, + configurable: true + }); + } + } + + static extendNodeUx(...classRefs) { + + classRefs.forEach(classRef => { + + const clsName = `ux${classRef.name}`; + + if (!Object.getOwnPropertyDescriptor(Node.prototype, clsName)) { + Object.defineProperty(Node.prototype, clsName, { + enumerable: false, + configurable: true, + writable: true, + value: function (...args) { + return new classRef(this, ...args); + }} + ); + } + }); + } + + static device = { + touchCapable : ('ontouchstart' in window), // Is Mobile Device Flag + isMobile : /Mobi/.test(navigator.userAgent), + swipe_h_threshold : 50, + swipe_v_threshold : 50, + startevent : ('ontouchstart' in window) ? 'touchstart' : 'mousedown', + endevent : ('ontouchstart' in window) ? 'touchend' : 'mouseup', + moveevent : ('ontouchstart' in window) ? 'touchmove' : 'mousemove' + } + + + /** + * Redirect + */ + static url = (location) => window.location.href = location; + + /** + * Decode HTML entity + */ + static unescape = str => ((typeof str === 'string') && (new RegExp(/&|<|>|"|'/).test(str))) + ? str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'") + : str; + + /** + * Encode HTML Entities + */ + htmlEntities = str => String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); + + /** + * Convert RGB to Hex + */ + rgb2hex = rgb => '#' + rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map(n => parseInt(n, 10).toString(16).padStart(2, '0')).join(''); + + /** + * Copy To Clipboard + */ + static copyToClipboard(target) { + + if (navigator.clipboard) { + + if (target instanceof Node) { + + target.select(); + target.setSelectionRange(0, 99999); + + target = target.value; + } + + navigator.clipboard.writeText(target); + + return; + } + + let tval = null; + + if (target instanceof Node) { + + if (target.dataset.notrusted) { + delete target.dataset.notrusted; + return; + } + + tval = target.value; + } + + const textarea = document.createElement('textarea'); + textarea.className = 'hidden'; + textarea.value = tval || target; + + document.body.prepend(textarea); + textarea.select(); + + try { + document.execCommand('copy'); + } + catch (error) { + console.error(error); + } + finally { + + textarea.remove(); + + if (target instanceof Node) { + + target.dataset.notrusted = 1; + + target.select(); + target.setSelectionRange(0, 99999); + } + } + } +}; \ No newline at end of file diff --git a/src/utils/dom.js b/src/utils/dom.js new file mode 100644 index 0000000..844dc21 --- /dev/null +++ b/src/utils/dom.js @@ -0,0 +1,765 @@ + import { Ut } from './Ut'; + + /** + * Smart Query Selector + */ + export function $(selector, list) { + + // DOM parser + if (selector.startsWith('<')) { + + // Case 1 -> return Node + let template = document.createElement('template'); + template.innerHTML = selector; + return template.content.firstChild; + } + else if (list === true) { + // Case 2 -> return Nodelist + return document.querySelectorAll(selector); + } + else if (typeof list === 'function') { + // Case 3 -> return Array considering the callback filter + return [... document.querySelectorAll(selector)].filter(list); + } + + // Most Cases -> return Node + return document.querySelector(selector); + }; + + /** + * Create a vdom element + */ + export function El(tag, attr, ...children) { + + attr = attr || {}; + + let elem = document.createElement(tag); + + for (let name in attr) { + + if (attr[name] === null) { + continue; + } + + elem.setAttribute(name, attr[name]); + } + + children.forEach(child => child && elem.append(child)); + + return elem; + }; + + /** + * Static Event Options + */ + export class EvtOptions { + static nsEvent = false; + static section = null; + } + + /** + * Set Dom Uti;s + */ + export const domUtils = { + + /** + * Attach an event handler function for one or more elements. + */ + on(events, selector, handler, opts) { + + let nsPref = (EvtOptions.nsEvent && EvtOptions.section) ? (EvtOptions.section + '.') : ''; + + opts = { ... opts }; + + if (!this[ Ut.EventsUID ]) { + this[ Ut.EventsUID ] = {}; + } + + if (!Ut.isSet(handler) || handler === null) { + + events.split(' ').forEach(function(evt) { + + evt = evt.split('.'); + + let eName = evt[0]; + let namespace = nsPref + (evt[1] || '*'); + + if (!this[ Ut.EventsUID ][ eName ]) { + this[ Ut.EventsUID ][ eName ] = []; + } + + let handler = function(event) { + + if (!event.detail || !event.detail.namespace || event.detail.namespace == nsPref + '*' || event.detail.namespace == namespace) { + selector.call(this, event, this, event.detail && event.detail.data && event.detail.data || null ); + } + }; + + this[ Ut.EventsUID ][ eName ].push([ namespace, null, handler ]); + + if (eName.startsWith('swipe')) { + // new SwipeEvents( this, eName, null, handler, opts ); + } + + this.addEventListener(eName, handler, opts); + + }, this); + } + else { + + events.split(' ').forEach(function(evt) { + + evt = evt.split('.'); + + let eName = evt[0]; + let namespace = nsPref + (evt[1] || '*'); + + if (!this[ Ut.EventsUID ][ eName ]) { + this[ Ut.EventsUID ][ eName ] = []; + } + + let _handler = function(event) { + + let target = event.target; + + while (target && target !== this) { + + if (target.matches(selector)) { + + if (!event.detail || !event.detail.namespace || event.detail.namespace == nsPref + '*' || event.detail.namespace == namespace) { + handler.call(target, event, target, event.detail && event.detail.data && event.detail.data || null); + } + + if (!opts.allChildren) { + break; + } + } + + target = target.parentNode; + } + }; + + this[ Ut.EventsUID ][ eName ].push([ namespace, selector, _handler ]); + + if (eName.startsWith('swipe')) { + // new SwipeEvents(this, eName, target, _handler, opts); + } + + this.addEventListener(eName, _handler, opts); + + }, this); + } + + return this; + }, + + /** + * Remove an event handler. + */ + off(events, selector) { + + let nsPref = (EvtOptions.nsEvent && EvtOptions.section) ? (EvtOptions.section + '.') : ''; + + if (!this[Ut.EventsUID]) { + return this; + } + + if (!events) { + this.replaceWith(this.cloneNode(true)); + return this; + } + + events.split(' ').forEach(function(evt) { + + evt = evt.split('.'); + + let eName = evt[0]; + let namespace = nsPref + (evt[1] || '*'); + + let eList = this[Ut.EventsUID][eName]; + + if (eList) { + + let i = eList.length; + + while(i--) { + + let et = eList[i]; + + if ((et[0] == namespace || namespace == nsPref + '*') && (!selector || (et[1] == selector)) ) { + + //if (customEvents[eName]) { + // customEvents[eName].destroy.call(this, eName, et[1]); + //} + + if (eName.startsWith('swipe')) { + + } + + this.removeEventListener(eName, et[2]); + + eList.splice(i, 1); + } + } + } + }, this); + + return this; + }, + + /** + * Find descendents + */ + find(selector, list) { + + if (list === true) { + // Case 1 -> Return NodeList + return this.querySelectorAll(selector); + } + else if (typeof list === 'function') { + // Case 2 -> return Array considering the callback filter + return [... this.querySelectorAll(selector)].filter(list); + } + + // Most cases -> Return First Node + return this.querySelector(selector); + }, + + children(selector, list) { + + if (list === true) { + + // Case 1 -> Array + + if (selector) { + + let children = []; + + [...this.children].forEach(child => { + if (child.matches(selector)) { + children.push(child); + } + }); + + return children; + } + + return [...this.children]; + } + else if (typeof list === 'function') { + // Case 2 Callback filter > Return filtered array + return [...this.children].filter((child, i) => { + if (!selector || child.matches(selector)) { + return list(child, i); + } + }); + } + + if (selector) { + return [...this.children].find(node => node.matches(selector)) || null; + } + + return this.children[0] || null; + }, + + /* + * Get Node Index Position + */ + index(nodes) { + return [...nodes].indexOf(this); + }, + + /** + * Get Nodes Siblings + */ + siblings(selector, list) { + + let sibs = []; + let elem = this.parentNode.firstChild; + + if (typeof list === 'function') { + + while (elem) { + + if (elem != this && elem.nodeType == 1 && (!selector || elem.matches(selector)) ) { + + if (list(elem)) { + sibs.push(elem); + } + } + + elem = elem.nextElementSibling; + } + } + else { + + while (elem) { + + if (elem != this && elem.nodeType == 1 && (!selector || elem.matches(selector)) ) { + sibs.push(elem); + } + + elem = elem.nextElementSibling; + } + } + + return sibs; + }, + + /** + * Get the next node sibling according to the selector + */ + next(selector) { + + let node = this.nextElementSibling; + + if (node && selector && !node.matches(selector)) { + return null; + } + + return node; + }, + + /** + * Get the previous node sibling according to the selector + */ + prev(selector) { + + let node = this.previousElementSibling; + + if (node && selector && !node.matches(selector)) { + return null; + } + + return node; + }, + + /** + * Get node parents according to the selector + */ + parents(selector, list) { + + let parents = []; + let parent = this.parentElement; + + while (parent) { + + if (!selector || parent.matches(selector)) { + + if (!Ut.isSet(list)) { + // Case 1 -> Return the closest parent Node + return parent; + } + else if (list === true) { + // Case 2 -> It will return an Array + parents.push(parent); + } + } + + parent = parent.parentElement; + } + + return list ? parents : null; + }, + + /** + * Node Filter + */ + filter(selector, callback) { + + if (this instanceof Node) { + + if (this.matches(selector)) { + return Ut.isFn(callback) ? callback(this) : this; + } + } + else if (this instanceof NodeList) { + + if (callback === true) { + return ([...this].filter((item) => item.matches(selector)) ); + } + else if (typeof callback === 'function') { + + return [...this].filter((item) => { + if (item.matches(selector)) { + return callback(item); + } + }); + } + + // return [ ... this ].dxFind(node => node.matches(selector)) || null; + } + + return null; + }, + + /** + * Dispath An Event + */ + trigger(evt) { + + evt = evt.split('.'); + + this.dispatchEvent( + new CustomEvent( + evt[0], { + bubbles: true, + detail: { + namespace: ((EvtOptions.nsEvent && EvtOptions.section) ? (EvtOptions.section + '.') : '') + (evt[1] || '*'), + data: [...arguments].splice(1) + } + } + ) + ); + + return this; + }, + css(prop, val) { + + if (Ut.isSet(val)) { + + if (!val) { + this.style.removeProperty(prop); + } + else { + this.style[ prop ] = val; + } + } + else { + Object.assign(this.style, prop); + } + + return this; + }, + + offset() { + + if (!this.getClientRects().length) { + return { top: 0, left: 0 }; + } + + const rect = this.getBoundingClientRect(); + const win = this.ownerDocument.defaultView; + + return { + top: rect.top + win.scrollY, + left: rect.left + win.scrollX + }; + }, + /** + * Store / Retreive Arbitrary Data + */ + data(key, val) { + + this[ Ut.DataUID ] = this[ Ut.DataUID ] || {}; + + if (!Ut.isSet(val)) { + return this[ Ut.DataUID ][ key ] || null; + } + + this[ Ut.DataUID ][ key ] = val; + + return this; + }, + + /** + * Remove Arbitrary Data + */ + removeData(key) { + + this[ Ut.DataUID ] = this[ Ut.DataUID ] || {}; + delete this[ Ut.DataUID ][ key ]; + + return this; + }, + + removeEvents() { + this.replaceWith(this.cloneNode(true)); + return this; + }, + + /** + * Inject HTML & Return Node + * Or return raw content + */ + html(content) { + + if (Ut.isSet(content)) { + this.innerHTML = content; + return this; + } + + return this.innerHTML; + }, + + /** Wrap element + * + * @return wrappewr + */ + wrap(wrapper) { + this.parentNode.insertBefore(wrapper, this); + wrapper.appendChild(this); + return this; + }, + + /** + * Set text & Return Node + * Or return text content + */ + text(content) { + + if (Ut.isSet(content)) { + this.textContent = content; + return this; + } + + return this.textContent; + }, + + /** + * Set value & Return Node + * Or return trimmed value + */ + val(content) { + + if (Ut.isSet(content)) { + this.value = content; + return this; + } + + return this.value.trim(); + }, + + /** + * Adds the specified class(es) to the selected element. + * @param String classNames One or more space-separated classes to be added + * @return Element Selected object + */ + addClass(classNames) { + this.classList.add(...classNames.split(' ')); + return this; + }, + + + /** + * Remove the specified class(es) to the selected element. + * @param String classNames One or more space-separated classes to be removed + * @return Element Selected object + */ + removeClass(classNames) { + this.classList.remove(...classNames.split(' ')); + return this; + }, + + /** + * Add or remove a class to the selected Element + * @param String classNames + * @param bool force If included, turns the toggle into a one way-only operation + */ + toggleClass(className, force) { + this.classList.toggle(className, force); + return this; + }, + + /* + * Get Computed Style Property + */ + getProp(name) { + return window.getComputedStyle(this).getPropertyValue(name); + }, + + /** + * Reset Form + */ + resetForm() { + + this.reset(); + + let i = 0; + + for(; i < this.elements.length; i++) { + + let el = this.elements[i]; + + if (el.type == 'radio' || el.type == 'checkbox') { + el.checked = el.value; + } + } + + return this; + }, + + /** + * Dom Content Loaded Handler + */ + ready(callback, context, ...args) { + + context = context || this; + + if (document.readyState != 'loading') { + callback.call(context, ...args); + } + else { + document.addEventListener('DOMContentLoaded', callback.bind(context, ...args) ); + } + }, + + /////////////////////// + // Call a widget fn + /////////////////////// + + handler(widgetName, method, ... args) { + + const context = domUtils.data.call(this, widgetName); + + if (!context) { + return this; + } + + let handler = context[ method ]; + + if (handler && typeof handler == 'function') { + handler.call(context, ... args); + } + + return this; + } + }; + + /** + * Fill El Form Data + */ + export function fillData(el, data) { + + for (let key in data) { + + let val = data[key]; + let target = el[key]; + + if (!target) { + continue; + } + + if (target instanceof RadioNodeList) { // RadioNodeList + + // Input Radio + + if (target[0].type == 'radio') { + + target.forEach(item => { + + if (item.matches('[value="'+ val +'"]')) { + + item.checked = true; + item.parentNode.classList.add('checked'); + + domUtils.trigger.call(item, 'change'); + } + else { + item.checked = false; + item.parentNode.classList.remove('checked'); + } + }); + } + else { + + // Input Checkbox + + let i = 0; + let choices = []; + + target.forEach((item) => { + + if (item.matches('[value='+ val +']')) { + item.checked = true; + item.parentNode.classList.add('checked'); + choices.push(selected.parentNode.textContent); + } + else { + item.checked = false; + item.parentNode.classList.remove('checked'); + } + }); + } + + continue; + } + + let tagName = target.tagName.toLowerCase(); + + switch (tagName) { + + case 'input': + + if (target.type == 'file' && val) { + + let uploader = domUtils.data.call(target, 'uploader'); + + if (uploader) { + uploader.updateFiles(Array.isArray(val) ? val : [ val ]); + } + + continue; + } + + if ('value' in target.dataset) { + + target.dataset.value = Ut.unescape(val); + target.value = Ut.unescape(data[key + '_value'] ?? val); + + if (domUtils.data.call(target, 'datepicker')) { + domUtils.trigger.call(target, 'restore') + } + + if (domUtils.data.call(target, 'counter')) { + domUtils.trigger.call(target, 'input') + } + + continue; + } + + target.value = Ut.unescape(val); + + break; + case 'select': + target.value = val; + target.dataset.val = val; + break; + + case 'textarea': + + target.value = Ut.decode(val); + + const autoexpand = domUtils.data.call(target, 'autoexpand'); + + if (autoexpand) { // Set Autoexpand + autoexpand.update(); + } + + if (domUtils.data.call(target, 'counter')) { + domUtils.trigger.call(target, 'input') + } + + break; + } + } + } + + /** + * DX Assign + */ + export function domify(prefix = 'dx') { + + Ut.each(domUtils, (fn, k) => Ut.extendNode(`${prefix}${Ut.ucFirst(k)}`, fn) ); + + window[ `${prefix}On` ] = domUtils.on; + window[ `${prefix}Off` ] = domUtils.off; + } + + /** + * Dx Ready + */ + export function dxReady(handler, prefix) { + domify(prefix || 'dx'); + domUtils.ready(handler); + } \ No newline at end of file diff --git a/src/utils/form-validator/FormValidator.js b/src/utils/form-validator/FormValidator.js new file mode 100644 index 0000000..83ff13f --- /dev/null +++ b/src/utils/form-validator/FormValidator.js @@ -0,0 +1,225 @@ +import './validator.css'; + +import { Ut } from '../Ut'; +import { El } from '../dom'; +import { Boot } from '../../core/Boot'; +import { Validator } from './Validator'; + +//////////////////////////// +// Class Form Validator +//////////////////////////// + +export class FormValidator extends Validator { + + constructor(target, opts) { + + super(target); + + this.target = target; + + if (this.target.dxData('form')) { + return; + } + + this.target.dxData('form', this); + + this.opts = { ... { + submit : false, + display_err: true, + reset_form : false + }, ... opts }; + + Ut.trigger(this.opts.onInit, this.target.elements); + + this.setEvents(); + } + + setEvents() { + + // Click on error UI + + this.target.dxOn('click.ui', '.sp-has-error', (_, t) => this.target.dxFind('input[name="' + t.dxFind('input').name + '"', item => item.parentNode?.classList.remove('sp-has-error')) ); + + // Input Focus - Handler + + this.target.dxOn('click.form', 'input, select, textarea', (_, t) => { + + if (this.opts.display_err) { + + let fieldWrapper = t.dxParents('.form-field'); + + fieldWrapper?.classList.remove('form-field-error'); + + fieldWrapper?.dxFind('.sp-label-error')?.remove(); + } + + Ut.trigger(this.opts.onClick); + }); + + // Submit - Handler + this.target.dxOn('submit.form', e => { + + if (!this.opts.submit) { + e.preventDefault(); + } + + this.entry = this.getData(); + + if (Ut.isFn(this.opts.valid) && !this.valid()) { + e.preventDefault(); + return; + } + + if (Ut.isFn(this.opts.onSubmit) && !this.opts.onSubmit(this.entry, this)) { + e.preventDefault(); + return; + } + + if (this.opts.submit) { + return; + } + + if (Ut.isFn(this.opts.onSubmit)) { + this.opts.onSubmit.call(this); + } + + if (this.opts.reset_form) { + this.target.dxResetForm(); + } + }); + + // Fix keydown enter bug + + let delayed = false; + + this.target.dxOn('keydown.form', e => { + + if (e.key == 'Enter' && e.target.tagName.toLowerCase() != 'textarea') { + + e.preventDefault(); + + if (!e.target.readOnly && !this.pendingRequest && !delayed) { + + delayed = true; + + setTimeout(() => delayed = false, 200); + this.target.dxTrigger('submit'); + } + } + }); + } + valid () { + + let pass = true; + + this.target.dxFind('.form-field-error', item => item.classList.remove('form-field-error', 'sp-has-error')); + this.target.dxFind('.sp-label-error', item => item.remove()); + + let rules = this.opts.valid(this.entry); + + for (let name in rules) { + + if (!(name in this.target.elements)) { + continue; + } + + let el = this.target.elements[ name ]; + + if (!('length' in el ) || el.nodeName.toLowerCase() == 'select') { + el = [ el ]; + } + + for (let input of el) { + + let val = (input.type && input.type == 'checkbox') + ? (input.checked ? input.value : '') + : El('p', {}, input.value).textContent.trim(); + + let ruleList = rules[ name ].split('|'); + + for (let tokenRule of ruleList) { + + let chunks = tokenRule.split(':'); + + let args = chunks[1] ?? null; + + let rule = chunks[0]; + + if (!this.handlers[rule]) { + throw new Error(`The rule "{${rule}}" does not exist`); + } + + let format = { value: Validator.INPUT_STR }; + + let result = this.handlers[ rule ] (val ?? null, !!input, args, format); + + if (result === false) { + + pass = false; + + let emsg = this.renderRuleMsg(format, rule, args); + + if (this.opts.display_err) { + this.inputError(input, emsg); + } + + Ut.trigger(this.opts.onError, emsg); + + break; + } + + if (result === null) { + break; + } + } + } + } + + return pass; + } + + renderRuleMsg(format, rule, args) { + + args = args ? args.split(',') : null; + + return Boot.intData.error[ this.formatName(format.value) + '_' + rule ].replace(/\$(\d+)/g, (_, i) => args[i - 1] ?? '') + } + + formatName(val) { + + switch (val) { + case Validator.INPUT_STR: return 'str'; + case Validator.INPUT_NUM: return 'num'; + case Validator.INPUT_FILE: return 'file'; + default: return 'str'; + } + } + + inputError(input, emsg) { + + if (input.type == 'checkbox' || input.type == 'radio') { + input.parentNode.classList.add('sp-has-error'); + } + else { + + let fieldWrapper = input.dxParents('.form-field'); + + if (!fieldWrapper) { + return false; + } + + fieldWrapper.classList.add('form-field-error'); + + fieldWrapper.append( El('div', { class: 'sp-label-error' }, emsg ) ); + } + + return false; + } + + destroy() { + this.target.dxOff('submit.form click.form keydown.form click.ui'); + this.target.dxRemoveData('form'); + } +} + +// Ut.extendNodeUx(FormValidator); \ No newline at end of file diff --git a/src/utils/form-validator/InputError.js b/src/utils/form-validator/InputError.js new file mode 100644 index 0000000..a6a5be7 --- /dev/null +++ b/src/utils/form-validator/InputError.js @@ -0,0 +1,45 @@ +import './validator.css'; + +//import { Ut } from "."; +import { El } from '../dom'; + +export class InputError { + + /** + * InputError, Constructor + */ + constructor(target, emsg) { + + this.target = target; + this.emsg = emsg; + + this.fieldWrapper = target.dxParents('.form-field'); + + if (!this.fieldWrapper) { + return; + } + + if (!this.fieldWrapper.classList.contains('form-field-error')) { + this.create(); + } + } + + /** + * InputError, Create + */ + create() { + this.fieldWrapper.classList.add('form-field-error'); + this.fieldWrapper.append( El('div', { class: 'sp-label-error' }, this.emsg) ); + } + + /** + * InputError, Destroy + */ + destroy() { + + this.fieldWrapper.classList.remove('form-field-error'); + this.fieldWrapper.dxFind('.sp-label-error')?.remove(); + } +} + +//Ut.extendNodeUx(InputError); \ No newline at end of file diff --git a/src/utils/form-validator/Validator.js b/src/utils/form-validator/Validator.js new file mode 100644 index 0000000..d620197 --- /dev/null +++ b/src/utils/form-validator/Validator.js @@ -0,0 +1,382 @@ +//////////////////// +// Class Validator +//////////////////// + +export class Validator { + + constructor(target) { + this.target = target; + } + + static INPUT_STR = 1; + static INPUT_NUM = 2; + static INPUT_FILE = 3; + + handlers = { + + required: (val, present) => present && val !== '' && val !== undefined && val !== null, + + not_empty: (val, present) => present && val !== '' && val !== undefined && val !== null && val !== 0 && val !== '0', + + sometimes: (_, present) => !present ? null : true, + + filled: (val, present) => !present ? null : val !== '' && val !== undefined && val !== null, + + present: (_, present) => present, + + missing: (_, present) => !present, + + nullable: (val, present) => (!present || val === '' || val === undefined || val === null) ? null: true, + + numeric: (val, present, arg, format) => { + format.value = Validator.INPUT_NUM; + return !present ? null : !isNaN(val); + }, + + integer: (val, present, arg, format) => { + format.value = Validator.INPUT_NUM; + return !present ? null : Number.isInteger(Number(val)); + }, + + boolean: (val, present, arg, format) => { + format.value = Validator.INPUT_NUM; + return !present ? null : [true, false, 0, 1, '0', '1'].includes(val); + }, + + string: (val, present, arg, format) => { + format.value = Validator.INPUT_STR; + return !present ? null : typeof val === 'string'; + }, + file: (val, present, arg, format) => { + + format.value = Validator.INPUT_FILE; + + return !present ? null : ( + typeof val === 'object' && + 'error' in val && + 'tmp_name' in val && + val.error === 0 + ); + }, + + image: (val, present, arg, format) => { + + format.value = Validator.INPUT_FILE; + + if (!present) return null; + + if ( + typeof val !== 'object' || + !('error' in val) || + !('tmp_name' in val) || + val.error !== 0 || + typeof val.name !== 'string' + ) { + return false; + } + + const ext = val.name.split('.').pop().toLowerCase(); + + return ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp'].includes(ext); + }, + + mimes: (val, present, arg, format) => { + + if (!present) return null; + + if ( + typeof val !== 'object' || + !('error' in val) || + !('tmp_name' in val) || + val.error !== 0 || + typeof val.name !== 'string' + ) { + return false; + } + + const allowed = arg.split(',').map(ext => ext.trim().toLowerCase()); + const ext = val.name.split('.').pop().toLowerCase(); + + return allowed.includes(ext); + }, + + min: (val, present, arg, format) => { + + if (!present) return null; + + return ( + (format.value === Validator.INPUT_STR && val.length >= arg) || + (format.value === Validator.INPUT_NUM && Number(val) >= arg) + ); + }, + + max: (val, present, arg, format) => { + + if (!present) return null; + + return ( + (format.value === Validator.INPUT_STR && val.length <= arg) || + (format.value === Validator.INPUT_NUM && Number(val) <= arg) + ); + }, + + size: (val, present, arg, format) => { + + if (!present) return null; + + return ( + (format.value === Validator.INPUT_STR && val.length == arg) || + (format.value === Validator.INPUT_NUM && Number(val) == arg) + ); + }, + + gt: (val, present, arg, format) => { + + if (!present) return null; + + return ( + (format.value === Validator.INPUT_STR && val.length > arg) || + (format.value === Validator.INPUT_NUM && Number(val) > arg) + ); + }, + + gte: (val, present, arg, format) => { + + if (!present) return null; + + return ( + (format.value === Validator.INPUT_STR && val.length >= arg) || + (format.value === Validator.INPUT_NUM && Number(val) >= arg) + ); + }, + + lt: (val, present, arg, format) => { + + if (!present) return null; + + return ( + (format.value === Validator.INPUT_STR && val.length < arg) || + (format.value === Validator.INPUT_NUM && Number(val) < arg) + ); + }, + + lte: (val, present, arg, format) => { + + if (!present) return null; + + return ( + (format.value === Validator.INPUT_STR && val.length <= arg) || + (format.value === Validator.INPUT_NUM && Number(val) <= arg) + ); + }, + + between: (val, present, arg, format) => { + + if (!present) return null; + + const [min, max] = arg.split(',').map(Number); + + if (isNaN(min) || isNaN(max)) return false; + + if (format.value === Validator.INPUT_STR) { + const len = val.length; + return len >= min && len <= max; + } else if (format.value === Validator.INPUT_NUM) { + return Number(val) >= min && Number(val) <= max; + } + + return false; + }, + + accepted: (val, present) => !present ? null : ['yes', 'on', 1, '1', true, 'true'].includes(val), + + declined: (val, present) => !present ? null : ['no', 'off', 0, '0', false, 'false'].includes(val), + + starts_with: (val, present, arg) => { + + if (!present) return null; + + return arg.split(',').some(prefix => val.startsWith(prefix)); + }, + + ends_with: (val, present, arg) => { + + if (!present) return null; + + return arg.split(',').some(suffix => val.endsWith(suffix)); + }, + + alpha: (val, present) => !present ? null : /^[a-zA-Z]+$/.test(val), + + alpha_num: (val, present) => !present ? null : /^[a-zA-Z0-9]+$/.test(val), + + uppercase: (val, present) => !present ? null : val === val.toUpperCase(), + + lowercase: (val, present) => !present ? null : val === val.toLowerCase(), + + domain: (val, present) => !present ? null : /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(val), + + email: (val, present) => !present ? null : /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val), + + url: (val, present) => { + + if (!present) return null; + + try { + new URL(val); + return true; + } catch { + return false; + } + }, + + ip: (val, present) => !present ? null : (this.handlers.ipv4(val, present) || this.handlers.ipv6(val, present) ), + + ipv4: (val, present) => !present ? null : /^(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)$/.test(val), + + ipv6: (val, present) => !present ? null : /^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4})$/.test(val), + + hash: (val, present) => !present ? null : /^[a-f0-9]+$/i.test(val), + + in: (val, present, arg) => !present ? null : arg.split(',').includes(val), + + same: (val, present, arg) => !present ? null : val === this.target.elements[arg].value, + + json: (val, present) => { + + if (!present) return null; + + try { + JSON.parse(val); + return true; + } catch { + return false; + } + }, + + date: (val, present, arg, format) => !present ? null : format.value === Validator.INPUT_STR && !isNaN(Date.parse(val)), + + cnp: (val, present, arg, format) => !present ? null : this.validCNP(val) + }; + + getData() { + + let fdata = new FormData(); + + [...this.target.elements].forEach(el => { + + if (!el.name || el.type == 'button' || el.type == 'submit') { + return true; + } + + let value; + let displayedVal = null; + + if ('value' in el.dataset) { + value = el.dataset.value; + displayedVal = el.value.trim(); + } + else { + value = el.value.trim(); + } + + switch (el.type) { + + case 'file': + + let uploader = el.dxData('uploader'); + + if (uploader) { + uploader.getFiles().forEach(filename => fdata.append('files_' + el.name + '[]', filename)); + } + + break; + case 'radio': + + if (!fdata.has(el.name)) { + fdata.set(el.name, ''); + } + + if (el.checked) { + fdata.set(el.name, value); + } + + break; + case 'checkbox': + + if (el.checked) { + fdata.append(el.name + '[]', value ); + } + + break; + case 'select-multiple': + + for (let j = 0; j < el.options.length; j++) { + if (el.options[j].selected && el.options[j].value != 0) { + fdata.append(el.name + '[]', /^\d+$/.test(el.options[j].value) + ? parseInt(el.options[j].value) + : el.options[j].value + ); + } + } + + break; + default: + + if (fdata.has(el.name)) { + + fdata.append(el.name + '[]', fdata.get(el.name) ); + fdata.delete(el.name); + fdata.append(el.name + '[]', value); + } + else if (fdata.has(el.name + '[]')) { + fdata.append(el.name + '[]', value); + } + else { + + fdata.set(el.name, value); + + if (displayedVal) { + fdata.set(el.name + '_value', displayedVal); + } + } + } + }); + + return fdata; + } + + validCNP(value) { + + let i = 0 , year = 0 , hashResult = 0 , cnp = [] , hashTable = [ 2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9 ]; + + if ( value.length !== 13 ) { return false; } + + for ( i = 0 ; i < 13 ; i++ ) { + + cnp[i] = parseInt( value.charAt(i) , 10 ); + + if( isNaN( cnp[i] ) ) { return false; } + + if( i < 12 ) { hashResult = hashResult + ( cnp[i] * hashTable[i] ); } + } + + hashResult = hashResult % 11; + + if( hashResult === 10 ) { hashResult = 1; } + + year = (cnp[1]*10)+cnp[2]; + + switch( cnp[0] ) { + case 1 : case 2 : { year += 1900; } break; + case 3 : case 4 : { year += 1800; } break; + case 5 : case 6 : { year += 2000; } break; + case 7 : case 8 : case 9 : { year += 2000; if( year > ( parseInt( new Date().getFullYear() , 10 ) - 14 ) ) { year -= 100; } } break; + default : { return false; } + } + + if( year < 1800 || year > 2099 ) { return false; } + + return ( cnp[12] === hashResult ); + } +} \ No newline at end of file diff --git a/src/utils/form-validator/validator.css b/src/utils/form-validator/validator.css new file mode 100644 index 0000000..2a047a3 --- /dev/null +++ b/src/utils/form-validator/validator.css @@ -0,0 +1,31 @@ +/* Validator */ + +.sp-has-error{ + color: red; +} + +.sp-label-error { + position: absolute; + background: #fb434a; + font-size: 12px; + padding: 5px 8px; + border-radius: 3px; + color: #fff; + z-index: 1; + white-space: nowrap; + bottom: -30px; +} + +.sp-label-error:after { + content: ''; + display: block; + position: absolute; + width: 0px; + height: 0px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #fb434a; + top: -5px; + left: 10px; + margin: auto; +} \ No newline at end of file diff --git a/src/widgets/auto-expand/AutoExpand.js b/src/widgets/auto-expand/AutoExpand.js new file mode 100644 index 0000000..cb7e42e --- /dev/null +++ b/src/widgets/auto-expand/AutoExpand.js @@ -0,0 +1,58 @@ +export class AutoExpand { + + /** + * Autoexpand, Constructor + */ + constructor(target, opts) { + + this.opts = { ... { minRows: 3 }, ... opts }; + + this.target = target; + + if (this.target.dxData('autoexpand')) { + return; + } + + this.target.dxData('autoexpand', this); + + this.update(); + + this.target.dxOn('input.autoExpand', this.setRows.bind(this)); + } + + /** + * Autoexpand, Update + */ + update() { + + let val = this.target.dxVal(); + + this.target.dxVal(''); + + this.target.dataset.baseScrollHeight = this.target.scrollHeight; + + this.target.dxVal(val); + + if (val) { + this.setRows(); + } + } + + /** + * Autoexpand, Set Rows + */ + setRows() { + this.target.rows = this.opts.minRows; + let rows = Math.ceil((this.target.scrollHeight - this.target.dataset.baseScrollHeight) / 16); + this.target.rows = this.opts.minRows + rows; + } + + /** + * Autoexpand, Destroy + */ + destroy = function() { + this.target.dxOff('input.autoExpand'); + this.target.rows = 2; + this.target.dxRemoveData('autoexpand'); + } +} \ No newline at end of file diff --git a/src/widgets/auto-expand/index.js b/src/widgets/auto-expand/index.js new file mode 100644 index 0000000..f6c1370 --- /dev/null +++ b/src/widgets/auto-expand/index.js @@ -0,0 +1 @@ +export { AutoExpand } from './AutoExpand'; \ No newline at end of file diff --git a/src/widgets/autocomplete/Autocomplete.js b/src/widgets/autocomplete/Autocomplete.js new file mode 100644 index 0000000..a32fccf --- /dev/null +++ b/src/widgets/autocomplete/Autocomplete.js @@ -0,0 +1,325 @@ +import './autocomplete.css'; + +import { Ut } from '../../utils/Ut' +import { El } from '../../utils/dom'; +import { Rc } from '../../core/Rc'; +import { Stack } from '../../core/Stack'; +import { Cancelable } from '../cancelable' + +export class Autocomplete { + + /** + * Autocomplete, Constructor + */ + constructor(target, data, opts) { + + this.target = target; + this.reqOpts = {}; + + if (this.target.dxData('autocomplete')) { + return; + } + + this.target.dxData('autocomplete', this); + + Stack.add(this); + + this.opts = { ... { + optionHeight: 32, + maxOptions: 4, + selectionColor: 'green' + }, ... opts }; + + this.target.dataset.value = ''; + + this.selected = null; + this.count = 0; + this.index = -1; + this.value = ''; + + if(typeof data === 'function') { + this.request = data; + } + else { + this.data = data || {}; + } + + new Cancelable(this.target); + + this.renderContainer(); + + this.setEvensts(); + } + + /** + * Autocomplete, Render Container + */ + renderContainer() { + + this.ui = { + wrapper: El('div', { class: 'sp-autocomplete sp-scrollbar' }) + }; + + this.ui.wrapper.style.maxHeight = (this.opts.maxOptions * this.opts.optionHeight) + 'px'; + } + + /** + * Autocomplete, Set Events + */ + setEvensts() { + + // Input Event Handler + this.target.dxOn('input.autocomplete' + this.ekey, (e, t) => this.inputHandler(e, t)); + + // Keydown Event Handler + this.target.dxOn('keydown.autocomplete' + this.ekey, (e, t) => this.keydownHandler(e, t)); + + // Click Event Handler + this.target.dxOn('click.autocomplete' + this.ekey, (e, t) => this.inputHandler(e, t)); + + // Click option Event Handler + this.ui.wrapper.dxOn('click', '.ac-option', (e, t) => this.clickOptionHandler(e, t)); + + // Click outside + document.body.dxOn('click.autocomplete', e => e.target != this.target && this.clearOptions()); + } + + /** + * Autocomplete, processData + */ + processData(val) { + + this.clearOptions(); + + let found = true; + let matchId = ''; + + for (let id in this.data) { + + let name = this.data[id]; + + let pos = name.toLowerCase().indexOf(val.toLowerCase()); + + if (pos != -1) { + + let label = El('span', { class: 'ac-label' }); + + if (pos) { + label.append(name.substring(0, pos)); + found = false; + } + + label.append(El('span', { class: 'text-semibold color-' + this.opts.selectionColor }, name.substring(pos, pos + val.length) )); + + if (pos + val.length < name.length) { + label.append( name.substring(pos + val.length, name.length ) ) + found = false; + } + + if (!this.opened) { + this.target.dxParents('.input-wrapper').after(this.ui.wrapper); + this.opened = true; + } + + let option = this.ui.wrapper.appendChild(El('div', { class: 'ac-option' }, label)); + + option.dataset.id = id; + option.style.height = this.opts.optionHeight + 'px'; + + this.count++; + + if (found && !matchId) { + matchId = id; + this.value = matchId; + } + } + } + + let changed = this.target.dataset.value != matchId; + + this.target.dataset.value = matchId; + + if (changed) { + Ut.trigger(this.opts.onChange, this); + this.target.dxTrigger('changed'); + } + } + + /** + * Autocomplete, Input Handler + */ + inputHandler(_, t) { + + //e.stopPropagation(); + + let val = t.value.trim(); + + if (this.request) { + + this.reqOpts.vars = { query: val }; + + this.request(this.reqOpts) + .then(jr => { + + if (jr.code == Rc.DONE) { + + this.data = jr.data; + this.processData(val); + } + }) + .catch(error => console.error(error)); + } + else { + this.processData(val); + } + } + + /** + * Autocomplete, Keydown Event Handler + */ + keydownHandler(e, t) { + + if (!this.count) { + return; + } + + if (e.key == 'ArrowUp') { + + if (!this.index) { + this.index = this.count - 1; + } + else { + + if (this.index == -1) { + this.index = this.count - 1; + } + else { + this.index--; + } + } + + this.setActiveOption(); + } + else if (e.key == 'ArrowDown') { + + if (this.index + 1 == this.count) { + this.index = 0; + } + else { + this.index++; + } + + this.setActiveOption(); + } + else if (e.key == 'Enter') { + + e.preventDefault(); + e.stopPropagation(); + + if (this.selected) { + this.updateVal(this.selected.dataset.id); + } + } + } + + /** + * Autocomplete, Click Option Handler + */ + clickOptionHandler(e, t) { + e.stopPropagation(); + this.updateVal(t.dataset.id); + } + + /** + * Autocomplete, Update Val + */ + updateVal(id) { + + this.value = id; + + if (id == this.target.dataset.value) { + this.clearOptions(); + return; + } + + this.target.dataset.value = id; + this.target.value = this.data[id]; + + Ut.trigger(this.opts.onChange, this); + this.target.dxTrigger('changed'); + + this.clearOptions(); + } + + /** + * Autocomplete, Set Active Option + */ + setActiveOption() { + + this.selected = this.ui.wrapper.children[this.index]; + + if (!this.selected) { + return; + } + + this.ui.wrapper.dxChildren('.ac-option', t => t.classList.remove('selected')); + + this.selected.classList.add('selected'); + + if (!this.index) { + this.ui.wrapper.scrollTop = 0; + } + else { + + if (this.index >= this.opts.maxOptions) { + this.ui.wrapper.scrollTop = (this.index + 1) * this.opts.optionHeight - this.opts.optionHeight * this.opts.maxOptions; + } + } + } + + /** + * Autocomplete Update Options + */ + updateOptions(data) { + + this.clearOptions(); + this.data = data; + } + + /** + * Autocomplete Clear Options + */ + clearOptions() { + + this.selected = null; + this.count = 0; + this.index = -1; + + this.opened = false; + + this.ui.wrapper.remove(); + this.ui.wrapper.dxHtml(''); + } + + /** + * Autocomplete, Destroy + */ + destroy() { + + this.ui.wrapper.remove(); + + delete this.target.dataset.value; + + document.body.dxOff('click.autocomplete' + this.ekey); + + this.target.dxOff('input.autocomplete' + this.ekey + ' keydown.autocomplete' + this.ekey + ' click.autocomplete' + this.ekey); + + Stack.delete(this); + + this.target.dxRemoveData('autocomplete'); + + Ut.trigger(this.opts.onDestroy, this); + } +} + +//Ut.extendNodeUx(Autocomplete); \ No newline at end of file diff --git a/src/widgets/autocomplete/autocomplete.css b/src/widgets/autocomplete/autocomplete.css new file mode 100644 index 0000000..c5d94eb --- /dev/null +++ b/src/widgets/autocomplete/autocomplete.css @@ -0,0 +1,37 @@ +/* ------------------ + Autocomplete +--------------------*/ + +.sp-autocomplete { + box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px; + background-color: #fff; + width: calc(100% - 8px); + border-radius: 0 0 3px 3px; + overflow-x: hidden; + position: absolute; + z-index: 100; +} + +.sp-autocomplete .ac-option { + padding: 0 12px; + color: #333; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; +} + +.sp-autocomplete .ac-option .ac-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; +} + +.sp-autocomplete .ac-option:hover { + color: var(--theme-widget-color); +} + +.sp-autocomplete .ac-option.selected { + background-color: #dfdfdf; +} \ No newline at end of file diff --git a/src/widgets/autocomplete/index.js b/src/widgets/autocomplete/index.js new file mode 100644 index 0000000..c7dea97 --- /dev/null +++ b/src/widgets/autocomplete/index.js @@ -0,0 +1 @@ +export { Autocomplete } from './Autocomplete'; \ No newline at end of file diff --git a/src/widgets/cancelable/Cancelable.js b/src/widgets/cancelable/Cancelable.js new file mode 100644 index 0000000..5a6dfba --- /dev/null +++ b/src/widgets/cancelable/Cancelable.js @@ -0,0 +1,71 @@ +import { Ut } from "../../utils/Ut"; +import { El } from "../../utils/dom"; + +export class Cancelable { + + /** + * Cancelable, Construct + */ + constructor(target, events) { + + let that = this; + + this.target = target; + + events = events || {}; + + target.dxData('cancelable', this); + + this.iconHandler = El('i', { class: 'icon-right icon-clear hide' }); + + let elType = target.tagName.toLowerCase(); + + if (elType == 'select' || (elType == 'input' && target.type == 'text')) { + + let val = this.target.dxVal(); + + let emptyVal = ''; + + if (elType == 'select' && target.dxFind('option[value="0"]')) { + emptyVal = '0'; + } + + this.iconHandler.classList.toggle('hide', val == emptyVal); + + target.after(this.iconHandler); + + target.dxOn(elType == 'input' ? 'input.cancelable changed.cancelable' : 'change.cancelable', function() { + that.iconHandler.classList.toggle('hide', this.dxVal() == emptyVal); + }); + + this.iconHandler.dxOn('click', function() { + + target.dxVal(emptyVal); + + if ('value' in that.target.dataset) { + that.target.dataset.value = ''; + } + + that.iconHandler.classList.add('hide'); + + that.target.dxTrigger('clear'); + Ut.trigger(events.clear, that); + }); + } + } + + /** + * Cancelble, Destroy + */ + destroy() { + + this.target.dxOff('input.cancelable change.cancelable'); + this.target.dxRemoveData('cancelable'); + + this.iconHandler.remove(); + + return this.target; + } +} + +//Ut.extendNodeUx(Cancelable); \ No newline at end of file diff --git a/src/widgets/cancelable/index.js b/src/widgets/cancelable/index.js new file mode 100644 index 0000000..3575859 --- /dev/null +++ b/src/widgets/cancelable/index.js @@ -0,0 +1 @@ +export { Cancelable } from './Cancelable'; \ No newline at end of file diff --git a/src/widgets/counter/Counter.js b/src/widgets/counter/Counter.js new file mode 100644 index 0000000..800b847 --- /dev/null +++ b/src/widgets/counter/Counter.js @@ -0,0 +1,82 @@ +import './counter.css'; + +import { El } from '../../utils/dom'; +import { Stack } from '../../core/Stack'; + +//////////////////////// +// Chars Counter +///////////////////////// + +export class Counter { + + /** + * Counter, Contructor + */ + constructor(target, opts) { + + this.target = target; + + if (this.target.dxData('counter')) { + return; + } + + this.target.dxData('counter', this); + + this.opts = { ... { limit: 1000 }, ... opts }; + + this.target.maxLength = this.opts.limit; + + this.indicator = El('div', { class: 'indicator' }); + this.counter = El('span', { class: 'counter' }, '0'); + + this.ui = El('div', { class: 'sp-chars-counter' }, + El('div', { class: 'progress-bar' }, this.indicator), + El('div', { class: 'label' }, this.counter, ' / '+ this.opts.limit) + ); + + this.target.after(this.ui); + + Stack.add(this); + + this.setEvents(); + } + + /** + * Counter, Update + */ + update() { + + let len = this.target.dxVal().length; + + this.indicator.style.width = Math.floor(100 * len / this.opts.limit) + '%'; + this.counter.dxText(len); + } + + /** + * Counter, Reset + */ + reset() { + + this.indicator.style.width = '0%'; + this.counter.dxText('0'); + } + + /** + * Counter, Set Events + */ + setEvents() { + + // Input - Handler + this.target.dxOn('input.counter' + this.ekey, this.update.bind(this)); + } + + /** + * Counter, Destroy + */ + destroy() { + + this.target.dxOff('input.counter' + this.ekey); + Stack.delete(this); + this.ui.remove(); + } +} \ No newline at end of file diff --git a/src/widgets/counter/counter.css b/src/widgets/counter/counter.css new file mode 100644 index 0000000..f1156d8 --- /dev/null +++ b/src/widgets/counter/counter.css @@ -0,0 +1,21 @@ +/* Counter */ + +.sp-chars-counter { + margin-top: 2px; +} + +.sp-chars-counter .progress-bar { + height: 3px; +} + +.sp-chars-counter .progress-bar .indicator { + width: 0; + height: 100%; + background-color: var(--theme-widget-color); + border-radius: 5px; +} + +.sp-chars-counter .label { + margin-top: 2px; + font-size: 12px; +} diff --git a/src/widgets/counter/index.js b/src/widgets/counter/index.js new file mode 100644 index 0000000..3e5010b --- /dev/null +++ b/src/widgets/counter/index.js @@ -0,0 +1 @@ +export { Counter } from './Counter'; \ No newline at end of file diff --git a/src/widgets/date-picker/DatePicker.js b/src/widgets/date-picker/DatePicker.js new file mode 100644 index 0000000..3bc9ee4 --- /dev/null +++ b/src/widgets/date-picker/DatePicker.js @@ -0,0 +1,1128 @@ +import './date-picker.css'; + +import { Ut } from '../../utils/Ut' +import { El, $ } from '../../utils/dom'; +import { Boot } from '../../core/Boot'; +import { Stack } from '../../core/Stack'; +import { Cancelable } from '../cancelable'; +import { Popover } from '../popover'; + +//////////////////// +// DatePicker +//////////////////// + +export class DatePicker { + + /** + * DatePicker, Constructor + */ + constructor(target, opts, events) { + + const that = this; + this.target = target; + + this.widget = Boot.intData?.widget?.datepicker?.children; + + if (!this.widget || this.target.dxData('datepicker')) { + return; + } + + this.target.dxData('datepicker', this); + Stack.add(this); + + this.opts = { ... { + style : 'normal', + disabled : false, + fullMonth : true, + navDisabled : false, + multiple : false, + chipbox : true, + setTime : false, + defaultDate : null, + minDate : null, + maxDate : null, + anim : true, + animTime : 200, + dateFormat : 'dd-mm-yyyy', + valueFormat : 'dd-mm-yyyy', + datesSep : ' / ', + valuesSep : '&', + locales : 'ro-RO', + cancelable : true, + hiddenMode : false + }, ... opts }; + + this.events = events || {}; + + this.conf = { + weekdays: this.widget.weekdays[0].split(',').map(function(val) { + return val.trim(); + }), + months: this.widget.months[0].split(',').map(function(val) { + return val.trim(); + }) + }; + + this.isDisabled = this.opts.disabled; + this.selectedDate = null; + + if (this.target.tagName.toLowerCase() == 'input') { + + this.isInput = true; + + if (this.target.type == 'text') { + + this.isToggled = true; + this.isOpened = false; + + this.target.setAttribute('autocomplete', 'off'); + this.target.readOnly = true; + } + } + + if (this.opts.minDate != null) { + this.#setMinDate(this.opts.minDate); + } + + if (this.opts.maxDate != null) { + this.#setMaxDate(this.opts.maxDate); + } + + this.#setTodayTime(); + + this.#setCurrentDate(); + + if (this.isToggled && this.opts.cancelable) { + new Cancelable(this.target, { + clear: () => that.clear() + }); + } + + this.scrollBar = this.target.dxParents('.sp-scrollbar'); + + this.initFlag = false; + + this.#restoreProperties(); + this.#updateInputVal(); + + if (!this.isToggled) { + this.#initUi(); + } + + if (this.isInput) { + this.#setTargetEvents(); + } + } + + /** + * DatePicker, Set min date + */ + #setMinDate(minDate) { + + if (minDate instanceof Date) { + this.minDate = minDate; + } + else if (typeof(minDate) === 'number') { + this.minDate = new Date(); + this.minDate.setDate(this.minDate.getDate() + minDate); + } + else { + this.minDate = new Date(minDate); + } + + this.minDate.setHours(0, 0, 0, 0); + } + + /** + * DatePicker, Set max date + */ + #setMaxDate(maxDate) { + + if (maxDate instanceof Date) { + this.maxDate = maxDate; + } + else if (typeof(maxDate) === 'number') { + this.maxDate = new Date() + this.maxDate.setDate(this.maxDate.getDate() + maxDate); + } + else { + this.maxDate = new Date(maxDate); + } + + this.maxDate.setHours(0, 0, 0, 0); + } + + /** + * DatePicker, Set Today Time + */ + #setTodayTime() { + + let today = new Date(); + today.setHours(0, 0, 0, 0); + + this.today = today.getTime(); + } + + /** + * DatePicker, Set Current Date + */ + #setCurrentDate() { + + if (this.opts.defaultDate != null) { + + if (this.opts.defaultDate instanceof Date) { + this.defaultDate = this.opts.defaultDate; + } + else if (typeof(this.opts.defaultDate) === 'number') { + this.defaultDate = new Date(); + this.defaultDate.setDate(this.defaultDate.getDate() + this.opts.defaultDate); + } + else { + this.defaultDate = new Date(this.opts.defaultDate); + } + + this.defaultDate.setHours(0, 0, 0, 0); + } + else { + this.defaultDate = new Date(); + } + + this.month = this.defaultDate.getMonth(); + this.year = this.defaultDate.getFullYear(); + } + + /** + * DatePicker, Init Ui + */ + #initUi() { + + this.#renderContainer(); + this.#renderMonth(); + + this.#setUiEvents(); + + this.initFlag = true; + } + + /** + * DatePicker, Restore Date Properties + */ + #restoreProperties(val = null) { + + this.selected = {}; + this.initialVal = val || this.target.value?.trim(); + + this.day = null; + this.hours = 0; + this.minutes = 0; + + if (!this.initialVal) { + return; + } + + if (this.initialVal instanceof Date) { + + this.day = this.initialVal.getDate(); + this.month = this.initialVal.getMonth(); + this.year = this.initialVal.getFullYear(); + + this.hours = this.initialVal.getHours(); + this.minutes = this.initialVal.getMinutes(); + + this.selected[ this.day + '.' + this.month + '.' + this.year ] = (this.minutes && this.hours) ? [ this.hours, this.minutes ] : true; + + } + else { + this.initialVal.split(this.opts.valuesSep).forEach(formattedDate => this.parseDate(formattedDate)); + } + + // Set last selected date + if (this.day && this.month && this.year) { + this.selectedDate = new Date(this.year, this.month, this.day, this.hours, this.minutes, 0, 0); + } + } + + /** + * Parse Date + */ + parseDate(formattedDate) { + + // Check for invalid format + if (/ddd|dddd|mmm|mmmm/.test(formattedDate)) { + return; + } + + let inDay = this.day; + let inMonth = this.month; + let inYear = this.year; + + let inHours = this.hours; + let inMinutes = this.minutes; + + const len = formattedDate.length; + + this.day = null; + this.month = null; + this.year = null; + + this.hours = 0; + this.minutes = 0; + + Ut.each({ dd : 'day', d: 'day', mm: 'month', m: 'month', yyyy : 'year', yy: 'year', hh: 'hours', h: 'hours', ii: 'minutes', i: 'minutes' }, (prop, type) => { + + let index = this.opts.valueFormat.indexOf(type); + + if (index != -1 && index + type.length - 1 < len && !this[prop]) { + + this[ prop ] = parseInt(formattedDate.substring(index, index + type.length + 1)); + + if (type == 'm' || type == 'mm') { + this.month--; + } + + if (type == 'yy') { + this.year += 2000; + } + } + }, this); + + if (!this.day || !this.month || !this.year) { + + // Set previously values + + this.day = inDay; + this.month = inMonth; + this.year = inYear; + + this.hours = inHours; + this.minutes = inMinutes; + + return; + } + + this.selected[ this.day + '.' + this.month + '.' + this.year ] = (this.minutes && this.hours) ? [ this.hours, this.minutes ] : true; + } + + /** + * DatePicker, Render Ui Container + */ + #renderContainer() { + + this.ui = { + container: El('div', { class: 'sp-datepicker ui-style-'+ this.opts.style + ' hide' }) + } + + if (this.isDisabled) { + this.ui.container.classList.add('disabled'); + } + + if (this.opts.slide) { + this.ui.container.classList.add('slide'); + } + + this.ui.prevMonth = El('a', { class: 'dp-prev-month' }) + this.ui.prevMonth.classList.toggle('disabled', this.opts.navDisabled); + + this.ui.nextMonth = El('a', { class: 'dp-next-month' }); + this.ui.nextMonth.classList.toggle('disabled', this.opts.navDisabled); + + this.ui.cMonth = El('span', { class: 'dp-current-month' }, this.#getMonthName(this.month) + ' ' + this.year) + + this.ui.container.append( El('div', { class: 'dp-months' }, + this.ui.prevMonth, + this.ui.cMonth, + this.ui.nextMonth + )); + + this.ui.weekDays = this.ui.container.appendChild(El('div', { class: 'dp-weekdays' })); + + this.conf.weekdays.forEach(wday => this.ui.weekDays.append(El('div', { class: 'wday' }, wday)) ); + + this.ui.tbody = this.ui.container.appendChild(El('div', { class: 'dp-grid' })); + + if (this.opts.multiple && this.opts.chipbox) { + this.ui.chipbox = this.ui.container.appendChild( El('div', { class: 'dp-chipbox sp-scrollbar hide'})); + } + + if (this.isInput) { + + if (this.isToggled) { + this.ui.container.classList.add('toggled', 'hide'); + $('body').append(this.ui.container); + } + else { + this.ui.container.classList.remove('hide'); + this.target.after(this.ui.container); + } + } + else { + this.ui.container.classList.remove('hide'); + this.target.append(this.ui.container); + } + + let popupContainer = this.target.dxParents('.popup-container'); + + if (popupContainer) { + popupContainer.dxData('popup')._plugins.push(this); + } + + Ut.trigger(this.events.init, this); + } + + /** + * DatePicker, Get Month Name + */ + #getMonthName(month) { + return (this.opts.fullMonth + ? this.conf.months[month] + : (this.conf.months[month].length > 3 + ? this.conf.months[month].substring(0, 3) + : this.conf.months[month] + ) + ); + } + + /** + * DatePicker, Set UI Events + */ + #setUiEvents() { + + if (this.isToggled) { + + // Click outside Handler + document.dxOn('click.datepicker' + this.ekey, () => this.hide()); + + // Win Resize handler + window.dxOn('resize.datepicker' + this.ekey + ' orientationchange.datepicker' + this.ekey, () => this.resize()); + + // ScrollBar Resize + if (this.scrollBar) { + this.scrollBar.dxOn('scroll.datepicker' + this.ekey, () => this.resize()); + window.dxOn('scroll.datepicker' + this.ekey, () => this.resize()); + } + + // Escape key handler + document.dxOn('keyup.datepicker' + this.ekey, e => e.key === 'Escape' && this.hide()); + } + + // Click inside handler + this.ui.container.dxOn('click', e => e.stopPropagation()); + + // Clieck on prev month - Handler + this.ui.prevMonth.dxOn('click', () => this.prevMonth()); + + // Click on next month - Handler + this.ui.nextMonth.dxOn('click', () => this.nextMonth()); + + // Click on day - Handler + this.ui.tbody.dxOn('click', '.dp-day', (_, target) => this.#dayClicked(target)); + + if (this.opts.multiple && this.opts.chipbox) { + this.ui.chipbox.dxOn('click', '.close', (_, target) => this.removeChipDate(target.parentElement)); + } + } + + /** + * DatePicker, Remove Chip Date + */ + removeChipDate(target) { + + if (this.fadeBusy) { + return; + } + + let date = target.dataset.id; + + let tokenDate = date.split('.'); + this.day = tokenDate[0]; + this.ui.tbody.dxFind('.dp-day', cell => cell.textContent == this.day && cell.classList.remove('dp-selday') ); + + target.remove(); + + this.changeDate(date, false); + + if (Ut.isEmptyObj(this.selected)) { + this.ui.chipbox.classList.add('hide'); + } + } + + /** + * DatePicker, Next Month Nav + */ + nextMonth() { + + if (this.isDisabled || this.fadeBusy) { + return; + } + + this.month++; + + if (this.month > 11) { + this.year++; + this.month = 0; + } + + this.#renderMonth(); + } + + /** + * DatePicker, Prev Month + */ + prevMonth() { + + if (this.isDisabled || this.fadeBusy) { + return; + } + + this.month--; + + if (this.month < 0) { + this.year--; + this.month = 11; + } + + this.#renderMonth(); + } + + /** + * DatePicker, Day Clicked Handler + */ + #dayClicked(cell) { + + if (this.isDisabled || this.fadeBusy) { + return; + } + + if (this.opts.multiple) { + + if (this.opts.setTime) { + + this.#setPopover(cell, + time => this.updateCellDate(cell, time), + () => this.updateCellDate(cell, false) + ); + } + else { + + if (!cell.classList.contains('dp-selday')) { + this.updateCellDate(cell, true); + return; + } + + this.#setPopover(cell, + null, + () => this.updateCellDate(cell, false) + ); + } + + + return; + } + + this.updateCellDate(cell, true); + } + + /** + * DatePicker, Render Cell + */ + updateCellDate(cell, checked) { + + if (!this.opts.multiple) { + this.clear(); // Clear old values + } + + this.day = cell.textContent.trim(); + + this.hours = 0; + this.minutes = 0; + + if (Array.isArray(checked)) { + this.hours = checked[0]; + this.minutes = checked[1]; + } + + cell.classList.toggle('dp-selday', !!checked); + + let date = this.day + '.' + this.month + '.' + this.year; + + if (this.opts.multiple && this.opts.chipbox) { + this.renderChipbox(date, checked); + } + + this.changeDate(date, checked); + } + + /** + * DatePicker, Render Chipbox + */ + renderChipbox(date, checked) { + + if (!checked) { + + this.ui.chipbox.dxFind('.sp-chip[data-id="'+ date +'"')?.remove(); + + if (Ut.isEmptyObj(this.selected)) { + this.ui.chipbox.classList.add('hide'); + } + + return; + } + + if (this.selected[date]) { + this.ui.chipbox.dxFind('.sp-chip[data-id="'+ date +'"] span')?.dxText(this.#getFormattedDate(date, checked, this.opts.dateFormat)); + return; + } + + this.ui.chipbox.append( El('div', { class: 'sp-chip', 'data-id': date }, + El('span', {}, this.#getFormattedDate(date, checked, this.opts.dateFormat)), + El('i', { class: 'close' }, 'x' ) + )); + + this.ui.chipbox.classList.remove('hide'); + } + + /** + * DatePicker, Set Popover + */ + #setPopover(target, callbackSet, callbackUnset) { + + let that = this; + + let date = target.textContent.trim() + '.' + this.month + '.' + this.year; + + let checked = this.selected[date]; + + let hours = '0'; + let minutes = '0'; + + // Set ui form + + let form; + + if (this.opts.setTime) { + + form = El('form', { novalidate: true, class: 'sp-form' }); + + Render.makeUiElements(Ut.objVal(this.widget.form[ 'hours' ]), form); + + if (Array.isArray(checked)) { + hours = checked[0]; + minutes = checked[1]; + } + + let selectHour = form.dxFind('select[name="hour"]'), + selectMin = form.dxFind('select[name="min"]'); + + // Select Time - Handlers + selectHour.dxOn('change', (_, t) => hours = t.value); + selectMin.dxOn('change', (_, t) => minutes = t.value); + + selectHour.value = hours; + selectMin.value = minutes; + } + else { + form = El('div', {}, + El('a', { class: 'cmd-unset' }, + El('i', { class: 'fas fa-calendar-xmark fz26 color-carbon' }) + ) + ); + } + + form.dxFind('.cmd-unset').classList.toggle('hide', !checked); + + // Set popover ui + + new Popover(target, form, { closeBtn: this.opts.setTime }, + { + init: (popover, ui) => { + + that.popover = popover; + + if (this.opts.setTime) { + + ui.dxOn('click', '.cmd-set', () => { + + popover.destroy(); + that.popover = null; + + callbackSet([ hours, minutes ]); + }); + } + + ui.dxOn('click', '.cmd-unset', () => { + + popover.destroy(); + that.popover = null; + + callbackUnset(); + }); + } + }); + } + + /** + * DatePicker, Change Date + */ + changeDate(date, checked) { + + // Update selected obj + if (checked) { + this.selected[ date ] = checked; + this.selectedDate = new Date(this.year, this.month, this.day, this.hours, this.minutes, 0, 0); + } + else { + delete this.selected[ date ]; + this.selectedDate = null; + } + + if (this.isToggled) { + this.hide(); + } + + // Update input val + if (this.isInput) { + this.#updateInputVal(); + } + + // Trigger input event + this.target.dxTrigger('changed'); + + // Call onSelect event at this point + Ut.trigger(this.events.onSelect, this, date, !!checked); + } + + /** + * DatePicker, Update Input Value (Formated) + */ + #updateInputVal() { + + // Values List + let inputVals = []; + let displayedVals = []; + + Ut.each(this.selected, (time, date) => { + inputVals.push(this.#getFormattedDate(date, time, this.opts.valueFormat)); + displayedVals.push(this.#getFormattedDate(date, time, this.opts.dateFormat)); + }); + + this.target.dataset.value = inputVals.join(this.opts.valuesSep) + this.target.value = displayedVals.join(this.opts.datesSep); + + this.target.dxData('cancelable')?.iconHandler.classList.remove('hide'); + } + + /** + * DatePicker resize hander + */ + resize() { + + let tm = null; + + if (tm) { + clearTimeout(tm); + tm = null; + } + + tm = setTimeout(() => this.setUiPosition(), 100); + } + + /** + * DatePicker, Set Ui Position + */ + setUiPosition() { + + let scrY = 0; + + if (this.scrollBar) { + scrY = window.scrollY; + } + + let left = Math.round(this.target.dxOffset().left); + + if ((this.ui.container.clientWidth + left) > window.innerWidth) { + left = 0; + } + + this.ui.container.dxCss({ + top : (Math.round(this.target.dxOffset().top) + this.target.clientHeight + 2) + scrY + 'px', + left : left + 'px', + }); + } + + /** + * DatePicker, Get Date Format + */ + #getFormattedDate(rtDate, time, format) { + + time = Array.isArray(time) ? time : [ 0, 0 ]; + + let tokenDate = rtDate.split('.'); + let date = new Date(tokenDate[2], tokenDate[1], tokenDate[0], time[0], time[1], 0, 0); + + let hours = date.getHours().toString(); + let leadHours = hours.padStart(2, '0'); + + let minutes = date.getMinutes().toString(); + let leadMinutes = minutes.padStart(2, '0'); + + let fullDay = date.toLocaleDateString(this.opts.locales, { weekday: 'long' }); + let shortDay = date.toLocaleDateString(this.opts.locales, { weekday: 'short' }); + let day = date.getDate().toString(); + let leadDay = day.padStart(2, '0'); + + let fullMonth = date.toLocaleString(this.opts.locales, { month: 'long' }); + let shortMonth = date.toLocaleString(this.opts.locales, { month: 'short' }); + let month = (date.getMonth() + 1).toString(); + let leadMonth = month.padStart(2, '0'); + + let fullYear = date.getFullYear().toString() + let year = fullYear.substring(2); + + let formatted = format + .replace(/dddd/gi, fullDay) + .replace(/ddd/gi, shortDay) + .replace(/dd/gi, leadDay) + .replace(/d/gi, day) + .replace(/mmmm/gi, fullMonth) + .replace(/mmm/gi, shortMonth) + .replace(/mm/gi, leadMonth) + .replace(/m/gi, month) + .replace(/yyyy/gi, fullYear) + .replace(/yy/gi, year) + .replace(/hh/gi, leadHours) + .replace(/h/gi, hours) + .replace(/ii/gi, leadMinutes) + .replace(/i/gi, minutes); + + return formatted.charAt(0).toUpperCase() + formatted.slice(1); + } + + /** + * DatePicker, Set Target Events + */ + #setTargetEvents() { + + let that = this; + + // Restore Selected - Handler + this.target.dxOn('restore.datepicker', () => that.restoreValue()); + + if (this.isToggled) { + + // Input clicked hander + this.target.dxOn('click.datepicker', e => { + + e.stopPropagation(); + + if (!that.opts.hiddenMode) { + that.show(); + } + }); + } + } + + /** + * DatePicker, Restore value + */ + restoreValue(val = null) { + + let month = null; + let year = null; + + if (this.month && this.year) { + month = this.month; + year = this.year; + } + + this.#restoreProperties(val); + + this.#updateInputVal(); + + if (!this.initFlag) { + return; + } + + // Same month + if (this.month == month && this.year == year) { + + // Unmark all days + this.ui.tbody.dxFind('.dp-day', cell => cell.classList.remove('dp-selday')); + } + else { + this.#renderMonth(); + } + + if (this.opts.multiple && this.opts.chipbox) { + this.ui.chipbox.dxHtml(''); + } + + // All cases (with or without time) + each (this.selected, (time, date) => { + + let token = date.split('.'); + + let day = parseInt(token[0]); + this.ui.tbody.dxFind('.dp-day', cell => cell.textContent == day && cell.classList.add('dp-selday') ); + + // Multiple values + if (this.opts.multiple && this.opts.chipbox) { + + this.ui.chipbox.append( El('div', { class: 'sp-chip', 'data-id': date }, + El('span', {}, this.#getFormattedDate(date, time, this.opts.dateFormat)), + El('i', { class: 'close' }, 'x') + )); + + this.ui.chipbox.classList.remove('hide'); + } + }, this); + } + + /** + * DatePicker, Show UI + */ + show() { + + if (this.isOpened || this.fadeBusy) { + return; + } + + if (!this.initFlag) { + this.#initUi(); + } + + Stack.widgets.DatePicker.forEach(datepicker => datepicker.hide()); + Stack.widgets.Hours.forEach(hours => hours.hide()); + + this.setUiPosition(); + + if (this.opts.anim) { + + this.fadeBusy = true; + + this.ui.container.dxFadeIn({ + duration: this.opts.animTime, + complete: () => { + this.isOpened = true; + this.fadeBusy = false; + } + }); + } + else { + this.ui.container.classList.remove('hide'); + } + + // Call onShow event at this point + Ut.trigger(this.events.onShow, this); + } + + /** + * DatePicker, Hide UI + */ + hide() { + + if (!this.isOpened || this.fadeBusy) { + return; + } + + this.isOpened = false; + + if (this.popover) { + this.popover.destroy(); + } + + this.ui.container.classList.add('hide'); + + Ut.trigger(this.events.onHide, this); + } + + /** + * DatePicker, Enable UI + */ + enable() { + this.isDisabled = false; + this.ui.container.classList.remove('disabled'); + } + + /** + * DatePicker, Disable UI + */ + disable() { + this.isDisabled = true; + this.ui.container.classList.add('disabled'); + } + + /** + * DatePicker, Render Mounth days + */ + #renderMonth() { + + if (this.popover) { + this.popover.destroy(); + } + + this.ui.cMonth.textContent = this.#getMonthName(this.month) + ' ' + this.year; + + let colStart = new Date(this.year, this.month, 0).getDay() + 1; + const days = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; + + this.totalDays = (this.month == 1 && !(this.year & 3) && (this.year % 1e2 || !(this.year % 4e2))) ? 29 : days[this.month]; + + this.ui.tbody.dxHtml(''); + + if (this.opts.multiple && this.opts.chipbox) { + this.ui.chipbox.dxHtml('').classList.add('hide'); + } + + for (let i = 1; i <= this.totalDays; i++) { + + let cell = this.ui.tbody.appendChild( El('div', { class: 'dp-day' }, i) ); + + if (i == 1) { + cell.style.gridColumnStart = colStart; + } + + let date = new Date(this.year, this.month, i); + + if (this.minDate) { + if (date < this.minDate) { + cell.className += ' disabled'; + } + } + + if (this.maxDate) { + if (date > this.maxDate) { + cell.className += ' disabled'; + } + } + + if (this.selected[ i + '.' + this.month + '.' + this.year ]) { + + cell.className += ' dp-selday'; + + if (this.opts.multiple && this.opts.chipbox) { + + Ut.each(this.selected, (checked, date) => { + + if (this.opts.multiple && this.opts.chipbox) { + + this.ui.chipbox.append( El('div', { class: 'sp-chip', 'data-id': date }, + El('span', {}, this.#getFormattedDate(date, checked, this.opts.dateFormat)), + El('i', { class: 'close' }, 'x' ) + )); + + this.ui.chipbox.classList.remove('hide'); + } + }); + } + } + + if (date.getTime() === this.today) { + cell.className += ' dp-today'; + } + } + } + + /** + * DatePicker, Clear All Values + */ + clear() { + + this.day = null; + + this.#setCurrentDate(); + + this.hours = 0; + this.minutes = 0; + + this.selected = {}; + this.selectedDate = null; + this.initialVal = ''; + + if (this.popover) { + this.popover.destroy(); + } + + if (this.isToggled) { + this.hide(); + } + + this.ui.container.dxFind('.dp-selday', cell => cell.classList.remove('dp-selday')); + + if (this.isInput) { + this.target.value = ''; + this.target.dataset.value = ''; + } + + if (this.opts.multiple && this.opts.chipbox) { + this.ui.chipbox.dxHtml('').classList.add('hide'); + } + } + + /** + * DatePicker, Set Date Range + */ + setDateRange(opts) { + + opts = opts || {}; + + let i; + + this.ui.tbody.dxFind('.dp-day', cell => cell.classList.remove('disabled')); + + if (opts.minDate) { + + this.#setMinDate(opts.minDate); + + if (this.minDate.getFullYear() == this.year && this.minDate.getMonth() == this.month) { + for (i = 1; i < this.minDate.getDate(); i++) { + this.ui.tbody.dxFind('.dp-day', cell => cell.textContent == i && cell.classList.add('disabled') ); + } + } + } + + if (opts.maxDate) { + + this.#setMaxDate(opts.maxDate); + + if (this.maxDate.getFullYear() == this.year && this.maxDate.getMonth() == this.month) { + for (i = this.maxDate.getDate() + 1; i < this.totalDays; i++) { + that.ui.tbody.dxFind('.dp-day', cell => cell.textContent == i && cell.classList.add('disabled') ); + } + } + } + } + + /** + * DatePicker, Destroy + */ + destroy() { + + if (this.popover) { + this.popover.destroy(); + } + + this.ui.container.remove(); + + if (this.isToggled) { + + this.target.dxOff('click.datepicker keydown.datepicker paste.datepicker restore.datepicker'); + document.dxOff('click.datepicker' + this.ekey + ' keyup.datepicker' + this.ekey); + window.dxOff('resize.datepicker' + this.ekey + ' orientationchange.datepicker' + this.ekey); + + + if (this.scrollBar) { + this.scrollBar.dxOff('scroll.datepicker' + this.ekey); + window.dxOff('scroll.datepicker' + this.ekey); + } + } + + Stack.delete(this); + + this.target.removeAttribute('data-value'); + this.target.dxRemoveData('datepicker'); + + return this.target; + } +} + +//Ut.extendNodeUx(DatePicker); \ No newline at end of file diff --git a/src/widgets/date-picker/date-picker.css b/src/widgets/date-picker/date-picker.css new file mode 100644 index 0000000..9e7149d --- /dev/null +++ b/src/widgets/date-picker/date-picker.css @@ -0,0 +1,226 @@ +/* DatePicker */ + +.sp-datepicker { + width: 300px; + padding: 8px 8px 16px 8px; + box-shadow: rgba(9, 30, 66, 0.13) 0px 0px 1px 1px; + border-radius: 5px; + font-size: 13px; + color: #666; + -webkit-user-select: none; + user-select: none; + background-color: #fff; + overflow: hidden; +} + +.sp-datepicker.ui-style-zoom { + width: 450px; +} + +.sp-datepicker.toggled { + position: absolute; + top: -9999px; + left: -9999px; +} + +.sp-datepicker.toggled:not(.hide) { + transition: top 0.1s, left 0.1s; +} + +.sp-datepicker.disabled { + opacity: 0.5; + pointer-events: none; +} + +.sp-datepicker.anim { + opacity: 0; + transition: opacity 0.2s; +} + +.sp-datepicker.anim.visible { + opacity: 1; +} + +.sp-datepicker .dp-months { + display: flex; + height: 40px; + justify-content: space-between; + align-items: center; +} + +.sp-datepicker .dp-prev-month, +.sp-datepicker .dp-next-month { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.1s; + border-radius: 50%; +} + +.sp-datepicker .dp-prev-month:hover, +.sp-datepicker .dp-next-month:hover { + background-color: #eee; +} + +.sp-datepicker .dp-prev-month:after, +.sp-datepicker .dp-next-month:after { + display: block; + content: ''; + border-top: 2px solid #333; + border-left: 2px solid #333; + cursor: pointer; + width: 8px; + height: 8px; +} + +.sp-datepicker.ui-style-zoom .dp-prev-month:after, +.sp-datepicker.ui-style-zoom .dp-next-month:after { + width: 12px; + height: 12px; +} + +.sp-datepicker .dp-prev-month:after { + transform: rotate(-45deg); + margin-left: 2px; +} + +.sp-datepicker .dp-next-month:after { + transform: rotate(135deg); + margin-right: 2px; +} + +.sp-datepicker .dp-current-month { + font-size: 16px; + font-weight: 600; + color: #333; + display: block; + flex: 1; + text-align: center; + white-space: nowrap; +} + +.sp-datepicker.ui-style-zoom .dp-current-month { + font-size: 20px; +} + +.sp-datepicker .dp-weekdays { + display: grid; + grid-template-columns: repeat(7, 1fr); + justify-items: center; + align-items: center; + font-size: 14px; + height: 40px; + font-weight: 500; +} + +.sp-datepicker.ui-style-zoom .dp-weekdays { + font-size: 16px; +} + +.sp-datepicker .dp-grid { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 4px; + justify-content: center; + justify-items: center; +} + +.sp-datepicker .dp-day { + width: 36px; + height: 36px; + cursor: pointer; + border: 1px solid #fff; + border-radius: 50%; + transition: all 0.1s; + display: flex; + align-items: center; + justify-content: center; + font-size: 13px; +} + +.sp-datepicker.ui-style-zoom .dp-day { + width: 50px; + height: 50px; +} + +.sp-datepicker .dp-day:not(.dp-selday).dp-today { + border-color: #e0e0e0; +} + +.sp-datepicker.ui-style-zoom .dp-day { + font-size: 14px; +} + +.sp-datepicker .dp-day:not(.dp-selday):hover { + background-color: #eee; +} + +.sp-datepicker .dp-selday { + background-color: var(--theme-widget-color); + border: 1px solid var(--theme-widget-color); + color: #fff; +} + +.sp-datepicker .dp-day.disabled { + opacity: 0.3; + pointer-events: none; +} + +.sp-datepicker .dp-chipbox { + margin-top: 13px; + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + max-height: 200px; +} + +.sp-datepicker .sp-chip { + background-color: var(--theme-widget-color); + font-size: 12px; + border-radius: 20px; + color: #fff; +} + +.sp-datepicker .sp-chip i { + font-style: normal; +} + +/* + +.sp-datepicker .sp-chip { + display: flex; + align-items: center; + justify-content: space-between; + background-color: var(--theme-widget-color); + color: #fff; + padding: 16px; +} + +.sp-datepicker.toggled .sp-chip { + width: 100%; + padding: 20px; + font-size: 16px; + text-align: center; +} + +.sp-datepicker.toggled .sp-chip span { + flex: 1; + text-align: center; +} +*/ + +.sp-datepicker .label { + display: block; +} + +.sp-datepicker .sp-chip i { + display: block; + color: #fff; + line-height: normal; +} + +.sp-datepicker .sp-chip i.ic-close { + cursor: pointer; +} \ No newline at end of file diff --git a/src/widgets/date-picker/index.js b/src/widgets/date-picker/index.js new file mode 100644 index 0000000..b35528f --- /dev/null +++ b/src/widgets/date-picker/index.js @@ -0,0 +1 @@ +export { DatePicker } from './DatePicker'; \ No newline at end of file diff --git a/src/widgets/dialog/Dialog.js b/src/widgets/dialog/Dialog.js new file mode 100644 index 0000000..7f0e858 --- /dev/null +++ b/src/widgets/dialog/Dialog.js @@ -0,0 +1,238 @@ +import './dialog.css'; + +import { El } from '../../utils/dom' +import { Overlay } from '../overlay'; +import { Stack } from '../../core/Stack'; + +//////////////////// +// Dialog +//////////////////// + +export class Dialog { + + /** + * Dialog, Constructor + */ + constructor(target, opts, events) { + + this.target = target; + + if (this.target.dxData('dialog')) { + return; + } + + this.target.dxData('dialog', this); + + this.opts = { ... { + title : this.target.dataset.title, + with : 400, + position : 'left', + multiple : false, + modal : false + }, ... opts }; + + this.events = events || {}; + + Stack.add(this); + + this.create(); + + this.setEvents(); + } + + /** + * Dialog, Create UI + */ + create() { + + let dialogBody = El('div', { class: 'dialog-body' }); + + this.ui = El('div', { class: 'sp-dialog' }, + El('div', { class: 'dialog-header' }, + El('span', { class: 'dialog-title' }, this.opts.title ), + El('a', { class: 'ic-close' }, + El('i', { class: 'fas fa-times' }) + ) + ), + dialogBody + ); + + this.ui.style.width = this.opts.width + 'px'; + + dialogBody.append(this.target); + + Ut.trigger(this.events.init, this, this.target); + + document.body.append(this.ui); + + if (this.opts.modal) { + Overlay.showModal(this.ui); + } + + this.uiPosition(); + + this.ui.classList.add('sp-anim-fade-in'); + } + + /** + * Dialog, Set UI Position + */ + uiPosition() { + + switch (this.opts.position) { + + case 'left': + this.ui.dxCss({ + top : '10px', + left: '10px' + }); + break; + case 'left-center': + this.ui.dxCss({ + top : ((window.innerHeight / 2) - this.ui.clientHeight / 2) + 'px', + left: '10px' + }); + break; + case 'center': + this.ui.dxCss({ + top : ((window.innerHeight / 2) - this.ui.clientHeight / 2) + 'px', + left: ((window.innerWidth / 2) - this.ui.clientWidth / 2) + 'px' }); + break; + case 'right': + this.ui.dxCss({ + top : '10px', + left: (window.innerWidth - this.ui.clientWidth - 10) + 'px' + }); + break; + case 'right-center': + this.ui.dxCss({ + top : ((window.innerHeight() / 2) - this.ui.clientHeight / 2) + 'px', + left: (window.innerWidth() - this.ui.clientWidth - 10) + 'px' + }); + } + } + + /** + * Dialog, Drag Move Handler + */ + draggMove(left, top) { + + this.ui.dxCss({ + left: left + 'px', + top: top + 'px' + }); + } + + /** + * Dialog, Drag Start Handler + */ + draggStart(e) { + + let that = this; + + if (e.which != 1) { + return; + } + + e.preventDefault(); + + let posX = e.clientX, + posY = e.clientY, + divTop = this.ui.style.top.replace('px', ''), + divLeft = this.ui.style.left.replace('px', ''), + eWi = parseInt(this.ui.clientWidth), + eHe = parseInt(this.ui.clientHeight), + cWi = document.body.clientWidth, + cHe = document.body.clientHeight, + diffX = posX - divLeft, + diffY = posY - divTop; + + document.onmousemove = function(evt) { + + evt = evt || window.event; + + let posX = evt.clientX, + posY = evt.clientY, + aX = posX - diffX, + aY = posY - diffY; + + if (aX < 0) aX = 0; + if (aY < 0) aY = 0; + if (aX + eWi > cWi) aX = cWi - eWi; + if (aY + eHe > cHe) aY = cHe - eHe; + + that.draggMove(aX, aY); + }; + } + + /** + * Dialog, Drag Stop Handler + */ + draggStop(e) { + e.preventDefault(); + document.onmousemove = function() {} + } + + /** + * Dialog, Set Events + */ + setEvents() { + + let that = this; + + this.ui.dxOn('mousedown', '.dialog-header', this.draggStart.bind(this)); + + document.dxOn('mouseup.dialog' + this.ekey, this.draggStop.bind(this)); + + // this.resize(); + + this.ui.addEventListener('animationend', function(e){ + this.classList.remove('sp-anim-fade-in'); + Ut.trigger(that.events.show, that, that.target); + }, { + once: true + }); + + // Events + + this.ui.dxOn('click', '.ic-close', function() { + that.destroy(); + }); + + document.dxOn('keydown.dialog' + this.ekey, function(e) { + + if (e.key == 'Escape') { + that.destroy(); + } + }); + } + + /** + * Dialog, Destroy Widget + */ + destroy() { + + const that = this; + + this.ui.classList.add('sp-anim-fade-out'); + + this.ui.addEventListener('animationend', function(e) { + + if (that.opts.modal) { + Overlay.hideModal(that.ui); + } + + that.ui.remove(); + + document.dxOff('keydown.dialog' + this.ekey + ' mouseup.dialog' + this.ekey); + + Stack.delete(this); + + that.target.dxRemoveData('dialog'); + + Ut.trigger(that.events.destroy); + }, { + once: true + }); + } +} \ No newline at end of file diff --git a/src/widgets/dialog/dialog.css b/src/widgets/dialog/dialog.css new file mode 100644 index 0000000..aab950e --- /dev/null +++ b/src/widgets/dialog/dialog.css @@ -0,0 +1,87 @@ +/* Dialog */ + +.sp-dialog { + background-color:#fff; + border-radius:5px; + box-shadow: 1px 1px 5px 0px rgba(204,204,204,1); + position: fixed; + overflow: hidden; + -webkit-user-select: none; + user-select: none; + max-width: 90%; +} + +.sp-dialog .dialog-header { + display: flex; + flex-wrap: nowrap; + align-items: center; + padding: 0 10px 0 20px; + height: 50px; + background-color: var(--theme-widget-color); + cursor: all-scroll; + color: #fff; +} + +.sp-dialog .dialog-title { + font-size: 16px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-transform: uppercase; + flex: 1; + font-size: 14px; +} + +.sp-dialog .dialog-body { + padding: 20px; +} + +.sp-dialog .ic-close { + flex-shrink: 0; + cursor: pointer; + transform: all 0.2s; + opacity: 0.85; + width: 35px; + height: 35px; + display: flex; + align-items: center; + justify-content: center; + color: #fff; +} + +.sp-dialog .ic-close i { + font-size: 16px; +} + +.sp-dialog .ic-close:hover { + opacity: 1; +} + +.sp-dialog button[type="submit"] { + background-color: #fff; + color: var(--theme-widget-color); +} + +/* Fade in & out */ + +.sp-anim-fade-in { + animation: fade_in 2.25s; + perspective: 1000; + backface-visibility: hidden; +} + +@keyframes fade_in { + from { transform: scale3d(0, 0, 0); opacity:0; } + to { transform: scale3d(1, 1, 1); opacity:1; } +} + +.sp-anim-fade-out { + animation: fade_out 2.25s; + perspective: 1000; + backface-visibility: hidden; +} + +@keyframes fade_out { + from { transform: scale3d(1, 1, 1); opacity:1; } + to { transform: scale3d(0, 0, 0); opacity:0; } +} \ No newline at end of file diff --git a/src/widgets/dialog/index.js b/src/widgets/dialog/index.js new file mode 100644 index 0000000..3ffeae6 --- /dev/null +++ b/src/widgets/dialog/index.js @@ -0,0 +1 @@ +export { Dialog } from './Dialog'; \ No newline at end of file diff --git a/src/widgets/dropdown/Dropdown.js b/src/widgets/dropdown/Dropdown.js new file mode 100644 index 0000000..1178273 --- /dev/null +++ b/src/widgets/dropdown/Dropdown.js @@ -0,0 +1,181 @@ +import './dropdown.css'; + +import { Ut } from '../../utils/Ut'; +import { El } from '../../utils/dom'; + +//////////////////// +// DopDown Menu +//////////////////// + +export class Dropdown { + + /** + * Dropdown Constructor + */ + constructor(target, opts) { + + this.target = target; + + if (this.target.dxData('dropdown')) { + return; + } + + this.target.dxData('dropdown', this); + + this.opts = { ... { items: [] }, ... opts }; + + this.isOpened = false; + this.isBusy = false; + + this.create(); + + this.setEvents(); + } + + /** + * Dropdown, Create UI + */ + create() { + + this.ui = El('div', { class: 'sp-dropdown hide' }); + + if (this.opts.cls) { + this.ui.classList.add(this.opts.cls); + } + + const list = El('ul'); + + if (this.opts.items.length) { + this.ui.append(list); + } + + this.items = []; + + this.opts.items.forEach(item => { + this.items.push( + list.appendChild( El('li', { class: 'color-'+ (item.color || 'black'), 'data-id': item.id }, + (item.icon ? El('i', { class: 'fa fa-'+ item.icon }) : null), + item.name + )) + ); + }); + + this.target.after(this.ui); + + Ut.trigger(this.opts.onInit, this); + } + + /** + * Dropdown, Set UI Position + */ + setPosition() { + + let left = Math.round(this.target.offsetLeft); + + if ((this.ui.clientWidth + left) > window.innerWidth) { + left = 0; + } + + this.ui.style.top = (Math.round(this.target.offsetTop) + this.target.clientHeight + 2) + 'px'; + this.ui.style.left = left + 'px'; + } + + /** + * Dropdown, Show UI + */ + show() { + + if (this.isBusy || this.isOpened) { + return; + } + + Ut.trigger(this.opts.onShow, this); + + this.setPosition(); + + this.ui.classList.remove('hide'); + this.ui.classList.add('opened'); + this.target.classList.add('dropdown-opened'); + + this.isOpened = true; + } + + /** + * Dropdown, Hide UI + */ + hide() { + + if (!this.isOpened) { + return; + } + + Ut.trigger(this.opts.onHide, this); + + let that = this; + + this.ui.addEventListener('transitionend', _ => !this.ui.classList.contains('opened') && this.ui.classList.add('hide'), { once: true }); + + this.ui.classList.remove('opened'); + this.target.classList.remove('dropdown-opened'); + + this.isOpened = false; + }; + + /** + * Dropdown, Set Evensts + */ + setEvents() { + + let that = this; + + document.body.dxOn('click.dropdown', _ => that.hide()); + + this.target.dxOn('click', e => { + + e.stopPropagation(); + + if (!that.ui.classList.contains('opened')) { + that.show(); + } + else { + that.hide(); + } + }); + + this.ui.dxOn('click', 'li', (_, t) => { + Ut.trigger(that.opts.items[ t.dxIndex(that.items) ].handler); + Ut.trigger(that.opts.onChange, t.dataset.id, this); + }); + + let tm = null; + + window.dxOn('resize.dropdown' + this.ekey + ' orientationchange.dropdown' + this.ekey, _ => { + + if (tm) { + clearTimeout(tm); + tm = null; + } + + tm = setTimeout(_ => that.setPosition(), 100); + }); + } + + /** + * Dropdown, Destroy + */ + destroy() { + + this.ui.remove(); + + document.body.dxOff('click.dropdown'); + window.dxOff('resize.dropdown' + this.ekey + ' orientationchange.dropdown' + this.ekey); + + this.target.dxRemoveData('dropdown'); + + Ut.trigger(this.events.destroy, this); + + return this.target; + } +} + +//Ut.extendNodeUx(Dropdown); \ No newline at end of file diff --git a/src/widgets/dropdown/dropdown.css b/src/widgets/dropdown/dropdown.css new file mode 100644 index 0000000..87acb58 --- /dev/null +++ b/src/widgets/dropdown/dropdown.css @@ -0,0 +1,61 @@ +/* Dropdown UI */ + +.sp-dropdown { + position: absolute; + z-index: 100; + border-color: #e4e9f0; + box-shadow: 0 10px 35px -5px rgba(0, 0, 0, 0.15); + top: -9999px; + left: -9999px; + min-width: 100px; + max-width: 300px; + opacity: 0; + transition: all 0.3s; + transform: translateY(20px); +} + +.sp-dropdown.opened { + transform: translateY(0); + opacity: 1; +} + +.sp-dropdown ul { + margin: 2px 0 0; + font-size: 14px; + color: #212529; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + display: block; +} + +.sp-dropdown li { + display: flex; + align-items: center; + flex-basis: 100%; + border-bottom: 1px solid #e4e9f0; + background: none; + padding: 12px 18px 12px 18px; + transition: all 0.2s; + white-space: nowrap; + cursor: pointer; +} + +.sp-dropdown.dp-interval li { + padding: 8px 12px 8px 12px; + font-size: 13px; +} + +.sp-dropdown li:last-child { + border-bottom: 0; +} + +.sp-dropdown li i { + display: block; + font-size: 16px; + margin-right: 8px; +} + +.sp-dropdown li:hover { + opacity: 0.75; +} \ No newline at end of file diff --git a/src/widgets/dropdown/index.js b/src/widgets/dropdown/index.js new file mode 100644 index 0000000..2cf560b --- /dev/null +++ b/src/widgets/dropdown/index.js @@ -0,0 +1 @@ +export { Dropdown } from './Dropdown'; \ No newline at end of file diff --git a/src/widgets/gallery/Gallery.js b/src/widgets/gallery/Gallery.js new file mode 100644 index 0000000..d5bf0e2 --- /dev/null +++ b/src/widgets/gallery/Gallery.js @@ -0,0 +1,71 @@ +import './gallery.css'; + +import { Ut } from '../../utils/Ut'; +import { El } from '../../utils/dom'; +import { Lightbox } from '../lightbox'; + +//////////////////// +// Gallery +//////////////////// + +export class Gallery { + + /** + * Gallery Constructor + */ + constructor(target, opts, events) { + + this.target = target; + this.events = events || {}; + + if (this.target.dxData('gallery')) { + return; + } + + this.target.dxData('gallery', this); + + this.opts = { ... { + files : [], + }, ... opts }; + + if (!Ut.isSet(this.opts.dir)) { + return; + } + + this.create(); + } + + /** + * Gallery Create Ui + */ + create() { + + let that = this; + + this.ui = El('div', { class: 'sp-gallery' }); + + Ut.each(this.opts.files, function(src) { + + that.ui.append( El('a', { href: that.opts.dir +'/' + src.replace('0_', '1_') }, + El('img', { src: that.opts.dir +'/' + src }) + )); + }); + + new Lightbox(this.ui); + + this.target.dxHtml(this.ui); + + Ut.trigger(that.events.init, this); + } + + /** + * Gallery, Destroy + */ + destroy() { + + this.ui.remove(); + this.target.dxRemoveData('gallery'); + + Ut.trigger(that.events.destroy, this); + } +} \ No newline at end of file diff --git a/src/widgets/gallery/gallery.css b/src/widgets/gallery/gallery.css new file mode 100644 index 0000000..413a0c2 --- /dev/null +++ b/src/widgets/gallery/gallery.css @@ -0,0 +1,120 @@ +/* Gallery UI */ + +.sp-gallery { + display: flex; + flex-wrap: wrap; + margin: 16px 0; +} + +.sp-gallery.sp-file { + margin: 0; +} + +.sp-gallery a { + background-color: #eee; + width: 85px; + height: 85px; + display: flex; + justify-content: center; + align-items: center; + margin: 1px; + position: relative; + transition: all 0.3s ease; +} + +.sp-gallery a.loading::after { + display: block; + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB2aWV3Qm94PSIwIDAgMjIgMjIiPjxnIGZpbGw9IiMyODMwM2YiIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIj48cGF0aCBmaWxsPSIjMjgzMDNmNDAiIGQ9Ik0xMSAuMjVhLjc1Ljc1IDAgMCAxIC43NS43NXYzYS43NS43NSAwIDAgMS0xLjUgMFYxQS43NS43NSAwIDAgMSAxMSAuMjV6bTAgMTdhLjc1Ljc1IDAgMCAxIC43NS43NXYzYS43NS43NSAwIDAgMS0xLjUgMHYtM2EuNzUuNzUgMCAwIDEgLjc1LS43NXpNMy4zOTggMy4zOThhLjc1Ljc1IDAgMCAxIDEuMDYxIDBMNi41OCA1LjUyYS43NS43NSAwIDEgMS0xLjA2IDEuMDZMMy4zOTggNC40NmEuNzUuNzUgMCAwIDEgMC0xLjA2ek0xNS40MiAxNS40MmEuNzUuNzUgMCAwIDEgMS4wNiAwbDIuMTIxIDIuMTJhLjc1Ljc1IDAgMCAxLTEuMDYgMS4wNjFMMTUuNDIgMTYuNDhhLjc1Ljc1IDAgMCAxIDAtMS4wNnpNMjEuNzUgMTFhLjc1Ljc1IDAgMCAxLS43NS43NWgtM2EuNzUuNzUgMCAwIDEgMC0xLjVoM2EuNzUuNzUgMCAwIDEgLjc1Ljc1em0tMTcgMGEuNzUuNzUgMCAwIDEtLjc1Ljc1SDFhLjc1Ljc1IDAgMCAxIDAtMS41aDNhLjc1Ljc1IDAgMCAxIC43NS43NXoiIGRhdGEtb3JpZ2luYWw9IiMyODMwM2Y0MCIvPjxwYXRoIGQ9Ik0xOC42MDEgMy4zOThhLjc1Ljc1IDAgMCAxIDAgMS4wNjFsLTIuMTIgMi4xMjFhLjc1Ljc1IDAgMCAxLTEuMDYyLTEuMDZsMi4xMjItMi4xMjJhLjc1Ljc1IDAgMCAxIDEuMDYgMHoiIGRhdGEtb3JpZ2luYWw9IiMyODMwM2YiLz48cGF0aCBmaWxsPSIjMjgzMDNmNDAiIGQ9Ik02LjU4IDE1LjQyYS43NS43NSAwIDAgMSAwIDEuMDZsLTIuMTIgMi4xMjFhLjc1Ljc1IDAgMSAxLTEuMDYxLTEuMDZMNS41MiAxNS40MmEuNzUuNzUgMCAwIDEgMS4wNiAweiIgZGF0YS1vcmlnaW5hbD0iIzI4MzAzZjQwIi8+PC9nPjwvc3ZnPg==); + width: 45px; + height: 45px; + background-size: contain; + z-index: 1; + animation:load-roto 1.2s infinite linear; +} + +.sp-gallery a:hover { + opacity: 0.9; +} + +.sp-gallery a.selected { + opacity: 0.3; +} + +.sp-gallery a img { + display:block; + max-width: 78px; + max-height: 78px; +} + +.sp-gallery .file-label { + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + overflow: hidden; +} + +.sp-gallery .file-name { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + word-break: break-all; + text-align: center; + padding: 0 3px; + font-size: 11px; +} + +.sp-gallery .file-icon { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB4PSIwIiB5PSIwIiB2aWV3Qm94PSIwIDAgNTE1LjI4MyA1MTUuMjgzIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIiBjbGFzcz0iIj48Zz48cGF0aCBkPSJNNDAwLjc3NSA1MTUuMjgzSDExNC41MDdjLTMwLjU4NCAwLTU5LjMzOS0xMS45MTEtODAuOTY4LTMzLjU0QzExLjkxMSA0NjAuMTE3IDAgNDMxLjM2MSAwIDQwMC43NzV2LTI4LjYyOGMwLTE1LjgxMSAxMi44MTYtMjguNjI4IDI4LjYyNy0yOC42MjhzMjguNjI3IDEyLjgxNyAyOC42MjcgMjguNjI4djI4LjYyOGMwIDE1LjI5MyA1Ljk1NiAyOS42NyAxNi43NjggNDAuNDgzIDEwLjgxNSAxMC44MTQgMjUuMTkyIDE2Ljc3MSA0MC40ODUgMTYuNzcxaDI4Ni4yNjhjMTUuMjkyIDAgMjkuNjY5LTUuOTU3IDQwLjQ4My0xNi43NzEgMTAuODE0LTEwLjgxNSAxNi43NzEtMjUuMTkyIDE2Ljc3MS00MC40ODN2LTI4LjYyOGMwLTE1LjgxMSAxMi44MTYtMjguNjI4IDI4LjYyNi0yOC42MjhzMjguNjI4IDEyLjgxNyAyOC42MjggMjguNjI4djI4LjYyOGMwIDMwLjU4NC0xMS45MTEgNTkuMzM4LTMzLjU0IDgwLjk2OC0yMS42MjkgMjEuNjI5LTUwLjM4NCAzMy41NC04MC45NjggMzMuNTR6TTI1Ny42NDEgNDAwLjc3NGEyOC41MzggMjguNTM4IDAgMCAxLTE5Ljk5OC04LjE0MmwtLjAwMi0uMDAyLS4wNTctLjA1Ni0uMDE2LS4wMTZjLS4wMTYtLjAxNC0uMDMtLjAyOS0uMDQ1LS4wNDRsLS4wMjktLjAyOWEuODkyLjg5MiAwIDAgMC0uMDMyLS4wMzFsLS4wNjItLjA2Mi0xMTQuNTA4LTExNC41MDljLTExLjE3OS0xMS4xNzktMTEuMTc5LTI5LjMwNSAwLTQwLjQ4NSAxMS4xNzktMTEuMTc5IDI5LjMwNi0xMS4xOCA0MC40ODUgMGw2NS42MzggNjUuNjM4VjI4LjYyN0MyMjkuMDE0IDEyLjgxNiAyNDEuODMgMCAyNTcuNjQxIDBzMjguNjI4IDEyLjgxNiAyOC42MjggMjguNjI3djI3NC40MDhsNjUuNjM3LTY1LjYzN2MxMS4xNzgtMTEuMTc5IDI5LjMwNy0xMS4xNzkgNDAuNDg1IDAgMTEuMTc5IDExLjE3OSAxMS4xNzkgMjkuMzA2IDAgNDAuNDg1TDI3Ny44ODMgMzkyLjM5bC0uMDYyLjA2Mi0uMDMyLjAzMS0uMDI5LjAyOWMtLjAxNC4wMTYtLjAzLjAzLS4wNDQuMDQ0bC0uMDE3LjAxNmExLjQ3OSAxLjQ3OSAwIDAgMS0uMDU2LjA1NmwtLjAwMi4wMDJjLS4zMTUuMzA3LS42MzQuNjA1LS45Ni44OTVhMjguNDQxIDI4LjQ0MSAwIDAgMS03Ljg5IDQuOTk1bC0uMDI4LjAxMmMtLjAxMS4wMDQtLjAyLjAxLS4wMzEuMDEzYTI4LjUgMjguNSAwIDAgMS0xMS4wOTEgMi4yMjl6IiBmaWxsPSIjMzYzNjM2IiBvcGFjaXR5PSIxIiBkYXRhLW9yaWdpbmFsPSIjMDAwMDAwIiBjbGFzcz0iIj48L3BhdGg+PC9nPjwvc3ZnPg==); + width: 26px; + height: 26px; + margin-top: 5px; + background-size: contain; + background-repeat: no-repeat; + display: block; +} + +.sp-gallery a .delete-file { + position:absolute; + right: 3px; + bottom: 3px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 50%; + background: #fff; + border: 1px solid #fff; +} + +.sp-gallery a .delete-file::after { + display: block; + content: ''; + width: 10px; + height: 10px; + background-position:center; + background-repeat:no-repeat; + display: flex; + justify-content: center; + align-items: center; + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB4PSIwIiB5PSIwIiB2aWV3Qm94PSIwIDAgMzIwLjU5MSAzMjAuNTkxIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIiBjbGFzcz0iIj48Zz48cGF0aCBkPSJNMzAuMzkxIDMxOC41ODNhMzAuMzcgMzAuMzcgMCAwIDEtMjEuNTYtNy4yODhjLTExLjc3NC0xMS44NDQtMTEuNzc0LTMwLjk3MyAwLTQyLjgxN0wyNjYuNjQzIDEwLjY2NWMxMi4yNDYtMTEuNDU5IDMxLjQ2Mi0xMC44MjIgNDIuOTIxIDEuNDI0IDEwLjM2MiAxMS4wNzQgMTAuOTY2IDI4LjA5NSAxLjQxNCAzOS44NzVMNTEuNjQ3IDMxMS4yOTVhMzAuMzY2IDMwLjM2NiAwIDAgMS0yMS4yNTYgNy4yODh6IiBmaWxsPSIjZmY0MTM2IiBvcGFjaXR5PSIxIiBkYXRhLW9yaWdpbmFsPSIjMDAwMDAwIiBjbGFzcz0iIj48L3BhdGg+PHBhdGggZD0iTTI4Ny45IDMxOC41ODNhMzAuMzcgMzAuMzcgMCAwIDEtMjEuMjU3LTguODA2TDguODMgNTEuOTYzQy0yLjA3OCAzOS4yMjUtLjU5NSAyMC4wNTUgMTIuMTQzIDkuMTQ2YzExLjM2OS05LjczNiAyOC4xMzYtOS43MzYgMzkuNTA0IDBsMjU5LjMzMSAyNTcuODEzYzEyLjI0MyAxMS40NjIgMTIuODc2IDMwLjY3OSAxLjQxNCA0Mi45MjItLjQ1Ni40ODctLjkyNy45NTgtMS40MTQgMS40MTRhMzAuMzY4IDMwLjM2OCAwIDAgMS0yMy4wNzggNy4yODh6IiBmaWxsPSIjZmY0MTM2IiBvcGFjaXR5PSIxIiBkYXRhLW9yaWdpbmFsPSIjMDAwMDAwIiBjbGFzcz0iIj48L3BhdGg+PC9nPjwvc3ZnPg==); + background-size: contain; + background-repeat: no-repeat; + transition: all 0.2s; +} + +.sp-gallery a .delete-file:hover { + opacity: 0.85; +} \ No newline at end of file diff --git a/src/widgets/gallery/index.js b/src/widgets/gallery/index.js new file mode 100644 index 0000000..6e89ed4 --- /dev/null +++ b/src/widgets/gallery/index.js @@ -0,0 +1 @@ +export { Gallery } from './Gallery'; \ No newline at end of file diff --git a/src/widgets/hours/Hours.js b/src/widgets/hours/Hours.js new file mode 100644 index 0000000..1cb6e25 --- /dev/null +++ b/src/widgets/hours/Hours.js @@ -0,0 +1,190 @@ +import './hours.css'; + +import { Ut } from '../../utils/Ut'; +import { El } from '../../utils/dom'; +import { Stack } from '../../core/Stack'; +import { Render } from '../../core/Render'; + +//////////////////// +// Hours +//////////////////// + +export class Hours { + + /** + * Hours Constructor + */ + constructor(target) { + + this.target = target; + + this.widget = App.intData.widget.datepicker.children; + + if (target.dxData('hours')) { + return; + } + + target.dxData('hours', this); + + this.makeUi(); + + Stack.add(this); + + this.hour = this.ui.dxFind('select[name=hour]'); + this.min = this.ui.dxFind('select[name=min]'); + + this.target.setAttribute('readonly', ''); + + this.setEvents(); + } + + /** + * Hours, Create UI + */ + makeUi() { + + let form = El('form', { novalidate: true, class: 'sp-form' }); + + this.ui = El('div', { class: 'sp-hours' }, form); + + Render.makeUiElements(Ut.objVal(this.widget.form).children, form); + + document.body.append(this.ui); + } + + /** + * Hours, Update target + */ + update() { + + let val = this.target.value.trim(); + + if (val) { + + let token = val.split(':'); + + if (token.length == 2) { + this.hour.value = parseInt(token[0]); + this.min.value = parseInt(token[1]); + } + } + + this.ui.dxFind('.cmd-unset').dxToggleClass('hide', !val); + } + + /** + * Hours, Hide UI + */ + hide() { + + this.isVisible = false; + + this.ui.dxCss({ + top: '-9999px', + left: '-9999px' + }); + } + + /** + * Hours, UI Reposition + */ + reposition() { + + this.ui.dxCss({ + top: (Math.round(this.target.dxOffset().top) + this.target.clientHeight + 2) + 'px', + left: Math.round(this.target.dxOffset().left) + 'px', + }); + } + + /** + * Hours, Set Events + */ + setEvents() { + + let that = this, + tm = null; + + this.ui.dxOn('click', e => e.stopPropagation()); + + this.target.dxOn('click.hours', function(e) { + + e.stopPropagation(); + + Stack.widgets.DatePicker.forEach(datepicker => datepicker.hide() ); + + that.isVisible = true; + + that.update(); + that.reposition(); + }); + + this.ui.dxOn('click', '.cmd-set', function() { + + let hour = that.hour.value, + min = that.min.value; + + if (hour < 10) { + hour = '0' + hour; + } + + if (min < 10) { + min = '0' + min; + } + + that.target.value = hour + ':' + min; + + that.hide(); + }); + + this.ui.dxOn('click', '.cmd-unset', (_, t) => { + t.classList.add('hide'); + that.target.value = ''; + that.hide(); + }); + + window.dxOn('resize.hours' + this.ekey + ' orientationchange.hours' + this.ekey, function() { + + if (!that.isVisible) { + return; + } + + if (tm) { + clearTimeout(tm); + tm = null; + } + + tm = setTimeout(function() { + that.reposition(); + }, 300); + }); + + document.body.dxOn('click.hours' + this.ekey, function(e) { + that.hide(); + + }); + + document.dxOn('keyup.hours' + this.ekey, function(e) { + + if (e.key == 'Escape') { + that.hide(); + } + }); + } + + /** + * Hours, Destroy Widget + */ + destroy() { + + this.ui.remove(); + + this.target.dxOff('click.hours'); + document.body.dxOff('click.hours'); + window.dxOff('resize.hours' + this.ekey + ' orientationchange.hours' + this.ekey + ' keyup.hours' + this.ekey); + + Stack.delete(this); + this.target.dxRemoveData('hours'); + + return this.target; + } +} \ No newline at end of file diff --git a/src/widgets/hours/hours.css b/src/widgets/hours/hours.css new file mode 100644 index 0000000..5d49f3c --- /dev/null +++ b/src/widgets/hours/hours.css @@ -0,0 +1,27 @@ +/* Hours UI */ + +.sp-hours { + position: absolute; + top: -9999px; + left: -9999px; + overflow: hidden; + padding: 12px 8px 4px 8px; + border: 1px solid #e4e9f0; + border-radius: 5px; + font-size: 13px; + color: #666; + background-color: #fff; + -webkit-user-select: none; + user-select: none; +} + +.sp-hours .form-field label, +.sp-hours .sp-button { + font-size: 13px; +} + +.sp-hours .sp-button { + padding: 10px; + border-radius: 5px; + align-self: center; +} \ No newline at end of file diff --git a/src/widgets/hours/index.js b/src/widgets/hours/index.js new file mode 100644 index 0000000..6c6a334 --- /dev/null +++ b/src/widgets/hours/index.js @@ -0,0 +1 @@ +export { Hours } from './Hours'; \ No newline at end of file diff --git a/src/widgets/interval-picker/IntervalPicker.js b/src/widgets/interval-picker/IntervalPicker.js new file mode 100644 index 0000000..d2a68d4 --- /dev/null +++ b/src/widgets/interval-picker/IntervalPicker.js @@ -0,0 +1,236 @@ +import { Ut } from '../../utils/Ut'; +import { Boot } from '../../core/Boot'; +import { TwoDatePicker } from '../two-datepicker/TwoDatepicker'; +import { Dropdown } from '../dropdown'; +import { Stack } from '../../core/Stack'; + +//////////////////// +// IntervalPicker +//////////////////// + +export class IntervalPicker { + + /** + * Intervalpicker, Constructor + */ + constructor(target, opts) { + + this.target = target; + + this.opts = { ... { + separator : ' - ', + locales : 'ro-RO', + style : 'normal', + disabled : 1 + }, ... opts }; + + if (this.target.dxData('intervalpicker')) { + return; + } + + this.target.dxData('intervalpicker', this); + + this.widget = Boot.intData.widget.intervalpicker.children; + + this.create(); + + Stack.add(this); + } + + /** + * Intervalpicker, Date Format + */ + dateFormat(date) { + return date.toLocaleDateString(this.opts.locales, { + day : '2-digit', + month : '2-digit', + year : 'numeric' + }); + //.replace(/\./g, '/'); + }; + + /** + * Intervalpicker, Create + */ + create() { + + let that = this; + + let options = Ut.objVal(this.widget.form).children.field.interval.option; + let items = []; + + for (let id in options) { + items.push({ id: id, name: options[id] }); + } + + new TwoDatePicker(this.target, { + anim: false, + hiddenMode: true, + style: this.opts.style, + disabled: this.opts.disabled + }, { + + init: twoDatePicker => that.twoDatePicker = twoDatePicker, + onChange: function(twoDatePicker, val, index) { + + const firstDp = twoDatePicker.datepicker[0]; + const secondDp = twoDatePicker.datepicker[1]; + + if (!val) { + that.interval = null; + } + + if (index == 0) { + + secondDp.clear(); + + secondDp.minDate = firstDp.selectedDate; + + if (firstDp.month != secondDp.month) { + + secondDp.year = firstDp.year; + secondDp.month = firstDp.month; + + secondDp.makeUI(); + } + else { + secondDp.setDateRange({ minDate: firstDp.selectedDate }); + } + } + else { + + if (!firstDp.selectedDate) { + return; + } + + that.target.dxVal(that.dateFormat(firstDp.selectedDate) + + that.opts.separator + + that.dateFormat(secondDp.selectedDate) + ); + + twoDatePicker.hide(); + that.target.dxTrigger('changed'); + } + }, + onHide: _ => that.dropdown.isBusy = false + }); + + const twoDp = this.target.dxData('two_datepicker'); + + new Dropdown(this.target, { + items: items, + cls: 'dp-interval', + onInit: dropdown => that.dropdown = dropdown, + onShow: _ => { + + Stack.widgets.IntervalPicker.forEach(intPicker => { + + if (intPicker.dropdown.isOpened) { + intPicker.dropdown.hide(); + } + + if (intPicker.dropdown.isBusy) { + intPicker.twoDatePicker.hide(); + } + }); + }, + onChange: id => { + + const date = new Date(); + + switch (id) { + case '0': + that.interval = [ date, date ]; + break; + case '1': + that.interval = [ null, new Date(date) ]; + date.setDate(date.getDate() - 1); + that.interval[0] = date; + break; + + case '2': + that.interval = [ null, new Date(date) ]; + date.setDate(date.getDate() - 7); + that.interval[0] = date; + break; + case '3': + that.interval = [ null, new Date(date) ]; + date.setDate(date.getDate() - 30); + that.interval[0] = date; + break; + case '4': + date.setDate(1); + that.interval = [ new Date(date), null ]; + date.setMonth(date.getMonth() + 1); + date.setDate(0); + that.interval[1] = date; + break; + case '5': + date.setDate(1); + date.setMonth(date.getMonth() - 1); + that.interval = [ new Date(date), null ]; + date.setMonth(date.getMonth() + 1); + date.setDate(0); + that.interval[1] = date; + break; + + case '6': + date.setDate(1); + date.setMonth(0); + that.interval = [ new Date(date), null ]; + date.setFullYear(date.getFullYear() + 1); + date.setDate(0); + that.interval[1] = date; + break; + + case '7': + date.setFullYear(date.getFullYear() - 1); + date.setMonth(0); + date.setDate(1); + that.interval = [ new Date(date), null ]; + date.setFullYear(date.getFullYear() + 1); + date.setDate(0); + that.interval[1] = date; + break; + case '8': + that.dropdown.isBusy = true; + twoDp.restoreValue(that.interval); + setTimeout(_ => twoDp.show()); + break; + } + + that.updateInput(); + } + }); + } + + /** + * Intervalpicker, Update Input + */ + updateInput() { + + if (!this.interval) { + return; + } + + const val = this.interval.map(date => this.dateFormat(date)).join(this.opts.separator); + + this.target.value = val; + + this.target.dxTrigger('changed'); + Ut.trigger(this.opts.onSelect, val, this); + } + + /** + * Intervalpicker, Destroy + */ + destroy() { + + this.twoDatePicker.destroy(); + this.dropdown.destroy(); + + Stack.delete(this); + + this.target.dxRemoveData('intervalpicker'); + } +} \ No newline at end of file diff --git a/src/widgets/interval-picker/index.js b/src/widgets/interval-picker/index.js new file mode 100644 index 0000000..18b59bb --- /dev/null +++ b/src/widgets/interval-picker/index.js @@ -0,0 +1 @@ +export { IntervalPicker } from './IntervalPicker'; \ No newline at end of file diff --git a/src/widgets/lightbox/Lightbox.js b/src/widgets/lightbox/Lightbox.js new file mode 100644 index 0000000..ac7d166 --- /dev/null +++ b/src/widgets/lightbox/Lightbox.js @@ -0,0 +1,326 @@ +import './lightbox.css'; + +import { Ut } from '../../utils/Ut'; +import { $ } from '../../utils/dom'; +import { Boot } from '../../core/Boot'; + +//////////////////// +// LightBox +//////////////////// + +export class Lightbox { + + /** + * Lightbox, Contructor + */ + constructor(target) { + + this.target = target; + + if (this.target.dxData('lightbox')) { + return; + } + + this.target.dxData('lightbox', this); + + this.setEvents(); + + this.widget = Boot.intData.widget.lightbox.children; + + this.opened = false; + this.busy = false; + } + + resize(load) { + + this.img.style.removeProperty('width'); + this.img.style.removeProperty('height'); + + let imWidth = this.img.dxGetProp('width').split('px')[0]; + let imHeight = this.img.dxGetProp('height').split('px')[0]; + + let viewWidth = window.innerWidth - (0.1 * window.innerWidth); + let viewHeight = window.innerHeight - (0.2 * window.innerHeight); + + if (imWidth > viewWidth) { + imWidth = viewWidth + 'px'; + imHeight = 'auto'; + } + else if (imHeight > viewHeight) { + imHeight = viewHeight + 'px'; + imWidth = 'auto'; + } + + this.img.style.width = imWidth; + this.img.style.height = imHeight; + + imWidth = this.img.dxGetProp('width').split('px')[0], + imHeight = this.img.dxGetProp('height').split('px')[0]; + + this.ui.style.width = imWidth + 'px'; + this.ui.style.height = imHeight + 'px'; + + if (load) { + this.update(); + } + } + + update() { + + this.img.classList.remove('hidden'); + this.ui.append(this.img); + + this.img.focus(); + this.img.classList.add('loaded'); + + this.loader.classList.add('hidden'); + + this.updateLabel(); + + this.busy = false; + } + + updateLabel() { + + if (this.title) { + this.labelTitle.classList.remove('hide'); + this.labelTitle.textContent = this.title; + } + else { + this.labelTitle.classList.add('hide'); + } + + this.labelCounter.textContent = this.widget.label.position.replace('%index', this.index + 1).replace('%count', this.square.length); + } + + create(a, update) { + + this.busy = true; + + this.title = a.getAttribute('title'); + + let src = a.href; + + if (update) { + this.loader.classList.remove('hidden'); + this.img.classList.add('hidden'); + this.img.setAttribute('src', src); + } + else { + + this.modal = $('
'); + this.ui = $('
'); + this.loader = $('
'); + + this.arrowLeft = $(''); + this.arrowRight = $(''); + + let label = $(''); + + this.labelTitle = label.dxFind('.label-title'); + this.labelCounter = label.dxFind('.label-number'); + this.close = label.dxFind('.close-popup'); + + this.modal.append(this.ui); + + this.ui.append(this.loader); + + if (!Ut.device.touchCapable) { + this.ui.append(this.arrowLeft); + this.ui.append(this.arrowRight); + } + + this.ui.append(label); + + document.body.append(this.modal); + + this.img = $(''); + + this.opened = true; + } + + this.img.classList.add('hidden'); + this.img.classList.remove('loaded'); + + document.body.append( this.img ); + + // Set Arrows + + this.arrowRight.classList.toggle('hide', this.square.length == this.index + 1); + this.arrowLeft.classList.toggle('hide', !this.index); + + Stack.add(this); + } + + hidden() { + + this.modal.remove(); + + this.busy = false; + this.opened = false; + + document.dxOff('keyup.lightbox' + this.ekey + ' resize.lightbox' + this.ekey + ' orientationchange.lightbox' + this.ekey); + } + + setEvents() { + + let that = this, + tm = null; + + // Click inside target + + this.target.dxOn('click.lightbox', 'a', function(e) { + + // No target attr + if (this.target) { + return; + } + + e.preventDefault(); + + // Already opened + if (that.opened) { + return; + } + + // Set square at this point + that.square = that.target.dxFind('a:not([target])', true); + + // Set Start Index + that.index = this.dxIndex(that.square); + + that.create(this); + + // UI TransitionEnd Handler + that.ui.addEventListener('transitionend', function(e) { + + if (that.busy && (e.propertyName == 'width' || e.propertyName == 'height')) { + that.update(); + } + }); + + // Load Img Handler + + that.img.dxOn('load', function() { + that.resize(true); + }); + + // Click on UI + + that.ui.dxOn('click', function(e) { + e.stopPropagation(); + }); + + // Nav Arrows Handlers + + that.arrowLeft.dxOn('click', function(e) { + that.prevIm(); + }); + + that.arrowRight.dxOn('click', function(e) { + that.nextIm(); + }); + + // Swipe Handlers + + that.ui.dxOn('swipeleft', function() { + that.nextIm(); + }); + + that.ui.dxOn('swiperight', function() { + that.prevIm(); + }); + + // Click On Modal + + that.modal.dxOn('click', function() { + that.hidden(); + }); + + // Click on close icon + + that.close.dxOn('click', function(e) { + e.stopPropagation(); + that.hidden(); + }); + + // Resize Handler + + window.dxOn('resize.popup.' + that.ekey + ' ' + 'orientationchange.popup' + that.ekey, function() { + + if (tm) { + clearTimeout(tm); + tm = null; + } + + tm = setTimeout(_ => { + that.resize(); + }, 300); + }); + + // Keydown Handler + + document.dxOn('keydown.popup' + this.ekey, function(e) { + switch (e.key) { + case 'ArrowLeft': + that.prevIm(); + break; + case 'ArrowRight': + that.nextIm(); + break; + case 'Escape': + that.hidden(); + break; + } + }); + }); + } + + prevIm() { + + if (this.busy || !this.index) { + return; + } + + this.index--; + + this.create(this.square.item(this.index), true); + } + + nextIm() { + + if (this.busy || this.square.length == this.index + 1) { + return; + } + + this.index++; + + this.create(this.square.item(this.index), true); + } + + destroy() { + + this.modal.remove(); + + this.target.dxOff('click.lightbox'); + document.dxOff('keyup.lightbox' + this.ekey + ' resize.lightbox' + this.ekey + ' orientationchange.lightbox' + this.ekey); + + Stack.delete(this); + + this.target.dxRemoveData('lightbox'); + } +} + +//Ut.extendNodeUx(Lightbox); \ No newline at end of file diff --git a/src/widgets/lightbox/index.js b/src/widgets/lightbox/index.js new file mode 100644 index 0000000..6fd81f5 --- /dev/null +++ b/src/widgets/lightbox/index.js @@ -0,0 +1 @@ +export { Lightbox } from './Lightbox'; \ No newline at end of file diff --git a/src/widgets/lightbox/lightbox.css b/src/widgets/lightbox/lightbox.css new file mode 100644 index 0000000..972758f --- /dev/null +++ b/src/widgets/lightbox/lightbox.css @@ -0,0 +1,215 @@ +/* LightBox UI */ + +.sp-lightbox-overlay { + background: rgba(0, 0, 0, 0.8); + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + transition: all 0.3s ease; + z-index: 100; +} + +.sp-lightbox-container { + position: fixed; + top: 30px; + left: 0; + right: 0; + margin: auto; + background-color: #fff; + width: 300px; + height: 300px; + display: flex; + justify-content: center; + align-items: center; + transition: width .3s ease-in-out, height .3s ease-in-out; +} + +.sp-lightbox-container img { + display: block; + opacity: 0; + transition: opacity .6s; +} + +.sp-lightbox-container img.loaded { + opacity: 1; + max-width: none; +} + +.sp-lightbox-container .loader { + border: 3px solid #ccc; + border-radius: 50%; + border-top: 3px solid var(--theme-widget-color); + animation: spin .3s linear infinite; + opacity: 1; + position: absolute; + width: 50px; + height: 50px; +} + +.sp-lightbox-container .loader.hidden { + opacity: 0; + animation: none; +} + +.sp-lightbox-container .nav-arrow { + position: absolute; + z-index: 1; + width: 35%; + height: 100%; + top: 0; + display: flex; + align-items: center; + opacity: 0; + transition: opacity .6s; +} + +.sp-lightbox-container .nav-arrow:hover { + opacity: 1; +} + +.sp-lightbox-container .nav-arrow i { + font-size: 30px; + color: #ccc; +} + +.sp-lightbox-container .nav-arrow { + opacity: 0; + transition: opacity .6s; + background-repeat: no-repeat; +} + +.sp-lightbox-container .nav-arrow:hover { + opacity: 1; +} + +.sp-lightbox-container .nav-arrow.arrow-left { + left: 0; + margin-left: 10px; +} + +.sp-lightbox-container .nav-arrow.arrow-right { + right: 0; + margin-right: 10px; + display: flex; + align-items: center; + justify-content: end; +} + +.sp-lightbox-container .nav-arrow.arrow-left::after, +.sp-lightbox-container .nav-arrow.arrow-right::after { + flex-shrink: 0; + display: block; + content: ''; + width: 30px; + height: 30px; + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB4PSIwIiB5PSIwIiB2aWV3Qm94PSIwIDAgNDkyLjAwNCA0OTIuMDA0IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIiBjbGFzcz0iIj48Zz48cGF0aCBkPSJNMzgyLjY3OCAyMjYuODA0IDE2My43MyA3Ljg2QzE1OC42NjYgMi43OTIgMTUxLjkwNiAwIDE0NC42OTggMHMtMTMuOTY4IDIuNzkyLTE5LjAzMiA3Ljg2bC0xNi4xMjQgMTYuMTJjLTEwLjQ5MiAxMC41MDQtMTAuNDkyIDI3LjU3NiAwIDM4LjA2NEwyOTMuMzk4IDI0NS45bC0xODQuMDYgMTg0LjA2Yy01LjA2NCA1LjA2OC03Ljg2IDExLjgyNC03Ljg2IDE5LjAyOCAwIDcuMjEyIDIuNzk2IDEzLjk2OCA3Ljg2IDE5LjA0bDE2LjEyNCAxNi4xMTZjNS4wNjggNS4wNjggMTEuODI0IDcuODYgMTkuMDMyIDcuODZzMTMuOTY4LTIuNzkyIDE5LjAzMi03Ljg2TDM4Mi42NzggMjY1YzUuMDc2LTUuMDg0IDcuODY0LTExLjg3MiA3Ljg0OC0xOS4wODguMDE2LTcuMjQ0LTIuNzcyLTE0LjAyOC03Ljg0OC0xOS4xMDh6IiBmaWxsPSIjQ0NDQ0NDIiBvcGFjaXR5PSIxIiBkYXRhLW9yaWdpbmFsPSIjQ0NDQ0NDIiBjbGFzcz0iIj48L3BhdGg+PC9nPjwvc3ZnPg==); + background-size: 30px 30px; + background-repeat: no-repeat; +} + +.sp-lightbox-container .nav-arrow.arrow-left::after { + transform: rotate(180deg); +} + +.sp-lightbox-container .nav-label { + position: absolute; + bottom: -30px; + left: 0; + width: 100%; +} + +.sp-lightbox-container .label-title, +.sp-lightbox-container .label-number { + color: #ccc; + font-size: 13px; +} + +.sp-lightbox-container .label-title { + margin-bottom: 3px; + font-weight: 700; +} + +.sp-lightbox-container .close-popup { + transition: all .6s ease; + position: absolute; + top: 0; + right: 10px; + width: 110px; + height: 30px; + cursor: pointer; + -webkit-user-select: none; + user-select: none; + display: block; +} + +.sp-lightbox-container .close-popup .container-close { + transition:all .6s ease; + transform:rotate(180deg); + position:absolute; + top:0; + left:0; + width:30px; + height:30px; + display: block; +} + +.sp-lightbox-container .close-popup:hover .container-close { + transform:rotate(0); +} + +.sp-lightbox-container .close-popup:hover .border { + background-color: #fff; +} + +.sp-lightbox-container .close-popup:hover .border-1 { + transform:rotate(22.5deg); + left:5px; + top:11px; + width:20px; +} + +.sp-lightbox-container .close-popup:hover .border-2 { + transform:rotate(-22.5deg); + left:5px; + top:18.2px; + width:20px; +} + +.sp-lightbox-container .close-popup .border { + display: block; + transition:all .3s ease; + position:absolute; + top:12px; + width:30px; + height:3px; + background-color: #6d6e71; +} + +.sp-lightbox-container .close-popup .border-1 { + transform:rotate(45deg); +} + +.sp-lightbox-container .close-popup .border-2 { + transform:rotate(-45deg); +} + +.sp-lightbox-container .close-popup .close-btn { + transition: all .3s ease; + font-size: 13px; + color: #6d6e71; + text-transform: uppercase; + cursor:pointer; + margin-top: 7px; + font-weight: bold; + letter-spacing: 2px; + margin-left: 36px; + line-height: 1.4em; + display: block; +} + +.sp-lightbox-container .close-popup:hover .close-btn { + display: block; + color: #fff; +} \ No newline at end of file diff --git a/src/widgets/msg/Msg.js b/src/widgets/msg/Msg.js new file mode 100644 index 0000000..8cbd074 --- /dev/null +++ b/src/widgets/msg/Msg.js @@ -0,0 +1,159 @@ +import './msg.css'; + +import { Ut } from '../../utils/Ut'; +import { $, El } from '../../utils/dom'; +import { Overlay } from '../overlay'; +import { Boot } from '../../core/Boot'; + +export class Msg { + + /** + * Snackbar Builder + */ + static renderSnackbar(msg, opts) { + + const prevNotif = document.body.dxData('snackbar'); + + if (prevNotif) { + clearTimeout(prevNotif.tm); + prevNotif.ui.remove(); + document.dxOff('keyup.alert'); + } + + const notif = { tm: null }; + document.body.dxData('snackbar', notif); + + opts = { ... { + type: 'slide', + duration: 5000, + color: 'green' + }, ... opts }; + + const hide = () => { + + notif.ui.addEventListener('animationend', _ => { + + notif.ui.remove(); + document.dxOff('keyup.alert'); + + Ut.trigger(opts.complete); + + }, { once: true }); + + notif.ui.classList.add(`${opts.type}-out`); + }; + + notif.ui = document.body.appendChild(El('div', { class: `sp-snackbar ui-color-${opts.color} ${opts.type}-in` }, msg)); + + notif.ui.addEventListener('animationend', _ => { + + notif.ui.classList.remove(`${opts.type}-in`); + + notif.tm = setTimeout(_ => hide(), opts.duration); + + }, { once: true }); + + document.dxOn('keyup.alert', e => { + + if (e.key === 'Escape') { + + if (notif.tm) { + clearTimeout(notif.tm); + notif.tm = null; + } + + hide(); + } + }); + } + + static notify = (msg, opts) => Msg.renderSnackbar(msg, { color: 'green', ... opts }); + static alert = (msg, opts) => Msg.renderSnackbar(msg, { color: 'red', ... opts }); + + /** + * Notif Bar Message + */ + static nbar(message, icon) { + + let nBarEL = $('.sp-nbar'); + + if (!message) { + nBarEL?.classList.add('hide'); + return; + } + + icon = icon || 'check'; + + if (!nBarEL) { + + nBarEL = El('div', { class: 's11 m6 l4 xl2 sp-nbar hide' }, + icon ? El('i', { class: 'fas fa-'+ icon }) : null, + El('span', {}, message) + ); + + document.body.append(nBarEL); + } + + nBarEL.classList.remove('hide'); + } + + /** + * Confirm dialog + */ + static confirm(msg, done, cancel) { + + let that = this; + + if ($('.sp-confirm')) { + return; + } + + let widget = Boot.intData.widget.confirm.children; + + const ui = El('div', { class: 'sp-confirm slide-in' }, + El('div', { class: 'text' }, msg), + El('div', { class: 'buttons' }, + El('button', { type: 'button', class: 'sp-button btn-cancel' }, widget.button.no), + El('button', { type: 'button', class: 'sp-button btn-ok' }, El('i', { class: 'fas fa-check' }), widget.button.yes), + ) + ); + + let busy = true; + + const destroy = function(callback) { + + if (busy) { + return; + } + + ui.addEventListener('animationend', function(e) { + + Overlay.hideModal(ui); + ui.remove(); + + document.dxOff('keyup.confirm'); + + Ut.trigger(callback, msg); + }, { + once: true + }); + + ui.classList.add('slide-out'); + }; + + document.body.append(ui); + + Overlay.showModal(ui); + + ui.dxOn('click', '.btn-ok', _ => destroy(done)); + ui.dxOn('click', '.btn-cancel', _ => destroy(cancel)); + + document.dxOn('keyup.confirm', e => { + if (e.key === 'Escape') { + destroy(cancel); + } + }); + + ui.addEventListener('animationend', () => busy = false, { once: true }); + } +} \ No newline at end of file diff --git a/src/widgets/msg/index.js b/src/widgets/msg/index.js new file mode 100644 index 0000000..18efae4 --- /dev/null +++ b/src/widgets/msg/index.js @@ -0,0 +1 @@ +export { Msg } from './Msg'; \ No newline at end of file diff --git a/src/widgets/msg/msg.css b/src/widgets/msg/msg.css new file mode 100644 index 0000000..f5b94c6 --- /dev/null +++ b/src/widgets/msg/msg.css @@ -0,0 +1,139 @@ +.sp-snackbar { + padding: 16px; + font-size: 14px; + color: #fff; + border-radius: 6px; + box-shadow: rgb(0, 0, 0) 0px 0px 0px 0px, rgba(21, 21, 21, 0.08) 0px 2px 8px -2px, rgba(21, 21, 21, 0.08) 0px 12px 16px -4px; + min-width: 300px; + right: 12px; + bottom: 12px; + display: flex; + align-items: center; + position: fixed; + z-index: 103; + gap: 10px; +} + +.sp-snackbar.slide-in { + animation: 300ms ease 0s 1 normal forwards running box_in; +} + +.sp-snackbar.slide-out { + animation: 300ms ease 0s 1 normal forwards running box_out; +} + +@keyframes box_in { + 0% { transform: translateY(75px); opacity: 0; } + 50% { opacity: 1; } + 100% { transform: translateY(0px); } +} + +@keyframes box_out { + 0% { transform: translateY(0px); } + 50% { opacity: 0; } + 100% { transform: translateY(75px); } +} + +/* Confirm UI */ + +.sp-confirm { + width: 98%; + padding: 15px 30px; + display: flex; + justify-content: center; + flex-direction: column; + max-width: 500px; + position: fixed; + box-shadow: 1px 1px 5px 0px rgba(204,204,204,1); + background-color: #fff; + border-radius: 0 0 10px 10px; + text-align: center; + overflow: hidden; + left: 0; + right: 0; + top: 0; + margin: 0 auto 0 auto; + transform: translateY(-100%); + opacity: 0; +} + +.sp-confirm.slide-in { + animation: cp_in linear 0.3s forwards; +} + +.sp-confirm.slide-out { + animation: cp_out linear 0.3s forwards; +} + +@keyframes cp_in { + 0% { transform: translateY(-100%); opacity: 0; } + 100% { transform: translateY(0px); opacity: 1; } +} + +@keyframes cp_out { + 0% { transform: translateY(0px); opacity: 1; } + 100% { transform: translateY(-100%); opacity: 0; } +} + +.sp-confirm .text { + font-size: 18px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + word-break: break-all; + white-space: pre; +} + +.sp-confirm .buttons { + margin-top: 20px; + display: flex; + justify-content: center; + white-space: nowrap; +} + +.sp-confirm .sp-button { + background-color: #fff; + margin: 0 5px; +} + +.sp-confirm .btn-cancel { + border: 1px solid var(--gray); + color: var(--gray); +} + +.sp-confirm .btn-ok { + color: var(--green); + border: 1px solid var(--green); +} + +.sp-confirm .btn-ok i { + margin-right: 5px; +} + +/* Notif Bar */ + +.sp-nbar { + position: fixed; + top: 0; + left: 0; + right: 0; + margin: auto; + padding: 15px; + background: rgba(1, 144, 254, 0.75); + color: #fff; + font-size: 14px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + -webkit-user-select: none; + user-select: none; +} + +.sp-nbar span { + margin-left: 10px; +} \ No newline at end of file diff --git a/src/widgets/number-picker/NumberPicker.js b/src/widgets/number-picker/NumberPicker.js new file mode 100644 index 0000000..c42dc37 --- /dev/null +++ b/src/widgets/number-picker/NumberPicker.js @@ -0,0 +1,130 @@ +import './numberpicker.css'; + +import { Ut } from '../../utils/Ut'; +import { El } from '../../utils/dom'; + +export class NumberPicker { + + /** + * NumberPicker, Constructor + */ + constructor(target, opts, events) { + + this.target = target; + + this.opts = { ... { + minVal: 0, + maxVal: 1 + }, ... opts}; + + this.events = events || {}; + + if (this.target.dxData('numberpicker')) { + return; + } + + this.target.dxData('numberpicker', this); + + this.val = this.target.dxVal(); + + if (!this.val) { + this.val = this.opts.minVal; + } + + if (this.val < this.opts.minVal) { + this.val = this.opts.minVal; + } + + if (this.val > this.opts.maxVal) { + this.val = this.opts.maxVal; + } + + this.target.dxVal(this.val); + + this.create(); + + this.setEvents(); + } + + /** + * NumberPicker, Create + */ + create() { + + this.ui = El('div', { class: 'sp-numberpicker' }, + El('a', { class: 'np-rem' }, El('i', { class: 'fas fa-minus' })), + El('span', { class: 'np-val' }, this.val), + El('a', { class: 'np-add' }, El('i', { class: 'fas fa-plus' })) + ); + + this.target.after(this.ui); + + this.npRem = this.ui.dxFind('.np-rem'), + this.npAdd = this.ui.dxFind('.np-add'), + this.label = this.ui.dxFind('.np-val'); + + if (this.val == this.opts.minVal) { + this.npRem.classList.add('disabled'); + } + + if (this.val == this.opts.maxVal) { + this.npAdd.classList.add('disabled'); + } + } + + /** + * NumberPicker, Update Value + */ + updateVal() { + + this.target.dxVal(this.val); + this.label.dxText(this.val); + + Ut.trigger(this.events.onChange, this.val, this); + this.target.dxTrigger('change'); + } + + /** + * NumberPicker, Set Events + */ + setEvents() { + + let that = this; + + this.npRem.dxOn('click', function() { + + if (that.val == that.opts.minVal) { + that.npRem.classList.add('disabled'); + return; + } + + that.val--; + + that.npAdd.classList.remove('disabled'); + + that.updateVal(); + }); + + this.npAdd.dxOn('click', function() { + + if (that.opts.maxVal && that.val == that.opts.maxVal) { + that.npAdd.classList.add('disabled'); + return; + } + + that.val++; + + that.npRem.classList.remove('disabled'); + + that.updateVal(); + }); + } + + /** + * NumberPicker, Destroy + */ + destroy() { + this.ui.remove(); + this.target.dxRemoveData('numberpicker') + } +} \ No newline at end of file diff --git a/src/widgets/number-picker/index.js b/src/widgets/number-picker/index.js new file mode 100644 index 0000000..929f231 --- /dev/null +++ b/src/widgets/number-picker/index.js @@ -0,0 +1 @@ +export { NumberPicker } from './NumberPicker'; \ No newline at end of file diff --git a/src/widgets/number-picker/numberpicker.css b/src/widgets/number-picker/numberpicker.css new file mode 100644 index 0000000..f79db1c --- /dev/null +++ b/src/widgets/number-picker/numberpicker.css @@ -0,0 +1,41 @@ +/* NumberPicker UI */ + +.sp-numberpicker { + display: inline-flex; + flex-wrap: nowrap; + align-items: stretch; + -webkit-user-select: none; + user-select: none; + overflow: hidden; + border: 1px solid #e4e9f0; + border-radius: 4px; + height: 38px; +} + +.sp-numberpicker a.np-rem, +.sp-numberpicker a.np-add { + display: flex; + align-items: center; + justify-content: center; + color: #333; + cursor: pointer; + padding: 0 16px; +} + +.sp-numberpicker a.np-rem.disabled, +.sp-numberpicker a.np-add.disabled { + opacity: 0.75; + pointer-events: none; +} + +.sp-numberpicker a.np-rem i, +.sp-numberpicker a.np-add i { + font-size: 0.65rem; +} + +.sp-numberpicker .np-val { + display: flex; + align-items: center; + justify-content: center; + padding: 0 16px; +} \ No newline at end of file diff --git a/src/widgets/overlay/Overlay.js b/src/widgets/overlay/Overlay.js new file mode 100644 index 0000000..04427b3 --- /dev/null +++ b/src/widgets/overlay/Overlay.js @@ -0,0 +1,76 @@ +import './overlay.css'; + +import { El } from '../../utils/dom'; + +export class Overlay { + + static modalEl = null; + static loaderEl = null; + + /** + * Show UI loader + */ + static showLoader() { + + if (!Overlay.loaderEl) { + Overlay.loaderEl = document.body.appendChild(El('div', { class: 'sp-loader' })); + } + else { + Overlay.loaderEl.classList.remove('hide'); + } + } + + /** + * Hide Loader UI + */ + static hideLoader() { + Overlay.loaderEl.classList.add('hide'); + } + + /** + * Show Modal UI + */ + static showModal(ui) { + + if (!ui) { + + if (!Overlay.modalEl ) { + Overlay.modalEl = document.body.appendChild( El('div', { class: 'sp-modal' } )); + return; + } + + Overlay.modalEl.classList.remove('hide'); + + return; + } + + ui.before( El('div', { class: 'sp-modal loaded show-bg' }) ); + } + + /** + * Hide Modal UI + */ + static hideModal(ui) { + + if (!ui) { + + if (!Overlay.modalEl) { + return; + } + + Overlay.modalEl.classList.add('hide'); + + return; + } + + const modalEl = ui.previousElementSibling; + + modalEl.classList.add('hide-bg'); + + modalEl.addEventListener('animationend', function() { + this.remove(); + }, { + once: true + }); + } + } \ No newline at end of file diff --git a/src/widgets/overlay/index.js b/src/widgets/overlay/index.js new file mode 100644 index 0000000..8b2e33e --- /dev/null +++ b/src/widgets/overlay/index.js @@ -0,0 +1 @@ +export { Overlay } from './Overlay'; \ No newline at end of file diff --git a/src/widgets/overlay/overlay.css b/src/widgets/overlay/overlay.css new file mode 100644 index 0000000..78f03f2 --- /dev/null +++ b/src/widgets/overlay/overlay.css @@ -0,0 +1,53 @@ +/* Modal */ + +.sp-modal { + background: rgba(0, 0, 0, 0.25); + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; +} + +/* Loader */ + +.sp-loader { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + width: 80px; + height: 80px; + background-size: cover; + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB4PSIwIiB5PSIwIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTIiIHhtbDpzcGFjZT0icHJlc2VydmUiIGNsYXNzPSIiPjxnPjxwYXRoIGZpbGw9IiMwNzgwZTMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTcuMDMgMi43NTdhMSAxIDAgMCAxIDEuMjEzLS43MjdsNCAxYTEgMSAwIDAgMSAuNTkgMS41MjVsLTIgM2ExIDEgMCAwIDEtMS42NjUtMS4xMWwuNzU1LTEuMTMyYTcuMDAzIDcuMDAzIDAgMCAwLTIuNzM1IDExLjc3IDEgMSAwIDAgMS0xLjM3NiAxLjQ1M0E4Ljk3OCA4Ljk3OCAwIDAgMSAzIDEyYTkgOSAwIDAgMSA0Ljg3NC04bC0uMTE3LS4wM2ExIDEgMCAwIDEtLjcyNy0xLjIxM3ptMTAuMDkyIDMuMDE3YTEgMSAwIDAgMSAxLjQxNC4wMzhBOC45NzMgOC45NzMgMCAwIDEgMjEgMTJhOSA5IDAgMCAxLTUuMDY4IDguMDk4IDEgMSAwIDAgMS0uNzA3IDEuODY0bC0zLjUtMWExIDEgMCAwIDEtLjU1Ny0xLjUxN2wyLTNhMSAxIDAgMCAxIDEuNjY0IDEuMTFsLS43NTUgMS4xMzJhNy4wMDMgNy4wMDMgMCAwIDAgMy4wMDYtMTEuNSAxIDEgMCAwIDEgLjAzOS0xLjQxM3oiIGNsaXAtcnVsZT0iZXZlbm9kZCIgb3BhY2l0eT0iMSIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9IiI+PC9wYXRoPjwvZz48L3N2Zz4=); + background-repeat: no-repeat; + animation:load-roto 1.2s infinite linear; + z-index: 101; +} + +.sp-modal.show-bg { + animation: show_bg 0.2s; + animation-fill-mode: forwards; + perspective: 1000; + backface-visibility: hidden; +} + +.sp-modal.hide-bg { + animation: hide_bg 0.2s; + animation-fill-mode: forwards; + perspective: 1000; + backface-visibility: hidden; +} + +@keyframes show_bg { + from { opacity:0; } + to { opacity: 1; } +} + +@keyframes hide_bg { + from { opacity:1; } + to { opacity: 0; } +} diff --git a/src/widgets/pin-box/PinBox.js b/src/widgets/pin-box/PinBox.js new file mode 100644 index 0000000..6d7b90d --- /dev/null +++ b/src/widgets/pin-box/PinBox.js @@ -0,0 +1,133 @@ +import './pin-box.css'; + +import { El } from '../../utils/dom'; + +export class PinBox { + + /** + * PinBox, Contructor + */ + constructor(target, opts, events) { + + this.target = target; + + if (this.target.dxData('pinbox')) { + return; + } + + this.target.dxData('pinbox', this); + + this.opts = { ... { + digits: this.target.dataset.pinboxDigits || 6, + }, ... opts }; + + this.events = events || {}; + + this.ui = El('div', { class: 'sp-pinbox' }); + + this.target.after(this.ui); + + for (let i = 0; i < this.opts.digits; i++) { + this.ui.append(El('input', { type: 'text', maxlength: 1, })); + } + + this.setEvents(); + } + + /** + * PinBox, Set Events + */ + setEvents() { + // Input Ev Handler + this.ui.dxOn('input', 'input', (_, target) => this.change(target)); + // Paste Ev Handler + this.ui.dxOn('paste', 'input', e => this.paste(e)); + } + + /** + * Pinbox, Change Inputs + */ + change(target) { + + let code = ''; + + let val = target.value.replace(/\D/g, ''); + + target.value = val; + + if (val) { + + target.nextElementSibling?.focus(); + + this.ui.dxFind('input[type="text"]', t => code += t.value); + + if (code.length == this.opts.digits) { + + this.target.dxVal(code).dxTrigger('complete'); + + Ut.trigger(this.events.complete, code, this); + } + else { + this.target.value = ''; + } + } + else { + this.target.value = ''; + } + } + + /** + * PinBox, Paste Code + */ + paste(e) { + + const code = e.clipboardData.getData('text').trim(); + + if (!new RegExp('^\\d{' + this.opts.digits + '}$').test(code)) { + return; + } + + const token = code.split(''); + + this.ui.dxFind('input[type="text"]', (input, i) => input.value = token[i] ); + } + + /** + * PinBox, Focus + */ + focus() { + this.ui.dxFind('input[type="text"]:first-child').focus(); + } + + /** + * PinBox, Enable + */ + enable() { + this.ui.dxFind('input[type="text"]', input => input.disabled = false); + } + + /** + * PinBox, Disable + */ + disable() { + this.ui.dxFind('input[type="text"]', input => input.disabled = true); + } + + /** + * PinBox, Clear Inputs + */ + clear() { + this.ui.dxFind('input[type="text"]', target => target.value = ''); + } + + /** + * PinBox, Destroy + */ + destroy() { + this.ui.remove(); + this.target.dxRemoveData('pinbox'); + Ut.trigger(this.events.destroy, this); + } +} + +//Ut.extendNodeUx(PinBox); \ No newline at end of file diff --git a/src/widgets/pin-box/index.js b/src/widgets/pin-box/index.js new file mode 100644 index 0000000..0b1f43d --- /dev/null +++ b/src/widgets/pin-box/index.js @@ -0,0 +1 @@ +export { PinBox } from './PinBox'; \ No newline at end of file diff --git a/src/widgets/pin-box/pin-box.css b/src/widgets/pin-box/pin-box.css new file mode 100644 index 0000000..4827fcf --- /dev/null +++ b/src/widgets/pin-box/pin-box.css @@ -0,0 +1,29 @@ +/* PinBox */ + +.sp-pinbox { + display: flex; + flex-wrap: nowrap; +} + +.sp-pinbox input { + width: 38px; + height: 38px; + text-align: center; + margin-right: 7px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 20px; +} + +.sp-pinbox input:last-child { + margin-right: 0; +} + +@media only screen and (min-width : 768px) { + + .sp-pinbox input { + width: 45px; + height: 45px; + font-size: 24px; + } +} \ No newline at end of file diff --git a/src/widgets/popover/Popover.js b/src/widgets/popover/Popover.js new file mode 100644 index 0000000..a948479 --- /dev/null +++ b/src/widgets/popover/Popover.js @@ -0,0 +1,182 @@ +import './popover.css'; + +import { Ut } from '../../utils/Ut'; +import { El } from "../../utils/dom"; +import { Stack } from '../../core/Stack'; +import { Overlay } from '../overlay'; + +export class Popover { + + /** + * Popover, Constructor + */ + constructor(target, content, opts, events) { + + this.target = target; + + if (this.target.dxData('popover')) { + return; + } + + this.target.dxData('popover', this); + + this.content = content; + + this.opts = { ... { + multiple : false, + modal : false, + timeout : false, + closeBtn : false + }, ... opts }; + + this.events = events || {}; + + if (!this.opts.multiple) { + // Destroy old instances + Stack.widgets.Popover.forEach(popover => popover.destroy()); + } + + Stack.add(this); + + this.create(); + + this.setPosition(); + + this.setEvents(); + + if (this.opts.timeout !== false) { + + this.tm = setTimeout(function() { + this.destroy(); + }, this.opts.timeout, this); + } + } + + /** + * Popover, Set Position + */ + setPosition() { + + let offset = this.target.dxOffset(), + + width = this.target.offsetWidth, + height = this.target.offsetHeight, + + centerX = offset.left - (this.ui.offsetWidth / 2) + (width / 2), + centerY = offset.top - this.ui.offsetHeight - 16; + + this.ui.dxCss({ left: centerX + 'px', top: centerY + 'px' }); + } + + /** + * Popover, Create UI + */ + create() { + + this.ui = El('div', { class: 'sp-popover' } ); + + if (this.opts.closeBtn) { + this.ui.append( El('i', { class: 'fas fa-times ic-close' }) ); + } + + let content = El('div', { class: 'po-content' }); + + this.ui.append(content); + + if (isStr(content)) { + content.dxHtml(this.content); + } + else { + content.dxHtml('').append(this.content); + } + + document.body.append(this.ui); + + if (this.opts.modal) { + Overlay.showModal(this.ui); + } + + Ut.trigger(this.events.init, this, this.ui); + } + + /** + * Popover, Set Events + */ + setEvents() { + + // Reposition on resize + + let tm = null; + let that = this; + + $('.sp-scrollbar')?.dxOn('scroll.popover', () => { + + if (tm) { + clearTimeout(tm); + tm = null; + } + + tm = setTimeout(function() { + that.setPosition(); + }, 100); + }); + + window.dxOn('resize.popover orientationchange.popover', () => { + + if (tm) { + clearTimeout(t); + tm = null; + } + + tm = setTimeout(function() { + that.setPosition(); + }, 100); + }); + + this.ui.dxOn('click', e => e.stopPropagation()); + + this.ui.dxOn('click', '.ic-close', () => this.destroy()); + + document.dxOn('keyup.popover', e => e.key === 'Escape' && this.destroy()); + + document.body.dxOn('click.popover', () => this.destroy()); + } + + /** + * Popover, Destroy + */ + destroy() { + + if (this.tm) { + clearTimeout(this.tm); + this.tm = null; + } + + if (this.opts.modal) { + Overlay.hideModal(this.ui); + } + + this.ui.remove(); + + document.body.dxOff('click.popover'); + document.dxOff('keyup.popover'); + window.dxOff('resize.popover orientationchange.popover'); + + $('.sp-scrollbar')?.dxOff('scroll.popover'); + + this.target.dxRemoveData('popover'); + + Stack.delete(this); + + Ut.trigger(this.events.destroy, this); + } +} + +export class Alert { + + constructor(target, msg) { + new Popover(target, msg, { color: 'red', timeout: 3000 }); + } +} + +//Ut.extendNodeUx(Popover, Alert); \ No newline at end of file diff --git a/src/widgets/popover/index.js b/src/widgets/popover/index.js new file mode 100644 index 0000000..05e1b14 --- /dev/null +++ b/src/widgets/popover/index.js @@ -0,0 +1 @@ +export { Popover } from './Popover'; \ No newline at end of file diff --git a/src/widgets/popover/popover.css b/src/widgets/popover/popover.css new file mode 100644 index 0000000..af05f16 --- /dev/null +++ b/src/widgets/popover/popover.css @@ -0,0 +1,65 @@ +/* Popover UI */ + +.sp-popover { + position: absolute; + background-color: #fff; + box-shadow: rgba(9, 30, 66, 0.13) 0px 0px 1px 1px; + border-radius: 5px; + top: -9999px; + left: -9999px; +} + +.sp-popover .po-content { + padding: 16px; +} + +.sp-popover .ic-close { + position: absolute; + top: 10px; + right: 10px; + cursor: pointer; +} + +.sp-popover::after { + display: block; + content: ""; + box-shadow: 1px 1px 1px rgba(9, 30, 66, 0.13); + left: 0; + right: 0; + margin: 0 auto -6px auto; + width: 12px; + height: 12px; + background-color: #fff; + transform: rotate(45deg); + position: relative; + +} + +.sp-popover .sp-button { + align-self: end; + height: 38px; + line-height: 38px; + margin-bottom: 20px; +} + +.sp-popover select { + color: inherit; + background: url(data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0Ljk1IDEwIj48ZGVmcz48c3R5bGU+LmNscy0ye2ZpbGw6I2U0ZTlmMDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPmFycm93czwvdGl0bGU+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjEuNDEgNC42NyAyLjQ4IDMuMTggMy41NCA0LjY3IDEuNDEgNC42NyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIzLjU0IDUuMzMgMi40OCA2LjgyIDEuNDEgNS4zMyAzLjU0IDUuMzMiLz48L3N2Zz4=) no-repeat 98% 50%; +} + +.sp-popover select:focus { + border-color: inherit; +} + +.sp-popover select option { + color: #666; +} + +.sp-popover .form-field { + margin-bottom: 0; +} + +.sp-popover .sp-button { + background: #707070; + margin-bottom: 0; +} \ No newline at end of file diff --git a/src/widgets/popup/Popup.js b/src/widgets/popup/Popup.js new file mode 100644 index 0000000..68dd0a2 --- /dev/null +++ b/src/widgets/popup/Popup.js @@ -0,0 +1,248 @@ +import './popup.css'; + +import { Ut } from '../../utils/Ut'; +import { El } from '../../utils/dom'; +import { Overlay } from '../overlay'; +import { Stack } from '../../core/Stack'; +import { Render } from '../../core/Render'; + +//////////////////// +// Popup Widget +//////////////////// + +export class Popup { + + /** + * Popup, Constructor + */ + constructor(target, opts) { + + let that = this; + this.target = target; + + if (this.target.dxData('popup')) { + return; + } + + this.target.dxData('popup', this); + + this.opts = { ... { + scrollBar : true, + marginTop : 50, + marginBottom : 50, + multiple : false, + width : 800, + height : 'auto', + modal : true, + cls : null + }, ... opts }; + + this.opened = false; + + if (!this.opts.multiple) { + + Stack.widgets.Popup?.forEach((popup, key) => { + + if (key == this.ekey - 1) { + that.opened = true; + popup.destroy(_ => that.init()); + } + else { + popup.destroy(); + } + }); + } + + if (!this.opened) { + this.opened = true; + this.init(); + } + } + + /** + * Popup, Init + */ + init() { + + Stack.add(this); + + this._plugins = []; + + this.create(); + + this.setEvents(); + } + + /** + * Popup, Create + */ + create() { + + this.card = { + ui: Render.makeCardElement({ + title: this.opts.title || this.target.dataset.title, + descr: this.opts.descr || this.target.dataset.descr, + scrollbar : this.opts.scrollBar || this.target.scrollbar + }, null) + }; + + this.card.toolbar = this.card.ui.dxFind('.top-bar-inner'); + this.card.container = this.card.ui.dxFind('.container'); + + this.target.classList.add('popup-container'); + + this.card.container.append(this.target); + + if (this.opts.toolbar) { + + if (!this.card.toolbar) { + this.card.toolbar = El('div', { class: 'top-bar-inner' }); + this.card.ui.prepend(El('div', { class: 'top-bar' }, this.card.toolbar)); + } + + this.card.toolbar.append(this.opts.toolbar); + } + + if (this.card.toolbar) { + this.card.toolbar.append( El('a', { class: 'ic-close' }) ); + } + + this.card.ui.classList.add('sp-popup'); + + if (this.opts.cls) { + this.card.ui.classList.add(this.opts.cls); + } + + this.card.ui.style.width = this.opts.width + 'px'; + + this.card.container.style.height = this.opts.height + 'px'; + + if (this.opts.scrollBar) { + this.card.container.classList.add('sp-scrollbar'); + } + + this.scrollTop = window.screenY; + + if (Ut.device.isMobile) { + document.body.dxCss({ position: 'fixed', top: '-' + this.scrollTop + 'px' }) + } + + document.body.append(this.card.ui); + + if (this.opts.modal) { + Overlay.showModal(this.card.ui); + } + + this.resize(); + + this.card.ui.dxCss({ top: this.opts.marginTop + 'px', opacity: 1 }); + + Ut.trigger(this.opts.onInit, this); + } + + /** + * Popup, Set Title + */ + setTitle(text) { + + let title = this.card.toolbar.dxFind('h2.title'); + + title.textContent = text; + title.classList.remove('hide'); + } + + /** + * Popup, resize + */ + resize() { + + if (this.opts.scrollBar) { + this.card.container.dxCss('max-height', + ( window.innerHeight - this.card.toolbar.clientHeight - this.opts.marginTop - this.opts.marginBottom ) + + 'px' + ); + } + + Ut.trigger(this.opts.onResize, + this, + this.card.container.clientWidth, + this.card.container.clientHeight + ); + } + + /** + * Popup, Set Events + */ + setEvents() { + + let that = this; + + if (this.opts.scrollBar) { + + let tm = null; + + window.dxOn('resize.popup' + this.ekey + ' orientationchange.popup' + this.ekey, function() { + + if (tm) { + clearTimeout(tm); + tm = null; + } + + tm = setTimeout(function() { + that.resize(); + }, 300); + }); + } + + this.card.toolbar.dxOn('click', '.ic-close', function() { + that.destroy(); + }); + + document.dxOn('keyup.popup' + this.ekey, function(e) { + + if (e.key == 'Escape') { + that.destroy(); + } + }); + } + + /** + * Popup, Destroy + */ + destroy(callback) { + + const that = this; + + this.card.ui.addEventListener('transitionend', function(e) { + + document.dxOff('keyup.popup' + that.ekey + ' resize.popup' + that.ekey + ' orientationchange.popup' + that.ekey); + + if (that.opts.modal) { + Overlay.hideModal(that.card.ui); + } + + that.card.ui.remove(); + + if (Ut.device.isMobile) { + document.body.dxCss({ position: '', top: '' }); + document.body.scrollTop = that.scrollTop; + } + + that._plugins.forEach(widget => widget.destroy()); + + Stack.delete(this); + + that.target.dxRemoveData('popup'); + + Ut.trigger(callback); + + Ut.trigger(that.opts.onDestroy); + }, { + once: true + }); + + this.card.ui.dxCss({ top: '-25%', opacity: 0 }); + } +} + +//Ut.extendNodeUx(Popup); \ No newline at end of file diff --git a/src/widgets/popup/index.js b/src/widgets/popup/index.js new file mode 100644 index 0000000..4ac2379 --- /dev/null +++ b/src/widgets/popup/index.js @@ -0,0 +1 @@ +export { Popup } from './Popup'; \ No newline at end of file diff --git a/src/widgets/popup/popup.css b/src/widgets/popup/popup.css new file mode 100644 index 0000000..fe9762c --- /dev/null +++ b/src/widgets/popup/popup.css @@ -0,0 +1,41 @@ +/* Popup */ + +.sp-popup { + background-color:#fff; + border-radius:5px; + box-shadow: 1px 1px 5px 0px rgba(204,204,204,1); + position: fixed; + left: 0; + right: 0; + margin: auto; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; + max-width: 94%; + overflow: hidden; +} + +.sp-popup .container { + padding-bottom: 0; +} + +.sp-popup .container:after { + display: block; + content: ''; + width: 100%; + height: 24px; +} + +.sp-popup .popup-container { + width: 100%; +} + +.sp-popup a.ic-close{ + width: 25px; + height: 25px; + display: block; + cursor: pointer; + background-size: 16px 16px; + background-position: center; + background-repeat: no-repeat; + background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%20id%3D%22Layer_1%22%20x%3D%220px%22%20y%3D%220px%22%20viewBox%3D%220%200%20500%20500%22%20xml%3Aspace%3D%22preserve%22%20enable-background%3D%22new%200%200%20500%20500%22%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%0A%09.st0%7Bopacity%3A0%3Bfill%3A%23CCCCCC%3B%7D%0A%09.st1%7Bfill%3A%23CCCCCC%3B%7D%0A%3C%2Fstyle%3E%3Crect%20id%3D%22XMLID_4_%22%20class%3D%22st0%22%20width%3D%22500%22%20height%3D%22500%22%2F%3E%3Cg%20id%3D%22XMLID_3_%22%3E%3Cpath%20id%3D%22XMLID_6_%22%20class%3D%22st1%22%20d%3D%22M478.4%20101.6L330%20250l148.5%20148.5c22.1%2022.1%2022.1%2057.9%200%2079.9%20-11%2011.1-25.5%2016.5-40%2016.5%20-14.5%200-28.9-5.5-40-16.5L250%20329.9%20101.5%20478.4C90.5%20489.5%2076%20495%2061.6%20495c-14.5%200-28.9-5.5-39.9-16.5%20-22.1-22.1-22.1-57.9%200-79.9L170.1%20250%2021.6%20101.6c-22.1-22.1-22.1-57.9%200-80%2022.1-22.1%2057.9-22.1%2079.9%200L250%20170.1%20398.5%2021.6c22.1-22.1%2057.9-22.1%2079.9%200C500.5%2043.6%20500.5%2079.4%20478.4%20101.6L478.4%20101.6zM478.4%20101.6%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"); +} \ No newline at end of file diff --git a/src/widgets/scheduler/Scheduler.js b/src/widgets/scheduler/Scheduler.js new file mode 100644 index 0000000..9394579 --- /dev/null +++ b/src/widgets/scheduler/Scheduler.js @@ -0,0 +1,511 @@ +import './scheduler.css'; + +import { El } from '../../utils/dom'; + +export class Scheduler { + + /** + * Scheduler, Constructor + */ + constructor(target, opts) { + + this.target = target; + + if (this.target.dxData('scheduler')) { + return; + } + + this.opts = { ... { + startTime: [8, 0], + endTime: [21, 30], + unitTime: 15, + cellSize: 22, + maxEvents: 1, + title: 'Title' + }, ... opts }; + + this.slots = 60 / this.opts.unitTime; + + if (!Number.isInteger(this.slots)) { + console.log('Invalid unit time'); + return; + } + + this.target.dxData('scheduler', this); + + this.ui = {}; + + this.eventList = []; + + this.resetSelection(); + + this.renderContainer(); + + this.setUiEvents(); + } + + /** + * Scheduler, Render Container + */ + renderContainer() { + + this.ui.wrapper = this.target.appendChild( El('div', { class: 'sp-scheduler' }) ); + + this.ui.header = this.ui.wrapper.appendChild( + El('div', { class: 'header-bar' }, + El('span', { class: 'label-title' }, this.opts.title) + ) + ) + + this.ui.scrollbar = this.ui.wrapper.appendChild( El('div', { class: 'sp-scrollbar' }) ); + this.ui.container = this.ui.scrollbar.appendChild( El('div', { class: 'scale-container' }) ); + this.ui.scale = this.ui.container.appendChild( El('div', { class: 'scale-holder' }) ); + this.ui.events = this.ui.container.appendChild( El('div', { class: 'scale-events' }) ); + + let remSlots = Math.floor(this.opts.startTime[1] / this.opts.unitTime); + + let unitHeight = this.opts.cellSize * this.slots; + + let i; + for (i = this.opts.startTime[0]; i <= this.opts.endTime[0]; i++) { + + let mins = 0; + let height = unitHeight; + + if (i == this.opts.startTime[0] && this.opts.startTime[1] != 0) { + height -= this.opts.cellSize * remSlots; + mins = this.opts.startTime[1]; + } + + this.ui.scale.append( + El('div', { class: 'scale-hour', style: 'height: '+ height + 'px;' }, + i.toString().padStart(2, '0') + ':' + mins.toString().padStart(2, '0') + ) + ); + } + + let n = remSlots; + + let hours = this.opts.startTime[0]; + let mins = 0; + let uiSlot; + + for (; n < this.slots * (this.opts.endTime[0] - this.opts.startTime[0]) ; n++) { + + uiSlot = El('div', { class: 'time-slot', style: 'height: '+ this.opts.cellSize + 'px;' }); + + uiSlot.dataset.time = hours.toString().padStart(2, '0') + ':' + mins.toString().padStart(2, '0'); + + mins += this.opts.unitTime; + + if ((n + 1) % this.slots == 0) { + + hours++; + mins = 0; + + uiSlot.className += ' last-line'; + } + + this.ui.events.append( uiSlot ); + } + + remSlots = Math.ceil(this.opts.endTime[1] / this.opts.unitTime); + + for (n = 0; n < remSlots; n++) { + + uiSlot = El('div', { class: 'time-slot', style: 'height: '+ this.opts.cellSize + 'px;' }); + + + uiSlot.dataset.time = hours.toString().padStart(2, '0') + ':' + mins.toString().padStart(2, '0'); + + mins += this.opts.unitTime; + + this.ui.events.append( uiSlot ); + } + } + + /** + * Scheduler, Set UI Events + */ + setUiEvents() { + + this.ui.events.dxOn('mousedown', '.time-slot', (e, t) => !e.button && this.mouseDownHandler(e, t)); + this.ui.events.dxOn('mouseup', e => !e.button && this.selTarget && this.mouseUpHandler()); + this.ui.events.dxOn('mouseleave', _ => this.selTarget && this.leaveSelection()); + this.ui.events.dxOn('click', '.ev-box', (_, t) => this.eventClickHandler(t) ); + } + + mouseUpHandler() { + + this.ui.events.dxOff('mouseover'); + this.ui.events.dxOff('mousemove'); + + this.ui.events.classList.remove('scale-select'); + + this.ui.label.classList.add('hide'); + + this.trigger('slotSelected'); + + this.selTarget = null; + } + + mouseDownHandler(e, t) { + + if (this.hasOverlap(t)) { + return; + } + + this.selectedTime = {} + + t.classList.add('main-selected'); + + this.selTarget = t; + this.slotStart = t; + + this.slotEnd = t.dxNext('.time-slot'); + + this.setSelectedData(); + + this.showLabel(); + this.setLabelPosition(e); + + this.ui.events.dxOn('mouseover', '.time-slot', (_, t) => this.mouseOverHandler(t)); + this.ui.events.dxOn('mousemove', e => this.setLabelPosition(e)); + + this.ui.events.classList.add('scale-select'); + } + + mouseOverHandler(t) { + + if (!this.selTarget) { + return; + } + + let oldNext = t.dxNext('.selected'); + + while (oldNext) { + oldNext.classList.remove('selected'); + oldNext = oldNext.dxNext('.selected'); + } + + let oldPrev = t.dxPrev('.selected'); + + while (oldPrev) { + oldPrev.classList.remove('selected'); + oldPrev = oldPrev.dxPrev('.selected'); + } + + let ref = this.selTarget.compareDocumentPosition(t); + + if (ref & Node.DOCUMENT_POSITION_PRECEDING) { + + let prev = this.selTarget.dxPrev('.time-slot'); + + while (prev) { + + if (this.hasOverlap(prev)) { + return; + } + + prev.classList.add('selected'); + + if (prev === t) { + + this.slotStart = t; + + this.setSelectedData(); + this.showLabel(); + + return; + } + + prev = prev.dxPrev('.time-slot'); + } + } + else if (ref & Node.DOCUMENT_POSITION_FOLLOWING) { + + this.slotStart = this.selTarget; + + let next = this.selTarget.dxNext('.time-slot'); + + while (next) { + + if (this.hasOverlap(next)) { + return; + } + + next.classList.add('selected'); + + if (next === t) { + + this.slotEnd = t.dxNext('.time-slot'); + + this.setSelectedData(); + this.showLabel(); + + return; + } + + next = next.dxNext('.time-slot'); + } + } + else { + + this.slotStart = this.selTarget; + this.slotEnd = this.selTarget.dxNext('.time-slot'); + + this.setSelectedData(); + this.showLabel(); + } + } + + setSelectedData() { + + this.selectedTime = { + start: this.slotStart.dataset.time + }; + + if (this.slotEnd) { + this.selectedTime.end = this.slotEnd.dataset.time + } + else { + this.selectedTime.end = this.opts.endTime[0].toString().padStart(2, '0') + ':' + this.opts.endTime[1].toString().padStart(2, '0') + } + } + + showLabel() { + + if (!this.ui.label) { + this.ui.label = this.ui.events.appendChild( El('div', { class: 'time-label' }) ); + } + + this.ui.label.textContent = this.selectedTime.start + ' - ' + this.selectedTime.end + ' ( ' + this.getTimeFormat() + ' )'; + + this.ui.label.classList.remove('hide'); + } + + setLabelPosition(e) { + + this.ui.label.style.top = (e.clientY - this.ui.events.dxOffset().top + 14) + 'px'; + this.ui.label.style.left = (e.pageX - this.ui.events.dxOffset().left + 14) + 'px'; + } + + getTimeFormat() { + + let raw = ''; + + let start = this.selectedTime.start.split(':').map(t => parseInt(t)); + let end = this.selectedTime.end.split(':').map(t => parseInt(t)); + + let total = (end[0] * 60 + end[1]) - (start[0] * 60 + start[1]); + let hours = total >= 60 ? Math.floor(total / 60) : 0; + let mins = total % 60; + + if (hours > 0) { + + if (hours == 1) { + raw += 'o oră'; + } + else { + raw += hours + ' ore'; + } + + if (mins) { + raw += ' și '; + } + } + + if (mins) { + raw += mins + ' minute'; + } + + return raw; + } + + addEvent(content) { + + let count = 0; + this.findSelection(_ => count++); + + let selStart = this.selectedTime.start; + let selEnd = this.selectedTime.end; + let first = null; + + this.ui.events.dxChildren('.ev-box', t => { + + if ((selStart <= t.dataset.start && selEnd > t.dataset.start) + || (selStart < t.dataset.end && selEnd > t.dataset.end) + || (selStart >= t.dataset.start && selEnd <= t.dataset.end) + ) { + first = t; + } + }); + + let evBox = this.ui.events.appendChild( + El('div', { class: 'ev-box' }, + El('div', { class: 'ev-content' }, content) + ) + ); + + let index = first ? first.dataset.index : Date.now(); + + evBox.dataset.index = index; + evBox.dataset.start = this.selectedTime.start; + evBox.dataset.end = this.selectedTime.end; + + evBox.style.top = this.slotStart.offsetTop + 'px'; + evBox.style.height = (this.opts.cellSize * count - 1) + 'px'; + + this.resizeGroup(index); + + this.clearSelection(); + } + + hasOverlap(target) { + + if (this.opts.maxEvents == -1) { + return false; + } + + let time = target.dataset.time; + + let found = 0; + + this.ui.events.dxChildren('.ev-box', t => { + if (time >= t.dataset.start && time < t.dataset.end ) { + found++; + } + }); + + return found >= this.opts.maxEvents; + } + + resizeGroup(index) { + + let cols = this.ui.events.dxChildren('.ev-box[data-index="'+ index +'"]', true); + + cols = cols.sort((a, b) => { + + if (a.dataset.start < b.dataset.start) { + return -1; + } + else if (a.dataset.start > b.dataset.start) { + return 1; + } + + return 0; + }); + + let unit = 100 / cols.length; + let space = 5 / cols.length; + + cols.forEach((t, i) => { + + t.style.width = (unit - space - 0.5) + '%' + t.style.left = (i * (unit - space) ) + '%'; + }); + } + + deleteEvent(ref) { + + ref.container.remove(); + this.resizeGroup(ref.index); + + this.trigger('eventDeleted'); + } + + findSelection(callback) { + + if (this.slotStart) { + callback(this.slotStart); + } + + let next = this.slotStart.dxNext('.time-slot'); + + while (next) { + + if (next === this.slotEnd) { + break; + } + + callback(next); + + next = next.dxNext('.time-slot'); + } + } + + eventClickHandler(t) { + + this.trigger('eventClick', + this, { + container: t, + content: t.dxChildren('.ev-content'), + index: t.dataset.index, + interval: { + start: t.dataset.start, + end: t.dataset.end + } + } + ); + } + + getStartTime() { + return this.selectedTime.start; + } + + getEndTime() { + return this.selectedTime.end; + } + + leaveSelection() { + + this.ui.events.dxOff('mouseover'); + this.ui.events.dxOff('mousemove'); + + this.ui.label.classList.add('hide'); + + this.findSelection(t => t.classList.remove('selected', 'main-selected')); + + //this.ui.events.dxChildren('.time-slot', t => t.classList.remove('selected', 'main-selected')); + + this.resetSelection(); + } + + resetSelection() { + + this.selTarget = null; + this.slotStart = null; + this.slotEnd = null; + this.selectedTime = {}; + } + + clearSelection() { + + if (!this.slotStart) { + return; + } + + this.findSelection(t => t.classList.remove('selected', 'main-selected')); + + this.resetSelection(); + + this.trigger('slotCleared'); + } + + trigger(event) { + + let handler = this.opts[ event ]; + + let args = [... arguments ].slice(1); + + if (!args.length) { + args = [ this ]; + } + + if (typeof handler === 'function') { + handler(... args); + } + + this.target.dxTrigger(event, { scheduler: this, args: args }); + } +} \ No newline at end of file diff --git a/src/widgets/scheduler/index.js b/src/widgets/scheduler/index.js new file mode 100644 index 0000000..59846f7 --- /dev/null +++ b/src/widgets/scheduler/index.js @@ -0,0 +1 @@ +export { Scheduler } from './Scheduler'; \ No newline at end of file diff --git a/src/widgets/scheduler/scheduler.css b/src/widgets/scheduler/scheduler.css new file mode 100644 index 0000000..9b59bae --- /dev/null +++ b/src/widgets/scheduler/scheduler.css @@ -0,0 +1,97 @@ +/* Scheduler UI */ + +.sp-scheduler { + width: 100%; + -webkit-user-select: none; + user-select: none; +} + +.sp-scheduler .header-bar { + display: flex; + justify-content: center; + padding: 10px; + background-color: #d0dbe3; + margin-left: 49px; +} + +.sp-scheduler .header-bar .label-title { + font-size: 14px; + font-weight: 600; + text-align: center; +} + +.sp-scheduler .sp-scrollbar { + max-height: 600px; + overflow-x: hidden; +} + +.sp-scheduler .scale-container { + color: #23272A; + display: flex; +} + +.sp-scheduler .scale-holder { + border-right: 1px solid #d0dbe3; + width: 50px; + flex-shrink: 0; +} + +.sp-scheduler .scale-hour { + border-bottom: 1px solid #d0dbe3; + font-size: 13px; + font-weight: 600; + color: #6c6c6c; + padding: 4px; +} + +.sp-scheduler .scale-hour:last-child { + border-bottom: 0; +} + +.sp-scheduler .scale-events { + flex: 1; + position: relative; +} + +.sp-scheduler .time-slot { + border-bottom: 1px dotted #d0dbe3; +} + +.sp-scheduler .time-slot.selected, +.sp-scheduler .time-slot.main-selected { + background-color: #e8fff2; +} + +.sp-scheduler .time-slot.last-line { + border-bottom-style: solid; +} + +.sp-scheduler .time-label { + position: absolute; + background-color: #fff; + border-radius: 5px; + box-shadow: 1px 1px 5px 0px rgba(204, 204, 204, 1); + padding: 8px; + font-size: 13px; + white-space: nowrap; + z-index: 101; +} + +.sp-scheduler .ev-box { + position: absolute; + border-radius: 2px; + background-color: #c3ffdd; + left: 0; + right: 0; + cursor: pointer; + width: 20%; +} + +.sp-scheduler .scale-events.scale-select .ev-box { + pointer-events: none; +} + +.sp-scheduler .ev-box .ev-content { + padding: 4px 5px; + font-size: 12px; +} \ No newline at end of file diff --git a/src/widgets/select-box/SelectBox.js b/src/widgets/select-box/SelectBox.js new file mode 100644 index 0000000..a74643d --- /dev/null +++ b/src/widgets/select-box/SelectBox.js @@ -0,0 +1,94 @@ +import './select-box.css'; + +import { El } from '../../utils/dom'; +import { Stack } from '../../core/Stack'; + +export class SelectBox { + + /** + * Select Box, Constructor + */ + constructor(target) { + + if (!target.length) { + return; + } + + this.target = target; + + this.labels = El('div', { class: 'labels hide' }); + this.choices = El('div', { class: 'choices' }); + + this.ui = El('div', { class: 'sp-form-control sp-select-box' }, + this.choices, + this.labels + ); + + let parent = this.target[0].dxParents('.form-field'); + + this.target.forEach(function(el) { + this.labels.append(el.parentElement); + }, this); + + parent.append(this.ui); + + Stack.add(this); + + this.setEvents(); + } + + /** + * Select Box, Set Events + */ + setEvents() { + + let that = this; + + this.ui.dxOn('click', function(e) { + e.stopPropagation(); + that.labels.classList.toggle('hide'); + }); + + window.dxOn('resize.selectbox' + this.ekey + ' orientationchange.selectbox' + this.ekey, function() { + that.labels.classList.add('hide'); + }); + + document.dxOn('click.selectbox' + this.ekey, function() { + that.labels.classList.add('hide'); + }); + + this.labels.dxOn('click', function(e) { + e.stopPropagation(); + }); + + this.labels.dxOn('change', 'input', this.update.bind(this) ); + } + + /** + * Select Box, Update + */ + update() { + + let checked = []; + + this.labels.dxFind('input', input => { + + if (input.checked) { + checked.push(input.parentElement.textContent); + } + }); + + this.choices.dxText(checked.join(', ')); + } + + /** + * Select Box, Destroy + */ + destroy() { + + window.dxOff('resize.selectbox' + this.ekey + ' orientationchange.selectbox' + this.ekey); + document.dxOff('click.selectbox' + this.ekey); + + Stack.delete(this); + } +} \ No newline at end of file diff --git a/src/widgets/select-box/index.js b/src/widgets/select-box/index.js new file mode 100644 index 0000000..50f2c0f --- /dev/null +++ b/src/widgets/select-box/index.js @@ -0,0 +1 @@ +export { SelectBox } from './SelectBox'; \ No newline at end of file diff --git a/src/widgets/select-box/select-box.css b/src/widgets/select-box/select-box.css new file mode 100644 index 0000000..e0c115d --- /dev/null +++ b/src/widgets/select-box/select-box.css @@ -0,0 +1,62 @@ +/* Select Box */ + +.sp-select-box { + width: 100%; + position:relative; + -webkit-user-select: none; + user-select: none; + padding: 0; +} + +.sp-select-box:after { + display: block; + content: ""; + position: absolute; + top:50%; + right:10px; + margin-top:-5px; + background-image:url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2211.949%2C3.404%207%2C8.354%202.05%2C3.404%20-0.071%2C5.525%207%2C12.596%2014.07%2C5.525%20%22%2F%3E%3C%2Fsvg%3E"); + width:10px; + height:10px; + background-size:10px 10px; +} + +.sp-select-box .choices { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + height:100%; + line-height: 38px; + padding: 0 16px; +} + +.sp-select-box .choices span { + margin-right: 1px; +} + +.sp-select-box .labels { + position:absolute; + z-index: 100; + width:100%; + max-height: 250px; + margin-top: 1px; + background-color:#fff; + border-left:1px solid #ccc; + border-right:1px solid #ccc; + border-bottom:1px solid #ccc; + border-bottom-left-radius:5px; + border-bottom-right-radius:5px; + padding: 8px; + overflow-x: hidden; + overflow-y: auto; +} + +.sp-select-box label { + display: block; + padding: 6px 0; +} + +.sp-select-box input { + vertical-align: middle; + margin-right: 7px; +} \ No newline at end of file diff --git a/src/widgets/table/Table.js b/src/widgets/table/Table.js new file mode 100644 index 0000000..e23441a --- /dev/null +++ b/src/widgets/table/Table.js @@ -0,0 +1,652 @@ +import './table.css'; + +import { Ut } from '../../utils/Ut'; +import { $, El, fillData } from '../../utils/dom'; +import { FormValidator } from '../../utils/form-validator/FormValidator'; +import { Rc } from '../../core/Rc'; +import { Render } from '../../core/Render'; + +export class Table { + + static CLEAN = 0; + static APPEND = 1; + static PREPEND = 2; + static UPDATE = 3; + + /** + * Table, Constructor + */ + constructor(target, caller, opts) { + + this.target = target; + this.caller = caller; + + this.vars = new FormData(); + + this.reqOpts = {}; + + this.vars.set('pg', 1); + + this.opts = { ... { + layout : 'auto', + filter : false, + header : {}, + label : '%rows results', + sortable : 0 + }, ... opts, ... opts.children }; + + this.rows = 0; + this.setHeader = true; + this.data = {}; + + if (!this.opts.header.cell || this.opts.display == 'none') { + this.setHeader = false; + } + + this.makeUiTable(); + + if (this.opts.filter) { // create filter form + this.makeFilterForm(); + } + + if (Ut.isSet(this.opts.toolbar_header)) { // create toolbar header + this.makeToolbarHeader(); + } + + if (this.opts.label) { // create info label + this.makeInfoLabel(); + } + + this.makeNewTable(); + + this.makePagination(); + + if (Ut.isSet(this.opts.toolbar_footer)) { // create toolbar footer + this.makeToolbarFooter(); + } + + this.setMode(Table.CLEAN); + + if ( Ut.isFn(caller) ) { // Data Mode + this.makeRequest(); + } + else { + this.setTableData(caller); + } + + if (this.opts.sortable) { + this.setSortable(); + } + + Ut.trigger(this.opts.onInit, this); + } + + setMode(mode) { + + this.mode = mode; + + if (mode == Table.CLEAN) { + this.id = 0; + } + } + + setTableData(data) { + + this.data = data; + this.rows = parseInt(this.data.count); + + this.updateInfoRows(); + + if (this.mode == Table.CLEAN) { + + this.table.dxHtml(''); + + if (this.rows) { + this.makeTableHead(); + } + } + + this.makeTableRows(); + + this.updatePagination(); + + Ut.trigger(this.opts.onUpdate, this); + } + + makeInfoLabel() { + this.info = El('div', { class: 'sp-table-info' }); + this.ui.append(this.info); + } + + // Make UI container + + makeUiTable() { + this.ui = El('div', { class: 'sp-table-ui' }); + this.target.append(this.ui); + } + + // Make filter form + + makeFilterForm() { + + let that = this; + + this.filterForm = El('form', { novalidate: true, class: 'sp-form filter-main' }); + + Render.makeUiElements(Ut.objVal(this.opts.filter).children, this.filterForm); + + this.ui.append(this.filterForm); + + this.filterForm.dxOn('changed clear', 'input[type="text"]', () => that.filterForm.dxTrigger('submit.form')); + this.filterForm.dxOn('change', 'select, input[type="radio"], input[type="checkbox"]', () => that.filterForm.dxTrigger('submit.form')); + + new FormValidator(this.filterForm, { + + onInit: el => that.filterFormEl = el, + onSubmit: function(vars) { + + that.vars = vars; + + that.vars.set('pg', 1); + + that.setMode(Table.CLEAN); + + that.makeRequest(); + } + }); + + fillData(that.filterFormEl, this.vars); + } + + // Make header toolbar + + makeToolbarHeader() { + + this.toolbarHeader = El('div', { class: 'sp-toolbar toolbar-header' + (this.opts.toolbar_header.cls ? ' ' + this.opts.toolbar_header.cls : '') }); + + Render.makeUiElements(Ut.objVal(this.opts.toolbar_header).children, this.toolbarHeader); + + this.ui.append( this.toolbarHeader ); + + Ut.trigger(this.opts.toolbarHeader, this); + } + + // Make Pagination Area + + makePagination() { + + this.paginationEl = this.ui.appendChild(El('div', { class: 'sp-pagination' })); + + this.paginationEl.dxOn('click', 'a', (e, t) => { + + this.vars.set('pg', t.dataset.id); + + this.setMode(Table.CLEAN); + + this.makeRequest(); + }); + + this.paginationEl.dxOn('click', 'a.btn-before', (_, t) => { + + this.vars.set('before', this.data.before); + this.vars.set('after', ''); + + this.setMode(Table.CLEAN); + + this.makeRequest(); + }); + + this.paginationEl.dxOn('click', 'a.btn-after', function() { + + this.vars.set('before', ''); + this.vars.set('after', this.data.after); + + this.setMode(Table.CLEAN); + + this.makeRequest(); + }); + } + + // Make footer toolbar + + makeToolbarFooter() { + + this.toolbarFooter = El('div', { class: 'sp-toolbar toolbar-footer' + (this.opts.toolbar_footer.cls ? ' ' + this.opts.toolbar_footer.cls : '') }); + + Render.makeUiElements(Ut.objVal(this.opts.toolbar_footer).children, this.toolbarFooter); + + this.ui.append(this.toolbarFooter); + + Ut.trigger(this.opts.toolbarFooter, this); + } + + // Make new ui table + + makeNewTable() { + this.table = El('div', { class: 'sp-table layout-' + this.opts.layout + (!this.setHeader ? ' no-header' : '') + (this.opts.cls ? ' ' + this.opts.cls : '')}); + this.ui.append(this.table); + } + + // Make table head + + makeTableHead() { + + if (!this.setHeader) { + return; + } + + let header = this.opts.header; + + let thead = El('div', { class: 'thead' + (header.cls ? ' ' + header.cls : '') }); + + let n = 1; + + for (; n < this.data.items[0].length; n++) { + let cell = header.cell ? (header.cell[ n - 1 ] || {}) : {}; + thead.append( El('div', { class: 'th' + (cell.cls ? ' ' + cell.cls : '') }, (cell.text || '') ) ); + } + + this.table.prepend(thead); + } + + // Make table rows + + makeTableRows() { + + let that = this; + + if (this.mode == Table.UPDATE) { + + let row = this.getRow(this.id); + + if (row) { + + row.dxHtml(''); + + let data = this.data.items[0]; + + let n = 1; + + for (; n < data.length; n++) { + + let cell = that.opts.header.cell ? (that.opts.header.cell[ n - 1 ] || {}) : {}; + + let column = El('div', { class: 't-col' }); + + row.append( El('div', { class: 'td' + (cell.cls ? ' ' + cell.cls : ''), 'data-label': cell.text }, + column + )); + + if (data[n]) { + column.dxHtml(data[n]); + } + + if (n == 1 && that.opts.sortable) { + column.classList.add('flex', 'v-center'); + column.prepend( El('i', { class: 'handle' }) ); + } + } + } + } + else { + + this.data.items.forEach((data, i) => { + + let tbody = El('div', { + class: 'tbody' + (that.opts.body && that.opts.body.cls ? ' ' + that.opts.body.cls : ''), + 'data-id': data[0], + 'data-pos': i + }); + + let n = 1; + + for (; n < data.length; n++) { + + let cell = that.opts.header.cell ? (that.opts.header.cell[ n - 1 ] || {}) : {}; + + let column = El('div', { class: 't-col' }); + + tbody.append( El('div', { class: 'td' + (cell.cls ? ' ' + cell.cls : ''), 'data-label': cell.text }, + column + )); + + if (data[n]) { + column.dxHtml(data[n]); + } + + if (n == 1 && that.opts.sortable) { + column.classList.add('flex', 'v-center'); + column.prepend( El('i', { class: 'handle' }) ); + } + + if (that.mode == Table.PREPEND) { + that.table.prepend(tbody); + } + else { + that.table.append(tbody); + } + } + }); + } + } + + // Set info rows + + updateInfoRows() { + this.setInfo(this.opts.label.replace(/%rows/g, this.rows)); + } + + // XHR Request + + makeRequest() { + + let that = this; + + let vars = {}; + + if (this.mode == Table.UPDATE) { + vars.id = this.id; + } + else { + vars = this.vars; + vars.set('id', 0); + } + + this.reqOpts.vars = vars; + + this.caller(this.reqOpts).then(jr => { + + if (jr.code != Rc.DONE) { + that.setInfo('No Data'); + return; + } + + that.setTableData(jr.data); + + }).catch(error => console.error(error)); + } + + // Set ui sortable + + setSortable() { + + let that = this, + isHandle = false, + toDrag; + + let isBefore = function(a, b) { + if (a.parentNode == b.parentNode) { + for (let cur = a; cur; cur = cur.previousSibling) { + if (cur === b) { + return true; + } + } + } + + return false; + } + + this.table.dxOn('mousedown mouseup', 'i.handle', function(e) { + + let parent = this.dxParents('.tbody'); + + if (e.type == 'mousedown') { + isHandle = true; + parent.setAttribute('draggable', true); + } + else { + isHandle = false; + parent.removeAttribute('draggable'); + } + + }); + + this.table.dxOn('dragenter', '.tbody', function(e) { + + if (!isHandle) { + return; + } + + let target = this.matches('.tbody') ? this : this.dxParents('.tbody'); + + if (isBefore( toDrag, target )) { + target.before(toDrag); + } + else { + + if (target.nextElementSibling === null) { + $('.sp-table').append( toDrag ); + } + else { + target.nextElementSibling.before(toDrag); + } + } + + }); + + this.table.dxOn('dragstart', '.tbody', function(e) { + + if (!isHandle) { + return; + } + + toDrag = this; + + e.dataTransfer.setData('text/plain', null); + e.dataTransfer.effectAllowed = 'move'; + + toDrag.style.opacity = 0.4; + }); + + this.table.dxOn('dragend', '.tbody', function(e) { + + if (!isHandle) { + return; + } + + isHandle = false; + + let onSort = false; + + toDrag.style.opacity = ''; + toDrag.removeAttribute('draggable'); + + let data = {}; + + that.table.dxFind('.tbody', (el, i) => { + + if (el.dataset.pos != i && !onSort) { + onSort = true; + } + + if (el.dataset.pos != i) { + + data[ el.dataset.id ] = i; + + el.dataset.pos = i; + } + }); + + if (onSort) { + Ut.trigger(that.opts.onSort, that, data); + onSort = false; + } + + }); + } + + // Update pagination area + + updatePagination() { + + this.paginationEl.dxHtml(''); + + if (this.data.pagination && this.data.pagination.count > 1) { + + let pg_btns = [], + pg_btn, + j = 0; + + if (this.data.pagination.start > 1) { + + pg_btns[j] = [ + 1, '<<', false + ]; + + j = 1; + } + + for (let i = this.data.pagination.start; i <= this.data.pagination.end; i++) { + + pg_btns[j] = [ + i, i, (this.vars.get('pg') == i) + ] + + j++; + } + + if (this.data.pagination.start > 1 && this.data.pagination.page != this.data.pagination.end) { + pg_btns[j] = [ + this.data.pagination.count, '>>', false + ]; + } + + pg_btns.forEach((dat) => { + + pg_btn = El('a', { + 'data-id': dat[0], + class: (dat[2] ? 'disabled' : '')}, dat[1] + ); + + this.paginationEl.append(pg_btn); + }); + } + else if (this.data.before || this.data.after) { // update UI + + if (this.data.before) { + this.paginationEl.append( El('a', { 'data-id': this.data.before, class: 'btn-before' }, '<') ); + } + + if (this.data.after) { + this.paginationEl.append( El('a', { 'data-id': this.data.after, class: 'btn-after' }, '>') ); + } + } + } + + // Get All Rows + + getRows(id) { + return this.table.dxFind('.tbody'); + } + + // Get Row By ID + + getRow(id) { + return this.table.dxFind('.tbody[data-id="'+ id +'"]'); + } + + // Update Row Data + + updateRow(id, data) { + + this.setMode(Table.UPDATE); + this.id = id; + + if (data) { + this.setTableData(data); + } + else { + this.makeRequest(); + } + } + + // Update All Rows + + update(data) { + + this.setMode(Table.CLEAN); + + if (data) { + this.setTableData(data); + } + else { + this.makeRequest(); + } + } + + // Delete Row + + deleteRow(id) { + + let row = this.getRow(id); + + if (row) { + + this.rows--; + this.updateInfoRows(); + + row.remove(); + + if (!this.rows) { + + let pg = this.vars.get('pg'); + + if (pg > 1) { + this.vars.set('pg', --pg); + } + + this.update(); + } + + Ut.trigger(this.opts.onUpdate, this); + } + } + + // Add Row At The End of the table + + appendRow(data) { + + this.setMode(this.table.children.length ? Table.APPEND : Table.CLEAN); + + if (data) { + this.setTableData(data); + } + else { + this.makeRequest(); + } + } + + // Add Row At Begining of the table + + prependRow(data) { + + this.setMode(this.table.children.length ? Table.APPEND : Table.CLEAN); + + if (data) { + this.setTableData(data); + } + else { + this.makeRequest(); + } + } + + // Set Info Table + + setInfo(text) { + this.info.textContent = text; + }; + + // Set Toolbar Header + + setHeaderToolbar(content) { + this.toolbarHeader.dxHtml(content); + } + + // Set Toolbar Footer + + setFooterToolbar(content) { + this.toolbarFooter.dxHtml(content); + } +} + +//Ut.extendNodeUx(Table); \ No newline at end of file diff --git a/src/widgets/table/index.js b/src/widgets/table/index.js new file mode 100644 index 0000000..b7e01c0 --- /dev/null +++ b/src/widgets/table/index.js @@ -0,0 +1 @@ +export { Table } from './Table'; \ No newline at end of file diff --git a/src/widgets/table/table.css b/src/widgets/table/table.css new file mode 100644 index 0000000..b2a777d --- /dev/null +++ b/src/widgets/table/table.css @@ -0,0 +1,276 @@ +.sp-table-ui { + width: 100%; +} + +.sp-table { + border-collapse: collapse; + color: #212529; + text-align: left; + width: 100%; +} + +.sp-table.layout-auto { + table-layout: auto; +} + +.sp-table.layout-fixed { + table-layout: fixed; +} + +.sp-table .thead { + display: none; +} + +.sp-table .td, +.sp-table .th { + padding: 12px; +} + +.sp-table .td { + border-top: 1px solid #eee; +} + +.sp-table .td:last-child { + border-bottom: 3px solid #eee; +} + +.sp-table .td:before { + display: block; + content: attr(data-label); + font-weight: bold; + margin-bottom: 16px; +} + +.sp-table-info { + width: 100%; + color: #c0bdd0; + padding-bottom: 20px; +} + +.sp-table.no-header{ + border-top: 1px solid #e4e9f0; +} + +.sp-table a.cmd-link { + font-size: 16px; + cursor: pointer; + color: #666; + transition: all 0.2s; +} + +.sp-table a.cmd-link:hover { + opacity: 0.75; +} + +.sp-table a.cmd-link i { + width: 18px; + height: 18px; + text-align: center; +} + +.sp-table .actions .t-col { + display: flex; + justify-content: space-between; +} + +.sp-table a.cmd { + cursor: pointer; + display: inline-flex; + justify-content: center; + align-items: center; + padding: 4px; +} + +.sp-table a.cmd:hover { + opacity: 0.85; +} + +.sp-table a.cmd.disabled { + opacity: 0.4; + pointer-events: none; +} + +.sp-table a.cmd i { + font-size: 24px; + color: #74708d; +} + +.sp-table a.cmd span { + margin: 0 10px; +} + +.sp-table a.cmd-link i { + width: 18px; + height: 18px; + text-align: center; +} + +.sp-table .tbody i.handle { + width: 16px; + height: 16px; + cursor: pointer; + display: block; + margin-right: 16px; + background-size: 16px 16px; + background-image: url("data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMS4xLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDMyIDMyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAzMiAzMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIxNnB4IiBoZWlnaHQ9IjE2cHgiPgo8Zz4KCTxnIGlkPSJtb3ZlIj4KCQk8Zz4KCQkJPHBvbHlnb24gcG9pbnRzPSIxOCwyMCAxOCwyNiAyMiwyNiAxNiwzMiAxMCwyNiAxNCwyNiAxNCwyMCAgICAiIGZpbGw9IiM2NjY2NjYiLz4KCQkJPHBvbHlnb24gcG9pbnRzPSIxNCwxMiAxNCw2IDEwLDYgMTYsMCAyMiw2IDE4LDYgMTgsMTIgICAgIiBmaWxsPSIjNjY2NjY2Ii8+CgkJCTxwb2x5Z29uIHBvaW50cz0iMTIsMTggNiwxOCA2LDIyIDAsMTYgNiwxMCA2LDE0IDEyLDE0ICAgICIgZmlsbD0iIzY2NjY2NiIvPgoJCQk8cG9seWdvbiBwb2ludHM9IjIwLDE0IDI2LDE0IDI2LDEwIDMyLDE2IDI2LDIyIDI2LDE4IDIwLDE4ICAgICIgZmlsbD0iIzY2NjY2NiIvPgoJCTwvZz4KCTwvZz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K"); +} + +.sp-table p { + line-height: 2.4; +} + +.sp-table p:last-child { + margin-bottom: 0; +} + +.sp-table p i { + margin-right: 10px; +} + +.sp-table a.option{ + color: #da5014; + cursor:pointer; +} + +.sp-table img{ + max-width:120px; + max-height:120px; +} + +/* ------------------ + Pagination +/* -----------------*/ + +.sp-pagination { + display: flex; + flex-wrap: wrap; +} + +.sp-pagination a { + -webkit-user-select: none; + user-select: none; + width: 25px; + height: 25px; + border-radius: 2px; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + cursor:pointer; + margin-left: 5px; + margin-bottom: 5px; + transition: all 0.2s; +} + +.sp-pagination a:hover, +.sp-pagination a:focus { + color: var(--theme-widget-color); +} + +.sp-pagination a.disabled { + background-color: var(--theme-widget-color); + color: #fff; + pointer-events: none; +} + +/* ------------------ + Table Toolbar +/* -----------------*/ + +.sp-toolbar { + padding: 16px 0; + width: 100%; + display: flex; + flex-wrap: wrap; +} + +.sp-toolbar .sp-button { + margin-right: 7px; +} + + +@media only screen and (min-width : 768px) { + + .sp-table .td { + display: flex; + align-items: center; + } + + .sp-table .td:before { + width: 20%; + margin-bottom: 0; + } + + .sp-table .t-col { + width: 80%; + white-space: nowrap; + } +} + +@media only screen and (min-width : 1200px) { + + .sp-table { + display: table; + } + + .sp-table .thead { + display: table-row; + border-top: 1px solid #dee2e6; + border-bottom: 1px solid #dee2e6; + border-bottom: 1px solid #e4e9f0; + background: #f2f4f8; + } + + .sp-table .th { + display: table-cell; + font-weight: bold; + } + + .sp-table .td, + .sp-table .th { + vertical-align: middle; + } + + .sp-table .tbody { + display: table-row; + border-bottom: 1px solid #dee2e6; + } + + .sp-table .td { + border-top: 0; + display: table-cell; + } + + .sp-table .td:last-child { + border-bottom: 0; + } + + .sp-table .tbody:hover { + background: #f2f4f8; + } + + .sp-table .td:before { + display: none; + } + + .sp-table .t-col { + width: auto; + } + + .sp-table p { + width: auto; + } + + .sp-table .th.actions { + text-align: right; + } + + .sp-table .actions .t-col { + justify-content: end; + } + + .sp-table .actions a.cmd { + padding: 4px; + margin: 0 6px; + } +} diff --git a/src/widgets/two-datepicker/TwoDatepicker.js b/src/widgets/two-datepicker/TwoDatepicker.js new file mode 100644 index 0000000..4068b55 --- /dev/null +++ b/src/widgets/two-datepicker/TwoDatepicker.js @@ -0,0 +1,298 @@ +import './two-datepicker.css'; + +import { Ut } from "../../utils/Ut"; +import { El } from "../../utils/dom"; +import { Cancelable } from '../cancelable'; +import { DatePicker } from '../date-picker'; +import { Stack } from '../../core/Stack'; + +export class TwoDatePicker { + + /** + * TwoDatePicker Constructor + */ + constructor(target, opts, events) { + + this.target = target; + + if (this.target.dxData('two_datepicker')) { + return; + } + + this.target.dxData('two_datepicker', this); + + this.opts = { ... { + style : 'normal', + disabled : false, + fullMonth : true, + navDisabled : false, + anim : true, + locales : 'ro-RO', + cancelable : true, + hiddenMode : false + }, ... opts }; + + this.events = events || {}; + + this.isDisabled = this.opts.disabled; + + if (this.target.tagName.toLowerCase() == 'input') { + + this.isInput = true; + + if (this.target.type == 'text') { + this.isToggled = true; + } + } + + this.create(); + + Stack.add(this); + + this.setEvents(); + + this.restoreValue(this.target.dxVal()); + } + + /** + * TwoDatePicker, Create UI + */ + create() { + + const that = this; + + this.ui = El('div', { class: 'sp-two-datepicker' }); + + let firstCol = El('div', { class: 'ui-col' }); + let secondCol = El('div', { class: 'ui-col' }); + + this.ui.append(firstCol); + this.ui.append(secondCol); + + let dpOpts = { + style : this.opts.style, + disabled : this.opts.disabled, + fullMonth : this.opts.fullMonth, + navDisabled : this.opts.navDisabled, + locales : this.opts.locales + }; + + this.datepicker = []; + + new DatePicker(firstCol, dpOpts, { + init: datepicker => that.datepicker[0] = datepicker, + onSelect: (_, date) => Ut.trigger(that.events.onChange, that, date, 0) + }); + + new DatePicker(secondCol, dpOpts, { + init: datepicker => that.datepicker[1] = datepicker, + onSelect: (_, date) => Ut.trigger(that.events.onChange, that, date, 1) + }); + + if (this.isInput) { + + if (this.isToggled) { + + this.ui.classList.add('toggled', 'hidden'); + + if (this.opts.cancelable && !this.target.dxData('cancelable') ) { + new Cancelable(this.target); + } + + document.body.append(this.ui); + } + else { + this.target.after(this.ui); + } + + this.target.setAttribute('autocomplete', 'off'); + } + else { + this.target.append(this.ui); + } + + Ut.trigger(this.events.init, this); + } + + /** + * TwoDatePicker, Show UI + */ + show() { + + this.ui.classList.remove('hidden'); + + if (this.opts.anim) { + this.ui.classList.add('visible'); + } + + this.setPosition(); + + this.isOpen = true; + + Ut.trigger(this.events.onShow, this); + } + + /** + * TwoDatePicker, Hide UI + */ + hide() { + + if (this.isOpen) { + + this.ui.classList.add('hidden'); + + if (this.opts.anim) { + this.ui.classList.remove('visible'); + } + + this.isOpen = false; + + Ut.trigger(this.events.onHide, this); + } + } + + /** + * TwoDatePicker, Set UI Posituion + */ + setPosition() { + + let left = Math.round(this.target.dxOffset().left); + + if ((this.ui.clientWidth + left) > window.innerWidth) { + left = 0; + } + + this.ui.dxCss({ + top : (Math.round(this.target.dxOffset().top) + this.target.clientHeight + 2) + 'px', + left : left + 'px', + }); + } + + /** + * TwoDatePicker, Clear Val + */ + clear() { + + if (this.isToggled) { + + if (this.opts.cancelable) { + this.target.dxData('cancelable').iconHandler.classList.add('hide'); + } + + this.hide(); + } + + if (this.isInput) { + this.target.dxVal(''); + } + + this.datepicker[0].clear(); + this.datepicker[1].clear(); + } + + /** + * TwoDatePicker, Restore Selected Val + */ + restoreValue(dates) { + + if (!dates || !dates.length) { + return; + } + + const [ fromDate, toDate ] = dates; + + if (fromDate.getTime() <= toDate.getTime()) { + this.datepicker[0].restoreValue(fromDate); + this.datepicker[1].restoreValue(toDate); + this.datepicker[1].setDateRange({ minDate: fromDate }); + } + } + + /** + * TwoDatePicker, Set Events + */ + setEvents() { + + let that = this; + + // Restore Selected - Handler + + if (this.isInput) { + this.target.dxOn('keydown.two_datepicker paste.two_datepicker', e => e.preventDefault()); + //this.target.dxOn('restore.two_datepicker', (_, t) => this.restoreValue(t.value)); + } + + if (this.isToggled) { + + // Click on UI - fixed Handler + + this.ui.dxOn('click', e => e.stopPropagation()); + + this.target.dxOn('click.two_datepicker', e => { + + e.stopPropagation(); + + if (!that.opts.hiddenMode) { + that.show(); + } + }); + + // Clear Icon Handler + + if (this.opts.cancelable) { + + this.target.dxOn('clear', function(e) { + + that.clear(); + + Ut.trigger(that.events.onChange, that, null, false); + + that.target.dxTrigger('changed'); + }); + } + + // Click outside - Handler + + document.body.dxOn('click.two_datepicker' + this.ekey, _ => this.hide()); + + let tm = null; + + // Resize window Handler + + window.dxOn('resize.two_datepicker' + this.ekey + ' orientationchange.two_datepicker' + this.ekey, _ => { + + if (tm) { + clearTimeout(tm); + tm = null; + } + + tm = setTimeout(_ => that.setPosition(), 100); + }); + } + } + + /** + * TwoDatePicker, Destroy Widget + */ + destroy() { + + this.datepicker[0].destroy(); + this.datepicker[1].destroy(); + + this.ui.remove(); + + if (this.isToggled) { + + this.target.dxOff('click.two_datepicker keydown.two_datepicker paste.two_datepicker restore.two_datepicker'); + + document.body.dxOff('click.two_datepicker' + this.ekey); + window.dxOff('resize.two_datepicker' + this.ekey + ' orientationchange.two_datepicker' + this.ekey); + } + + this.target.dxRemoveData('two_datepicker'); + + Stack.delete(this); + } +} + +//Ut.extendNodeUx(TwoDatePicker); \ No newline at end of file diff --git a/src/widgets/two-datepicker/index.js b/src/widgets/two-datepicker/index.js new file mode 100644 index 0000000..f3ae09e --- /dev/null +++ b/src/widgets/two-datepicker/index.js @@ -0,0 +1 @@ +export { TwoDatePicker } from './TwoDatepicker'; \ No newline at end of file diff --git a/src/widgets/two-datepicker/two-datepicker.css b/src/widgets/two-datepicker/two-datepicker.css new file mode 100644 index 0000000..b8fa5e5 --- /dev/null +++ b/src/widgets/two-datepicker/two-datepicker.css @@ -0,0 +1,27 @@ +/* Two DatePicker */ + +.sp-two-datepicker { + display: flex; + flex-wrap: nowrap; + border-radius: 4px; + overflow: hidden; +} + +.sp-two-datepicker .ui-col:last-child { + margin-left: 4px; +} + +.sp-two-datepicker.toggled { + position: absolute; + top: -9999px; + left: -9999px; +} + +.sp-two-datepicker.anim { + opacity: 0; + transition: opacity 0.3s; +} + +.sp-two-datepicker.anim.visible { + opacity: 1; +} \ No newline at end of file diff --git a/src/widgets/uploader/Uploader.js b/src/widgets/uploader/Uploader.js new file mode 100644 index 0000000..59db338 --- /dev/null +++ b/src/widgets/uploader/Uploader.js @@ -0,0 +1,710 @@ +import './uploader.css'; + +import { Ut } from '../../utils/Ut'; +import { El } from '../../utils/dom'; +import { Rt } from '../../core/Rt'; +import { Boot } from '../../core/Boot'; +import { Msg } from '../msg/'; + +export class Uploader { + + static FILE_SIZE_EXC_SINGLE = 1020; + static FILE_SIZE_EXC_MULTI = 1030; + static INVALID_EXT_SINGLE = 1040; + static INVALID_EXT_MULTI = 1050; + static IMG_LOW_RES_SINGLE = 1060; + static IMG_LOW_RES_MULTI = 1070; + static IMG_COUNT_EXC = 1080; + + /** + * Uploader, Constructor + */ + constructor(target, opts, events) { + + this.target = target; + + if (!target.name || target.dxData('uploader')) { + return; + } + + target.dxData('uploader', this); + + this.opts = { ... { + id: target.dataset.id, + preview: true, + routerUri: '/media', + saveCache: false, + }, ... opts }; + + this.events = events || {}; + + this.widget = Boot.intData?.widget?.uploader?.children; + + if (!this.widget || !this.setRouter()) { + return; + } + + if (this.router.multiple) { + this.target.multiple = true; + } + + this.errors = { + fileSizeExcSingle : this.widget.error.file_size_exceeded_single, + fileSizeExcMulti : this.widget.error.file_size_exceeded_multiple, + invalidExtSingle : this.widget.error.invalid_extension_single, + invalidExtMulti : this.widget.error.invalid_extension_multiple, + imgLowResSingle : this.widget.error.image_resolution_low_single, + imgLowResMulti : this.widget.error.image_resolution_low_multiple, + imgCountExc : this.widget.error.image_count_exceeded + }; + + this.targetFiles = []; + this.files = []; + this.fileList = []; + + this.setDir(); + + this.makeUi(); + this.setEvents(); + } + + /** + * Set Router + */ + setRouter() { + + let router = Boot.intData?.config?.media; + + if (!router) { + return false; + } + + this.router = router[ this.target.name ]; + + if (!this.router) { + return false; + } + + this.router.path = this.router.path || '/media'; + this.router.size_limit = this.router.size_limit || 20; + this.router.ext = this.router.ext || ''; + + let ext = []; + + if (this.router.ext) { + ext = this.router.ext.split('|'); + } + + if (this.router.img) { + ext.push('jpg', 'jpeg', 'png', 'webp'); + } + + this.router.ext = [ ... new Set(ext) ].join('|'); + + return true; + } + + /** + * Uploader, Make User Interface + */ + makeUi() { + + this.ui = El('div', { class: 'sp-file sp-gallery' }); + + this.box = El('div', { class: 'sp-input-file' }, + El('i', { class: 'fas fa-cloud-upload-alt' }), + El('span', { class: 'ic-label' }, this.widget.label.upload_file), + El('div', { class: 's-loader' }, + El('div', { class: 'l-bar' }), + El('div', { class: 'l-bar' }), + El('div', { class: 'l-bar' }), + El('div', { class: 'l-bar' }) + ) + ); + + this.ui.append(this.box); + + this.target.after(this.ui); + + this.box.append(this.target); + } + + /** + * Uploader, Set Events + */ + setEvents() { + + // Load file, Ev andler + this.target.dxOn('change.uploader', e => this.loadFile(e)); + + if (this.opts.preview) { + this.ui.dxOn('click', '.delete-file', e => this.deleteTargetFile(e)); + } + } + + /** + * Change File + */ + loadFile(e) { + + e.stopPropagation(); + e.preventDefault(); + + if (this.ui.classList.contains('loading')) { + return; + } + + if (!this.target.value) { + return; + } + + this.targetFiles = e.target.files; + + if (!this.targetFiles || !this.targetFiles.length) { + return; + } + + this.ui.classList.add('loading'); + + // Check count files limits + if (this.router.multiple && this.router.count_limit && (this.files.length + this.targetFiles.length) > this.router.count_limit) { + + new Alert(this.target, this.errors.imgCountExc.replace('%s', this.router.count_limit)); + + this.unload(); + + return; + } + + this.vars = new FormData(); + + this.vars.set('path', this.target.name); + + if (this.opts.id) { + this.vars.set('id', this.opts.id); + } + + this.fileList.forEach(filename => this.vars.append('filelist[]', filename)); + + let i = 0; + let error = false; + + for (; i < this.targetFiles.length; i++) { + + let value = this.targetFiles[i]; + + let token = value.name.split('.'), + ext = token[ token.length -1 ]; + + // Check for a valid extension + if (this.router.ext && !new RegExp('^' + this.router.ext + '$', 'i').test(ext) ) { + + error = this.targetFiles.length > 1 ? this.errors.invalidExtMulti : this.errors.invalidExtSingle; + + new Alert(this.target, error); + + this.unload(); + + return false; + } + + // Check file size + if ((value.size / 1024 / 1024) > this.router.size_limit) { + + error = (that.targetFiles.length > 1 ? this.errors.fileSizeExcMulti : this.errors.fileSizeExcSingle).replace('%s', this.router.size_limit); + + new Alert(this.target, error); + + this.unload(); + + return false; + } + + this.vars.append(i, value); + } + + if (error) { + + this.unload(); + + Ut.trigger( this.events.error, error); + this.target.dxTrigger('error', error, this); + + return; + } + + Ut.trigger(this.events.changed); + + this.uploadFileRequest(); + } + + /** + * Get Cache Data + */ + getCacheData() { + + let files = localStorage.getItem(Boot.section + '.uploader.' + this.target.name); + + if (!files) { + return false; + } + + return JSON.parse(files); + } + + /** + * Uploader, Set Cache Data + */ + setCacheData() { + localStorage.setItem(Boot.section + '.uploader.' + this.target.name, JSON.stringify(this.files)); + } + + /** + * Clear Cache Data + */ + clearCacheData() { + localStorage.removeItem(LOader.section + '.uploader.' + this.target.name); + } + + /** + * Uploader, Restore Cache + */ + restoreCache() { + + this.files = this.getCacheData(); + + if (!this.files) { + return; + } + + if (this.opts.preview) { + this.ui.dxFind('a', thumb => thumb.remove()); + this.updatePreviewList(this.files); + } + } + + /** + * Uploader, Remove or delete file + */ + deleteTargetFile(e) { + + if (!this.opts.preview) { + return; + } + + let that = this; + + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + + let thumb = e.target.dxParents('a'); + + thumb.classList.add('selected'); + + if (that.router.allow_delete) { + Msg.confirm(that.widget.message.delete_confirm, + () => that.deleteFileRequest(thumb.dataset.id, () => thumb.remove() ), + () => thumb.classList.remove('selected') + ); + } + else { + that.removeFile(thumb.dataset.id, () => thumb.remove() ); + } + } + + /** + * Uploader, Delete File Request + */ + deleteFileRequest(file, callback) { + + let that = this; + + let vars = { path: that.target.name, file: file }; + + if (that.opts.id) { + vars.id = that.opts.id; + } + + Rt.request.delete(that.opts.routerUri + '/' + this.target.name, vars, { + + done: rs => { + + if (rs == Rc.DONE) { + that.removeFile(file, callback); + that.fileList.splice(this.fileList.indexOf(file), 1); + } + } + }); + } + + /** + * Remove File From FilesList + */ + removeFile(file, callback) { + + this.files = this.files.filter(f => !f.startsWith(file)); + + if (this.opts.saveCache) { + this.setCacheData(); + } + + if (callback) { + callback(); + } + + this.target.dxTrigger('delete', file, this); + Ut.trigger(this.events.delete, file, this); + } + + /** + * Uploader, Upload File Request + */ + uploadFileRequest() { + + let that = this; + + Rt.request.post(that.opts.routerUri + '/' + this.target.name, this.vars, { + + done: (rs, data) => { + + switch (rs) { + + case Rc.DONE: + + if (!data.files) { + console.log('Invalid Response!'); + return; + } + + if (that.router.multiple) { + that.files.push(...data.files); + } + else { + that.files = data.files; + } + + if (that.opts.saveCache) { + that.setCacheData(); + } + + if (data.over_count) { + new Alert(that.target, that.errors.imgCountExc.replace('%s', that.router.count_limit)); + } + + if (that.opts.preview) { + that.updatePreviewList(data.files); + } + + Ut.trigger(that.events.done, data); + that.target.dxTrigger('done', data, that); + + break; + + case Uploader.FILE_SIZE_EXC_SINGLE: + new Alert(that.target, this.errors.fileSizeExcSingle.replace('%s', this.router.size_limit)); + break; + + case Uploader.FILE_SIZE_EXC_MULTI: + new Alert(that.target, this.errors.fileSizeExcMulti.replace('%s', this.router.size_limit)); + break; + + case Uploader.INVALID_EXT_SINGLE: + new Alert(that.target, this.errors.invalidExtSingle); + break; + + case Uploader.INVALID_EXT_MULTI: + new Alert(that.target, this.errors.invalidExtMulti); + break; + + case Uploader.IMG_LOW_RES_SINGLE: + new Alert(that.target, this.errors.imgLowResSingle.replace('%s', that.router.img.req_size)); + break; + + case Uploader.IMG_LOW_RES_MULTI: + new Alert(that.target, this.errors.imgLowResMulti.replace('%s', that.router.img.req_size)); + break; + } + + Ut.trigger(that.events.error, rs, data); + that.target.dxTrigger('error', rs, data); + + that.unload(); + }, + error: _ => that.unload() + }); + } + + /** + * Unload + */ + unload() { + this.target.value = ''; + this.ui.classList.remove('loading'); + } + + /** + * Uploader, Update pictures + */ + updatePreviewList(files) { + + let that = this; + + if (!files.length) { + return; + } + + let thumbs = this.ui.dxFind('a', true); + let thumb; + + if (this.target.multiple) { // Multiple Files + + Ut.each(files, function(src) { + + let _now = Date.now(); + + if (that.isImageFile(src)) { // Multiple Files > Image Extension + + thumb = El('a', { class: 'loading' }, + El('img', { src: that.dir + '/' + src + '?' + _now }), + El('span', { class: 'delete-file' }) + ); + + thumb.dataset.id = src; + + thumb.href = that.dir + '/' + src.replace('0_', '1_') + '?' + _now; + } + else { // Multiple Files > Another File Extension + + let fileToken = src.split('/'); + + thumb = El('a', {}, + El('span', { class: 'file-label' }, + El('span', { class: 'file-name' }, fileToken[1] || 'Uknown.' + that.getFileExt(fileToken[0]) ), + El('span', { class: 'file-icon' }), + ), + El('span', { class: 'delete-file' }) + ); + + thumb.dataset.id = fileToken[0]; + thumb.href = that.dir + '/' + fileToken[0] + '?' + _now; + thumb.target = '_blank'; + + if (fileToken.length == 2) { + thumb.download = fileToken[1]; + } + } + + that.box.before(thumb); + }); + + this.preloadImgs(); + + return; + } + + // Single File + + let ext = that.getFileExt(files[0]); + let _now = Date.now(); + let filename = files[0]; + + if (that.isImageFile(files[0])) { // Single File > Image Extension + + if (!thumbs.length) { + + let thumb = El('a', { class: 'loading' }, + El('img', { src: this.dir + '/' + filename + '?' + _now } ), + El('span', { class: 'delete-file' }) + ); + + thumb.dataset.id = filename; + thumb.href = this.dir + '/' + filename.replace('0_', '1_') + '?' + _now; + + this.ui.prepend(thumb); + } + else { + + let thumb = thumbs[0]; + + let img = thumb.dxFind('img'); + img.src = this.dir + '/' + filename + '?' + _now; + img.classList.remove('icon'); + + thumb.dataset.id = filename; + + thumb.removeAttribute('target'); + + thumb.href = this.dir + '/' + filename.replace('0_', '1_') + '?' + _now; + } + } + else { // Single File > Another File Extension + + let fileToken = filename.split('/'); + + if (!thumbs.length) { + + thumb = El('a', {}, + El('span', { class: 'file-label' }, + El('span', { class: 'file-name' }, fileToken[1] || 'Uknown.' + that.getFileExt(fileToken[0]) ), + El('span', { class: 'file-icon' }), + ), + El('span', { class: 'delete-file' }) + ); + + this.ui.prepend(thumb); + } + + thumb.href = this.dir + '/' + fileToken[0] + '?' + _now; + thumb.target = '_blank'; + thumb.dataset.id = fileToken[0]; + + thumb.dxFind('.file-ext').dxText(that.getFileExt(fileToken[0])); + + if (fileToken.length > 1) { + thumb.download = fileToken[1]; + } + } + + this.preloadImgs(); + } + + /** + * Preload Imgs + */ + preloadImgs() { + + let imgs = this.ui.dxFind('img', true); + let i = 0; + + imgs.forEach(target => target.dxOn('load', (_, img) => { + + let thumb = img.parentElement; + + thumb.classList.remove('loading'); + + if (this.opts.saveCache && imgs.length == ++i) { + this.setCacheData(); + } + }), this); + + imgs.forEach(target => target.dxOn('error', (_, img) => { + + let thumb = img.parentElement; + + let fileKey = this.files.indexOf(thumb.dataset.id); + + if (fileKey != -1) { + this.files.splice(fileKey, 1); + } + + let fileListKey = this.fileList.indexOf(thumb.dataset.id); + + if (fileListKey != -1) { + this.fileList.splice(fileListKey, 1); + } + + thumb.remove(); + + if (this.opts.saveCache && imgs.length == ++i) { + this.setCacheData(); + } + }), this); + + newLightbox(this.ui); + } + + /** + * Uploader , Check for imgs ext + */ + isImageFile(filename) { + let ext = this.getFileExt(filename); + return (ext == 'jpg' || ext == 'jpeg' || ext == 'png' || ext == 'webp') + } + + /** + * Uploader, Get files + */ + getFiles() { + return this.files; + } + + /** + * Uploader, Update Files + */ + updateFiles(files) { + + this.removeAllFiles(); + + this.files = files; + this.fileList = files.map(filename => filename.split('/')[0]); + + if (this.opts.saveCache) { + this.setCacheData(); + } + + if (this.opts.preview) { + this.updatePreviewList(files); + } + } + + /** + * Uploader, Set Directory + */ + setDir() { + + this.dir = this.router.path + '/' + this.target.name; + + if (this.opts.id) { + this.dir += '/' + this.opts.id; + } + } + + /** + * Uploader, Get picture directory + */ + getDir() { + return this.dir; + } + + /** + * Uploader, Get file extension + */ + getFileExt(file) { + let token = file.split('?')[0].split('.', 2); + return token.length == 2 ? token[1] : ''; + } + + /** + * Uploader, Remove All Files + */ + removeAllFiles() { + + this.files = []; + + if (this.opts.saveCache) { + this.clearCacheData(); + } + + if (this.opts.preview) { + this.ui.dxFind('a', thumb => thumb.remove()); + } + } + + /** + * Uploader, Destroy + */ + destroy() { + + this.ui.after(this.target); + this.ui.remove(); + + this.target.dxOff('change.uploader'); + + this.target.dxRemoveData('uploader'); + + Ut.trigger(this.events.destroy, this) + } +} + +//Ut.extendNodeUx(Uploader); \ No newline at end of file diff --git a/src/widgets/uploader/index.js b/src/widgets/uploader/index.js new file mode 100644 index 0000000..2ab76ee --- /dev/null +++ b/src/widgets/uploader/index.js @@ -0,0 +1 @@ +export { Upload } from './Uploader'; \ No newline at end of file diff --git a/src/widgets/uploader/uploader.css b/src/widgets/uploader/uploader.css new file mode 100644 index 0000000..71e615f --- /dev/null +++ b/src/widgets/uploader/uploader.css @@ -0,0 +1,116 @@ +/* File Uploader UI */ + +.sp-file { + overflow-x: auto; + overflow-y: hidden; +} + +.sp-file .sp-input-file { + width: 85px; + height: 85px; + border: 1px dashed #ccc; + cursor: pointer; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + position: relative; + margin: 1px; +} + +.sp-file .sp-input-file i { + font-size: 26px; + color: var(--gray); + display: block; +} + +.sp-file .sp-input-file .ic-label { + font-size: 12px; + margin-top: 8px; +} + +.sp-file input[type="file"] { + border: 0; + outline: 0; + width: 100%; + height: 100%; + border-radius: inherit; + -webkit-appearance: none; + appearance: none; + background: rgba(255,255,255,0); + opacity: 0; + background-image:none; + position: absolute; + top: 0; + left: 0; + cursor: pointer; +} + +.sp-file .sp-chip { + cursor: pointer; + -webkit-user-select: none; + user-select: none; +} + +.sp-file.loading{ + cursor: default; +} + +.sp-file .s-loader { + display: none; +} + +.sp-file.loading .s-loader { + display: flex; + justify-content: center; + align-items: center; + z-index: 1; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #fff; +} + +.sp-file.loading .s-loader .l-bar { + width: 4px; + height: 18px; + border-radius: 4px; + animation: loading 1s ease-in-out infinite; + margin-left: 2px; + margin-right: 2px; +} + +.sp-file.loading .s-loader .l-bar:nth-child(1) { + background-color: #666; + animation-delay: 0; +} + +.sp-file.loading .s-loader .l-bar:nth-child(2) { + background-color: #666; + animation-delay: 0.09s; +} + +.sp-file.loading .s-loader .l-bar:nth-child(3) { + background-color: #666; + animation-delay: .18s; +} + +.sp-file.loading .s-loader .l-bar:nth-child(4) { + background-color: #666; + animation-delay: .27s; +} + +@keyframes loading { + + 0% { + transform: scale(1); + } + 20% { + transform: scale(1, 2.2); + } + 40% { + transform: scale(1); + } +} \ No newline at end of file