This commit is contained in:
2024-01-17 17:31:41 +08:00
parent 84190ed9f1
commit fb9e920c15
35 changed files with 3003 additions and 1304 deletions

View File

@@ -5,26 +5,23 @@ import { createBox, appendMedia } from "./lib";
let r = lang;
export default class CustomerRecordComment {
#container;
#option;
#enter;
#message;
_var = {};
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
@@ -32,28 +29,28 @@ export default class CustomerRecordComment {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
}
/**
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag === true;
this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : '';
this._var.enter.disabled = flag === true;
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : '';
}
create() {
const readonly = this.#option.readonly;
const readonly = this._var.option.readonly;
const container = createBox(
createElement('div', null,
createElement('div', div => {
@@ -65,7 +62,7 @@ export default class CustomerRecordComment {
createElement('button', button => {
button.className = 'roundbtn button-close';
button.style.backgroundColor = 'transparent';
if (this.#option.hasClose !== true) {
if (this._var.option.hasClose !== true) {
button.style.display = 'none';
return;
}
@@ -73,8 +70,8 @@ export default class CustomerRecordComment {
fill: '#000'
}));
button.addEventListener('click', () => {
if (typeof this.#option.onClose === 'function') {
this.#option.onClose();
if (typeof this._var.option.onClose === 'function') {
this._var.option.onClose();
}
})
})
@@ -83,16 +80,16 @@ export default class CustomerRecordComment {
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
enter.maxLength = this.#option.maxLength ??= 3000;
enter.maxLength = this._var.option.maxLength ??= 3000;
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
if (readonly === true) {
enter.disabled = true;
}
this.#enter = enter;
this._var.enter = enter;
container.appendChild(
createElement('div', 'message-bar',
enter,
@@ -108,8 +105,8 @@ export default class CustomerRecordComment {
// setTooltip(button, r('P_M3_SENDMESSAGE', 'Send Message'));
setTooltip(button, r('P_CU_POSTNOTE', 'Post Note'));
button.addEventListener('click', () => {
if (typeof this.#option.onAddComment === 'function') {
this.#option.onAddComment(this.text);
if (typeof this._var.option.onAddComment === 'function') {
this._var.option.onAddComment(this.text);
}
})
})
@@ -118,9 +115,9 @@ export default class CustomerRecordComment {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
load(data) {
@@ -129,7 +126,7 @@ export default class CustomerRecordComment {
for (let comment of data) {
const div = createElement('div', 'item-div');
// if (sendto !== '') {
// sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
// sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + `\n${sendto}`;
// }
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
@@ -137,8 +134,8 @@ export default class CustomerRecordComment {
}));
const content = createElement('div', 'item-content');
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts));
if (comment.IsMMS && comment.MMSParts?.length > 0) {
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Comment)), mmsParts));
if (comment.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comment.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
@@ -155,8 +152,8 @@ export default class CustomerRecordComment {
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@@ -4,11 +4,10 @@ import { isEmail, nullOrEmpty, r as lang } from "../../utility";
let r = lang;
export class Contact {
#option;
#refs;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@@ -18,7 +17,7 @@ export class Contact {
async show(parent = document.body) {
const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3;
const c = this.#option.contact;
const c = this._var.option.contact;
const contactName = createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
@@ -54,7 +53,7 @@ export class Contact {
txt.style.height = '100px';
});
const buttons = [];
if (this.#option.company) {
if (this._var.option.company) {
buttons.push({
text: c == null ? r('P_WO_ADDCONTACTRECORD', 'Add Contact Record') : r('P_WO_EDITCONTACTRECORD', 'Edit Contact Record'),
// tabIndex: tabIndex + 7,
@@ -64,8 +63,8 @@ export class Contact {
return false;
}
item.SaveToCustomer = 1;
if (typeof this.#option.onSave === 'function') {
return this.#option.onSave.call(this, item, 'customerrecord');
if (typeof this._var.option.onSave === 'function') {
return this._var.option.onSave.call(this, item, 'customerrecord');
}
}
});
@@ -81,8 +80,8 @@ export class Contact {
}
//item.Id = -1;
item.SaveToCustomer = 0;
if (typeof this.#option.onSave === 'function') {
return this.#option.onSave.call(this, item, 'workorder');
if (typeof this._var.option.onSave === 'function') {
return this._var.option.onSave.call(this, item, 'workorder');
}
}
},
@@ -92,7 +91,7 @@ export class Contact {
}
);
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title: c == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact'),
content: createElement('div', wrapper => {
wrapper.className = 'setting-wrapper';
@@ -135,7 +134,7 @@ export class Contact {
} else {
preferences.select('0');
}
this.#refs = {
this._var.refs = {
contactName,
preferences,
contactEmail,
@@ -149,35 +148,35 @@ export class Contact {
}
prepare() {
const name = this.#refs.contactName.value;
const pref = this.#refs.preferences.selected.value;
const email = this.#refs.contactEmail.value;
const phone = this.#refs.contactMobile.value;
const opt = this.#refs.checkOpt.querySelector('input').checked;
const notes = this.#refs.contactNotes.value;
const title = this.#option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact');
const name = this._var.refs.contactName.value;
const pref = this._var.refs.preferences.selected.value;
const email = this._var.refs.contactEmail.value;
const phone = this._var.refs.contactMobile.value;
const opt = this._var.refs.checkOpt.querySelector('input').checked;
const notes = this._var.refs.contactNotes.value;
const title = this._var.option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact');
if (nullOrEmpty(name)) {
showAlert(title, r('P_CR_CONTACTNAMECANNOTBEEMPTY', 'Contact Name cannot be empty.'), 'warn')
.then(() => this.#refs.contactName.focus());
.then(() => this._var.refs.contactName.focus());
return null;
}
if ((pref == 0 || pref == 2) && nullOrEmpty(phone)) {
showAlert(title, r('P_CR_MOBILECANNOTBEEMPTY', 'Mobile cannot be empty.'), 'warn')
.then(() => this.#refs.contactMobile.focus());
.then(() => this._var.refs.contactMobile.focus());
return null;
}
if (pref == 1 && nullOrEmpty(email)) {
showAlert(title, r('P_CU_EMAILCANNOTBEEMPTY', 'Email cannot be empty.'), 'warn')
.then(() => this.#refs.contactEmail.focus());
.then(() => this._var.refs.contactEmail.focus());
return null;
}
if (!nullOrEmpty(email) && !isEmail(email)) {
showAlert(title, r('P_CR_EMAILISNOTAVALIDEMAILADDRESS', 'The email address is invalid.'), 'warn')
.then(() => this.#refs.contactEmail.focus());
.then(() => this._var.refs.contactEmail.focus());
return null;
}
let contact = this.#option.contact;
let contact = this._var.option.contact;
if (contact == null) {
contact = {};
} else if (contact.OptOut !== opt) {
@@ -198,11 +197,10 @@ export class Contact {
}
export class CustomerRecordContact {
#option;
#grid;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@@ -214,7 +212,7 @@ export class CustomerRecordContact {
const gridContainer = createElement('div', 'selcontact-grid');
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title,
content: createElement('div', 'selcontact-wrapper',
gridContainer
@@ -224,8 +222,8 @@ export class CustomerRecordContact {
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
if (typeof this.#option.onOk === 'function') {
return this.#option.onOk.call(this, this.#grid.source.filter(f => f.selected));
if (typeof this._var.option.onOk === 'function') {
return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.selected));
}
}
},
@@ -250,16 +248,16 @@ export class CustomerRecordContact {
{ key: 'Notes', caption: r("P_CR_NOTES", "Notes"), width: 120 }
];
grid.init();
grid.source = this.#option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this.#grid = grid;
grid.source = this._var.option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid = grid;
return result;
}
set source(contacts) {
this.#option.contacts = contacts;
const grid = this.#grid;
this._var.option.contacts = contacts;
const grid = this._var.grid;
if (grid != null) {
this.#grid.source = contacts;
this._var.grid.source = contacts;
}
}
}

View File

@@ -1,6 +1,6 @@
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup } from "../../ui";
import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../../utility";
import { createBox, appendMedia, fileSupported, insertFile } from "./lib";
import { createBox, appendMedia, fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib";
import { Contact, CustomerRecordContact } from "./contact";
import Follower from "./follower";
@@ -31,31 +31,22 @@ class NoteCol extends GridColumn {
let r = lang;
export default class CustomerCommunication {
#container;
#option;
#contacts;
#followers;
#buttonFollower;
#enter;
#fileControl;
#file;
#message;
#data = {};
#gridContact;
#gridWo;
_var = {
data: {}
};
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get #autoUpdates() { return this.#container.querySelector('.check-auto-update>input') }
get autoUpdatesEnabled() { return this.#autoUpdates?.disabled !== true }
get _autoUpdates() { return this._var.container.querySelector('.check-auto-update>input') }
get autoUpdatesEnabled() { return this._autoUpdates?.disabled !== true }
set autoUpdatesEnabled(flag) {
const element = this.#autoUpdates;
const element = this._autoUpdates;
if (element == null) {
return;
}
@@ -67,9 +58,9 @@ export default class CustomerCommunication {
element.parentElement?.classList?.remove('disabled');
}
}
get autoUpdates() { return this.#autoUpdates?.checked }
get autoUpdates() { return this._autoUpdates?.checked }
set autoUpdates(flag) {
const element = this.#autoUpdates;
const element = this._autoUpdates;
if (element == null) {
return;
}
@@ -77,10 +68,10 @@ export default class CustomerCommunication {
element.dispatchEvent(new Event('change'));
}
get #statusLink() { return this.#container.querySelector('.check-status-link>input') }
get statusLinkEnabled() { return this.#statusLink?.disabled !== true }
get _statusLink() { return this._var.container.querySelector('.check-status-link>input') }
get statusLinkEnabled() { return this._statusLink?.disabled !== true }
set statusLinkEnabled(flag) {
const element = this.#statusLink;
const element = this._statusLink;
if (element == null) {
return;
}
@@ -92,9 +83,9 @@ export default class CustomerCommunication {
element.parentElement?.classList?.remove('disabled');
}
}
get statusLink() { return this.#statusLink?.checked }
get statusLink() { return this._statusLink?.checked }
set statusLink(flag) {
const element = this.#statusLink;
const element = this._statusLink;
if (element == null) {
return;
}
@@ -106,33 +97,33 @@ export default class CustomerCommunication {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.customer-name>.ui-input').disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this.#container.querySelector('.button-edit-contacts').disabled = flag;
this.#container.querySelector('.button-edit-followers').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.customer-name>.ui-input').disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
this._var.container.querySelector('.button-edit-contacts').disabled = flag;
this._var.container.querySelector('.button-edit-followers').disabled = flag;
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
get file() { return this._var.file || this._var.fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
this._var.fileControl?.remove();
const label = this._var.container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
this._var.fileControl = null;
this._var.file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
@@ -140,15 +131,15 @@ export default class CustomerCommunication {
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
this._var.fileControl = f;
this._var.file = f.files[0];
const link = this._var.container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
this._var.fileControl = null;
this._var.file = f;
}
if (label != null) {
label.style.display = '';
@@ -158,9 +149,9 @@ export default class CustomerCommunication {
}
}
get customerName() { return this.#container.querySelector('.customer-name>.ui-input')?.value }
get customerName() { return this._var.container.querySelector('.customer-name>.ui-input')?.value }
set customerName(name) {
const element = this.#container.querySelector('.customer-name>.ui-input');
const element = this._var.container.querySelector('.customer-name>.ui-input');
if (element == null) {
return;
}
@@ -168,7 +159,7 @@ export default class CustomerCommunication {
}
get contacts() {
return [...this.#contacts.children].filter(el => {
return [...this._var.contacts.children].filter(el => {
return el.querySelector('span').dataset.notsend == "false";
}).map(el => {
const span = el.querySelector('span');
@@ -176,11 +167,20 @@ export default class CustomerCommunication {
});
}
set contacts(contacts) {
this.#contacts.replaceChildren();
this.setData('contacts', contacts);
this._var.contacts.replaceChildren();
if (contacts?.length > 0) {
var cs = contacts.sort(function (a, b) {
if (a.Name == b.Name) return 0; return a.Name > b.Name ? 1 : -1;
if (a.Name == b.Name) {
return 0;
}
return a.Name > b.Name ? 1 : -1;
});
const messages = this._var.data.messages;
if (this._var.contactsUpdated !== true && messages?.length > 0) {
updateCustomerName(messages, contacts);
this._var.contactsUpdated = true;
}
for (let c of cs) {
//if (c.OptOut || c.OptOut_BC || c.selected === false) {
// continue;
@@ -195,19 +195,28 @@ export default class CustomerCommunication {
const to = pref === '1' ? email : mp;
let icon;
let method;
let tipstr;
if (c.OptOut || c.OptOut_BC || c.selected === false) {
icon = 'times';
method = r('P_CU_OPTEDOUT_COLON', 'Opted Out:');
tipstr = r('P_CU_OPTEDOUT_PROMPT', 'User has opted out of messages');
}
else {
switch (pref) {
case '0':
icon = 'comment-lines';
if (c.MobilePhoneStatus !== 0) {
icon = 'times';
if (c.MobilePhoneStatus === 412) {
// landline
tipstr = r('P_CU_LANDLINE', 'Landline');
}
} else {
icon = 'comment-lines';
}
method = r('P_CU_TEXTSTO_COLON', 'Texts to:');
break;
case '2':
icon = 'mobile';
method = r('P_CU_CALLSTO_COLON', 'Calls to:');
icon = 'phone';
tipstr = r('P_CU_NOMESSAGE', 'No Messages Sent');
break;
default:
icon = 'envelope';
@@ -225,14 +234,14 @@ export default class CustomerCommunication {
createIcon('fa-light', icon, { 'fill': (c.OptOut || c.OptOut_BC || c.selected === false) ? 'red' : '' }),
span
);
this.#contacts.appendChild(item);
let tip = `${method} ${to}`;
this._var.contacts.appendChild(item);
let tip = tipstr || `${method} ${to}`;
if (span.scrollWidth > span.offsetWidth) {
tip = r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}\n${tip}`;
}
setTooltip(span, tip);
}
this.#message.scrollTop = this.#message.scrollHeight
this._var.message.scrollTop = this._var.message.scrollHeight
}
}
@@ -240,11 +249,11 @@ export default class CustomerCommunication {
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
const link = this.#container.querySelector('.check-status-link');
const link = this._var.container.querySelector('.check-status-link');
if (flag === true) {
link.classList.add('disabled');
} else {
@@ -252,31 +261,31 @@ export default class CustomerCommunication {
}
link.querySelector('input').disabled = flag;
const display = flag === true ? 'none' : '';
this.#container.querySelector('.button-edit-contacts').style.display = display;
this.#container.querySelector('.button-edit-followers').style.display = display;
// this.#enter.disabled = flag === true;
this.#container.querySelector('.message-bar').style.display = display;
// this.#container.querySelector('.button-send-message').style.display = display;
this._var.container.querySelector('.button-edit-contacts').style.display = display;
this._var.container.querySelector('.button-edit-followers').style.display = display;
// this._var.enter.disabled = flag === true;
this._var.container.querySelector('.message-bar').style.display = display;
// this._var.container.querySelector('.button-send-message').style.display = display;
}
/**
* @param {boolean} flag
*/
set recordReadonly(flag) {
this.#option.recordReadonly = flag;
if (this.#container == null) {
this._var.option.recordReadonly = flag;
if (this._var.container == null) {
return;
}
this.#container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : '';
}
/**
* @param {String} name
*/
set companyName(name) {
this.#option.companyName = name;
const div = this.#container.querySelector('.title-company');
this._var.option.companyName = name;
const div = this._var.container.querySelector('.title-company');
if (nullOrEmpty(name)) {
div.style.display = 'none';
} else {
@@ -288,9 +297,9 @@ export default class CustomerCommunication {
* @param {String} code
*/
set companyCode(code) {
const option = this.#option;
const option = this._var.option;
option.companyCode = code;
const div = this.#container.querySelector('.title-company');
const div = this._var.container.querySelector('.title-company');
if (nullOrEmpty(option.companyName)) {
div.style.display = 'none';
} else {
@@ -302,18 +311,18 @@ export default class CustomerCommunication {
}
get followers() {
return [...this.#followers.children].map(el => {
return [...this._var.followers.children].map(el => {
const span = el.querySelector('span');
return { 'Email': span.dataset.email, 'MobilePhone': span.dataset.mp, 'Value': span.dataset.name };
});
}
set followers(followers) {
this.#data.followers = followers;
this.#followers.replaceChildren();
this._var.data.followers = followers;
this._var.followers.replaceChildren();
if (followers?.length > 0) {
this.#container.querySelector('.follower-bar').style.display = '';
setTooltip(this.#buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
this.#container.querySelector('.follower-bar>.bar-list').appendChild(this.#buttonFollower);
this._var.container.querySelector('.follower-bar').style.display = '';
setTooltip(this._var.buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
this._var.container.querySelector('.follower-bar>.bar-list').appendChild(this._var.buttonFollower);
for (let f of followers) {
if (f.OptOut) {
continue;
@@ -326,7 +335,7 @@ export default class CustomerCommunication {
tips.push(r('P_CU_EMAILSTO_COLON', 'Emails to:') + ` ${email}`);
}
if (f.SendText) {
tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:' + ` ${mpDisplay}`));
tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:') + ` ${mpDisplay}`);
}
let icon;
if (f.SendText && f.SendEmail) {
@@ -348,26 +357,26 @@ export default class CustomerCommunication {
createIcon('fa-light', icon),
span
);
this.#followers.appendChild(item);
this._var.followers.appendChild(item);
if (span.scrollWidth > span.offsetWidth) {
tips.splice(0, 0, r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}`);
}
setTooltip(span, tips.join('\n'));
}
} else {
this.#container.querySelector('.follower-bar').style.display = 'none';
setTooltip(this.#buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers'));
this.#container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this.#buttonFollower)
this._var.container.querySelector('.follower-bar').style.display = 'none';
setTooltip(this._var.buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers'));
this._var.container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this._var.buttonFollower)
}
this.#message.scrollTop = this.#message.scrollHeight
this._var.message.scrollTop = this._var.message.scrollHeight
}
setData(key, data) {
this.#data[key] = data;
this._var.data[key] = data;
}
create() {
const option = this.#option;
const option = this._var.option;
const readonly = option.readonly;
// functions
const checkAutoUpdate = createCheckbox({
@@ -428,9 +437,9 @@ export default class CustomerCommunication {
]
);
// contacts
this.#contacts = this.#createContacts(container, option);
this._var.contacts = this._createContacts(container, option);
// followers
this.#followers = this.#createFollowers(container, option);
this._var.followers = this._createFollowers(container, option);
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERMESSAGEHERE', 'Enter Message Here');
@@ -442,7 +451,7 @@ export default class CustomerCommunication {
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
enter.addEventListener('paste', e => {
if (option.customerNameVisible === true) {
@@ -466,7 +475,7 @@ export default class CustomerCommunication {
// this.file = insertFile(container, file, r);
// }
});
this.#enter = enter;
this._var.enter = enter;
container.appendChild(
createElement('div', div => {
div.className = 'message-bar';
@@ -498,52 +507,49 @@ export default class CustomerCommunication {
},
enter,
createElement('div', div => div.style.textAlign = 'right',
createElement('div', div => {
div.className = 'customer-name';
if (option.customerNameVisible !== true) {
div.style.display = 'none';
}
},
createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')),
createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
})
),
createElement('div', selector => {
selector.className = 'file-selector';
if (option.customerNameVisible === true) {
selector.style.display = 'none';
}
},
createElement('div', 'customer-left',
createElement('div', div => {
div.className = 'selector-link';
div.addEventListener('click', () => {
this.#fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this.#fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
div.className = 'customer-name';
if (option.customerNameVisible !== true) {
div.style.display = 'none';
}
},
createIcon('fa-regular', 'link')
createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')),
createElement('input', input => {
input.type = 'text';
input.className = 'ui-input';
})
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
createElement('div', 'file-selector',
createElement('div', div => {
div.className = 'selector-link';
div.addEventListener('click', () => {
this._var.fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this._var.fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
)
),
createElement('div', 'prompt-count'),
createElement('button', button => {
@@ -575,12 +581,12 @@ export default class CustomerCommunication {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
#createContacts(container, option) {
_createContacts(container, option) {
const readonly = option.readonly;
const recordReadonly = option.recordReadonly;
const contacts = createElement('div', 'bar-list-container');
@@ -616,6 +622,7 @@ export default class CustomerCommunication {
createElement('div', div => div.innerText = r('P_CU_EDITCONTACTS', 'Edit Contacts')),
createElement('div', div => {
div.className = 'title-company';
div.style.maxWidth = '540px';
if (nullOrEmpty(option.companyName)) {
div.style.display = 'none';
} else {
@@ -652,8 +659,8 @@ export default class CustomerCommunication {
});
const result = option.onSelectCRContacts(list);
}
const r = this.#data.contacts;
this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
const r = this._var.data.contacts;
this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -662,7 +669,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -681,7 +688,7 @@ export default class CustomerCommunication {
if (typeof result?.then === 'function') {
return result.then(r => {
r.map(c => {
for (let cc of this.#data.contacts) {
for (let cc of this._var.data.contacts) {
if (c.Id === cc.Id) {
c.selected = true;
break;
@@ -692,7 +699,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#data.contacts.filter(c => c.Id >= 0).map(c => {
this._var.data.contacts.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -729,7 +736,7 @@ export default class CustomerCommunication {
// onMasking: option.onMasking,
company: !nullOrEmpty(option.companyName),
onSave: item => {
const exists = this.#gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone);
const exists = this._var.gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone);
if (exists) {
showAlert(r('P_CR_ADDCONTACT', 'Add Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn');
return false;
@@ -738,7 +745,7 @@ export default class CustomerCommunication {
const result = option.onSave(item, true);
if (typeof result?.then === 'function') {
return result.then(r => {
this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -747,7 +754,7 @@ export default class CustomerCommunication {
}
return c;
});
this.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -807,7 +814,7 @@ export default class CustomerCommunication {
enabled: item => !item.OptOut && !item.OptOut_BC,
onchanged: function () {
if (typeof option.onChanged === 'function') {
option.onChanged([...This.#gridContact.source, ...This.#gridWo.source]);
option.onChanged([...This._var.gridContact.source, ...This._var.gridWo.source]);
}
},
tooltip: item => item.selected ? r('P_CU_OPTEDIN', 'Opted In') : r('P_CU_OPTEDOUT', 'Opted Out')
@@ -850,8 +857,8 @@ export default class CustomerCommunication {
company: !nullOrEmpty(option.companyName),
onSave: (item, _op) => {
const exists =
This.#gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) ||
This.#gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone);
This._var.gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) ||
This._var.gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone);
if (exists) {
showAlert(r('P_CR_EDITCONTACT', 'Edit Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn');
return false;
@@ -860,7 +867,7 @@ export default class CustomerCommunication {
const result = option.onSave(item);
if (typeof result?.then === 'function') {
return result.then(r => {
This.#gridContact.source = r.filter(c => c.Id >= 0).map(c => {
This._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -869,7 +876,7 @@ export default class CustomerCommunication {
}
return c;
});
This.#gridWo.source = r.filter(c => c.Id < 0).map(c => {
This._var.gridWo.source = r.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -957,7 +964,7 @@ export default class CustomerCommunication {
}
];
grid.init(pop.container.querySelector('.contacts-record'));
const customerRecords = this.#data.contacts.filter(c => c.Id >= 0).map(c => {
const customerRecords = this._var.data.contacts.filter(c => c.Id >= 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -969,11 +976,11 @@ export default class CustomerCommunication {
grid.extraRows = customerRecords.filter(c => !nullOrEmpty(c.Notes)).length;
grid.source = customerRecords;
grid.selectedRowChanged = index => {
if (index >= 0 && this.#gridWo.selectedIndexes?.length > 0) {
this.#gridWo.selectedIndexes = [];
if (index >= 0 && this._var.gridWo.selectedIndexes?.length > 0) {
this._var.gridWo.selectedIndexes = [];
}
};
this.#gridContact = grid;
this._var.gridContact = grid;
// contacts from work order only
const gridWo = new Grid();
@@ -1016,7 +1023,7 @@ export default class CustomerCommunication {
}
];
gridWo.init(pop.container.querySelector('.contacts-wo'));
const workOrderOnly = this.#data.contacts.filter(c => c.Id < 0).map(c => {
const workOrderOnly = this._var.data.contacts.filter(c => c.Id < 0).map(c => {
if (c.OptOut || c.OptOut_BC) {
return c;
}
@@ -1028,11 +1035,11 @@ export default class CustomerCommunication {
gridWo.extraRows = workOrderOnly.filter(c => !nullOrEmpty(c.Notes)).length;
gridWo.source = workOrderOnly;
gridWo.selectedRowChanged = index => {
if (index >= 0 && this.#gridContact.selectedIndexes?.length > 0) {
this.#gridContact.selectedIndexes = [];
if (index >= 0 && this._var.gridContact.selectedIndexes?.length > 0) {
this._var.gridContact.selectedIndexes = [];
}
};
this.#gridWo = gridWo;
this._var.gridWo = gridWo;
});
});
})
@@ -1072,7 +1079,7 @@ export default class CustomerCommunication {
return contacts;
}
#createFollowers(container, option) {
_createFollowers(container, option) {
const readonly = option.readonly;
const recordReadonly = option.recordReadonly;
const followers = createElement('div', 'bar-list-container');
@@ -1086,7 +1093,7 @@ export default class CustomerCommunication {
setTooltip(button, r('P_CU_EDITFOLLOWERS', 'Edit Followers'));
button.addEventListener('click', () => {
if (typeof option.onInitFollower === 'function') {
option.onInitFollower(this.#data.followers).then(data => {
option.onInitFollower(this._var.data.followers).then(data => {
if (typeof data === 'string') {
showAlert(r('P_CUSTOMERRECORD', 'Customer Record'), data, 'warn');
return;
@@ -1108,13 +1115,13 @@ export default class CustomerCommunication {
}
}
});
var title = this.#data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers');
var title = this._var.data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers');
add.show(title, container);
});
}
});
});
this.#buttonFollower = buttonEditFollower;
this._var.buttonFollower = buttonEditFollower;
container.append(
createElement('div', div => {
div.className = 'contact-bar follower-bar';
@@ -1141,8 +1148,16 @@ export default class CustomerCommunication {
load(data, contacts, followers) {
const children = [];
if (data?.length > 0) {
contacts ??= this.#data.contacts;
followers ??= this.#data.allfollowers;
contacts ??= this._var.data.contacts;
followers ??= this._var.data.allfollowers;
this.setData('messages', data);
if (this._var.contactsUpdated !== true) {
const contacts = this._var.data.contacts;
if (contacts?.length > 0) {
updateCustomerName(data, contacts);
this._var.contactsUpdated = true;
}
}
for (let comm of data) {
const div = createElement('div', 'item-div');
let name;
@@ -1153,32 +1168,7 @@ export default class CustomerCommunication {
name = c?.Name;
}
name ??= comm.IsReply && String(comm.FormatSender) !== '' ? comm.FormatSender : comm.Sender;
let sendto = '';
if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) {
for (let oriph of comm.OriPhoneNumbers) {
let cname;
const email = isEmail(oriph);
if (contacts?.length > 0) {
let c = email ?
contacts.find(c => c.Email === oriph) :
contacts.find(c => c.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
} else if (followers?.length > 0) {
c = email ?
followers.find(f => f.Email === oriph) :
followers.find(f => f.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
}
}
}
sendto += (cname ?? oriph) + '\n';
}
}
if (sendto !== '') {
sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
}
const sendto = getMessageSendTo(comm, contacts, followers, r)
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
div.innerText = name;
@@ -1190,13 +1180,13 @@ export default class CustomerCommunication {
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => {
if (/https?:\/\//i.test(comm.Message)) {
span.innerHTML = escapeEmoji(formatUrl(comm.Message));
span.innerHTML = formatUrl(escapeEmoji(comm.Message));
} else {
span.innerText = escapeEmoji(comm.Message);
}
span.appendChild(mmsParts);
}));
if (comm.IsMMS && comm.MMSParts?.length > 0) {
if (comm.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comm.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
@@ -1206,39 +1196,16 @@ export default class CustomerCommunication {
div.classList.add('item-other');
} else {
div.classList.add('item-self');
const [status, statusmsg] = this.#getMessageStatus(comm);
const [status, text, color, tips] = getMessageStatus(comm, r, this._var);
if (status !== -100) {
let statustext;
switch (status) {
case 0:
statustext = r('P_CU_PENDING', 'Pending');
content.style.backgroundColor = '#ffc107';
break;
case 1:
statustext = r('P_WO_SENT', 'Sent');
break;
case 9:
statustext = r('P_MA_FAILED', 'Failed');
content.style.backgroundColor = '#ffc107';
break;
case 10:
statustext = r('P_CU_OPTOUT', 'Opt-Out');
content.style.backgroundColor = '#ffc107';
break;
case 412:
statustext = r('P_CU_LANDLINE', 'Landline');
content.style.backgroundColor = '#ffc107';
break;
default:
statustext = r('P_CU_UNDELIVERED', 'Undelivered');
content.style.backgroundColor = '#ffc107';
break;
if (color != null) {
content.style.backgroundColor = color;
}
const divstatus = createElement('div', div => {
div.className = 'item-status';
div.innerText = statustext;
if (status == -10) {
setTooltip(div, statusmsg);
div.innerText = text;
if (tips != null) {
setTooltip(div, tips);
}
});
content.appendChild(divstatus);
@@ -1255,43 +1222,8 @@ export default class CustomerCommunication {
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
}
#getMessageStatus(comm) {
let status = -100; // 没有状态,页面上不显示
const ls = [];
let statusmsg = '';
if (!comm.StatusIncorrect && comm.Participator?.length > 0) {
for (let p of comm.Participator) {
if (!isEmail(p.CustomerNumber)) {
if (ls.indexOf(p.Status) < 0) {
ls.push(p.Status);
}
if (statusmsg.length > 0) {
statusmsg += '\n';
}
statusmsg += `${p.CustomerNumber}: `;
const st = ({
0: r('P_CU_UNDELIVERED', 'Undelivered'),
1: r('P_WO_SENT', 'Sent'),
9: r('P_MA_FAILED', 'Failed')
})[p.Status];
if (st != null) {
statusmsg += st;
}
else
statusmsg += r('P_MA_XXXXXX', 'Unknown');
}
}
}
if (ls.length === 1) {
status = ls[0];
} else if (ls.length > 1) {
status = -10; // 多种状态
}
return [status, statusmsg];
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@@ -4,11 +4,10 @@ import { nullOrEmpty, r as lang, contains } from "../../utility";
let r = lang;
export default class Follower {
#option;
#grid;
_var = {};
constructor(option = {}) {
this.#option = option;
this._var.option = option;
const getText = option?.getText;
if (typeof getText === 'function') {
r = getText;
@@ -20,7 +19,7 @@ export default class Follower {
const gridContainer = createElement('div', 'follower-grid');
const popup = new Popup({
onMasking: this.#option.onMasking,
onMasking: this._var.option.onMasking,
title,
content: createElement('div', 'follower-wrapper',
createElement('div', div => div.innerText = r('P_CR_WHODOYOUWANTTORECEIVECUSTOMERNOTIFICATIONS', 'Who do you want to receive customer notifications?')),
@@ -31,9 +30,9 @@ export default class Follower {
search.addEventListener('input', () => {
const key = search.value;
if (nullOrEmpty(key)) {
this.#grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
} else {
this.#grid.source = this.#option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true))
this._var.grid.source = this._var.option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true))
.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
}
});
@@ -45,8 +44,8 @@ export default class Follower {
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
if (typeof this.#option.onOk === 'function') {
return this.#option.onOk.call(this, this.#grid.source.filter(f => f.Email || f.Text));
if (typeof this._var.option.onOk === 'function') {
return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.Email || f.Text));
}
}
},
@@ -76,8 +75,8 @@ export default class Follower {
}
];
grid.init();
grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this.#grid = grid;
grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
this._var.grid = grid;
return result;
}
}

View File

@@ -1,33 +1,48 @@
import { createElement, setTooltip, createIcon } from "../../ui";
import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility";
import { createBox, appendMedia } from "./lib";
// import { fileSupported, insertFile } from "./lib";
import { fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib";
let r = lang;
export default class InternalComment {
#container;
#option;
#enter;
#fileControl;
#file;
#message;
_var = {};
// _var.container;
// _var.option;
// _var.enter;
// _var.fileControl;
// _var.file;
// _var.message;
constructor(opt) {
this.#option = opt ?? {};
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get text() { return this.#enter?.value }
get text() { return this._var.enter?.value }
set text(s) {
const element = this.#enter;
const element = this._var.enter;
if (element != null) {
element.value = s
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
}
}
/**
* @param {any} contacts
*/
set contacts(contacts) {
this._var.contacts = contacts;
if (this._var.contactsUpdated !== true && contacts?.length > 0) {
const comments = this._var.comments;
if (comments?.length > 0) {
updateCustomerName(this._var.comments, contacts);
this._var.contactsUpdated = true;
}
}
}
@@ -35,21 +50,21 @@ export default class InternalComment {
* @param {boolean} flag
*/
set loading(flag) {
if (this.#container == null) {
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag;
this.#container.querySelector('.button-send-message').disabled = flag;
this.#container.querySelector('.button-post-note').disabled = flag;
this._var.enter.disabled = flag;
this._var.container.querySelector('.button-send-message').disabled = flag;
this._var.container.querySelector('.button-post-note').disabled = flag;
}
get file() { return this.#file || this.#fileControl?.files?.[0] }
get file() { return this._var.file || this._var.fileControl?.files?.[0] }
set file(f) {
this.#fileControl?.remove();
const label = this.#container.querySelector('.file-selector>.selector-name');
this._var.fileControl?.remove();
const label = this._var.container.querySelector('.file-selector>.selector-name');
if (f == null) {
this.#fileControl = null;
this.#file = null;
this._var.fileControl = null;
this._var.file = null;
if (label != null) {
label.style.display = 'none';
label.innerText = '';
@@ -57,15 +72,15 @@ export default class InternalComment {
}
} else {
if (f instanceof HTMLInputElement) {
this.#fileControl = f;
this.#file = f.files[0];
const link = this.#container.querySelector('.file-selector>.selector-link');
this._var.fileControl = f;
this._var.file = f.files[0];
const link = this._var.container.querySelector('.file-selector>.selector-link');
if (link != null) {
link.appendChild(f);
}
} else {
this.#fileControl = null;
this.#file = f;
this._var.fileControl = null;
this._var.file = f;
}
if (label != null) {
label.style.display = '';
@@ -79,16 +94,17 @@ export default class InternalComment {
* @param {boolean} flag
*/
set readonly(flag) {
this.#option.readonly = flag;
if (this.#container == null) {
this._var.option.readonly = flag;
if (this._var.container == null) {
return;
}
this.#enter.disabled = flag === true;
this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this.#container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
this._var.enter.disabled = flag === true;
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
this._var.container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
}
create() {
const option = this._var.option;
const container = createBox(
createElement('div', null,
createElement('div', div => {
@@ -97,96 +113,93 @@ export default class InternalComment {
})
), []
);
const readonly = this.#option.readonly;
const readonly = option.readonly;
// enter box
const enter = createElement('textarea', 'ui-text');
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
enter.maxLength = this.#option.maxLength ??= 3000;
enter.maxLength = option.maxLength ??= 3000;
enter.addEventListener('input', () => {
const val = this.text;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength);
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength);
this._var.container.querySelector('.message-bar .prompt-count').innerText = s;
});
if (readonly === true) {
enter.disabled = true;
}
// enter.addEventListener('paste', e => {
// const file = e.clipboardData.files[0];
// if (file != null) {
// e.preventDefault();
// this.file = insertFile(container, file, r);
// }
// });
this.#enter = enter;
enter.addEventListener('paste', e => {
const file = e.clipboardData.files[0];
if (file != null) {
e.preventDefault();
this.file = insertFile(container, file, r);
}
});
this._var.enter = enter;
container.appendChild(
createElement('div', div => {
div.className = 'message-bar';
// div.addEventListener('dragover', e => {
// if (option.readonly !== true) {
// const item = e.dataTransfer.items[0];
// if (item?.kind === 'file') {
// e.preventDefault();
// if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) {
// e.dataTransfer.dropEffect = 'none';
// } else {
// e.dataTransfer.dropEffect = 'link';
// }
// }
// }
// });
// div.addEventListener('drop', e => {
// if (option.readonly !== true) {
// const file = e.dataTransfer.files[0];
// if (file != null) {
// e.preventDefault();
// this.file = insertFile(container, file, r);
// }
// }
// });
div.addEventListener('dragover', e => {
if (option.readonly !== true) {
const item = e.dataTransfer.items[0];
if (item?.kind === 'file') {
e.preventDefault();
if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) {
e.dataTransfer.dropEffect = 'none';
} else {
e.dataTransfer.dropEffect = 'link';
}
}
}
});
div.addEventListener('drop', e => {
if (option.readonly !== true) {
const file = e.dataTransfer.files[0];
if (file != null) {
e.preventDefault();
this.file = insertFile(container, file, r);
}
}
});
},
enter,
createElement('div', div => div.style.textAlign = 'right',
createElement('div', selector => {
selector.className = 'file-selector';
if (this.#option.noMessage === true) {
selector.style.display = 'none';
}
},
createElement('div', div => {
div.className = 'selector-link';
div.style.display = 'none';
// div.addEventListener('click', () => {
// this.#fileControl?.remove();
// const file = createElement('input', input => {
// input.type = 'file';
// input.accept = fileSupported.join(',');
// input.addEventListener('change', () => {
// const file = insertFile(container, input.files?.[0], r);
// if (file != null) {
// this.file = file;
// }
// });
// });
// div.appendChild(this.#fileControl = file);
// file.dispatchEvent(new MouseEvent('click'));
// });
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
createElement('div', 'customer-left',
createElement('div', 'file-selector',
createElement('div', div => {
div.className = 'selector-link';
// div.style.display = 'none';
div.addEventListener('click', () => {
this._var.fileControl?.remove();
const file = createElement('input', input => {
input.type = 'file';
input.accept = fileSupported.join(',');
input.addEventListener('change', () => {
const file = insertFile(container, input.files?.[0], r);
if (file != null) {
this.file = file;
}
});
});
div.appendChild(this._var.fileControl = file);
file.dispatchEvent(new MouseEvent('click'));
});
},
createIcon('fa-regular', 'link')
),
createElement('span', span => {
span.className = 'selector-name';
span.style.display = 'none';
}),
createElement('layer', layer => {
layer.appendChild(createIcon('fa-regular', 'times'));
layer.addEventListener('click', () => this.file = null);
})
)
),
createElement('div', 'prompt-count'),
createElement('button', button => {
button.className = 'roundbtn button-send-message';
button.style.backgroundColor = 'rgb(19, 150, 204)';
if (readonly === true || this.#option.noMessage === true) {
if (readonly === true || option.noMessage === true) {
button.style.display = 'none';
}
button.appendChild(createIcon('fa-solid', 'paper-plane'));
@@ -196,9 +209,9 @@ export default class InternalComment {
if (nullOrEmpty(val?.trim())) {
return;
}
if (typeof this.#option.onAddMessage === 'function') {
if (typeof option.onAddMessage === 'function') {
this.loading = true;
this.#option.onAddMessage(this.text);
option.onAddMessage(this.text);
}
})
}),
@@ -216,9 +229,9 @@ export default class InternalComment {
if (nullOrEmpty(val?.trim())) {
return;
}
if (typeof this.#option.onAddComment === 'function') {
if (typeof option.onAddComment === 'function') {
this.loading = true;
this.#option.onAddComment(this.text, this.file);
option.onAddComment(this.text, this.file);
}
})
})
@@ -227,54 +240,77 @@ export default class InternalComment {
);
const message = createElement('div', 'list-bar');
this.#message = message;
this._var.message = message;
container.appendChild(message);
return this.#container = container;
return this._var.container = container;
}
load(data) {
const children = [];
if (data?.length > 0) {
this._var.comments = data;
if (this._var.contactsUpdated !== true) {
const contacts = this._var.contacts;
if (contacts?.length > 0) {
updateCustomerName(data, contacts);
this._var.contactsUpdated = true;
}
}
for (let comment of data) {
const div = createElement('div', 'item-div');
// if (sendto !== '') {
// sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`;
// }
const sendto = getMessageSendTo(comment, null, null, r)
div.appendChild(createElement('div', div => {
div.className = 'item-poster';
div.innerText = comment.UserName;
div.innerText = comment.Sender;
if (sendto?.length > 0) {
setTooltip(div, sendto);
}
}));
const content = createElement('div', 'item-content');
const mmsParts = createElement('div', div => div.style.display = 'none');
content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts));
if (comment.IsMMS && comment.MMSParts?.length > 0) {
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Message)), mmsParts));
if (comment.MMSParts?.length > 0) {
mmsParts.style.display = '';
for (let kv of comment.MMSParts) {
appendMedia(mmsParts, kv.Key, kv.Value);
}
}
if (comment.FollowUp?.length > 0) {
div.classList.add('item-sent');
const sendto = r('P_CU_SENDTO_COLON', 'Send To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
content.appendChild(createElement('div', div => {
// if (comment.FollowUp?.length > 0) {
// div.classList.add('item-sent');
// const sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
// content.appendChild(createElement('div', div => {
// div.className = 'item-status';
// div.innerText = r('P_WO_SENT', 'Sent');
// setTooltip(div, sendto);
// }));
// }
const [status, text, color, tips] = getMessageStatus(comment, r, this._var);
if (status !== -100) {
if (color != null) {
content.style.backgroundColor = color;
}
const divstatus = createElement('div', div => {
div.className = 'item-status';
div.innerText = r('P_WO_SENT', 'Sent');
setTooltip(div, sendto);
}));
div.innerText = text;
if (tips != null) {
setTooltip(div, tips);
}
});
content.appendChild(divstatus);
}
div.append(
content,
createElement('div', div => {
div.className = 'item-time';
div.innerText = comment.SubmitDateStr;
div.innerText = comment.TimeStr;
})
);
children.push(div);
}
children[0].style.marginTop = '0';
}
this.#message.replaceChildren(...children);
this.#message.scrollTop = this.#message.scrollHeight
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
this._var.message.replaceChildren(...children);
this._var.message.scrollTop = this._var.message.scrollHeight
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
}
}

View File

@@ -1,4 +1,5 @@
import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile } from "../../ui";
import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile, createIcon, Popup, Grid, Dropdown } from "../../ui";
import { global, isEmail } from "../../utility";
export function createBox(title, functions) {
const container = createElement('div', 'comm');
@@ -13,22 +14,30 @@ export function createBox(title, functions) {
export function appendMedia(container, mimeType, url) {
switch (mimeType) {
case 'application/pdf':
case '.pdf':
container.appendChild(createFile(url, 'file-pdf'));
break;
case 'application/msword':
case 'application/vnd.ms-word':
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
case '.doc':
case '.docx':
container.appendChild(createFile(url, 'file-word'));
break;
case 'application/vnd.ms-excel':
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
case '.xls':
case '.xlsx':
container.appendChild(createFile(url, 'file-excel'));
break;
case 'application/vnd.ms-powerpoint':
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
case '.ppt':
case '.pptx':
container.appendChild(createFile(url, 'file-powerpoint'));
break;
case 'application/smil':
case '.smil':
// TODO: ignore smil files
// container.appendChild(createFile(url, 'smile'));
break;
@@ -41,10 +50,18 @@ export function appendMedia(container, mimeType, url) {
case 'audio/opus':
case 'audio/wav':
case 'audio/webm':
case '.aac':
case '.amr':
case '.mp3':
case '.ogg':
case '.opus':
case '.wav':
container.appendChild(createAudio(mimeType, url));
break;
case 'text/plain':
case 'text/x-vcard':
case '.txt':
case '.vcard':
container.appendChild(createFile(url, 'id-card'));
break;
case 'video/3gpp':
@@ -54,8 +71,22 @@ export function appendMedia(container, mimeType, url) {
case 'video/x-mpeg':
case 'video/quicktime':
case 'video/webm':
case '.3gp':
case '.3gpp':
case '.mp4':
case '.mpg':
case '.mov':
case '.webm':
container.appendChild(createVideo(url));
break;
case '.jpg':
case '.jpeg':
case '.jfif':
case '.png':
case '.gif':
case '.bmp':
container.appendChild(createPicture(url));
break;
default:
if (/^image\//.test(mimeType)) {
container.appendChild(createPicture(url));
@@ -151,4 +182,295 @@ export function insertFile(container, file, r) {
}
return file;
}
};
};
function getStatusText(status, dict) {
switch (status) {
case 0:
case 1:
case 5:
case 6:
return dict[status];
case 9:
case 10:
case 412:
return dict[9];
default:
return dict[9999];
}
}
const SymbolDropdown = Symbol.for('ui-dropdown');
class DropdownColumn {
static create(col, trigger, parent) {
const drop = new Dropdown({ ...col.dropOptions, parent });
drop.onselected = trigger;
return drop.create();
}
static _getDrop(element) {
const dropGlobal = global[SymbolDropdown];
if (dropGlobal == null) {
return null;
}
const dropId = element.dataset.dropId;
const drop = dropGlobal[dropId];
if (drop == null) {
return null;
}
return drop;
}
static _setValue(source, element, val) {
const data = source?.find(v => v.value === val);
if (data != null) {
val = data.text;
}
super.setValue(element, val);
}
static setValue(element, val, _item, col) {
if (element.tagName !== 'DIV') {
this._setValue(col.source, element, val);
return;
}
const drop = this._getDrop(element);
if (drop == null) {
return;
}
if (drop.source == null || drop.source.length === 0) {
let source = col.source;
if (source != null) {
drop.source = source;
}
}
drop.select(val, true);
}
static getValue(e) {
return e.value;
}
}
export function getMessageStatus(comm, r, _var) {
const messageStatus = {
0: r('P_CU_PENDING', 'Pending'),
1: r('P_WO_SENT', 'Sent'),
5: r('P_CU_DELIVERYCONFIRMED', 'Delivery Confirmed'),
6: r('P_CU_RESENT', 'Resent'),
9: r('P_MA_FAILED', 'Failed'),
9999: r('P_CU_UNKNOWN', 'Unknown')
};
const knownStatus = [0, 1, 5, 6, 9, 10, 412];
const okStatus = [1, 5, 6];
const failedStatus = [9, 10, 412];
let status = -100; // 没有状态,页面上不显示
const ls = [];
const msgs = [];
if (!comm.StatusIncorrect && comm.Participator?.length > 0) {
// if (comm.Id === 433339) {
// comm.Participator[4].Status = 6;
// }
for (let p of comm.Participator) {
if (!isEmail(p.CustomerNumber)) {
if (ls.indexOf(p.Status) < 0) {
ls.push(p.Status);
}
p.statusText = getStatusText(p.Status, messageStatus);
msgs.push(p);
}
}
}
if (ls.length === 1) {
status = ls[0];
} else if (ls.length > 1) {
// status = -10; // 多种状态
status = ls
.filter(s => okStatus.indexOf(s) < 0) // ok status
.sort((a, b) => b - a)[0] ?? 1;
}
const statusText = messageStatus[failedStatus.includes(status) ? 9 : status] ?? messageStatus[9999];
const statusColor = okStatus.includes(status) ? null : '#ffc107';
const statusUpdatable = _var.option.statusUpdatable;
let statusTips;
if (statusUpdatable !== false || ls.length > 1) {
statusTips = createElement('div', tip => {
for (let i = 0; i < msgs.length; i++) {
tip.appendChild(createElement('div', t => {
const p = msgs[i];
if (statusUpdatable !== false && p.StatusChanged) {
t.append(
createElement('span', s => s.innerText = `${p.CustomerNumber}: `),
createElement('span', s => {
s.style.color = '#2140fb';
s.style.cursor = 'pointer';
s.innerText = p.statusText;
s.addEventListener('click', () => {
if (typeof _var.option.onMessageStatusClicked === 'function') {
_var.option.onMessageStatusClicked(p);
}
});
})
)
} else {
t.innerText = `${p.CustomerNumber}: ${p.statusText}`;
}
}));
}
if (statusUpdatable !== false) {
tip.appendChild(createElement('div', b => {
b.className = 'tip-function-button';
// setTooltip(b, r('P_CU_UPDATESTATUS', 'Update Status'));
b.addEventListener('click', async () => {
for (let p of comm.Participator) {
switch (p.Status) {
case 0:
case 1:
case 5:
case 6:
p.statusChanged = String(p.Status);
break;
case 9:
case 10:
case 412:
p.statusChanged = '9';
break;
default:
p.statusChanged = '-1';
break;
}
}
const gridContainer = createElement('div', 'status-grid');
const popup = new Popup({
onMasking: _var.option.onMasking,
title: r('P_CU_UPDATESTATUS', 'Update Status'),
content: createElement('div', wrapper => {
wrapper.className = 'update-status-wrapper';
wrapper.style.width = '500px';
},
gridContainer
),
buttons: [
{
text: r('P_WO_OK', 'OK'),
key: 'ok',
trigger: () => {
const changed = msgs.filter(m => {
switch (m.statusChanged) {
case '-1':
return knownStatus.includes(m.Status);
case '9':
return failedStatus.indexOf(m.Status) < 0;
default:
return String(m.Status) !== m.statusChanged;
}
}).map(m => {
let status = Number(m.statusChanged);
if (isNaN(status) || status < 0) {
status = 9999;
}
return {
Id: m.Id,
Status: status
};
});
if (typeof _var.option.onUpdateMessageStatus === 'function') {
_var.option.onUpdateMessageStatus(changed);
}
}
},
{
text: r('P_WO_CANCEL', 'Cancel'),
key: 'cancel'
}
]
});
await popup.show();
const grid = new Grid(gridContainer);
// grid.headerVisible = false;
grid.allowHtml = true;
grid.columns = [
{
key: 'CustomerNumber',
caption: r('P_JS_NUMBER', 'Number'),
width: 150
},
/*{
key: 'customerName',
caption: r('P_WOS_CUSTOMERNAME', 'Customer Name'),
width: 120
},*/
{
key: 'statusText',
caption: r('P_CU_CURRENTSTATUS', 'Current Status'),
width: 155
},
{
key: 'statusChanged',
caption: r('P_CU_REVISEDSTATUS', 'Revised Status'),
width: 155,
type: DropdownColumn,
source: [
{ value: '-1', text: messageStatus[9999] },
{ value: '0', text: messageStatus[0] },
{ value: '1', text: messageStatus[1] },
{ value: '5', text: messageStatus[5] },
{ value: '6', text: messageStatus[6] },
{ value: '9', text: messageStatus[9] }
]
}
];
grid.init();
grid.source = msgs;
});
}, createIcon('fa-light', 'wave-sine')));
}
});
}
return [status, statusText, statusColor, statusTips];
};
export function getMessageSendTo(comm, contacts, followers, r) {
let sendto = '';
if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) {
for (let oriph of comm.OriPhoneNumbers) {
let cname;
const email = isEmail(oriph);
if (contacts?.length > 0) {
let c = email ?
contacts.find(c => c.Email === oriph) :
contacts.find(c => c.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
} else if (followers?.length > 0) {
c = email ?
followers.find(f => f.Email === oriph) :
followers.find(f => f.MobilePhone === oriph);
if (c != null) {
cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`;
}
}
}
sendto += (cname ?? oriph) + '\n';
}
}
if (sendto !== '') {
sendto = r('P_CU_SENDTO_COLON', 'Sent to :') + `\n${sendto}`;
}
return sendto;
}
export function updateCustomerName(messages, contacts) {
if (messages?.length > 0 && contacts?.length > 0) {
for (let m of messages) {
if (m.Participator?.length > 0) {
for (let p of m.Participator) {
const contact = contacts.filter(c => c.MobilePhoneDisplayText === p.CustomerNumber)[0];
p.customerName = contact?.Name;
}
}
}
}
}

View File

@@ -1,10 +1,24 @@
@import "../../ui/css/functions/func.scss";
.ui-popup-mask .wrapper-edit-method {
width: 100%;
.ui-popup-mask {
.wrapper-edit-method {
width: 100%;
.ui-check-wrapper {
padding: 0 28px;
.ui-check-wrapper {
padding: 0 28px;
}
}
.status-grid,
.contacts-record,
.contacts-wo {
>.ui-grid {
overflow-x: visible;
>.ui-grid-body {
overflow: visible;
}
}
}
}
@@ -13,7 +27,7 @@
flex-direction: column;
width: 320px;
background-color: var(--dark-fore-color);
border: 1px solid var(--title-bg-color);
border: 1px solid var(--title-ctrlbg-color);
margin-left: 12px;
& {
@@ -69,7 +83,7 @@
flex: 0 0 auto;
padding: 5px 0 5px 10px;
color: var(--title-color);
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
line-height: 24px;
display: flex;
align-items: center;
@@ -132,7 +146,7 @@
flex: 0 0 auto;
padding: 4px 0;
display: flex;
border-bottom: 1px solid var(--title-bg-color);
border-bottom: 1px solid var(--title-ctrlbg-color);
position: relative;
>.bar-icon {
@@ -253,13 +267,13 @@
.message-bar {
flex: 0 0 auto;
border-bottom: 1px solid var(--title-bg-color);
border-bottom: 1px solid var(--title-ctrlbg-color);
display: flex;
flex-direction: column;
>textarea {
padding: 10px 10px 0;
border: 1px solid var(--title-bg-color);
border: 1px solid var(--title-ctrlbg-color);
border-radius: 5px;
height: 70px;
resize: none;
@@ -274,73 +288,75 @@
>div {
padding: 0 10px 10px;
>.customer-name {
>.customer-left {
float: left;
text-align: left;
>span {
font-size: var(--font-smaller-size);
}
>.ui-input {
margin-left: 4px;
width: 150px;
border-top: none;
border-left: none;
border-right: none;
}
}
>.file-selector {
float: left;
display: inline-flex;
align-items: center;
height: 30px;
>.selector-link {
cursor: pointer;
display: flex;
>svg {
width: 16px;
height: 16px;
fill: var(--secondary-link-color);
>.customer-name {
>span {
font-size: var(--font-smaller-size);
}
>input {
display: none;
>.ui-input {
margin-left: 4px;
width: 150px;
border-top: none;
border-left: none;
border-right: none;
}
}
>.selector-name {
max-width: 130px;
padding: 0 20px 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
>.file-selector {
display: inline-flex;
align-items: center;
height: 30px;
+layer {
display: none;
margin-left: -20px;
>.selector-link {
cursor: pointer;
display: flex;
>svg {
width: 16px;
height: 16px;
fill: var(--red-color);
fill: var(--secondary-link-color);
}
&:hover {
>input {
display: none;
}
}
>.selector-name {
max-width: 130px;
padding: 0 20px 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+layer {
display: none;
margin-left: -20px;
cursor: pointer;
>svg {
width: 16px;
height: 16px;
fill: var(--red-color);
}
&:hover {
display: flex;
}
}
&:hover+layer {
display: flex;
}
}
&:hover+layer {
display: flex;
}
>.ui-tooltip-wrapper img {
max-width: 120px;
max-height: 80px;
>.ui-tooltip-wrapper img {
max-width: 120px;
max-height: 80px;
}
}
}
}
@@ -421,6 +437,25 @@
margin-right: -12px;
font-size: .625rem;
float: right;
.ui-tooltip-content .tip-function-button {
text-align: right;
>svg {
width: 20px;
height: 20px;
cursor: pointer;
border: 1px solid;
border-radius: 10px;
padding: 2px;
box-sizing: border-box;
transition: opacity .12s;
&:hover {
opacity: .5;
}
}
}
}
}

6
lib/element.js Normal file
View File

@@ -0,0 +1,6 @@
import "./element/style.scss";
import ScheduleItem from "./element/schedule";
export {
ScheduleItem
}

274
lib/element/schedule.js Normal file
View File

@@ -0,0 +1,274 @@
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup, Dropdown, validation } from "../ui";
import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../utility";
let r = lang;
function datepicker(element) {
if (typeof $?.fn?.datepicker === 'function') {
$(element).datepicker({
autoHide: true,
format: 'm/dd/yyyy'
});
}
return element;
}
export default class ScheduleItem {
_var = {};
constructor(opt) {
this._var.option = opt ?? {};
const getText = opt?.getText;
if (typeof getText === 'function') {
r = getText;
}
}
get checkOccurOnce() { return this._var.container.querySelector('.schedule-id-box-occur-once>input'); }
get checkOccurEvery() { return this._var.container.querySelector('.schedule-id-box-occur-every>input'); }
get inputOccurOnce() { return this._var.container.querySelector('.schedule-id-occur-once'); }
get inputOccurEvery() { return this._var.container.querySelector('.schedule-id-occur-every'); }
get inputOccurStarting() { return this._var.container.querySelector('.schedule-id-occur-starting') }
get inputOccurEnding() { return this._var.container.querySelector('.schedule-id-occur-ending') }
changeDailyFrequency(once) {
this.inputOccurOnce.disabled = !once;
this.inputOccurEvery.disabled = once;
this.inputOccurStarting.disabled = once;
this.inputOccurEnding.disabled = once;
}
getParameters() {
return {
Enabled: this._var.container.querySelector('.schedule-id-enabled>input').checked,
Schedule: {
Frequency: Number(this.dropFrequency.selected.value),
Daily: {
OcurrsOnce: this.checkOccurOnce.checked,
OcurrsOnceAt: this.inputOccurOnce.value,
OcurrsInterval: Number(this.inputOccurEvery.value),
StartingAt: this.inputOccurStarting.value,
EndingAt: this.inputOccurEnding.value
},
Monday: this._var.container.querySelector('.schedule-id-1>input').checked,
Tuesday: this._var.container.querySelector('.schedule-id-2>input').checked,
Wednesday: this._var.container.querySelector('.schedule-id-3>input').checked,
Thursday: this._var.container.querySelector('.schedule-id-4>input').checked,
Friday: this._var.container.querySelector('.schedule-id-5>input').checked,
Saturday: this._var.container.querySelector('.schedule-id-6>input').checked,
Sunday: this._var.container.querySelector('.schedule-id-7>input').checked,
DayOfMonth: Number(this._var.container.querySelector('.schedule-id-dayofmonth').value),
StartDate: this._var.container.querySelector('.schedule-id-duration-start').value,
EndDate: this._var.container.querySelector('.schedule-id-duration-end').value
}
};
}
getDateTime(s) {
if (typeof s === 'string') {
const d = new Date(s);
return isNaN(d.getTime()) ? new Date() : d;
}
return s;
}
getTimeString(s) {
const d = this.getDateTime(s);
return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
}
getDateString(s) {
const d = this.getDateTime(s);
return String(d.getMonth() + 1).padStart(2, '0') + '/' + String(d.getDate()).padStart(2, '0') + '/' + String(d.getFullYear());
}
setParameters(p) {
this._var.container.querySelector('.schedule-id-enabled>input').checked = p.Enabled;
const schedule = p.Schedule || {};
this.dropFrequency.select(String(schedule.Frequency));
let checker = schedule.Daily.OcurrsOnce ? this.checkOccurOnce : this.checkOccurEvery;
checker.checked = true;
checker.dispatchEvent(new Event('change'));
this.inputOccurOnce.value = this.getTimeString(schedule.Daily.OcurrsOnceAt);
this.inputOccurEvery.value = String(schedule.Daily.OcurrsInterval);
this.inputOccurStarting.value = this.getTimeString(schedule.Daily.StartingAt);
this.inputOccurEnding.value = this.getTimeString(schedule.Daily.EndingAt);
this._var.container.querySelector('.schedule-id-1>input').checked = schedule.Monday;
this._var.container.querySelector('.schedule-id-2>input').checked = schedule.Tuesday;
this._var.container.querySelector('.schedule-id-3>input').checked = schedule.Wednesday;
this._var.container.querySelector('.schedule-id-4>input').checked = schedule.Thursday;
this._var.container.querySelector('.schedule-id-5>input').checked = schedule.Friday;
this._var.container.querySelector('.schedule-id-6>input').checked = schedule.Saturday;
this._var.container.querySelector('.schedule-id-7>input').checked = schedule.Sunday;
this._var.container.querySelector('.schedule-id-dayofmonth').value = String(schedule.DayOfMonth);
const start = this.getDateString(schedule.StartDate);
const end = this.getDateString(schedule.EndDate);
if (typeof $?.fn?.datepicker === 'function') {
$(this._var.container.querySelector('.schedule-id-duration-start')).datepicker('setDate', new Date(start));
$(this._var.container.querySelector('.schedule-id-duration-end')).datepicker('setDate', new Date(end));
} else {
this._var.container.querySelector('.schedule-id-duration-start').value = start;
this._var.container.querySelector('.schedule-id-duration-end').value = end;
}
}
create() {
const option = this._var.option;
const drop = new Dropdown({ selected: '0' });
this.dropFrequency = drop;
drop.source = [
{ value: '0', text: 'Daily' },
{ value: '1', text: 'Weekly' },
{ value: '2', text: 'Monthly' }
];
drop.onselected = item => {
container.querySelector('.schedule-item-weekly').style.display = item.value === '1' ? '' : 'none';
const monthly = item.value === '2';
container.querySelector('.schedule-item-monthly').style.display = monthly ? '' : 'none';
if (!monthly) {
const dayofmonth = this._var.container.querySelector('.schedule-id-dayofmonth');
if (dayofmonth.classList.contains('validation-error')) {
dayofmonth.value = '1';
}
}
};
const container = createElement('div', 'schedule-item-container',
createElement('fieldset', 'schedule-item-frequency',
createElement('legend', legend => legend.innerText = 'Frequency'),
createElement('div', 'schedule-item-line',
createElement('span', span => span.innerText = 'Occurs'),
drop.create()
),
createElement('div', div => {
div.className = 'schedule-item-panel schedule-item-weekly';
div.style.display = 'none';
},
createElement('table', 'schedule-item-table',
createElement('tr', 'schedule-item-tr',
createElement('td', null, createCheckbox({ className: 'schedule-id-1', label: 'Monday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-3', label: 'Wednesday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-5', label: 'Friday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-6', label: 'Saturday' }))
),
createElement('tr', 'schedule-item-tr',
createElement('td', null, createCheckbox({ className: 'schedule-id-2', label: 'Tuesday' })),
createElement('td', null, createCheckbox({ className: 'schedule-id-4', label: 'Thursday' })),
createElement('td'),
createElement('td', null, createCheckbox({ className: 'schedule-id-7', label: 'Sunday' }))
)
)
),
createElement('div', div => {
div.className = 'schedule-item-panel schedule-item-monthly';
div.style.display = 'none';
},
createElement('div', 'schedule-item-line',
createElement('span', span => span.innerText = 'On day'),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-dayofmonth', i.maxLength = 2 }),
/^([0]?[1-9]|[12][0-9]|[3][01])$/
),
createElement('span', span => span.innerText = 'of the month')
)
)
),
createElement('fieldset', 'schedule-item-daily-frequency',
createElement('legend', legend => legend.innerText = 'Daily frequency'),
createElement('div', 'schedule-item-line',
createRadiobox({
name: 'schedule-daily-occurs',
checked: true,
className: 'schedule-id-box-occur-once',
label: 'Occurs once at',
onchange: e => this.changeDailyFrequency(e.target.checked)
}),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-once', i.maxLength = 5 }),
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
)
),
createElement('div', 'schedule-item-line schedule-item-line-occur-every',
createRadiobox({
name: 'schedule-daily-occurs',
className: 'schedule-id-box-occur-every',
label: 'Occurs every',
onchange: e => this.changeDailyFrequency(!e.target.checked)
}),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-every', i.maxLength = 5 }),
/^([0][1-9]+|[1-9][0-9]*)$/
),
createElement('span', span => span.innerText = 'minute(s)'),
createElement('div', 'schedule-item-placeholder'),
createElement('div', 'schedule-item-block',
createElement('div', 'scheldule-item-line',
createElement('span', span => span.innerText = 'Starting at'),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-starting', i.maxLength = 5 }),
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
)
),
createElement('div', 'scheldule-item-line',
createElement('span', span => span.innerText = 'Ending at'),
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-ending', i.maxLength = 5 }),
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
)
)
)
)
),
createElement('fieldset', 'schedule-item-duration',
createElement('legend', legend => legend.innerText = 'Duration'),
createElement('div', 'schedule-item-line schedule-item-line-duration',
createElement('span', span => span.innerText = 'Start date'),
datepicker(
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-start', i.maxLength = 10 }),
/^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/
)
),
createElement('div', 'schedule-item-placeholder'),
createElement('span', span => span.innerText = 'End date'),
datepicker(
validation(
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-end', i.maxLength = 10 }),
/^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/
)
)
),
createElement('div', 'schedule-item-line',
createCheckbox({ className: 'schedule-id-enabled', checked: true, label: 'Enabled' })
)
)
);
this._var.container = container;
if (option.parameter == null) {
option.parameter = {
Enabled: true,
Schedule: {
Frequency: 0,
Daily: {
OcurrsOnce: true,
OcurrsOnceAt: '00:00',
OcurrsInterval: 60,
StartingAt: '00:00',
EndingAt: '23:00'
},
Monday: true,
Tuesday: false,
Wednesday: false,
Thursday: false,
Friday: false,
Saturday: false,
Sunday: false,
DayOfMonth: 1,
StartDate: '',
EndDate: ''
}
};
}
this.setParameters(option.parameter);
return container;
}
}

1
lib/element/style.css Normal file
View File

@@ -0,0 +1 @@
.schedule-item-container fieldset{margin-top:10px;border-width:1px;border-radius:4px;border-color:var(--border-color)}.schedule-item-container fieldset legend,.schedule-item-container fieldset span{font-weight:400;font-size:var(--font-size);padding-left:8px;padding-right:6px;color:var(--color)}.schedule-item-container fieldset .ui-input{line-height:20px}.schedule-item-container fieldset .schedule-item-monthly{margin-top:5px}.schedule-item-container fieldset .schedule-item-monthly .ui-input{width:40px}.schedule-item-container fieldset.schedule-item-daily-frequency .ui-input{vertical-align:top;margin-top:5px}.schedule-item-container fieldset .schedule-item-table{width:100%}.schedule-item-container fieldset .schedule-item-line-occur-every{display:flex;align-items:flex-start}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line{display:flex;align-items:center;margin-top:5px}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>span{flex:1 1 auto}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>.ui-input{margin-top:0}.schedule-item-container fieldset .schedule-item-line-occur-every>span{line-height:36px}.schedule-item-container fieldset .schedule-item-line-occur-every .ui-input{width:70px}.schedule-item-container fieldset .schedule-item-line-duration{display:flex;align-items:center;height:36px}.schedule-item-container fieldset .schedule-item-line>.schedule-item-placeholder{flex:1 1 auto}.schedule-item-container .schedule-item-frequency{margin-top:0}

91
lib/element/style.scss Normal file
View File

@@ -0,0 +1,91 @@
.schedule-item-container {
fieldset {
margin-top: 10px;
border-width: 1px;
border-radius: 4px;
border-color: var(--border-color);
legend,
span {
font-weight: 400;
font-size: var(--font-size);
padding-left: 8px;
padding-right: 6px;
color: var(--color);
}
.ui-input {
line-height: 20px;
height: 20px;
}
.schedule-item-monthly {
margin-top: 5px;
.ui-input {
width: 40px;
}
}
&.schedule-item-daily-frequency .ui-input {
vertical-align: top;
margin-top: 5px;
}
.schedule-item-table {
width: 100%;
}
.schedule-item-line-occur-every {
display: flex;
align-items: flex-start;
>.schedule-item-block>.scheldule-item-line {
display: flex;
align-items: center;
margin-top: 5px;
>span {
flex: 1 1 auto;
}
>.ui-input {
margin-top: 0;
}
}
>span {
line-height: 36px;
}
.ui-input {
width: 70px;
}
}
.schedule-item-line-duration {
display: flex;
align-items: center;
height: 36px;
}
.schedule-item-line {
>.schedule-item-placeholder {
flex: 1 1 auto;
}
}
}
.schedule-item-frequency {
margin-top: 0;
>.schedule-item-line {
line-height: 24px;
}
}
.ui-drop-wrapper>.ui-drop-header {
height: 24px;
}
}

View File

@@ -9,6 +9,7 @@ import { Grid } from "./ui/grid/grid";
import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, GridIconColumn, GridTextColumn } from './ui/grid/column';
import { Popup, createPopup, showAlert, showConfirm } from "./ui/popup";
import { createPicture, createAudio, createVideo, createFile } from './ui/media';
import { validation } from './ui/extension';
export {
createElement,
@@ -42,5 +43,7 @@ export {
createPicture,
createAudio,
createVideo,
createFile
createFile,
// extension
validation
}

View File

@@ -6,6 +6,15 @@
font-family: var(--font-family);
@include outborder();
&.validation-error {
border-color: var(--red-color);
&:focus,
&:hover {
border-color: var(--red-color);
}
}
}
.ui-input {

1
lib/ui/css/grid.css Normal file

File diff suppressed because one or more lines are too long

View File

@@ -3,10 +3,7 @@
.ui-grid {
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: visible;
overflow-x: hidden;
overflow: auto;
& {
--cell-hover-bg-color: lightyellow;
@@ -52,6 +49,7 @@
}
@include outline();
@include scrollbar();
&,
input[type="text"],
@@ -67,321 +65,338 @@
visibility: hidden;
}
>.ui-grid-header {
width: 100%;
min-width: 100%;
margin: 0;
border-bottom: 1px solid var(--header-border-color);
background-color: var(--header-bg-color);
color: var(--header-fore-color);
user-select: none;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
>.ui-grid-wrapper {
position: relative;
tr {
position: relative;
>.ui-grid-table {
position: absolute;
width: 100%;
min-width: 100%;
margin: 0;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
>th {
padding: 0;
margin: 0;
word-wrap: break-word;
white-space: normal;
position: relative;
>thead {
tr {
color: var(--header-fore-color);
position: sticky;
top: 0;
z-index: 2;
>div {
line-height: var(--header-line-height);
min-height: var(--row-height);
display: flex;
align-items: center;
padding: var(--header-padding);
>th {
background-color: var(--header-bg-color);
user-select: none;
padding: 0;
margin: 0;
word-wrap: break-word;
white-space: normal;
position: relative;
&.sticky {
position: sticky;
z-index: 2;
}
>div {
border-bottom: 1px solid var(--header-border-color);
line-height: var(--header-line-height);
min-height: var(--row-height);
display: flex;
align-items: center;
padding: var(--header-padding);
box-sizing: border-box;
// overflow-x: hidden;
>span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
>.arrow {
width: 0;
height: 0;
top: 50%;
margin-top: calc(0px - var(--arrow-size) / 2);
right: calc(var(--arrow-size) / 2);
position: absolute;
&.asc {
border-bottom: var(--arrow-size) solid var(--dark-border-color);
}
&.desc {
border-top: var(--arrow-size) solid var(--dark-border-color);
}
&.asc,
&.desc {
border-left: var(--arrow-size) solid transparent;
border-right: var(--arrow-size) solid transparent;
}
}
>.filter {
width: var(--filter-size);
height: var(--filter-size);
top: 50%;
margin-top: calc(0px - var(--filter-size) / 2);
right: calc(var(--arrow-size) * 2 + 4px);
position: absolute;
display: flex;
>svg {
width: 100%;
height: 100%;
fill: var(--color);
opacity: .2;
transition: opacity .12s ease;
&:hover {
opacity: .8;
}
}
&.hover>svg {
opacity: .8;
}
&.active>svg {
opacity: 1;
}
}
>.spliter {
position: absolute;
height: 100%;
top: 0;
right: calc(0px - var(--split-width) /2);
width: var(--split-width);
cursor: ew-resize;
z-index: 1;
&::after {
content: '';
height: 100%;
width: 1px;
display: block;
margin: 0 auto;
transition: background-color .12s ease;
}
&:hover::after {
background-color: var(--split-border-color);
}
}
>.dragger {
position: absolute;
left: 0;
top: 0;
min-width: var(--dragger-size);
height: 100%;
background-color: var(--dragger-bg-color);
opacity: var(--dragger-opacity);
display: none;
}
>.dragger-cursor {
position: absolute;
top: 0;
height: 100%;
border: 1px solid var(--dragger-cursor-color);
box-sizing: border-box;
margin-left: 0;
opacity: var(--dragger-cursor-opacity);
display: none;
transition: left .12s ease;
&::before {
top: -1px;
border-top: var(--dragger-cursor-size) solid;
}
&::after {
bottom: -1px;
border-bottom: var(--dragger-cursor-size) solid;
}
&::before,
&::after {
content: '';
position: absolute;
left: var(--dragger-cursor-pos);
border-left: var(--dragger-cursor-size) solid transparent;
border-right: var(--dragger-cursor-size) solid transparent;
}
}
&.header-filter>div {
padding: var(--header-filter-padding);
}
}
}
}
>tbody {
color: var(--cell-fore-color);
>.ui-grid-row {
line-height: var(--line-height);
white-space: nowrap;
background-color: var(--row-bg-color);
border-bottom: 1px solid var(--cell-border-color);
box-sizing: border-box;
// overflow-x: hidden;
>span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&:hover {
background-color: var(--row-active-bg-color);
>.arrow {
width: 0;
height: 0;
top: 50%;
margin-top: calc(0px - var(--arrow-size) / 2);
right: calc(var(--arrow-size) / 2);
position: absolute;
&.asc {
border-bottom: var(--arrow-size) solid var(--dark-border-color);
}
&.desc {
border-top: var(--arrow-size) solid var(--dark-border-color);
}
&.asc,
&.desc {
border-left: var(--arrow-size) solid transparent;
border-right: var(--arrow-size) solid transparent;
}
}
>.filter {
width: var(--filter-size);
height: var(--filter-size);
top: 50%;
margin-top: calc(0px - var(--filter-size) / 2);
right: calc(var(--arrow-size) * 2 + 4px);
position: absolute;
display: flex;
>svg {
width: 100%;
height: 100%;
fill: var(--color);
opacity: .2;
transition: opacity .12s ease;
&:hover {
opacity: .8;
>td.sticky {
background-color: var(--row-active-bg-color);
}
}
&.hover>svg {
opacity: .8;
&.selected {
background-color: var(--row-selected-bg-color);
>td.sticky {
background-color: var(--row-selected-bg-color);
}
}
&.active>svg {
>td {
padding: 0;
&.sticky {
position: sticky;
z-index: 1;
background-color: var(--row-bg-color);
}
>span {
padding: var(--spacing-cell);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
>input[type="text"],
>textarea {
border: none;
box-sizing: border-box;
width: 100%;
padding: 0;
@include outline();
&:disabled {
color: var(--text-disabled-color);
}
}
>input[type="text"] {
height: var(--row-height);
text-indent: var(--text-indent);
}
>textarea {
resize: none;
line-height: var(--line-height);
display: block;
padding: var(--spacing-cell);
white-space: nowrap;
@include scrollbar();
}
.ui-check-wrapper {
display: flex;
justify-content: center;
.ui-check-inner {
&,
>svg {
transition: none;
}
}
}
.ui-drop-wrapper {
height: var(--row-height);
width: 100%;
display: flex;
flex-direction: column;
>.ui-drop-header {
border: none;
height: 100%;
>.ui-drop-text {
padding: var(--spacing-cell);
}
}
>.ui-drop-box {
top: calc(var(--row-height) + 2px);
&.slide-up {
top: unset;
bottom: calc(var(--row-height) + 2px);
}
}
}
.col-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
position: relative;
>svg {
width: 16px;
height: 16px;
fill: var(--primary-color);
transition: opacity .12s ease;
}
&:hover>svg {
opacity: .4;
}
&.disabled {
cursor: unset;
>svg {
fill: var(--header-border-color);
opacity: unset;
}
}
}
}
}
.ui-grid-hover-holder {
box-sizing: border-box;
position: absolute;
line-height: var(--line-height);
padding: var(--spacing-cell);
background-color: var(--cell-hover-bg-color);
white-space: pre;
display: flex;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear .12s, opacity .12s ease;
&.active {
visibility: visible;
opacity: 1;
}
}
>.spliter {
position: absolute;
height: 100%;
top: 0;
right: calc(0px - var(--split-width) /2);
width: var(--split-width);
cursor: ew-resize;
z-index: 1;
&::after {
content: '';
height: 100%;
width: 1px;
display: block;
margin: 0 auto;
transition: background-color .12s ease;
}
&:hover::after {
background-color: var(--split-border-color);
}
}
>.dragger {
position: absolute;
left: 0;
top: 0;
min-width: var(--dragger-size);
height: 100%;
background-color: var(--dragger-bg-color);
opacity: var(--dragger-opacity);
display: none;
}
>.dragger-cursor {
position: absolute;
top: 0;
height: 100%;
border: 1px solid var(--dragger-cursor-color);
box-sizing: border-box;
margin-left: 0;
opacity: var(--dragger-cursor-opacity);
display: none;
transition: left .12s ease;
&::before {
top: -1px;
border-top: var(--dragger-cursor-size) solid;
}
&::after {
bottom: -1px;
border-bottom: var(--dragger-cursor-size) solid;
}
&::before,
&::after {
content: '';
position: absolute;
left: var(--dragger-cursor-pos);
border-left: var(--dragger-cursor-size) solid transparent;
border-right: var(--dragger-cursor-size) solid transparent;
}
}
&.header-filter>div {
padding: var(--header-filter-padding);
}
}
}
}
>.ui-grid-body {
flex: 1 1 auto;
overflow: auto;
color: var(--cell-fore-color);
@include scrollbar();
.ui-grid-body-content {
position: absolute;
min-width: 100%;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
>.ui-grid-row {
line-height: var(--line-height);
white-space: nowrap;
background-color: var(--row-bg-color);
border-bottom: 1px solid var(--cell-border-color);
box-sizing: border-box;
&:hover {
background-color: var(--row-active-bg-color);
}
&.selected {
background-color: var(--row-selected-bg-color);
}
>td {
padding: 0;
>span {
padding: var(--spacing-cell);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
>input[type="text"],
>textarea {
border: none;
box-sizing: border-box;
width: 100%;
padding: 0;
@include outline();
&:disabled {
color: var(--text-disabled-color);
}
}
>input[type="text"] {
height: var(--row-height);
text-indent: var(--text-indent);
}
>textarea {
resize: none;
line-height: var(--line-height);
display: block;
padding: var(--spacing-cell);
white-space: nowrap;
@include scrollbar();
}
.ui-check-wrapper {
display: flex;
justify-content: center;
.ui-check-inner {
&,
>svg {
transition: none;
}
}
}
.ui-drop-wrapper {
height: var(--row-height);
width: 100%;
display: flex;
flex-direction: column;
>.ui-drop-header {
border: none;
height: 100%;
>.ui-drop-text {
padding: var(--spacing-cell);
}
}
>.ui-drop-box {
top: calc(var(--row-height) + 2px);
&.slide-up {
top: unset;
bottom: calc(var(--row-height) + 2px);
}
}
}
.col-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
position: relative;
>svg {
width: 16px;
height: 16px;
fill: var(--primary-color);
transition: opacity .12s ease;
}
&:hover>svg {
opacity: .4;
}
&.disabled {
cursor: unset;
>svg {
fill: var(--header-border-color);
opacity: unset;
}
}
}
}
}
}
.ui-grid-hover-holder {
box-sizing: border-box;
position: absolute;
line-height: var(--line-height);
padding: var(--spacing-cell);
background-color: var(--cell-hover-bg-color);
white-space: pre;
display: flex;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear .12s, opacity .12s ease;
&.active {
visibility: visible;
opacity: 1;
}
}
}
@@ -423,6 +438,7 @@
opacity: 0;
display: flex;
flex-direction: column;
z-index: 3;
&.active {
transform: scaleY(1);

View File

@@ -48,7 +48,7 @@ $buttonHeight: 28px;
border-radius: var(--corner-radius) var(--corner-radius) 0 0;
line-height: $headerLineHeight;
user-select: none;
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
color: var(--title-color);
display: flex;
align-items: center;
@@ -214,7 +214,7 @@ $buttonHeight: 28px;
text-align: center;
cursor: pointer;
user-select: none;
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
transition: opacity .12s ease;
&:focus,

View File

@@ -82,17 +82,18 @@ function filterSource(searchkeys, textkey, key, source) {
}
export class Dropdown {
#options;
_var = {};
// _var.options;
#wrapper;
#container;
#label;
// _var.wrapper;
// _var.container;
// _var.label;
#allChecked;
#source;
#lastSelected;
#selected;
#selectedList;
// _var.allChecked;
// _var.source;
// _var.lastSelected;
// _var.selected;
// _var.selectedList;
sourceFilter;
onselectedlist;
@@ -105,18 +106,18 @@ export class Dropdown {
options.valuekey ??= 'value';
options.htmlkey ??= 'html';
options.maxlength ??= 500;
this.#options = options;
this._var.options = options;
}
create() {
const options = this.#options;
const options = this._var.options;
// wrapper
const wrapper = createElement('div', 'ui-drop-wrapper');
const dropId = String(Math.random()).substring(2);
wrapper.dataset.dropId = dropId;
dropdownGlobal[dropId] = this;
this.#wrapper = wrapper;
this._var.wrapper = wrapper;
// header
const header = createElement('div', 'ui-drop-header');
@@ -131,8 +132,8 @@ export class Dropdown {
if (up || down) {
const source = this.source;
const count = source.length;
const valuekey = this.#options.valuekey;
let index = source?.indexOf(this.#selected);
const valuekey = this._var.options.valuekey;
let index = source?.indexOf(this._var.selected);
if (isNaN(index) || index < -1) {
index = -1;
} else if (index >= count) {
@@ -158,19 +159,19 @@ export class Dropdown {
this.select(target);
}
} else if (e.key === 'Tab') {
this.#dropdown(false);
this._dropdown(false);
}
});
header.addEventListener('click', () => {
if (this.disabled) {
return;
}
const active = this.#expanded;
const label = this.#label;
const active = this._expanded;
const label = this._var.label;
if (active && label.ownerDocument.activeElement === label) {
return;
}
this.#dropdown(!active);
this._dropdown(!active);
if (!active && typeof this.onexpanded === 'function') {
setTimeout(() => this.onexpanded(), 120);
}
@@ -187,21 +188,21 @@ export class Dropdown {
label.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this.#container.classList.add('active');
this._filllist(source);
this._var.container.classList.add('active');
});
label.addEventListener('blur', e => this.select(e.target.value));
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
label.addEventListener('mousedown', e => this._expanded && e.stopPropagation());
} else {
isPositive(options.tabIndex) && header.setAttribute('tabindex', options.tabIndex);
label = createElement('label', 'ui-drop-text');
}
this.#label = label;
this._var.label = label;
if (options.multiselect) {
if (Array.isArray(options.selectedlist)) {
this.selectlist(options.selectedlist, true);
} else {
this.#allChecked = true;
this._var.allChecked = true;
label.innerText = r('allItem', '( All )');
}
} else if (options.selected != null) {
@@ -214,23 +215,23 @@ export class Dropdown {
return wrapper;
}
get multiselect() { return this.#options.multiselect }
get multiselect() { return this._var.options.multiselect }
get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.ui-drop-header.disabled') != null }
get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }
set disabled(flag) {
if (this.#wrapper == null) {
if (this._var.wrapper == null) {
return;
}
if (flag) {
this.#wrapper.querySelector('.ui-drop-header').classList.add('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.add('disabled');
} else {
this.#wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
}
}
get source() {
let source = this.#source;
let source = this._var.source;
if (source == null || !Array.isArray(source)) {
if (typeof this.sourceFilter === 'function') {
source = this.sourceFilter();
@@ -238,7 +239,7 @@ export class Dropdown {
if (!Array.isArray(source)) {
source = [];
}
this.#source = source;
this._var.source = source;
}
return source;
}
@@ -247,59 +248,59 @@ export class Dropdown {
if (!Array.isArray(list)) {
return;
}
this.#source = list;
if (this.#expanded) {
setTimeout(() => this.#dropdown(), 120);
this._var.source = list;
if (this._expanded) {
setTimeout(() => this._dropdown(), 120);
}
}
get selected() { return this.#selected }
get selected() { return this._var.selected }
get selectedlist() { return this.#selectedList || [] }
get selectedlist() { return this._var.selectedList || [] }
select(selected, silence) {
if (this.#lastSelected === selected) {
if (this._var.lastSelected === selected) {
return false;
}
this.#lastSelected = selected;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
this._var.lastSelected = selected;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
let item = this.source.find(it => it[valuekey] === selected);
if (this.#options.input) {
if (this._var.options.input) {
if (item == null) {
item = { [valuekey]: selected };
}
this.#label.value = selected;
this._var.label.value = selected;
} else {
const expanded = this.#expanded;
const expanded = this._expanded;
if (expanded) {
this.#container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
this._var.container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
}
if (item == null) {
this.#selected = null;
this.#label.innerText = ' ';
this._var.selected = null;
this._var.label.innerText = ' ';
return false;
}
const html = item[htmlkey];
if (html instanceof HTMLElement) {
this.#label.replaceChildren(html.cloneNode(true));
this._var.label.replaceChildren(html.cloneNode(true));
} else {
let text = item[textkey];
if (nullOrEmpty(text)) {
text = ' ';
}
this.#label.innerText = text;
this._var.label.innerText = text;
}
if (expanded) {
const val = selected.replace(/"/g, '\\"');
const li = this.#container.querySelector(`li[data-value="${val}"]`);
const li = this._var.container.querySelector(`li[data-value="${val}"]`);
if (li != null) {
li.classList.add('selected');
}
}
}
this.#selected = item;
this._var.selected = item;
if (!silence && typeof this.onselected === 'function') {
this.onselected(item);
}
@@ -307,9 +308,9 @@ export class Dropdown {
selectlist(selectedlist, silence) {
const source = this.source;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const itemlist = selectedlist.map(v => {
let item = source.find(it => it[valuekey] === v);
if (item == null) {
@@ -318,22 +319,22 @@ export class Dropdown {
return item;
});
if (itemlist.length === 0) {
this.#selectedList = null;
this.#label.innerText = none;
this._var.selectedList = null;
this._var.label.innerText = none;
return false;
}
selectItems(this.#label, itemlist, htmlkey, textkey);
this.#selectedList = itemlist;
selectItems(this._var.label, itemlist, htmlkey, textkey);
this._var.selectedList = itemlist;
if (!silence && typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}
}
get #expanded() { return this.#container?.classList?.contains('active') }
get _expanded() { return this._var.container?.classList?.contains('active') }
#dropdown(flag = true) {
const options = this.#options;
let panel = this.#container;
_dropdown(flag = true) {
const options = this._var.options;
let panel = this._var.container;
if (panel == null) {
panel = createElement('div', 'ui-drop-box');
// search box
@@ -346,7 +347,7 @@ export class Dropdown {
input.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this._filllist(source);
})
search.append(input, createIcon('fa-light', 'search'));
panel.appendChild(search);
@@ -369,8 +370,12 @@ export class Dropdown {
});
}
panel.appendChild(list);
this.#container = panel;
this.#wrapper.appendChild(panel);
this._var.container = panel;
if (options.holder instanceof HTMLElement) {
options.holder.appendChild(panel);
} else {
this._var.wrapper.appendChild(panel);
}
}
if (flag) {
let source = this.source;
@@ -380,11 +385,11 @@ export class Dropdown {
source = filterSource(options.searchkeys, options.textkey, search.value, source);
}
}
this.#filllist(source);
this._filllist(source);
// slide direction
if (!options.slidefixed) {
let parent = options.parent ?? document.body;
let p = this.#wrapper;
let p = this._var.wrapper;
let top = p.offsetTop;
while ((p = p.parentElement) != null && p !== parent) {
top -= p.scrollTop;
@@ -401,11 +406,11 @@ export class Dropdown {
}
}
#filllist(source) {
const list = this.#container.querySelector('.ui-drop-list');
_filllist(source) {
const list = this._var.container.querySelector('.ui-drop-list');
list.replaceChildren();
const multiselect = this.multiselect;
const allchecked = this.#allChecked;
const allchecked = this._var.allChecked;
if (multiselect) {
list.appendChild(
createElement('li', null,
@@ -413,15 +418,15 @@ export class Dropdown {
label: r('allItem', '( All )'),
checked: allchecked,
customAttributes: { 'isall': '1' },
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
})
)
);
}
// TODO: virtual mode
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const selected = this.selected;
const selectedlist = this.selectedlist;
let scrolled;
@@ -448,7 +453,7 @@ export class Dropdown {
'class': 'dataitem',
'data-value': val
},
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
});
li.appendChild(box);
} else {
@@ -469,43 +474,43 @@ export class Dropdown {
}
}
#triggerselect(checkbox) {
_triggerselect(checkbox) {
let list;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
if (checkbox.getAttribute('isall') === '1') {
const allchecked = this.#allChecked = checkbox.checked;
const boxes = this.#container.querySelectorAll('input.dataitem');
const allchecked = this._var.allChecked = checkbox.checked;
const boxes = this._var.container.querySelectorAll('input.dataitem');
boxes.forEach(box => box.checked = allchecked);
list = [];
} else if (checkbox.checked) {
if (this.#container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this.#allChecked = true;
this.#container.querySelector('input[isall="1"]').checked = true;
if (this._var.container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this._var.allChecked = true;
this._var.container.querySelector('input[isall="1"]').checked = true;
list = [];
} else {
const source = this.source;
list = [...this.#container.querySelectorAll('input.dataitem:checked')]
list = [...this._var.container.querySelectorAll('input.dataitem:checked')]
.map(c => source.find(it => it[valuekey] === c.dataset.value))
.filter(it => it != null);
}
} else {
const val = checkbox.dataset.value;
if (this.#allChecked) {
this.#allChecked = false;
this.#container.querySelector('input[isall="1"]').checked = false;
if (this._var.allChecked) {
this._var.allChecked = false;
this._var.container.querySelector('input[isall="1"]').checked = false;
list = this.source.filter(it => it[valuekey] !== val);
} else {
list = this.selectedlist.filter(it => it[valuekey] !== val);
}
}
if (this.#allChecked) {
this.#label.innerText = r('allItem', '( All )');
if (this._var.allChecked) {
this._var.label.innerText = r('allItem', '( All )');
} else {
selectItems(this.#label, list, htmlkey, textkey);
selectItems(this._var.label, list, htmlkey, textkey);
}
this.#selectedList = list;
this._var.selectedList = list;
if (typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}

1
lib/ui/extension.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export function validation(element: HTMLElement, regex: RegExp) : HTMLElement

12
lib/ui/extension.js Normal file
View File

@@ -0,0 +1,12 @@
export function validation(element, regex) {
if (element instanceof HTMLElement && regex instanceof RegExp) {
element.addEventListener('change', e => {
if (regex.test(e.target.value)) {
e.target.classList.remove('validation-error');
} else {
e.target.classList.add('validation-error');
}
})
}
return element;
}

View File

@@ -102,12 +102,16 @@ const SymbolDropdown = Symbol.for('ui-dropdown');
export class GridDropdownColumn extends GridColumn {
static createEdit(trigger, col, parent) {
const drop = new Dropdown({ ...col.dropOptions, parent });
const drop = new Dropdown({
...col.dropOptions,
parent,
holder: parent
});
drop.onselected = trigger;
return drop.create();
}
static #getDrop(element) {
static _getDrop(element) {
const dropGlobal = global[SymbolDropdown];
if (dropGlobal == null) {
return null;
@@ -120,7 +124,7 @@ export class GridDropdownColumn extends GridColumn {
return drop;
}
static #getSource(item, col) {
static _getSource(item, col) {
let source = col.source;
if (typeof source === 'function') {
source = source(item);
@@ -128,7 +132,7 @@ export class GridDropdownColumn extends GridColumn {
return source;
}
static #setValue(source, element, val) {
static _setValue(source, element, val) {
const data = source?.find(v => v.value === val);
if (data != null) {
val = data.text;
@@ -138,20 +142,20 @@ export class GridDropdownColumn extends GridColumn {
static setValue(element, val, item, col) {
if (element.tagName !== 'DIV') {
let source = this.#getSource(item, col);
let source = this._getSource(item, col);
if (source instanceof Promise) {
source.then(s => this.#setValue(s, element, val));
source.then(s => this._setValue(s, element, val));
} else {
this.#setValue(source, element, val);
this._setValue(source, element, val);
}
return;
}
const drop = this.#getDrop(element);
const drop = this._getDrop(element);
if (drop == null) {
return;
}
if (drop.source == null || drop.source.length === 0) {
let source = this.#getSource(item, col);
let source = this._getSource(item, col);
if (source instanceof Promise) {
source.then(s => {
drop.source = s;
@@ -171,7 +175,7 @@ export class GridDropdownColumn extends GridColumn {
static setEnabled(element, enabled) {
super.setEnabled(element , enabled);
const drop = this.#getDrop(element);
const drop = this._getDrop(element);
if (drop == null) {
return;
}

File diff suppressed because it is too large Load Diff

View File

@@ -67,7 +67,7 @@ function getTimeLabel(time) {
}
export function createAudio(mime, url) {
if (mime === 'audio/amr' && typeof AMR !== 'undefined') {
if ((mime === 'audio/amr' || mime === '.amr') && typeof AMR !== 'undefined') {
const timestamp = createElement('span', 'ui-media-timestamp');
timestamp.textContent = '00:00 / 00:00';
let context;

View File

@@ -39,16 +39,17 @@ function trimPx(px) {
}
export class Popup {
#mask;
#option;
#bounds;
// #cursor;
_var = {};
// _var.mask;
// _var.option;
// _var.bounds;
// _var.cursor;
constructor(opts = {}) {
this.#option = opts;
this._var.option = opts;
}
get container() { return this.#mask.querySelector('.ui-popup-container') }
get container() { return this._var.mask.querySelector('.ui-popup-container') }
get rect() {
const container = this.container;
@@ -57,7 +58,7 @@ export class Popup {
}
const style = global.getComputedStyle(container);
const collapsed = container.classList.contains('ui-popup-collapse');
const bounds = this.#bounds;
const bounds = this._var.bounds;
return {
collapsed,
left: trimPx(style.left),
@@ -81,7 +82,7 @@ export class Popup {
const collapse = container.querySelector('.ui-popup-header-icons>.icon-expand');
if (r.collapsed === true) {
css.push('width: 160px', 'height: 40px');
this.#bounds = r;
this._var.bounds = r;
container.classList.add('ui-popup-collapse');
if (collapse != null) {
changeIcon(collapse, 'fa-regular', 'expand-alt');
@@ -94,7 +95,7 @@ export class Popup {
css.push(`height: ${r.height}px`);
}
container.classList.remove('ui-popup-collapse');
this.#bounds = null;
this._var.bounds = null;
if (collapse != null) {
changeIcon(collapse, 'fa-regular', 'compress-alt');
}
@@ -105,7 +106,7 @@ export class Popup {
}
close(animation = true) {
const mask = this.#mask;
const mask = this._var.mask;
if (animation) {
mask.classList.add('ui-popup-active');
mask.style.opacity = 0;
@@ -113,17 +114,17 @@ export class Popup {
} else {
mask.remove();
}
if (typeof this.#option.onMasking === 'function') {
this.#option.onMasking.call(this, false);
if (typeof this._var.option.onMasking === 'function') {
this._var.option.onMasking.call(this, false);
}
if (typeof this.#option.resolve === 'function') {
this.#option.resolve();
if (typeof this._var.option.resolve === 'function') {
this._var.option.resolve();
}
}
create() {
const mask = createElement('div', 'ui-popup-mask');
const option = this.#option;
const option = this._var.option;
if (option.mask === false) {
mask.classList.add('ui-popup-transparent');
} else if (typeof option.onMasking === 'function') {
@@ -135,7 +136,7 @@ export class Popup {
const container = createElement('div', 'ui-popup-container');
if (option.changeZIndex === true) {
container.addEventListener('mousedown', () => {
const masks = [...this.#mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask'));
const masks = [...this._var.mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask'));
let max = 200;
masks.forEach(m => {
let index;
@@ -223,16 +224,16 @@ export class Popup {
});
collapse.addEventListener('click', () => {
if (container.classList.contains('ui-popup-collapse')) {
const bounds = this.#bounds;
const bounds = this._var.bounds;
if (bounds != null) {
container.style.cssText += `width: ${bounds.width}px; height: ${bounds.height}px`;
this.#bounds = null;
this._var.bounds = null;
}
container.classList.remove('ui-popup-collapse');
changeIcon(collapse, 'fa-regular', 'compress-alt');
} else {
const rect = this.rect;
this.#bounds = rect;
this._var.bounds = rect;
container.style.cssText += `width: 160px; height: 40px`;
container.classList.add('ui-popup-collapse');
changeIcon(collapse, 'fa-regular', 'expand-alt');
@@ -310,40 +311,40 @@ export class Popup {
container.append(
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.right, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.right, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottom, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottom, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.left, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.left, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.top, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.top, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomRight, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomRight, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomLeft, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomLeft, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topLeft, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.topLeft, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topRight, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.topRight, e));
})
)
}
mask.appendChild(container);
this.#mask = mask;
this._var.mask = mask;
return mask;
}
@@ -351,7 +352,7 @@ export class Popup {
if (parent == null) {
return;
}
let mask = this.#mask ?? this.create();
let mask = this._var.mask ?? this.create();
// const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
const exists = parent.querySelectorAll('.ui-popup-mask');
let zindex = 0;
@@ -365,7 +366,7 @@ export class Popup {
mask.style.zIndex = String(zindex + 1);
}
parent.appendChild(mask);
if (this.#option.mask === false) {
if (this._var.option.mask === false) {
// calculator position
const container = this.container;
container.style.left = String((parent.offsetWidth - container.offsetWidth) / 2) + 'px';
@@ -380,9 +381,9 @@ export class Popup {
});
}
get loading() { return this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
set loading(flag) {
let loading = this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading');
let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
if (loading == null) {
return;
}
@@ -395,17 +396,17 @@ export class Popup {
}
}
#resize(mod, e) {
_resize(mod, e) {
if (e.buttons !== 1) {
return;
}
const container = this.container;
const option = this.#option;
const option = this._var.option;
if (typeof option.onResizeStarted === 'function') {
option.onResizeStarted.call(this);
}
const mask = this.#mask;
// this.#cursor = mask.style.cursor;
const mask = this._var.mask;
// this._var.cursor = mask.style.cursor;
// mask.style.cursor = Cursors[mod];
const originalX = e.clientX;
const originalY = e.clientY;
@@ -471,7 +472,7 @@ export class Popup {
const up = () => {
parent.removeEventListener('mousemove', move, { passive: false });
parent.removeEventListener('mouseup', up);
// mask.style.cursor = this.#cursor;
// mask.style.cursor = this._var.cursor;
if (resized === true && typeof option.onResizeEnded === 'function') {
option.onResizeEnded.call(this);
}

4
lib/ui/tooltip.d.ts vendored
View File

@@ -2,8 +2,8 @@
* 为元素设置一个 tooltip
* @param container 要设置 tooltip 的容器元素
* @param content 提示内容,可以是字符串也可以是 html 元素
* @param flag 是否仅在元素内容没有呈现完全时显示 tooltip
* @param parent 呈现在哪个父元素上,默认添加在 `container` 末端
* @param [flag=true] 是否仅在元素内容没有呈现完全时显示 tooltip
* @param [parent=null] 呈现在哪个父元素上,默认添加在 `container` 末端
*/
export function setTooltip(container: HTMLElement, content: string | HTMLElement, flag?: boolean, parent?: HTMLElement): HTMLElement

View File

@@ -91,6 +91,9 @@ export function setTooltip(container, content, flag = false, parent = null) {
let t = c.offsetTop;
let l = c.offsetLeft;
p = c.offsetParent;
if (p == null) {
return;
}
let lastWidth = p.clientWidth;
let lastHeight = p.clientHeight;
while (p != null) {

View File

@@ -22,7 +22,7 @@ export function getCookie(name) {
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.indexOf(name) === 0) {
if (cookie.startsWith(name)) {
return decodeURIComponent(cookie.substring(name.length));
}
}

View File

@@ -10,16 +10,16 @@ export function contains(s, key, ignoreCase) {
key = String(key);
}
if (ignoreCase) {
return s.toLowerCase().indexOf(key.toLowerCase()) >= 0;
return s.toLowerCase().includes(key.toLowerCase());
}
return s.indexOf(key) >= 0;
return s.includes(key);
}
export function endsWith(s, suffix) {
if (nullOrEmpty(s) || nullOrEmpty(suffix)) {
return false;
}
return s.indexOf(suffix) === s.length - suffix.length;
return s.endsWith(suffix); // === s.length - suffix.length;
}
export function padStart(s, num, char) {