first commit
This commit is contained in:
+770
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
};
|
||||||
+1191
File diff suppressed because it is too large
Load Diff
+207
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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('');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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('<html lang="'+ opts.lang +'"><head><title>Print</title>');
|
||||||
|
|
||||||
|
if (opts.style) {
|
||||||
|
doc.write('<link rel="stylesheet" href="'+ opts.style +'" type="text/css" />');
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.write('</head><body>');
|
||||||
|
doc.write(this.cloneNode(true).innerHTML);
|
||||||
|
doc.write('</body></html>');
|
||||||
|
|
||||||
|
doc.close();
|
||||||
|
|
||||||
|
setTimeout(() => win.print(), 500);
|
||||||
|
|
||||||
|
win.onafterprint = function() {
|
||||||
|
win.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ut.extendNodeUx(Print);
|
||||||
@@ -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;
|
||||||
+148
@@ -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, '>').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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { AutoExpand } from './AutoExpand';
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Autocomplete } from './Autocomplete';
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Cancelable } from './Cancelable';
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Counter } from './Counter';
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { DatePicker } from './DatePicker';
|
||||||
@@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Dialog } from './Dialog';
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Dropdown } from './Dropdown';
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Gallery } from './Gallery';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Hours } from './Hours';
|
||||||
@@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { IntervalPicker } from './IntervalPicker';
|
||||||
@@ -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 = $('<div class="sp-lightbox-overlay" />');
|
||||||
|
this.ui = $('<div class="sp-lightbox-container" />');
|
||||||
|
this.loader = $('<div class="loader" />');
|
||||||
|
|
||||||
|
this.arrowLeft = $('<a class="nav-arrow arrow-left"></a>');
|
||||||
|
this.arrowRight = $('<a class="nav-arrow arrow-right"></a>');
|
||||||
|
|
||||||
|
let label = $('<div class="nav-label"> \
|
||||||
|
<div> \
|
||||||
|
<div class="label-title"></div> \
|
||||||
|
<div class="label-number"></div> \
|
||||||
|
</div> \
|
||||||
|
<a href="#" class="close-popup"> \
|
||||||
|
<span class="container-close"> \
|
||||||
|
<span class="border border-1"></span> \
|
||||||
|
<span class="border border-2"></span> \
|
||||||
|
</span> \
|
||||||
|
<span class="close-btn">ÎNCHIDE</span> \
|
||||||
|
</a> \
|
||||||
|
</div>');
|
||||||
|
|
||||||
|
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 = $('<img src="'+ src +'" />');
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Lightbox } from './Lightbox';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Msg } from './Msg';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { NumberPicker } from './NumberPicker';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Overlay } from './Overlay';
|
||||||
@@ -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; }
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { PinBox } from './PinBox';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Popover } from './Popover';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Popup } from './Popup';
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
@@ -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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Scheduler } from './Scheduler';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { SelectBox } from './SelectBox';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Table } from './Table';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { TwoDatePicker } from './TwoDatepicker';
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Upload } from './Uploader';
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user