first commit

This commit is contained in:
2025-11-25 01:21:34 +02:00
commit 4af0db8263
84 changed files with 18906 additions and 0 deletions
+770
View File
@@ -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();
}
}
}
+93
View File
@@ -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);
}
}
}
+30
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+207
View File
@@ -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");
}
}
}
}
+41
View File
@@ -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;
}
}
+345
View File
@@ -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
+991
View File
@@ -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;
}
}
+37
View File
@@ -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);
+60
View File
@@ -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);
+127
View File
@@ -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);
+71
View File
@@ -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('');
}
}
+35
View File
@@ -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);
+199
View File
@@ -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
View File
@@ -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(/&amp;|&lt;|&gt;|&quot;|&#39;/).test(str)))
? str.replace(/&amp;/g, "&").replace(/&lt/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, "\"").replace(/&#39;/g, "'")
: str;
/**
* Encode HTML Entities
*/
htmlEntities = str => String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
/**
* 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);
}
}
}
};
+765
View File
@@ -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);
}
+225
View File
@@ -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);
+45
View File
@@ -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);
+382
View File
@@ -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 );
}
}
+31
View File
@@ -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;
}
+58
View File
@@ -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');
}
}
+1
View File
@@ -0,0 +1 @@
export { AutoExpand } from './AutoExpand';
+325
View File
@@ -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);
+37
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
export { Autocomplete } from './Autocomplete';
+71
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
export { Cancelable } from './Cancelable';
+82
View File
@@ -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();
}
}
+21
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
export { Counter } from './Counter';
File diff suppressed because it is too large Load Diff
+226
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
export { DatePicker } from './DatePicker';
+238
View File
@@ -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
});
}
}
+87
View File
@@ -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; }
}
+1
View File
@@ -0,0 +1 @@
export { Dialog } from './Dialog';
+181
View File
@@ -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);
+61
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
export { Dropdown } from './Dropdown';
+71
View File
@@ -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);
}
}
+120
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
export { Gallery } from './Gallery';
+190
View File
@@ -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;
}
}
+27
View File
@@ -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;
}
+1
View File
@@ -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');
}
}
+1
View File
@@ -0,0 +1 @@
export { IntervalPicker } from './IntervalPicker';
+326
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
export { Lightbox } from './Lightbox';
+215
View File
@@ -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;
}
+159
View File
@@ -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 });
}
}
+1
View File
@@ -0,0 +1 @@
export { Msg } from './Msg';
+139
View File
@@ -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;
}
+130
View File
@@ -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')
}
}
+1
View File
@@ -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;
}
+76
View File
@@ -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
});
}
}
+1
View File
@@ -0,0 +1 @@
export { Overlay } from './Overlay';
+53
View File
@@ -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; }
}
+133
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
export { PinBox } from './PinBox';
+29
View File
@@ -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;
}
}
+182
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
export { Popover } from './Popover';
+65
View File
@@ -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;
}
+248
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
export { Popup } from './Popup';
+41
View File
@@ -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");
}
+511
View File
@@ -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 });
}
}
+1
View File
@@ -0,0 +1 @@
export { Scheduler } from './Scheduler';
+97
View File
@@ -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;
}
+94
View File
@@ -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);
}
}
+1
View File
@@ -0,0 +1 @@
export { SelectBox } from './SelectBox';
+62
View File
@@ -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;
}
+652
View File
@@ -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' }, '&lt') );
}
if (this.data.after) {
this.paginationEl.append( El('a', { 'data-id': this.data.after, class: 'btn-after' }, '&gt;') );
}
}
}
// 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);
+1
View File
@@ -0,0 +1 @@
export { Table } from './Table';
+276
View File
@@ -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;
}
}
+298
View File
@@ -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);
+1
View File
@@ -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;
}
+710
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
export { Upload } from './Uploader';
+116
View File
@@ -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);
}
}