diff --git a/lib/app/communications/contact.js b/lib/app/communications/contact.js index 6eee87b..0a7ae8a 100644 --- a/lib/app/communications/contact.js +++ b/lib/app/communications/contact.js @@ -1,4 +1,4 @@ -import { Grid, Dropdown, createElement, createCheckbox, createPopup, showAlert } from "../../ui"; +import { Grid, Dropdown, createElement, createCheckbox, Popup, showAlert } from "../../ui"; import { isEmail, nullOrEmpty, r } from "../../utility"; class Contact { @@ -85,9 +85,10 @@ class Contact { // tabIndex: tabIndex + 9 } ); - const popup = createPopup( - c == null ? r('addContact', 'Add Contact') : r('editContact', 'Edit Contact'), - createElement('div', wrapper => { + const popup = new Popup({ + onMasking: this.#option.onMasking, + title: c == null ? r('addContact', 'Add Contact') : r('editContact', 'Edit Contact'), + content: createElement('div', wrapper => { wrapper.className = 'setting-wrapper'; wrapper.style.width = '500px'; }, @@ -116,8 +117,8 @@ class Contact { contactNotes ) ), - ...buttons - ) + buttons + }) if (c != null) { contactName.value = c.Name; preferences.select(String(c.ContactPreference)); @@ -194,25 +195,28 @@ class CustomerRecordContact { } async show(title, parent = document.body) { - const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3; + // const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3; const gridContainer = createElement('div', 'selcontact-grid'); - const popup = createPopup( + const popup = new Popup({ + onMasking: this.#option.onMasking, title, - createElement('div', 'selcontact-wrapper', + content: createElement('div', 'selcontact-wrapper', gridContainer ), - { - text: r('ok', 'OK'), - key: 'ok', - trigger: () => { - if (typeof this.#option.onOk === 'function') { - return this.#option.onOk.call(this, this.#grid.source.filter(f => f.selected)); + buttons: [ + { + text: r('ok', 'OK'), + key: 'ok', + trigger: () => { + if (typeof this.#option.onOk === 'function') { + return this.#option.onOk.call(this, this.#grid.source.filter(f => f.selected)); + } } - } - }, - { text: r('cancel', 'Cancel'), key: 'cancel' } - ); + }, + { text: r('cancel', 'Cancel'), key: 'cancel' } + ] + }); const result = await popup.show(parent); // grid const grid = new Grid(gridContainer); diff --git a/lib/app/communications/customer.js b/lib/app/communications/customer.js index 10357c1..ab388e2 100644 --- a/lib/app/communications/customer.js +++ b/lib/app/communications/customer.js @@ -1,4 +1,4 @@ -import { Grid, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, createPopup, showAlert, showConfirm } from "../../ui"; +import { Grid, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup } from "../../ui"; import { r, nullOrEmpty, formatUrl, isEmail, isPhone } from "../../utility"; import { createBox } from "./lib"; import { Contact, CustomerRecordContact } from "./contact"; @@ -34,7 +34,6 @@ class CustomerCommunication { #data = {}; #gridContact; #gridWo; - #gridFollower; constructor(opt) { this.#option = opt ?? {}; @@ -468,8 +467,9 @@ class CustomerCommunication { button.appendChild(createIcon('fa-solid', 'user-edit')); setTooltip(button, r('editContacts', 'Edit Contacts')); button.addEventListener('click', () => { - const pop = createPopup( - createElement('div', div => { + const pop = new Popup({ + onMasking: option.onMasking, + title: createElement('div', div => { div.style.display = 'flex'; div.style.alignItems = 'center'; div.append( @@ -505,6 +505,7 @@ class CustomerCommunication { })); button.addEventListener('click', () => { const sel = new CustomerRecordContact({ + // onMasking: option.onMasking, contacts: [], onOk: list => { if (typeof this.#option.onSelectCRContacts === 'function') { @@ -587,6 +588,7 @@ class CustomerCommunication { })); button.addEventListener('click', () => { const add = new Contact({ + // onMasking: option.onMasking, company: !nullOrEmpty(this.#option.companyName), onSave: item => { const exists = this.#gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone); @@ -629,7 +631,7 @@ class CustomerCommunication { }) ) }), - createElement('div', null, + content: createElement('div', null, createElement('div', div => { if (nullOrEmpty(this.#option.companyName)) { div.style.display = 'none'; @@ -657,7 +659,7 @@ class CustomerCommunication { div.style.overflow = 'auto'; }) ) - ); + }); pop.show(container).then(() => { const selectedCol = This => { return { @@ -704,6 +706,7 @@ class CustomerCommunication { events: { onclick: function () { const edit = new Contact({ + // onMasking: option.onMasking, contact: this, company: !nullOrEmpty(This.#option.companyName), onSave: (item, _op) => { @@ -920,13 +923,14 @@ class CustomerCommunication { return; } const add = new Follower({ + onMasking: option.onMasking, followers: data, onOk: list => { if (typeof this.#option.onAddFollower === 'function') { const result = this.#option.onAddFollower(list); if (typeof result?.then === 'function') { return result.then(r => { - //this.#gridFollower.source = r; + // this.followers = r; return r; }); } @@ -938,220 +942,6 @@ class CustomerCommunication { add.show(title, container); }); } - return; - - const pop = createPopup( - createElement('div', div => { - div.style.display = 'flex'; - div.style.alignItems = 'center'; - div.append( - createElement('div', div => { - div.className = 'ui-popup-move'; - div.style.flex = '1 1 auto'; - div.innerText = r('editContacts', 'Edit Contacts') + '\n' + r('followers', 'Followers'); - }), - createElement('button', button => { - button.style.flex = '0 0 auto'; - button.style.backgroundColor = 'rgb(1, 199, 172)'; - button.style.marginRight = '10px'; - button.className = 'roundbtn button-add-follower'; - button.appendChild(createIcon('fa-solid', 'user-plus', { - width: '16px', - height: '16px' - })); - button.addEventListener('click', () => { - if (typeof this.#option.onInitFollower === 'function') { - this.#option.onInitFollower().then(data => { - if (typeof data === 'string') { - showAlert(r('customerRecord', 'Customer Record'), data, 'warn'); - return; - } - const add = new Follower({ - followers: data, - onOk: list => { - if (typeof this.#option.onAddFollower === 'function') { - const result = this.#option.onAddFollower(list); - if (typeof result?.then === 'function') { - return result.then(r => { - this.#gridFollower.source = r; - return r; - }); - } - return false; - } - } - }); - add.show(container); - }); - } - }); - setTooltip(button, r('addFollower', 'Add Follower')) - }) - ) - }), - createElement('div', null, - createElement('div', div => { - div.style.fontWeight = 'bold'; - div.innerText = r('contactFromRecord', 'Contacts from Customer Record'); - }), - createElement('div', div => { - div.className = 'followers-record'; - div.style.maxHeight = '400px'; - div.style.width = '675px'; - div.style.overflow = 'auto'; - }) - ) - ); - pop.show(container).then(() => { - const buttonCol = { - type: Grid.ColumnTypes.Icon, - width: 40, - align: 'center', - iconType: 'fa-light' - }; - const grid = new Grid(); - grid.height = 0; - grid.allowHtml = true; - grid.headerVisible = false; - grid.columns = [ - { - key: 'type', - type: Grid.ColumnTypes.Icon, - width: 50, - filter: c => c.SendText && c.SendEmail ? 'at' : (c.SendText ? 'comment-lines' : 'envelope'), - className: 'icon-contact-type', - iconType: 'fa-light' - }, - { key: 'Name', width: 160 }, - { key: 'Email', width: 180 }, - { key: 'MobilePhoneDisplayText', width: 130 }, - { - key: 'edit', - ...buttonCol, - text: 'edit', - tooltip: r('edit', 'Edit'), - events: { - onclick: function () { - if (typeof option.onInitFollower === 'function') { - option.onInitFollower().then(data => { - if (typeof data === 'string') { - showAlert(r('customerRecord', 'Customer Record'), data, 'warn'); - return; - } - const contact = data.find(d => d.IID === this.UserIID); - showConfirm( - r('editContactMethod', 'Edit Contact Method'), - createElement('div', 'wrapper-edit-method', - createElement('div', div => { - div.style.display = 'flex'; - div.style.justifyContent = 'center'; - div.style.marginTop = '20px'; - }, - createCheckbox({ - label: r('text', 'Text'), - checked: this.SendText && !nullOrEmpty(contact?.Mobile), - enabled: !nullOrEmpty(contact?.Mobile), - className: 'check-method-text' - }), - createCheckbox({ - label: r('email', 'Email'), - checked: this.SendEmail, - className: 'check-method-email' - }) - ) - ), - [ - { - key: 'ok', - text: r('ok', 'OK'), - trigger: (popup, button) => { - const text = popup.container.querySelector('.check-method-text>input').checked; - const email = popup.container.querySelector('.check-method-email>input').checked; - - if (!text && !email) { - return showConfirm(r('editContactMethod', 'Edit Contact Method'), r('promptRemoveFollower', 'Contact method is required. If you continue, user will be removed as a follower.'), [ - { key: 'update', text: r('updateContactMethod', 'Update Contact Method') }, - { key: 'remove', text: r('removeFollower', 'Remove Follower') } - ], 'question').then(result => { - if (result?.key === 'remove') { - return { - key: result.key, - popup - }; - } - return false; - }); - } - return { - key: button.key, - popup - }; - } - }, - { key: 'cancel', text: r('cancel', 'Cancel') } - ], - null - ).then(result => { - const key = result?.key; - if (key === 'remove') { - if (typeof option.onDeleteFollower === 'function') { - option.onDeleteFollower(result.key, this); - } - const index = grid.source.indexOf(this); - if (index >= 0) { - const source = grid.source; - source.splice(index, 1); - grid.extraRows = source.filter(c => !nullOrEmpty(c.Notes)).length; - grid.source = source; - } - } else if (key === 'ok') { - const text = result.popup.container.querySelector('.check-method-text>input').checked; - const email = result.popup.container.querySelector('.check-method-email>input').checked; - if (typeof option.onChangeFollower === 'function') { - option.onChangeFollower(result.key, this, text, email); - } - this.SendText = text; - this.SendEmail = email; - grid.refresh(); - } - }); - }); - } - } - } - }, - { - key: 'delete', - ...buttonCol, - text: 'times', - tooltip: r('delete', 'Delete'), - events: { - onclick: function () { - showConfirm( - r('deleteFollower', 'Delete Follower'), - r('promptDeleteFollower', 'Do you want to delete this follower?') - ).then(result => { - if (result?.key === 'yes') { - if (typeof option.onDeleteFollower === 'function') { - option.onDeleteFollower(result.key, this); - } - const index = grid.source.indexOf(this); - if (index >= 0) { - const source = grid.source; - source.splice(index, 1); - grid.extraRows = source.filter(c => !nullOrEmpty(c.Notes)).length; - grid.source = source; - } - } - }); - } - } - } - ]; - grid.init(pop.container.querySelector('.followers-record')); - grid.source = this.#data.followers; - this.#gridFollower = grid; - }); }); }); this.#buttonFollower = buttonEditFollower; diff --git a/lib/app/communications/follower.js b/lib/app/communications/follower.js index 03dc41f..fa2ba78 100644 --- a/lib/app/communications/follower.js +++ b/lib/app/communications/follower.js @@ -1,4 +1,4 @@ -import { Grid, createElement, createPopup } from "../../ui"; +import { Grid, createElement, Popup } from "../../ui"; import { nullOrEmpty, r, contains } from "../../utility"; class Follower { @@ -13,9 +13,10 @@ class Follower { const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3; const gridContainer = createElement('div', 'follower-grid'); - const popup = createPopup( + const popup = new Popup({ + onMasking: this.#option.onMasking, title, - createElement('div', 'follower-wrapper', + content: createElement('div', 'follower-wrapper', createElement('div', div => div.innerText = r('whoWantReceiveCustomerNotification', 'Who do you want to receive customer notifications?')), createElement('input', search => { search.type = 'text'; @@ -33,17 +34,19 @@ class Follower { }), gridContainer ), - { - text: r('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)); + buttons: [ + { + text: r('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)); + } } - } - }, - { text: r('cancel', 'Cancel'), key: 'cancel' } - ); + }, + { text: r('cancel', 'Cancel'), key: 'cancel' } + ] + }); const result = await popup.show(parent); result.querySelector('.follower-search').focus(); // grid diff --git a/lib/app/communications/style.scss b/lib/app/communications/style.scss index 84583d6..acbf88c 100644 --- a/lib/app/communications/style.scss +++ b/lib/app/communications/style.scss @@ -239,6 +239,7 @@ flex: 1 1 auto; overflow: auto; margin-top: 8px; + min-height: 100px; .item-div { margin-top: 5px; diff --git a/lib/ui/popup.js b/lib/ui/popup.js index 712a1dd..5958127 100644 --- a/lib/ui/popup.js +++ b/lib/ui/popup.js @@ -109,10 +109,13 @@ class Popup { if (animation) { mask.classList.add('ui-popup-active'); mask.style.opacity = 0; - setTimeout(() => mask.remove(), 120); + setTimeout(() => { mask.remove(); }, 120); } else { mask.remove(); } + if (typeof this.#option.onMasking === 'function') { + this.#option.onMasking.call(this, false); + } } create() { @@ -120,6 +123,8 @@ class Popup { const option = this.#option; if (option.mask === false) { mask.classList.add('ui-popup-transparent'); + } else if (typeof option.onMasking === 'function') { + this.#option.onMasking.call(this, true); } if (!isNaN(option.zIndex)) { mask.style.zIndex = String(option.zIndex); @@ -151,11 +156,6 @@ class Popup { tabIndex = 0; } container.tabIndex = tabIndex + 1; - const close = () => { - mask.classList.add('ui-popup-active'); - mask.style.opacity = 0; - setTimeout(() => mask.remove(), 120); - }; let content = option.content; if (!(content instanceof HTMLElement)) { content = createElement('div', d => d.innerText = content); @@ -174,13 +174,20 @@ class Popup { if (option.movable !== false) { const move = title.querySelector('.ui-popup-move') ?? title; move.addEventListener('mousedown', e => { + if (e.buttons !== 1) { + return; + } const x = e.clientX - container.offsetLeft; const y = e.clientY - container.offsetTop; let moved; const move = e => { - container.style.left = `${e.clientX - x}px`; - container.style.top = `${e.clientY - y}px`; - moved = true; + if (e.buttons === 1) { + container.style.left = `${e.clientX - x}px`; + container.style.top = `${e.clientY - y}px`; + moved = true; + } else { + mask.dispatchEvent(new MouseEvent('mouseup')); + } }; mask.addEventListener('mousemove', move, { passive: false }); const up = () => { @@ -229,10 +236,10 @@ class Popup { cancel.tabIndex = tabIndex + 3; cancel.addEventListener('keypress', e => { if (e.key === ' ' || e.key === 'Enter') { - close(); + this.close(); } }); - cancel.addEventListener('click', () => close()); + cancel.addEventListener('click', () => this.close()); header.appendChild(cancel); }), createElement('div', 'ui-popup-body', content, createElement('div', 'ui-popup-loading', @@ -256,14 +263,14 @@ class Popup { if (typeof result?.then === 'function') { result.then(r => { if (r !== false) { - close(); + this.close(); } - }).catch(() => { }); + }).catch(reason => console.warn(reason)); } else if (result !== false) { - close(); + this.close(); } } else { - close(); + this.close(); } }); return button; @@ -375,6 +382,9 @@ class Popup { } #resize(mod, e) { + if (e.buttons !== 1) { + return; + } const container = this.container; const option = this.#option; if (typeof option.onResizeStarted === 'function') { @@ -394,7 +404,12 @@ class Popup { const minWidth = option.minWidth ?? 200; const minHeight = option.minHeight ?? 200; let resized; + const parent = option.mask === false ? mask.parentElement : mask; const move = e => { + if (e.buttons !== 1) { + parent.dispatchEvent(new MouseEvent('mouseup')); + return; + } const offsetX = e.clientX - originalX; const offsetY = e.clientY - originalY; let width = original.width; @@ -438,7 +453,6 @@ class Popup { } resized = true; } - const parent = option.mask === false ? mask.parentElement : mask; parent.addEventListener('mousemove', move, { passive: false }); const up = () => { parent.removeEventListener('mousemove', move, { passive: false });