diff --git a/lib/app/communications/customer.js b/lib/app/communications/customer.js index 7fbd896..01b304b 100644 --- a/lib/app/communications/customer.js +++ b/lib/app/communications/customer.js @@ -276,6 +276,7 @@ export default class CustomerCommunication { } link.querySelector('input').disabled = flag; const display = flag === true ? 'none' : ''; + this._var.container.querySelector('.title-company').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; @@ -291,8 +292,13 @@ export default class CustomerCommunication { if (this._var.container == null) { return; } - this._var.container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : ''; - this._var.container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : ''; + const display = flag === true ? 'none' : ''; + const companySelector = this._var.container.querySelector('.title-company .title-company-selector'); + if (companySelector != null) { + companySelector.querySelector('.title-company-selector').style.display = display; + } + this._var.container.querySelector('.button-edit-contacts').style.display = display; + this._var.container.querySelector('.button-edit-followers').style.display = display; } /** @@ -301,12 +307,18 @@ export default class CustomerCommunication { set companyName(name) { this._var.option.companyName = name; const div = this._var.container.querySelector('.title-company'); - if (nullOrEmpty(name)) { - div.style.display = 'none'; - } else { - div.innerText = name; - div.style.display = ''; + const companyCode = div.querySelector('.title-company-code'); + if (companyCode != null) { + if (nullOrEmpty(name)) { + div.querySelector('.title-company-name').innerText = r('P_WO_NOCUSTOMERASSIGNED', 'No Customer Assigned'); + companyCode.innerText = ' /\n' + r('P_WO_SELECTCUSTOMER', 'Select Customer'); + companyCode.style.display = ''; + } else { + div.querySelector('.title-company-name').innerText = name; + companyCode.style.display = 'none'; + } } + div.style.display = ''; } /** * @param {String} code @@ -315,14 +327,23 @@ export default class CustomerCommunication { const option = this._var.option; option.companyCode = code; const div = this._var.container.querySelector('.title-company'); - if (nullOrEmpty(option.companyName)) { - div.style.display = 'none'; - } else { - div.innerText = option.companyName; - if (!nullOrEmpty(code)) - div.innerText = option.companyName + "/" + code; - div.style.display = ''; + const companyCode = div.querySelector('.title-company-code'); + if (companyCode != null) { + if (nullOrEmpty(option.companyName)) { + div.querySelector('.title-company-name').innerText = r('P_WO_NOCUSTOMERASSIGNED', 'No Customer Assigned'); + companyCode.innerText = ' /\n' + r('P_WO_SELECTCUSTOMER', 'Select Customer'); + companyCode.style.display = ''; + } else { + div.querySelector('.title-company-name').innerText = option.companyName; + if (nullOrEmpty(code)) { + companyCode.style.display = 'none'; + } else { + companyCode.innerText = ' / ' + code; + companyCode.style.display = ''; + } + } } + div.style.display = ''; } get followers() { @@ -436,18 +457,42 @@ export default class CustomerCommunication { }, createHideMessageTitleButton(this, 'showMessageHidden') ), + // option.allowCustomer === false ? createElement('div', 'title-company') : createElement('div', div => { div.className = 'title-company'; - if (nullOrEmpty(option.companyName)) { - div.style.display = 'none'; - } else { - if (nullOrEmpty(option.companyCode)) { - div.innerText = option.companyName; + // div.style.display = 'none'; + }, + createElement('span', span => { + span.className = 'title-company-name'; + if (nullOrEmpty(option.companyName)) { + span.innerText = r('P_WO_NOCUSTOMERASSIGNED', 'No Customer Assigned'); } else { - div.innerText = option.companyName + "/" + option.companyCode; + span.innerText = option.companyName; } - } - }) + }), + createElement('span', span => { + span.className = 'title-company-code'; + if (nullOrEmpty(option.companyName)) { + span.innerText = ' /\n' + r('P_WO_SELECTCUSTOMER', 'Select Customer'); + } else if (!nullOrEmpty(option.companyCode)) { + span.innerText = ' / ' + option.companyCode; + } else { + span.style.display = 'none'; + } + }), + createElement('span', span => { + span.className = 'title-company-selector'; + if (option.recordReadonly) { + span.style.display = 'none'; + } + setTooltip(span, r('P_WO_SELECTCUSTOMER', 'Select Customer')); + if (typeof option.onAddCompany === 'function') { + span.addEventListener('click', () => option.onAddCompany.call(this)); + } + }, + createIcon('fa-light', 'search') + ) + ) ), [ setTooltip(checkAutoUpdate, r('P_CU_AUTOUPDATESENABLED', 'Auto Updates Enabled')), @@ -626,98 +671,67 @@ export default class CustomerCommunication { button.appendChild(createIcon('fa-solid', 'user-edit')); setTooltip(button, r('P_CU_EDITCONTACTS', 'Edit Contacts')); button.addEventListener('click', () => { - const pop = new Popup({ - onMasking: option.onMasking, - title: createElement('div', div => { - div.style.display = 'flex'; - div.style.alignItems = 'center'; - div.style.padding = '10px 0 6px 12px'; - div.append( - createElement('div', div => { - div.className = 'ui-popup-move'; - div.style.flex = '1 1 auto'; - }, - createElement('div', div => div.innerText = r('P_CU_EDITCONTACTS', 'Edit Contacts')), + const editContacts = () => { + const pop = new Popup({ + onMasking: option.onMasking, + title: createElement('div', div => { + div.style.display = 'flex'; + div.style.alignItems = 'center'; + div.style.padding = '10px 0 6px 12px'; + div.append( createElement('div', div => { - div.className = 'title-company'; - div.style.maxWidth = '540px'; - if (nullOrEmpty(option.companyName)) { - div.style.display = 'none'; - } else { - if (nullOrEmpty(option.companyCode)) { - div.innerText = option.companyName; - } else { - div.innerText = option.companyName + "/" + option.companyCode; + div.className = 'ui-popup-move'; + div.style.flex = '1 1 auto'; + }, + 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'; } - } - }) - ), - createElement('button', button => { - button.style.flex = '0 0 auto'; - button.style.backgroundColor = 'rgb(1, 199, 172)'; - button.style.marginRight = '10px'; - button.className = 'roundbtn button-from-customer-record'; - if (recordReadonly) { - button.style.display = 'none'; - } - button.appendChild(createIcon('fa-solid', 'handshake', { - width: '16px', - height: '16px' - })); - button.addEventListener('click', () => { - const sel = new CustomerRecordContact({ - getText: option.getText, - // onMasking: option.onMasking, - contacts: [], - onOk: list => { - if (typeof option.onSelectCRContacts === 'function') { - list?.map(c => { - delete c.selected; - return c; - }); - const result = option.onSelectCRContacts(list); + }, + createElement('span', span => { + span.className = 'title-company-name'; + if (!nullOrEmpty(option.companyName)) { + span.innerText = option.companyName; } - 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; + }), + createElement('span', span => { + span.className = 'title-company-code'; + if (!nullOrEmpty(option.companyCode)) { + span.innerText = '/' + option.companyCode; + } + }) + ) + ), + createElement('button', button => { + button.style.flex = '0 0 auto'; + button.style.backgroundColor = 'rgb(1, 199, 172)'; + button.style.marginRight = '10px'; + button.className = 'roundbtn button-from-customer-record'; + if (recordReadonly || nullOrEmpty(option.companyName)) { + button.style.display = 'none'; + } + button.appendChild(createIcon('fa-solid', 'handshake', { + width: '16px', + height: '16px' + })); + button.addEventListener('click', () => { + const sel = new CustomerRecordContact({ + getText: option.getText, + // onMasking: option.onMasking, + contacts: [], + onOk: list => { + if (typeof option.onSelectCRContacts === 'function') { + list?.map(c => { + delete c.selected; + return c; + }); + const result = option.onSelectCRContacts(list); } - if (typeof c.selected === 'undefined') { - c.selected = true; - } - return c; - }); - this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { - if (c.OptOut || c.OptOut_BC) { - return c; - } - if (typeof c.selected === 'undefined') { - c.selected = true; - } - return c; - }); - } - }); - var title = r('P_CU_SELECTFROMCUSTOMERRECORD', 'Select from Customer Record'); - sel.show(title, container); - - if (typeof option.onOpenSelectCRContacts === 'function') { - const result = option.onOpenSelectCRContacts(); - if (typeof result?.then === 'function') { - return result.then(r => { - r.map(c => { - for (let cc of this._var.data.contacts) { - if (c.Id === cc.Id) { - c.selected = true; - break; - } - } - if (typeof c.selected === 'undefined') { - c.selected = false; - } - return c; - }); - this._var.data.contacts.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; } @@ -726,339 +740,402 @@ export default class CustomerCommunication { } return c; }); + this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } + return c; + }); + } + }); + var title = r('P_CU_SELECTFROMCUSTOMERRECORD', 'Select from Customer Record'); + sel.show(title, container); - sel.source = r; - return r; - }); - } - return false; - } - }); - setTooltip(button, r('P_CU_SELECTFROMCUSTOMERRECORD', 'Select from Customer Record')) - }), - 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-contact'; - if (recordReadonly) { - button.style.display = 'none'; - } - button.appendChild(createIcon('fa-solid', 'user-plus', { - width: '16px', - height: '16px' - })); - button.addEventListener('click', () => { - const add = new Contact({ - getText: option.getText, - // onMasking: option.onMasking, - company: !nullOrEmpty(option.companyName), - onSave: item => { - 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; - } - if (typeof option.onSave === 'function') { - const result = option.onSave(item, true); - if (typeof result?.then === 'function') { - return result.then(r => { - this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => { - if (c.OptOut || c.OptOut_BC) { - return c; - } - if (typeof c.selected === 'undefined') { + if (typeof option.onOpenSelectCRContacts === 'function') { + const result = option.onOpenSelectCRContacts(); + if (typeof result?.then === 'function') { + return result.then(r => { + r.map(c => { + for (let cc of this._var.data.contacts) { + if (c.Id === cc.Id) { c.selected = true; + break; } - return c; - }); - this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { - if (c.OptOut || c.OptOut_BC) { - return c; - } - if (typeof c.selected === 'undefined') { - c.selected = true; - } - return c; - }); - return r; + } + if (typeof c.selected === 'undefined') { + c.selected = false; + } + return c; }); - } - return false; + this._var.data.contacts.filter(c => c.Id >= 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } + return c; + }); + + sel.source = r; + return r; + }); } + return false; } }); - add.show(container); - }); - setTooltip(button, r('P_CR_ADDCONTACT', 'Add Contact')) + setTooltip(button, r('P_CU_SELECTFROMCUSTOMERRECORD', 'Select from Customer Record')) + }), + 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-contact'; + if (recordReadonly) { + button.style.display = 'none'; + } + button.appendChild(createIcon('fa-solid', 'user-plus', { + width: '16px', + height: '16px' + })); + button.addEventListener('click', () => { + const add = new Contact({ + getText: option.getText, + // onMasking: option.onMasking, + company: !nullOrEmpty(option.companyName), + onSave: item => { + 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; + } + if (typeof option.onSave === 'function') { + const result = option.onSave(item, true); + if (typeof result?.then === 'function') { + return result.then(r => { + this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } + return c; + }); + this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } + return c; + }); + return r; + }); + } + return false; + } + } + }); + add.show(container); + }); + setTooltip(button, r('P_CR_ADDCONTACT', 'Add Contact')) + }) + ) + }), + content: createElement('div', null, + createElement('div', div => { + if (nullOrEmpty(option.companyName)) { + div.style.display = 'none'; + } + div.style.fontWeight = 'bold'; + div.innerText = r('P_CU_CONTACTSFROMCUSTOMERRECORD', 'Contacts from Customer Record'); + }), + createElement('div', div => { + if (nullOrEmpty(option.companyName)) { + div.style.display = 'none'; + } + div.className = 'contacts-record'; + // div.style.maxHeight = '200px'; + div.style.width = '675px'; + // div.style.overflow = 'auto'; + }), + createElement('div', div => { + div.style.fontWeight = 'bold'; + div.innerText = r('P_CU_CONTACTSNOTCUSTOMERRECORD', 'Contacts not on Customer Record'); + }), + createElement('div', div => { + div.className = 'contacts-wo'; + // div.style.maxHeight = '200px'; + div.style.width = '675px'; + // div.style.overflow = 'auto'; }) ) - }), - content: createElement('div', null, - createElement('div', div => { - if (nullOrEmpty(option.companyName)) { - div.style.display = 'none'; + }); + pop.show(container).then(() => { + const selectedCol = This => { + return { + key: 'selected', + type: Grid.ColumnTypes.Checkbox, + width: 50, + enabled: item => !item.OptOut && !item.OptOut_BC, + onChanged: function () { + if (typeof option.onChanged === 'function') { + 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') } - div.style.fontWeight = 'bold'; - div.innerText = r('P_CU_CONTACTSFROMCUSTOMERRECORD', 'Contacts from Customer Record'); - }), - createElement('div', div => { - if (nullOrEmpty(option.companyName)) { - div.style.display = 'none'; - } - div.className = 'contacts-record'; - // div.style.maxHeight = '200px'; - div.style.width = '675px'; - // div.style.overflow = 'auto'; - }), - createElement('div', div => { - div.style.fontWeight = 'bold'; - div.innerText = r('P_CU_CONTACTSNOTCUSTOMERRECORD', 'Contacts not on Customer Record'); - }), - createElement('div', div => { - div.className = 'contacts-wo'; - // div.style.maxHeight = '200px'; - div.style.width = '675px'; - // div.style.overflow = 'auto'; - }) - ) - }); - pop.show(container).then(() => { - const selectedCol = This => { - return { - key: 'selected', - type: Grid.ColumnTypes.Checkbox, + }; + const iconCol = { + key: 'type', + type: Grid.ColumnTypes.Icon, width: 50, - enabled: item => !item.OptOut && !item.OptOut_BC, - onChanged: function () { - if (typeof option.onChanged === 'function') { - option.onChanged([...This._var.gridContact.source, ...This._var.gridWo.source]); + filter: c => { + switch (String(c.ContactPreference)) { + case '0': return 'comment-lines'; + case '2': return 'mobile'; + default: return 'envelope'; } }, - tooltip: item => item.selected ? r('P_CU_OPTEDIN', 'Opted In') : r('P_CU_OPTEDOUT', 'Opted Out') - } - }; - const iconCol = { - key: 'type', - type: Grid.ColumnTypes.Icon, - width: 50, - filter: c => { - switch (String(c.ContactPreference)) { - case '0': return 'comment-lines'; - case '2': return 'mobile'; - default: return 'envelope'; - } - }, - className: 'icon-contact-type', - iconType: 'fa-light' - }; - const nameCol = { key: 'Name', type: NoteCol, width: 160 }; - const buttonCol = { - type: Grid.ColumnTypes.Icon, - width: 40, - visible: !recordReadonly, - align: 'center', - iconType: 'fa-light' - }; - const createEditCol = (This) => { - return { - key: 'edit', - ...buttonCol, - text: 'edit', - tooltip: r('P_WO_EDIT', 'Edit'), - events: { - onclick: function () { - const edit = new Contact({ - getText: option.getText, - // onMasking: option.onMasking, - contact: this, - company: !nullOrEmpty(option.companyName), - onSave: (item, _op) => { - const exists = - 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; - } - if (typeof option.onSave === 'function') { - const result = option.onSave(item); - if (typeof result?.then === 'function') { - return result.then(r => { - This._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => { - if (c.OptOut || c.OptOut_BC) { - return c; - } - if (typeof c.selected === 'undefined') { - c.selected = true; - } - return c; - }); - This._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { - if (c.OptOut || c.OptOut_BC) { - return c; - } - if (typeof c.selected === 'undefined') { - c.selected = true; - } - return c; - }); - return r; - }); + className: 'icon-contact-type', + iconType: 'fa-light' + }; + const nameCol = { key: 'Name', type: NoteCol, width: 160 }; + const buttonCol = { + type: Grid.ColumnTypes.Icon, + width: 40, + visible: !recordReadonly, + align: 'center', + iconType: 'fa-light' + }; + const createEditCol = (This) => { + return { + key: 'edit', + ...buttonCol, + text: 'edit', + tooltip: r('P_WO_EDIT', 'Edit'), + events: { + onclick: function () { + const edit = new Contact({ + getText: option.getText, + // onMasking: option.onMasking, + contact: this, + company: !nullOrEmpty(option.companyName), + onSave: (item, _op) => { + const exists = + 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; + } + if (typeof option.onSave === 'function') { + const result = option.onSave(item); + if (typeof result?.then === 'function') { + return result.then(r => { + This._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } + return c; + }); + This._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } + return c; + }); + return r; + }); + } + return false; } - return false; } - } - }); - edit.show(container); + }); + edit.show(container); + } } } - } - }; - // contacts from customer record - const grid = new Grid(); - grid.height = 0; - grid.allowHtml = true; - grid.headerVisible = false; - grid.columns = [ - selectedCol(this), - iconCol, - nameCol, - { key: 'Email', width: 180 }, - { key: 'MobilePhoneDisplayText', width: 130 }, - createEditCol(this), - { - key: 'delete', - ...buttonCol, - text: 'times', - tooltip: r('P_WO_DELETE', 'Delete'), - events: { - onclick: function () { - showConfirm( - r('P_CU_REMOVECONTACT', 'Remove Contact'), - createElement('div', null, - createElement('div', div => { - div.style.paddingLeft = '16px'; - div.innerText = r('P_CU_REMOVEFROM', 'Remove {name} from').replace('{name}', this.Name); - }), - createElement('div', div => { - div.style.display = 'flex'; - div.style.justifyContent = 'center'; - div.style.marginTop = '10px'; - }, - createRadiobox({ - name: 'remove-type', - label: r('P_CUSTOMERRECORD', 'Customer Record'), - checked: true, - className: 'radio-customer-record' + }; + // contacts from customer record + const grid = new Grid(); + grid.height = 0; + grid.allowHtml = true; + grid.headerVisible = false; + grid.columns = [ + selectedCol(this), + iconCol, + nameCol, + { key: 'Email', width: 180 }, + { key: 'MobilePhoneDisplayText', width: 130 }, + createEditCol(this), + { + key: 'delete', + ...buttonCol, + text: 'times', + tooltip: r('P_WO_DELETE', 'Delete'), + events: { + onclick: function () { + showConfirm( + r('P_CU_REMOVECONTACT', 'Remove Contact'), + createElement('div', null, + createElement('div', div => { + div.style.paddingLeft = '16px'; + div.innerText = r('P_CU_REMOVEFROM', 'Remove {name} from').replace('{name}', this.Name); }), - createRadiobox({ - name: 'remove-type', - label: r('P_WORKORDER', 'Work Order') - }) - ) - ), - [ - { key: 'ok', text: r('P_WO_OK', 'OK') }, - { key: 'cancel', text: r('P_WO_CANCEL', 'Cancel') } - ] - ).then(result => { - if (result?.key === 'ok') { - const isRecord = result.popup.container.querySelector('.radio-customer-record>input').checked; - if (typeof option.onDelete === 'function') { - option.onDelete(result.key, this, isRecord); + createElement('div', div => { + div.style.display = 'flex'; + div.style.justifyContent = 'center'; + div.style.marginTop = '10px'; + }, + createRadiobox({ + name: 'remove-type', + label: r('P_CUSTOMERRECORD', 'Customer Record'), + checked: true, + className: 'radio-customer-record' + }), + createRadiobox({ + name: 'remove-type', + label: r('P_WORKORDER', 'Work Order') + }) + ) + ), + [ + { key: 'ok', text: r('P_WO_OK', 'OK') }, + { key: 'cancel', text: r('P_WO_CANCEL', 'Cancel') } + ] + ).then(result => { + if (result?.key === 'ok') { + const isRecord = result.popup.container.querySelector('.radio-customer-record>input').checked; + if (typeof option.onDelete === 'function') { + option.onDelete(result.key, this, isRecord); + } + 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; + } } - 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('.contacts-record')); - const customerRecords = this._var.data.contacts.filter(c => c.Id >= 0).map(c => { - if (c.OptOut || c.OptOut_BC) { + ]; + grid.init(pop.container.querySelector('.contacts-record')); + const customerRecords = this._var.data.contacts.filter(c => c.Id >= 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } return c; - } - if (typeof c.selected === 'undefined') { - c.selected = true; - } - return c; - }); - grid.extraRows = customerRecords.filter(c => !nullOrEmpty(c.Notes)).length; - grid.source = customerRecords; - grid.onSelectedRowChanged = index => { - if (index >= 0 && this._var.gridWo.selectedIndexes?.length > 0) { - this._var.gridWo.selectedIndexes = []; - } - }; - this._var.gridContact = grid; + }); + grid.extraRows = customerRecords.filter(c => !nullOrEmpty(c.Notes)).length; + grid.source = customerRecords; + grid.onSelectedRowChanged = index => { + if (index >= 0 && this._var.gridWo.selectedIndexes?.length > 0) { + this._var.gridWo.selectedIndexes = []; + } + }; + this._var.gridContact = grid; - // contacts from work order only - const gridWo = new Grid(); - gridWo.height = 0; - gridWo.allowHtml = true; - gridWo.headerVisible = false; - gridWo.columns = [ - selectedCol(this), - iconCol, - nameCol, - { key: 'Email', width: 180 }, - { key: 'MobilePhoneDisplayText', width: 130 }, - createEditCol(this), - { - key: 'delete', - ...buttonCol, - text: 'times', - tooltip: r('P_WO_DELETE', 'Delete'), - events: { - onclick: function () { - showConfirm(r('P_CU_REMOVECONTACT', 'Remove Contact'), r('P_CU_REMOVEFROMWORKORDER', 'You are removing {name} from work order.\n\nDo you want to Continue?').replace('{name}', this.Name), [ - { key: 'continue', text: r('P_JS_CONTINUE', 'Continue') }, - { key: 'cancel', text: r('P_WO_CANCEL', 'Cancel') } - ]).then(result => { - if (result?.key === 'continue') { - if (typeof option.onDelete === 'function') { - option.onDelete(result.key, this); + // contacts from work order only + const gridWo = new Grid(); + gridWo.height = 0; + gridWo.allowHtml = true; + gridWo.headerVisible = false; + gridWo.columns = [ + selectedCol(this), + iconCol, + nameCol, + { key: 'Email', width: 180 }, + { key: 'MobilePhoneDisplayText', width: 130 }, + createEditCol(this), + { + key: 'delete', + ...buttonCol, + text: 'times', + tooltip: r('P_WO_DELETE', 'Delete'), + events: { + onclick: function () { + showConfirm(r('P_CU_REMOVECONTACT', 'Remove Contact'), r('P_CU_REMOVEFROMWORKORDER', 'You are removing {name} from work order.\n\nDo you want to Continue?').replace('{name}', this.Name), [ + { key: 'continue', text: r('P_JS_CONTINUE', 'Continue') }, + { key: 'cancel', text: r('P_WO_CANCEL', 'Cancel') } + ]).then(result => { + if (result?.key === 'continue') { + if (typeof option.onDelete === 'function') { + option.onDelete(result.key, this); + } + const index = gridWo.source.indexOf(this); + if (index >= 0) { + const source = gridWo.source; + source.splice(index, 1); + gridWo.extraRows = source.filter(c => !nullOrEmpty(c.Notes)).length; + gridWo.source = source; + } } - const index = gridWo.source.indexOf(this); - if (index >= 0) { - const source = gridWo.source; - source.splice(index, 1); - gridWo.extraRows = source.filter(c => !nullOrEmpty(c.Notes)).length; - gridWo.source = source; - } - } - }); + }); + } } } - } - ]; - gridWo.init(pop.container.querySelector('.contacts-wo')); - const workOrderOnly = this._var.data.contacts.filter(c => c.Id < 0).map(c => { - if (c.OptOut || c.OptOut_BC) { + ]; + gridWo.init(pop.container.querySelector('.contacts-wo')); + const workOrderOnly = this._var.data.contacts.filter(c => c.Id < 0).map(c => { + if (c.OptOut || c.OptOut_BC) { + return c; + } + if (typeof c.selected === 'undefined') { + c.selected = true; + } return c; - } - if (typeof c.selected === 'undefined') { - c.selected = true; - } - return c; + }); + gridWo.extraRows = workOrderOnly.filter(c => !nullOrEmpty(c.Notes)).length; + gridWo.source = workOrderOnly; + gridWo.onSelectedRowChanged = index => { + if (index >= 0 && this._var.gridContact.selectedIndexes?.length > 0) { + this._var.gridContact.selectedIndexes = []; + } + }; + this._var.gridWo = gridWo; }); - gridWo.extraRows = workOrderOnly.filter(c => !nullOrEmpty(c.Notes)).length; - gridWo.source = workOrderOnly; - gridWo.onSelectedRowChanged = index => { - if (index >= 0 && this._var.gridContact.selectedIndexes?.length > 0) { - this._var.gridContact.selectedIndexes = []; + }; + if (nullOrEmpty(option.companyName)) { + showConfirm( + r('P_CU_EDITCONTACTS', 'Edit Contacts'), + r('P_CUSTOMER_ADDCOMPANYPROMPT', 'There is no company associated with this work order. Would you like to add one?\n\nIf no company is indicated, contacts must be added as "Work Order Only".'), + [ + { key: 'add', text: r('P_CUSTOMER_ADDCOMPANY', 'Add Company') }, + { key: 'skip', text: r('P_CUSTOMER_SKIPTHISSTEP', 'Skip This Step') } + ] + ).then(r => { + if (r == null) { + return; } - }; - this._var.gridWo = gridWo; - }); + if (r.key === 'add') { + if (typeof option.onAddCompany === 'function') { + option.onAddCompany.call(this); + } + } else { + editContacts(); + } + }); + } else { + editContacts(); + } }); }) ), diff --git a/lib/app/communications/style.scss b/lib/app/communications/style.scss index 4f975a4..d14c5d2 100644 --- a/lib/app/communications/style.scss +++ b/lib/app/communications/style.scss @@ -90,6 +90,33 @@ >div { flex: 1 1 auto; + + >.title-company { + line-height: 1rem; + padding: 2px 10px; + // background-color: rgba(0, 0, 0, .15); + + >.title-company-name { + font-weight: bold; + } + + >.title-company-selector { + cursor: pointer; + vertical-align: middle; + + &:hover { + background-color: #ccc; + } + + >svg { + width: 14px; + height: 14px; + fill: rgb(123, 28, 33); + margin: 0 5px 3px; + vertical-align: middle; + } + } + } } >.title-functions { diff --git a/lib/element.js b/lib/element.js index 8241ca1..ca4faf3 100644 --- a/lib/element.js +++ b/lib/element.js @@ -1,6 +1,8 @@ import "./element/style.scss"; import ScheduleItem from "./element/schedule"; +import AddWorkOrder from "./element/addworkorder"; export { - ScheduleItem + ScheduleItem, + AddWorkOrder } \ No newline at end of file diff --git a/lib/element/addworkorder.js b/lib/element/addworkorder.js new file mode 100644 index 0000000..3aab3e1 --- /dev/null +++ b/lib/element/addworkorder.js @@ -0,0 +1,144 @@ +import { createElement, Dropdown, Popup, validation, toDateValue, showAlert } from "../ui"; +import { r as lang, nullOrEmpty } from "../utility"; + +let r = lang; + +const iconWorkOrder = 'data:image/png;base64,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'; + +export default class AddWorkOrder { + _var = { + option: { + isCustomerRecord: true + }, + /** + * @private + * @type {HTMLElement} + */ + container: null + }; + + constructor(opt) { + opt ??= {}; + this._var.option = opt; + const getText = opt.getText; + if (typeof getText === 'function') { + r = getText; + } + } + + get complaint() { + return this._var.container.querySelector('.wo-complaint')?.value; + } + + show() { + const option = this._var.option; + var title = r('P_WO_OPENWORKORDER', 'Open Work Order'); + const container = createElement('div', 'open-wo-container', + createElement('div', 'open-wo-header', + createElement('img', img => img.src = iconWorkOrder), + createElement('h3', h3 => h3.innerText = title) + ), + createElement('div', 'open-wo-content', + createElement('div', 'wo-line', + createElement('span', span => { + span.className = 'wo-title wo-title-required'; + span.innerText = r('P_WO_ASSET_COLON', 'Asset:'); + }) + ), + createElement('div', 'wo-line', + createElement('span', span => { + span.className = 'wo-title wo-title-required'; + span.innerText = r('P_WO_COMPLAINTCOLON', 'Complaint:'); + }) + ), + createElement('div', 'wo-line wo-sub-line', + createElement('textarea', textarea => { + textarea.className = 'ui-text wo-complaint'; + textarea.placeholder = r('P_WO_ENTERCOMPLAINT', 'Enter Complaint'); + }) + ), + createElement('div', 'wo-line', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_WORKORDERTYPE_COLON', 'Work Order Type:'); + }) + ), + createElement('div', 'wo-line wo-customer-record', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_COMPANYNAME_COLON', 'Company Name:'); + }) + ), + createElement('div', 'wo-line', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_STATUS_COLON', 'Status:'); + }) + ), + createElement('div', 'wo-line wo-sub-line wo-status-closed', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_COMPLETEDDATE_COLON', 'Completed Date:'); + }) + ), + createElement('div', 'wo-line wo-sub-line wo-status-closed', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_HOURS_COLON', 'Hours:'); + }) + ), + createElement('div', 'wo-line wo-sub-line wo-status-closed', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_ODOMETER_COLON', 'Odometer:'); + }) + ), + createElement('div', 'wo-line', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_ASSIGNEDTO_COLON', 'Assigned To:'); + }) + ), + createElement('div', 'wo-line wo-customer-record', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_ADVISOR_COLON', 'Advisor:'); + }) + ), + createElement('div', 'wo-line wo-customer-record', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_LOCATION_COLON', 'Location:'); + }) + ), + createElement('div', 'wo-line wo-customer-record', + createElement('span', span => { + span.className = 'wo-title'; + span.innerText = r('P_WO_DEPARTMENT_COLON', 'Department:'); + }) + ) + ) + ); + this._var.container = container; + const popup = new Popup({ + title, + content: container, + buttons: [ + { + key: 'open', + text: title, + trigger: () => { + if (nullOrEmpty(this.complaint)) { + showAlert(title, r('P_WO_COMPLAINTREQUIRED', 'Complaint is required.')).then(() => container.querySelector('.wo-complaint')?.focus()); + return false; + } + } + }, + { text: r('P_WO_CANCEL', 'Cancel') } + ] + }); + popup.create(); + popup.rect = { width: 600 }; + return popup.show(); + } +} \ No newline at end of file diff --git a/lib/element/style.scss b/lib/element/style.scss index 5c9c758..103b127 100644 --- a/lib/element/style.scss +++ b/lib/element/style.scss @@ -94,4 +94,49 @@ .ui-drop-wrapper>.ui-drop-header { height: 24px; } +} + +.open-wo-container { + display: flex; + flex-direction: column; + + >.open-wo-header { + flex: 0 0 auto; + display: flex; + align-items: flex-start; + + >h3 { + font-size: var(--font-header-size); + font-family: var(--header-font-family); + margin: 10px; + } + } + + >.open-wo-content { + flex: 1 1 auto; + margin: 10px; + + >.wo-line { + line-height: var(--settings-line-height); + + &.wo-sub-line { + margin-left: 10px; + } + + >.wo-title { + &.wo-title-required { + &::after { + content: '*'; + color: var(--red-color); + } + } + } + + >.ui-text { + width: 100%; + height: 80px; + box-sizing: border-box; + } + } + } } \ No newline at end of file diff --git a/lib/ui.js b/lib/ui.js index 4212002..cc7b2ce 100644 --- a/lib/ui.js +++ b/lib/ui.js @@ -14,6 +14,38 @@ import { validation, convertCssStyle } from './ui/extension'; import { createDateInput, toDateValue, getFormatter, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date'; import * as utility from './utility'; +function requestAnimationFrame(callback) { + if (typeof utility.global.requestAnimationFrame === 'function') { + utility.global.requestAnimationFrame(callback); + } else { + setTimeout(callback, 0); + } +} + +function scrollLeft() { + const n = document.documentElement; + return (utility.global.scrollX || n.scrollLeft) - (n.clientLeft || 0); +} + +function scrollTop() { + const n = document.documentElement; + return (utility.global.scrollY || n.scrollTop) - (n.clientTop || 0); +} + +/** + * @private + * @param {HTMLElement} e + */ +function offset(e) { + const rect = e.getBoundingClientRect(); + return { + top: rect.top + scrollTop(), + left: rect.left + scrollLeft(), + height: rect.height, + width: rect.width + }; +} + export { createElement, // icon @@ -63,5 +95,8 @@ export { validation, convertCssStyle, // utility - utility + utility, + // functions + requestAnimationFrame, + offset } diff --git a/lib/ui/css/checkbox.scss b/lib/ui/css/checkbox.scss index 9f86564..2ea8e62 100644 --- a/lib/ui/css/checkbox.scss +++ b/lib/ui/css/checkbox.scss @@ -51,7 +51,6 @@ } .ui-switch { - position: relative; line-height: 1rem; user-select: none; cursor: pointer; @@ -59,6 +58,7 @@ >span:first-of-type { display: inline-flex; align-items: center; + position: relative; &::before { content: ''; @@ -76,6 +76,7 @@ content: ''; position: absolute; left: 1px; + top: calc(50% - 7px); width: 14px; height: 14px; background-color: white; diff --git a/lib/ui/css/variables/definition.scss b/lib/ui/css/variables/definition.scss index 11e6bee..9ab7be8 100644 --- a/lib/ui/css/variables/definition.scss +++ b/lib/ui/css/variables/definition.scss @@ -38,11 +38,14 @@ --border-radius: 2px; --text-indent: 4px; --line-height: 18px; + --settings-line-height: 28px; --font-size: .8125rem; // 13px --font-smaller-size: .75rem; // 12px --font-larger-size: .875rem; // 14px + --font-header-size: 1.5rem; // 24px --font-family: "Franklin Gothic Book", "San Francisco", "Segoe UI", "Open Sans", "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei UI", sans-serif; + --header-font-family: Arial, sans-serif; } /*@media (prefers-color-scheme: dark) { diff --git a/lib/ui/dropdown.js b/lib/ui/dropdown.js index 1c38913..04633ef 100644 --- a/lib/ui/dropdown.js +++ b/lib/ui/dropdown.js @@ -1,5 +1,5 @@ import './css/dropdown.scss'; -import { r } from "../utility/lgres"; +import { r as lang } from "../utility/lgres"; import { contains, nullOrEmpty } from "../utility/strings"; import { global, isPositive, throttle } from "../utility"; import { createElement } from "../functions"; @@ -9,6 +9,7 @@ import { createIcon } from "./icon" const SymbolDropdown = Symbol.for('ui-dropdown'); const DropdownItemHeight = 30; +let r = lang; let dropdownGlobal = global[SymbolDropdown]; if (dropdownGlobal == null) { @@ -51,8 +52,13 @@ if (dropdownGlobal == null) { }); } -function selectItems(label, itemlist, htmlkey, textkey) { - const htmls = itemlist.map(it => it[htmlkey]); +function selectItems(label, itemlist, template, htmlkey, textkey) { + let htmls; + if (typeof template === 'function') { + htmls = itemlist.map(it => template.call(this, it)); + } else { + htmls = itemlist.map(it => it[htmlkey]); + } if (htmls.some(it => it instanceof HTMLElement)) { label.replaceChildren(...htmls.filter(it => it != null).map(it => it.cloneNode(true))); } else { @@ -87,6 +93,7 @@ function filterSource(searchkeys, textkey, key, source) { * @property {string} [textKey=text] - 文本关键字 * @property {string} [valueKey=value] - 值关键字 * @property {string} [htmlKey=html] - 源码显示的关键字 + * @property {Function} [htmlTemplate] - 模板创建函数 * @property {number} [maxLength=500] - 最大输入长度 * @property {boolean} [multiSelect] - 是否允许多选 * @property {string} [selected] - 选中值 @@ -129,6 +136,10 @@ export class Dropdown { options.htmlKey ??= 'html'; options.maxLength ??= 500; this._var.options = options; + const getText = options.getText; + if (typeof getText === 'function') { + r = getText; + } } create() { @@ -300,6 +311,7 @@ export class Dropdown { this._var.lastSelected = selected; const valuekey = this._var.options.valueKey; const textkey = this._var.options.textKey; + const template = this._var.options.htmlTemplate; const htmlkey = this._var.options.htmlKey; let item = this.source.find(it => (ignoreCase ? String(it[valuekey]).toLowerCase() : String(it[valuekey])) === selected); if (this._var.options.input) { @@ -317,7 +329,12 @@ export class Dropdown { this._var.label.innerText = ' '; return false; } - const html = item[htmlkey]; + let html; + if (typeof template === 'function') { + html = template.call(this, item); + } else { + html = item[htmlkey]; + } if (html instanceof HTMLElement) { this._var.label.replaceChildren(html.cloneNode(true)); } else if (typeof html === 'string') { @@ -354,6 +371,7 @@ export class Dropdown { const source = this.source; const valuekey = this._var.options.valueKey; const textkey = this._var.options.textKey; + const template = this._var.options.htmlTemplate; const htmlkey = this._var.options.htmlKey; const itemlist = selectedlist.map(a => { const v = typeof a === 'string' ? a : String(a); @@ -371,7 +389,7 @@ export class Dropdown { this._var.label.innerText = r('none', '( None )'); return false; } - selectItems(this._var.label, itemlist, htmlkey, textkey); + selectItems(this._var.label, itemlist, template, htmlkey, textkey); this._var.selectedList = itemlist; if (!silence && typeof this.onSelectedList === 'function') { this.onSelectedList(itemlist); @@ -563,6 +581,7 @@ export class Dropdown { const multiselect = this.multiSelect; const valuekey = this._var.options.valueKey; const textkey = this._var.options.textKey; + const template = this._var.options.htmlTemplate; const htmlkey = this._var.options.htmlKey; const selected = this.selected; let scrolled; @@ -575,7 +594,12 @@ export class Dropdown { li.dataset.value = val; li.title = item[textkey]; let label; - const html = item[htmlkey]; + let html; + if (typeof template === 'function') { + html = template.call(this, item); + } else { + html = item[htmlkey]; + } if (html instanceof HTMLElement) { label = html; } else if (typeof html === 'string') { @@ -617,6 +641,7 @@ export class Dropdown { let list; const valuekey = this._var.options.valueKey; const textkey = this._var.options.textKey; + const template = this._var.options.htmlTemplate; const htmlkey = this._var.options.htmlKey; if (checkbox.getAttribute('isall') === '1') { const allchecked = this._var.allChecked = checkbox.checked; @@ -653,7 +678,7 @@ export class Dropdown { if (this._var.allChecked) { this._var.label.innerText = r('allItem', '( All )'); } else { - selectItems(this._var.label, list, htmlkey, textkey); + selectItems(this._var.label, list, template, htmlkey, textkey); } this._var.selectedList = list; if (typeof this.onSelectedList === 'function') { diff --git a/lib/ui/grid/grid.js b/lib/ui/grid/grid.js index cf2cddc..d5e16e2 100644 --- a/lib/ui/grid/grid.js +++ b/lib/ui/grid/grid.js @@ -71,13 +71,13 @@ let r = lang; /** * 键值字典 * @template T - * @typedef {{[key: string]: T}} KeyMap + * @typedef {Map} KeyMap */ /** * 索引字典 * @template T - * @typedef {{[index: number]: T}} IndexMap + * @typedef {Map} IndexMap */ /** @@ -1077,7 +1077,7 @@ export class Grid { /** * 行发生变化时触发的事件 * @event - * @param {("update" | "add" | "remove")} action - 变动类型 + * @param {("update" | "add" | "remove" | "drag")} action - 变动类型 * @param {GridRowItem[]} items - 发生变动的行对象 * @param {(number | number[])} indexes - 变动的索引集合 * @this Grid @@ -1244,7 +1244,7 @@ export class Grid { * 获取已过滤的数据数组,或者设置数据并刷新列表 * @type {GridRowItem[]} */ - get source() { return this._var.currentSource?.map(s => s.values) } + get source() { return this._var.currentSource?.map(s => s.values) ?? [] } set source(list) { if (!Array.isArray(list)) { throw new Error('source is not an Array.') @@ -1463,6 +1463,9 @@ export class Grid { this.removeItem(src); this.addItem(row, target); this.selectedIndexes = [e.ctrlKey ? target : target - 1]; + if (typeof this.onRowChanged === 'function') { + this.onRowChanged('drag', [row], target); + } }); } container.replaceChildren(grid); @@ -2862,6 +2865,9 @@ export class Grid { this.removeItem(src); this.addItem(row, target); this.selectedIndexes = [target]; + if (typeof this.onRowChanged === 'function') { + this.onRowChanged('drag', [row], target); + } }); } const virtualRow = { cells: {} }; @@ -3648,6 +3654,8 @@ export class Grid { } ]).then(result => { if (result?.key === 'yes') { + const sortCol = this.sortArray.find(c => c.column === col.key); + this.sortDirection = sortCol?.order === 'asc' ? -1 : 1; this._onDoHeaderSort(col); } }); diff --git a/lib/ui/tooltip.js b/lib/ui/tooltip.js index f17abef..b27d550 100644 --- a/lib/ui/tooltip.js +++ b/lib/ui/tooltip.js @@ -1,5 +1,6 @@ import './css/tooltip.scss'; import { createElement } from "../functions"; +import { global } from '../utility'; const pointerHeight = 12; @@ -98,7 +99,7 @@ export function setTooltip(container, content, flag = false, parent = null) { let lastWidth = p.clientWidth; let lastHeight = p.clientHeight; while (p != null) { - const overflow = window.getComputedStyle(p).overflow; + const overflow = global.getComputedStyle(p).overflow; if (overflow !== 'visible') { break; } diff --git a/lib/utility.js b/lib/utility.js index bebafdb..ca03be1 100644 --- a/lib/utility.js +++ b/lib/utility.js @@ -1,5 +1,5 @@ import { getCookie, setCookie, deleteCookie } from "./utility/cookie"; -import { init, r, lang } from "./utility/lgres"; +import { domLoad, init, r, lang } from "./utility/lgres"; import { get, post, upload } from "./utility/request"; import { nullOrEmpty, contains, endsWith, padStart, formatUrl, escapeHtml, escapeEmoji } from "./utility/strings"; @@ -100,5 +100,6 @@ export { truncate, isEmail, isPhone, - verifyPassword + verifyPassword, + domLoad } \ No newline at end of file diff --git a/lib/utility/lgres.js b/lib/utility/lgres.js index bedccac..47d27eb 100644 --- a/lib/utility/lgres.js +++ b/lib/utility/lgres.js @@ -109,6 +109,20 @@ function applyLanguage(dom, result) { } } +export function domLoad() { + if (document.readyState === 'loading') { + return new Promise((resolve, reject) => { + let tid = setTimeout(() => reject('timeout'), 30000); + document.addEventListener('DOMContentLoaded', () => { + clearTimeout(tid); + tid = void 0; + resolve(); + }); + }); + } + return Promise.resolve(); +} + export async function init(dom = document.body, options = {}) { const lgid = getCurrentLgId(); let lgres = localStorage.getItem(getStorageKey(lgid)); @@ -126,20 +140,7 @@ export async function init(dom = document.body, options = {}) { } try { - if (document.readyState === 'loading') { - return await new Promise((resolve, reject) => { - let tid = setTimeout(() => reject('timeout'), 30000); - document.addEventListener('DOMContentLoaded', () => { - clearTimeout(tid); - tid = void 0; - if (typeof options.callback === 'function') { - options.callback(result); - } - applyLanguage(dom, result); - resolve(result); - }); - }); - } + await domLoad(); if (typeof options.callback === 'function') { options.callback(result); } diff --git a/package-lock.json b/package-lock.json index 2c05e12..6718dd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,9 @@ "docdash": "^2.0.2", "jsdoc": "^4.0.3", "postcss-preset-env": "^9.5.14", - "sass": "^1.77.2", + "sass": "^1.77.4", "typedoc": "^0.25.13", - "vite": "^5.2.11", + "vite": "^5.2.12", "vite-plugin-externals": "^0.6.2" } }, @@ -1865,9 +1865,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001621", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz", - "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==", + "version": "1.0.30001627", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz", + "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", "dev": true, "funding": [ { @@ -2080,9 +2080,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.783", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.783.tgz", - "integrity": "sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==", + "version": "1.4.789", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.789.tgz", + "integrity": "sha512-0VbyiaXoT++Fi2vHGo2ThOeS6X3vgRCWrjPeO2FeIAWL6ItiSJ9BqlH8LfCXe3X1IdcG+S0iLoNaxQWhfZoGzQ==", "dev": true }, "node_modules/entities": { @@ -3366,9 +3366,9 @@ } }, "node_modules/sass": { - "version": "1.77.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz", - "integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==", + "version": "1.77.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.4.tgz", + "integrity": "sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -3503,9 +3503,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, "node_modules/typedoc": { @@ -3601,9 +3601,9 @@ "dev": true }, "node_modules/vite": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", - "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", + "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", "dev": true, "dependencies": { "esbuild": "^0.20.1", diff --git a/package.json b/package.json index b462870..d7b1184 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "docdash": "^2.0.2", "jsdoc": "^4.0.3", "postcss-preset-env": "^9.5.14", - "sass": "^1.77.2", + "sass": "^1.77.4", "typedoc": "^0.25.13", - "vite": "^5.2.11", + "vite": "^5.2.12", "vite-plugin-externals": "^0.6.2" } }