sync
This commit is contained in:
@@ -51,6 +51,11 @@ export default class CustomerRecordComment {
|
|||||||
this._var.enter.disabled = flag;
|
this._var.enter.disabled = flag;
|
||||||
this._var.container.querySelector('.button-send-message').disabled = flag;
|
this._var.container.querySelector('.button-send-message').disabled = flag;
|
||||||
}
|
}
|
||||||
|
get replyMsgId() { return this._var.replymsgid || -1 }
|
||||||
|
set replyMsgId(v) {
|
||||||
|
this._var.replymsgid = null;
|
||||||
|
this._var.replymsgctrl.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} flag
|
* @param {boolean} flag
|
||||||
@@ -111,6 +116,24 @@ export default class CustomerRecordComment {
|
|||||||
container.appendChild(
|
container.appendChild(
|
||||||
createElement('div', 'message-bar',
|
createElement('div', 'message-bar',
|
||||||
enter,
|
enter,
|
||||||
|
createElement('div', div => {
|
||||||
|
div.className = 'customer-reply';
|
||||||
|
div.style.display = 'none';
|
||||||
|
this._var.replymsgctrl = div;
|
||||||
|
},
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'reply-user';
|
||||||
|
}),
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'reply-msg';
|
||||||
|
}),
|
||||||
|
createElement('layer', layer => {
|
||||||
|
layer.appendChild(createIcon('fa-light', 'times'));
|
||||||
|
layer.addEventListener('click', () => {
|
||||||
|
this.replyMsgId = null;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
),
|
||||||
createElement('div', div => div.style.textAlign = 'right',
|
createElement('div', div => div.style.textAlign = 'right',
|
||||||
createElement('div', 'prompt-count'),
|
createElement('div', 'prompt-count'),
|
||||||
createElement('button', button => {
|
createElement('button', button => {
|
||||||
@@ -124,7 +147,8 @@ export default class CustomerRecordComment {
|
|||||||
setTooltip(button, r('FLTL_02301', 'Post Note'));
|
setTooltip(button, r('FLTL_02301', 'Post Note'));
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
if (typeof this._var.option.onAddComment === 'function') {
|
if (typeof this._var.option.onAddComment === 'function') {
|
||||||
this._var.option.onAddComment(this.text);
|
this._var.option.onAddComment(this.text, this._var.replymsgid);
|
||||||
|
this.replyMsgId = null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -158,6 +182,13 @@ export default class CustomerRecordComment {
|
|||||||
div.innerText = comment.UserName;
|
div.innerText = comment.UserName;
|
||||||
}));
|
}));
|
||||||
const content = createElement('div', 'item-content');
|
const content = createElement('div', 'item-content');
|
||||||
|
if (comment.ReplyMessage) {
|
||||||
|
const reply = createElement('div', div => {
|
||||||
|
div.className = 'reply';
|
||||||
|
div.innerHTML = comment.ReplyMessage.Comment;
|
||||||
|
});
|
||||||
|
content.appendChild(reply);
|
||||||
|
}
|
||||||
const mmsParts = createElement('div', div => div.style.display = 'none');
|
const mmsParts = createElement('div', div => div.style.display = 'none');
|
||||||
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Comment)), mmsParts));
|
content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Comment)), mmsParts));
|
||||||
if (comment.MMSParts?.length > 0) {
|
if (comment.MMSParts?.length > 0) {
|
||||||
|
|||||||
@@ -188,10 +188,7 @@ export default class CustomerCommunication {
|
|||||||
this._var.contacts.replaceChildren();
|
this._var.contacts.replaceChildren();
|
||||||
if (contacts?.length > 0) {
|
if (contacts?.length > 0) {
|
||||||
var cs = contacts.sort(function (a, b) {
|
var cs = contacts.sort(function (a, b) {
|
||||||
if (a.Name == b.Name) {
|
return String(a.Name).localeCompare(String(b.Name));
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return a.Name > b.Name ? 1 : -1;
|
|
||||||
});
|
});
|
||||||
const messages = this._var.data.messages;
|
const messages = this._var.data.messages;
|
||||||
if (this._var.contactsUpdated !== true && messages?.length > 0) {
|
if (this._var.contactsUpdated !== true && messages?.length > 0) {
|
||||||
@@ -308,6 +305,8 @@ export default class CustomerCommunication {
|
|||||||
*/
|
*/
|
||||||
set companyName(name) {
|
set companyName(name) {
|
||||||
this._var.option.companyName = name;
|
this._var.option.companyName = name;
|
||||||
|
this._var.container.querySelector('.button-edit-contacts').style.display =
|
||||||
|
this._var.option.recordReadonly && nullOrEmpty(name) ? 'none' : '';
|
||||||
const div = this._var.container.querySelector('.title-company');
|
const div = this._var.container.querySelector('.title-company');
|
||||||
const companyCode = div.querySelector('.title-company-code');
|
const companyCode = div.querySelector('.title-company-code');
|
||||||
if (companyCode != null) {
|
if (companyCode != null) {
|
||||||
@@ -674,7 +673,7 @@ export default class CustomerCommunication {
|
|||||||
createElement('button', button => {
|
createElement('button', button => {
|
||||||
button.className = 'roundbtn button-edit-contacts';
|
button.className = 'roundbtn button-edit-contacts';
|
||||||
button.style.backgroundColor = 'rgb(1, 199, 172)';
|
button.style.backgroundColor = 'rgb(1, 199, 172)';
|
||||||
if (readonly === true) {
|
if (readonly === true || (recordReadonly && nullOrEmpty(option.companyName))) {
|
||||||
button.style.display = 'none';
|
button.style.display = 'none';
|
||||||
}
|
}
|
||||||
button.appendChild(createIcon('fa-solid', 'user-edit'));
|
button.appendChild(createIcon('fa-solid', 'user-edit'));
|
||||||
|
|||||||
@@ -106,6 +106,11 @@ export default class InternalComment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
get replyMsgId() { return this._var.replymsgid || -1 }
|
||||||
|
set replyMsgId(v) {
|
||||||
|
this._var.replymsgid = null;
|
||||||
|
this._var.replymsgctrl.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} flag
|
* @param {boolean} flag
|
||||||
@@ -192,6 +197,24 @@ export default class InternalComment {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
enter,
|
enter,
|
||||||
|
createElement('div', div => {
|
||||||
|
div.className = 'customer-reply';
|
||||||
|
div.style.display = 'none';
|
||||||
|
this._var.replymsgctrl = div;
|
||||||
|
},
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'reply-user';
|
||||||
|
}),
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'reply-msg';
|
||||||
|
}),
|
||||||
|
createElement('layer', layer => {
|
||||||
|
layer.appendChild(createIcon('fa-light', 'times'));
|
||||||
|
layer.addEventListener('click', () => {
|
||||||
|
this.replyMsgId = null;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
),
|
||||||
createElement('div', div => div.style.textAlign = 'right',
|
createElement('div', div => div.style.textAlign = 'right',
|
||||||
createElement('div', 'customer-left',
|
createElement('div', 'customer-left',
|
||||||
createElement('div', 'file-selector',
|
createElement('div', 'file-selector',
|
||||||
@@ -280,7 +303,7 @@ export default class InternalComment {
|
|||||||
}
|
}
|
||||||
if (typeof option.onAddComment === 'function') {
|
if (typeof option.onAddComment === 'function') {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
option.onAddComment(this.text, this.file);
|
option.onAddComment(this.text, this.file, this.replyMsgId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -323,6 +346,15 @@ export default class InternalComment {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const content = createElement('div', 'item-content');
|
const content = createElement('div', 'item-content');
|
||||||
|
if (comment.ReplyMessage) {
|
||||||
|
const reply = createElement('div', div => {
|
||||||
|
div.className = 'reply';
|
||||||
|
div.innerHTML = comment.ReplyMessage.Message;
|
||||||
|
if (comment.ReplyMessage.MessageType !== 2)
|
||||||
|
div.title = comment.ReplyMessage.Sender + " " + comment.ReplyMessage.TimeStr + "\r\n" + comment.ReplyMessage.Message;
|
||||||
|
});
|
||||||
|
content.appendChild(reply);
|
||||||
|
}
|
||||||
const mmsParts = createElement('div', div => div.style.display = 'none');
|
const mmsParts = createElement('div', div => div.style.display = 'none');
|
||||||
content.appendChild(createElement('span', span => {
|
content.appendChild(createElement('span', span => {
|
||||||
if (comment.MessageType === 2) {
|
if (comment.MessageType === 2) {
|
||||||
|
|||||||
@@ -511,6 +511,33 @@ export function createHideMessageCommentTail(This, optionName, comment, commentT
|
|||||||
span.appendChild(icon);
|
span.appendChild(icon);
|
||||||
span.addEventListener('click', () => hisFunc(comment.Id));
|
span.addEventListener('click', () => hisFunc(comment.Id));
|
||||||
}),
|
}),
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'sbutton iconreply';
|
||||||
|
span.style.padding = '0';
|
||||||
|
span.style.fontSize = '12px';
|
||||||
|
setTooltip(span, option?.getText('FLTL_03432', 'Reply'));
|
||||||
|
span.style.display = comment.AllowReply ? '' : 'none';
|
||||||
|
if (comment.AllowReply) {
|
||||||
|
span.addEventListener('click', function () {
|
||||||
|
This._var.replymsgid = comment.Id;
|
||||||
|
This._var.replymsgctrl.querySelector('.reply-user').innerText = (comment.Sender || comment.UserName) + ":";
|
||||||
|
var msg = comment.Message || comment.Comment;
|
||||||
|
if (comment.MessageType == 2)
|
||||||
|
msg = option?.getText('FLTL_00491', 'Call Log');
|
||||||
|
This._var.replymsgctrl.querySelector('.reply-msg').textContent = msg;
|
||||||
|
This._var.replymsgctrl.style.display = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
createElement('span', span => {
|
||||||
|
span.style.margin = '0 5px 0 0';
|
||||||
|
span.style.color = '#2594fd';
|
||||||
|
span.style.display = comment.ReplyCount > 0 ? '' : 'none';
|
||||||
|
if (comment.ReplyCount > 1)
|
||||||
|
span.innerText = comment.ReplyCount + ' ' + option?.getText('FLTL_03433', 'Replies');
|
||||||
|
else
|
||||||
|
span.innerText = comment.ReplyCount + ' ' + option?.getText('FLTL_03432', 'Reply');
|
||||||
|
}),
|
||||||
createElement('span', span => {
|
createElement('span', span => {
|
||||||
span.innerText = comment[commentTime];
|
span.innerText = comment[commentTime];
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -311,6 +311,33 @@
|
|||||||
@include outline();
|
@include outline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .customer-reply {
|
||||||
|
background-color: #d3d3d3;
|
||||||
|
padding: 5px 10px 0 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 0 6px 3px 6px;
|
||||||
|
display: flex;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 22px;
|
||||||
|
|
||||||
|
> .reply-msg {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: inline;
|
||||||
|
margin-left: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
> layer {
|
||||||
|
> .ui-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
fill: var(--secondary-link-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
padding: 0 10px 10px;
|
padding: 0 10px 10px;
|
||||||
|
|
||||||
@@ -435,7 +462,8 @@
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
max-width: 240px;
|
max-width: 240px;
|
||||||
background-color: rgb(244, 244, 244);
|
/*background-color: rgb(244, 244, 244);*/
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
|
||||||
audio[controls] {
|
audio[controls] {
|
||||||
width: 220px;
|
width: 220px;
|
||||||
@@ -483,6 +511,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reply {
|
||||||
|
background-color: white;
|
||||||
|
padding: 5px;
|
||||||
|
border: solid 2px #f2f2f2;
|
||||||
|
border-left: solid 2px lightgray;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
max-height: 36px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-time {
|
.item-time {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createElement, Dropdown, Popup, showAlert, createIcon, DateSelector, showConfirm, Grid, OptionBase } from "../ui";
|
import { createElement, Dropdown, Popup, showAlert, createIcon, DateSelector, showConfirm, Grid, OptionBase, createCheckbox } from "../ui";
|
||||||
import { nullOrEmpty } from "../utility";
|
import { nullOrEmpty } from "../utility";
|
||||||
import AssetSelector from "./assetSelector";
|
import AssetSelector from "./assetSelector";
|
||||||
|
|
||||||
@@ -131,6 +131,11 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
* @private
|
* @private
|
||||||
* @type {Dropdown}
|
* @type {Dropdown}
|
||||||
*/
|
*/
|
||||||
|
dropAssetStatus: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {DateSelector}
|
||||||
|
*/
|
||||||
dropAssignedTo: null,
|
dropAssignedTo: null,
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -221,6 +226,18 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
Status: -1
|
Status: -1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let assetstatus = el.dropAssetStatus.selected;
|
||||||
|
if (assetstatus != null) {
|
||||||
|
assetstatus = {
|
||||||
|
AssetCustomStatus: assetstatus.Id,
|
||||||
|
AssetCustomStatusType: assetstatus.Type,
|
||||||
|
AssetCustomStatusName: assetstatus.Name
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
assetstatus = {
|
||||||
|
AssetCustomStatus: -1
|
||||||
|
};
|
||||||
|
}
|
||||||
let machine = this._var.asset;
|
let machine = this._var.asset;
|
||||||
if (machine == null) {
|
if (machine == null) {
|
||||||
showAlert(title, this.r('FLTL_00311', 'Asset cannot be empty.')).then(() => this._var.container.querySelector('.wo-asset>svg')?.focus());
|
showAlert(title, this.r('FLTL_00311', 'Asset cannot be empty.')).then(() => this._var.container.querySelector('.wo-asset>svg')?.focus());
|
||||||
@@ -238,7 +255,7 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
AssignedTo: el.dropAssignedTo.selected?.IID ?? '',
|
AssignedTo: el.dropAssignedTo.selected?.IID ?? '',
|
||||||
AdvisorId: el.dropAdvisor.selected?.Key ?? '',
|
AdvisorId: el.dropAdvisor.selected?.Key ?? '',
|
||||||
LocationId: el.dropLocation.selected?.ID || -1,
|
LocationId: el.dropLocation.selected?.ID || -1,
|
||||||
DepartmentId: el.dropDepartment.selected?.Id || -1,
|
DepartmentId: el.dropDepartment.checked?.Id || -1,
|
||||||
AssetID: machine.Id,
|
AssetID: machine.Id,
|
||||||
VIN: machine.VIN,
|
VIN: machine.VIN,
|
||||||
AssetName: machine.DisplayName,
|
AssetName: machine.DisplayName,
|
||||||
@@ -250,7 +267,8 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
LaborCost: -1,
|
LaborCost: -1,
|
||||||
HourlyRate: -1,
|
HourlyRate: -1,
|
||||||
InspectionTemplateId: -1,
|
InspectionTemplateId: -1,
|
||||||
...status
|
...status,
|
||||||
|
...assetstatus
|
||||||
};
|
};
|
||||||
item.CustomerId = this._var.customer?.Id ?? -1;
|
item.CustomerId = this._var.customer?.Id ?? -1;
|
||||||
item.Contacts = this._var.contacts ?? [];
|
item.Contacts = this._var.contacts ?? [];
|
||||||
@@ -259,19 +277,18 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
showAlert(title, this.r('FLTL_00602', 'Complaint is required.')).then(() => el.textComplaint.focus());
|
showAlert(title, this.r('FLTL_00602', 'Complaint is required.')).then(() => el.textComplaint.focus());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
item.MeterType = machine.OnRoad ? 'Odometer' : 'HourMeter';
|
||||||
if (el.dropStatus.selected?.Completed) {
|
if (el.dropStatus.selected?.Completed) {
|
||||||
if (!el.dateCompleted.element.validity.valid) {
|
if (!el.dateCompleted.element.validity.valid) {
|
||||||
showAlert(title, this.r('FLTL_00613', 'Completed Date cannot be empty.')).then(() => el.dateCompleted.element.focus());
|
showAlert(title, this.r('FLTL_00613', 'Completed Date cannot be empty.')).then(() => el.dateCompleted.element.focus());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (machine.OnRoad) {
|
if (machine.OnRoad) {
|
||||||
item.MeterType = 'Odometer';
|
|
||||||
if (nullOrEmpty(item.Odometer) || isNaN(item.Odometer) || item.Odometer < 0) {
|
if (nullOrEmpty(item.Odometer) || isNaN(item.Odometer) || item.Odometer < 0) {
|
||||||
showAlert(title, this.r('FLTL_02044', 'Odometer format error.')).then(() => el.inputOdometer.focus());
|
showAlert(title, this.r('FLTL_02044', 'Odometer format error.')).then(() => el.inputOdometer.focus());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item.MeterType = 'HourMeter';
|
|
||||||
if (nullOrEmpty(item.HourMeter) || isNaN(item.HourMeter) || item.HourMeter < 0) {
|
if (nullOrEmpty(item.HourMeter) || isNaN(item.HourMeter) || item.HourMeter < 0) {
|
||||||
showAlert(title, this.r('FLTL_01516', 'Hour Meter format error.')).then(() => el.inputHours.focus());
|
showAlert(title, this.r('FLTL_01516', 'Hour Meter format error.')).then(() => el.inputHours.focus());
|
||||||
return null;
|
return null;
|
||||||
@@ -284,6 +301,9 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
async show() {
|
async show() {
|
||||||
const option = this._option;
|
const option = this._option;
|
||||||
const allowCustomer = option.allowCustomer === true;
|
const allowCustomer = option.allowCustomer === true;
|
||||||
|
const allowCommunicate = option.allowCommunicate === true;
|
||||||
|
const commReadOnly = option.commReadOnly === true;
|
||||||
|
const msgVariables = option.msgVariables || [];
|
||||||
const title = this.r('FLTL_02078', 'Open Work Order');
|
const title = this.r('FLTL_02078', 'Open Work Order');
|
||||||
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 textComplaint = createElement('textarea', textarea => {
|
const textComplaint = createElement('textarea', textarea => {
|
||||||
@@ -333,6 +353,16 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
{ value: 'Mile', text: this.r('FLTL_01922', 'Mile') },
|
{ value: 'Mile', text: this.r('FLTL_01922', 'Mile') },
|
||||||
{ value: 'Kilometre', text: this.r('FLTL_01694', 'Kilometer') }
|
{ value: 'Kilometre', text: this.r('FLTL_01694', 'Kilometer') }
|
||||||
]
|
]
|
||||||
|
const dropAssetStatus = new Dropdown({
|
||||||
|
tabIndex: tabIndex + 5,
|
||||||
|
htmlTemplate: it => createElement('span', 'wo-color-line',
|
||||||
|
createElement('em', em => em.style.backgroundColor = it.Color),
|
||||||
|
createElement('label', label => label.innerText = it.Name)
|
||||||
|
),
|
||||||
|
search: true,
|
||||||
|
textKey: 'Name',
|
||||||
|
valueKey: 'Id'
|
||||||
|
});
|
||||||
const dropAssignedTo = new Dropdown({
|
const dropAssignedTo = new Dropdown({
|
||||||
tabIndex: tabIndex + 10,
|
tabIndex: tabIndex + 10,
|
||||||
selected: '',
|
selected: '',
|
||||||
@@ -360,6 +390,18 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
valueKey: 'Id',
|
valueKey: 'Id',
|
||||||
textKey: 'Name'
|
textKey: 'Name'
|
||||||
});
|
});
|
||||||
|
const dropMSGVariables = new Dropdown({
|
||||||
|
selected: -1,
|
||||||
|
search: true,
|
||||||
|
valueKey: 'Id',
|
||||||
|
textKey: 'Name'
|
||||||
|
});
|
||||||
|
dropMSGVariables.source = msgVariables.map(v => { return { Id: v.Name, Name: v.Name }; });
|
||||||
|
const textMessage = createElement('textarea', textarea => {
|
||||||
|
textarea.className = 'ui-text wo-message';
|
||||||
|
});
|
||||||
|
const dropMSGVariablesElement = dropMSGVariables.create();
|
||||||
|
dropMSGVariablesElement.style.width = '200px';
|
||||||
|
|
||||||
// save variables
|
// save variables
|
||||||
this._var.el = {
|
this._var.el = {
|
||||||
@@ -370,6 +412,7 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
inputHours,
|
inputHours,
|
||||||
inputOdometer,
|
inputOdometer,
|
||||||
dropOdometerUnit,
|
dropOdometerUnit,
|
||||||
|
dropAssetStatus,
|
||||||
dropAssignedTo,
|
dropAssignedTo,
|
||||||
dropAdvisor,
|
dropAdvisor,
|
||||||
dropLocation,
|
dropLocation,
|
||||||
@@ -415,6 +458,7 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
el.inputOdometer.value = '';
|
el.inputOdometer.value = '';
|
||||||
el.dropOdometerUnit.select('Mile');
|
el.dropOdometerUnit.select('Mile');
|
||||||
}
|
}
|
||||||
|
el.dropAssetStatus.select(it.CustomStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._var.asset = it;
|
this._var.asset = it;
|
||||||
@@ -537,6 +581,11 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
patternValidation(inputOdometer, '\\d+\\.?\\d*'),
|
patternValidation(inputOdometer, '\\d+\\.?\\d*'),
|
||||||
dropOdometerUnit.create()
|
dropOdometerUnit.create()
|
||||||
),
|
),
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'wo-title';
|
||||||
|
span.innerText = this.r('FLTL_03590', 'Asset Availability:');
|
||||||
|
}),
|
||||||
|
dropAssetStatus.create(),
|
||||||
createElement('span', span => {
|
createElement('span', span => {
|
||||||
span.className = 'wo-title';
|
span.className = 'wo-title';
|
||||||
span.innerText = this.r('FLTL_00382', 'Assigned Tech:');
|
span.innerText = this.r('FLTL_00382', 'Assigned Tech:');
|
||||||
@@ -606,7 +655,7 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
key: 'open',
|
key: 'open',
|
||||||
text: title,
|
text: this.r('FLTL_00700', 'Create Work Order'),
|
||||||
trigger: async () => {
|
trigger: async () => {
|
||||||
popup.loading = true;
|
popup.loading = true;
|
||||||
try {
|
try {
|
||||||
@@ -620,7 +669,7 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const next = await showConfirm(title, this.r('FLTL_02992', 'The selected asset is hidden and a work order cannot be created.') + '\n\n' + this.r('FLTL_00999', 'Do you wish to "Un-Hide" the asset?'), [
|
const next = await showConfirm(title, this.r('FLTL_02992', 'The selected asset is hidden and a work order cannot be created.') + '\n\n' + this.r('FLTL_00999', 'Do you wish to "Un-Hide" the asset?'), [
|
||||||
{ key: 'unhide', text: this.r('FLTL_03136', 'Unhide') },
|
{ key: 'unhide', text: this.r('FLTL_03378', 'Yes') },
|
||||||
{ key: 'cancel', text: this.r('FLTL_00502', 'Cancel Work Order') }
|
{ key: 'cancel', text: this.r('FLTL_00502', 'Cancel Work Order') }
|
||||||
]);
|
]);
|
||||||
if (next.result !== 'unhide') {
|
if (next.result !== 'unhide') {
|
||||||
@@ -672,10 +721,142 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
if (next !== 'create') {
|
if (next !== 'create') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((item.Status == 100 || item.StatusType == 100)
|
||||||
|
&& (item.AssetCustomStatus == 10 || item.AssetCustomStatusType == 10)) {
|
||||||
|
let msgdiv = createElement('div', div => {
|
||||||
|
div.style.minWidth = "300px";
|
||||||
|
div.style.marginLeft = "50px";
|
||||||
|
div.style.fontSize = "14px";
|
||||||
|
let msghtml = this.r('FLTL_03591', 'Asset Availability is set to');
|
||||||
|
msghtml += ' <b style="color:#3d7892;">' + item.AssetCustomStatusName + '</b>';
|
||||||
|
msghtml += '<br/><br/>' + this.r('FLTL_03592', 'Would you like to update?');
|
||||||
|
div.innerHTML = msghtml;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const nextassetstatus = await showConfirm(title, msgdiv, [
|
||||||
|
{ key: 'yes', text: this.r('FLTL_03378', 'Yes') },
|
||||||
|
{ key: 'no', text: this.r('FLTL_01978', 'No') }
|
||||||
|
]);
|
||||||
|
if (nextassetstatus.result !== 'no') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const el = this._var.el;
|
||||||
|
if (item.Status > 0 && item.CustomerId > 0 && item.StatusMessage && allowCommunicate && !commReadOnly) {
|
||||||
|
const next = await new Promise(resolve => {
|
||||||
|
let addButton;
|
||||||
|
const chkSend = createCheckbox({
|
||||||
|
label: this.r('FLTL_02700', 'Send Update To Customer'),
|
||||||
|
onchange: () => {
|
||||||
|
const chk = chkSend.querySelector('input');
|
||||||
|
el.chkLink.querySelector('input').disabled = !chk.checked;
|
||||||
|
el.txtMessage.disabled = !chk.checked;
|
||||||
|
el.txtPhoneNumber.disabled = !chk.checked;
|
||||||
|
dropMSGVariables.disabled = !chk.checked;
|
||||||
|
addButton.disabled = !chk.checked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
el.chkSend = chkSend;
|
||||||
|
chkSend.style.paddingLeft = '0px';
|
||||||
|
|
||||||
|
const chkLink = createCheckbox({
|
||||||
|
label: this.r('FLTL_01580', 'Include Status Link')
|
||||||
|
});
|
||||||
|
el.chkLink = chkLink;
|
||||||
|
chkLink.style.paddingLeft = '0px';
|
||||||
|
el.txtMessage = textMessage;
|
||||||
|
|
||||||
|
const iconmobile = createIcon('fa-solid', 'mobile-alt');
|
||||||
|
const sPopup = new Popup({
|
||||||
|
title: this.r('FLTL_02824', 'Status Change') + " - " + item.StatusName,
|
||||||
|
content: createElement('div', 'wo-send-status-msg',
|
||||||
|
createElement('label'),
|
||||||
|
chkSend,
|
||||||
|
createElement('div', div => {
|
||||||
|
div.style.textAlign = 'right';
|
||||||
|
div.style.paddingRight = '5px';
|
||||||
|
div.appendChild(iconmobile);
|
||||||
|
}),
|
||||||
|
createElement('input', input => {
|
||||||
|
input.type = 'text';
|
||||||
|
el.txtPhoneNumber = input;
|
||||||
|
}),
|
||||||
|
createElement('label'),
|
||||||
|
chkLink,
|
||||||
|
createElement('label', label => {
|
||||||
|
label.innerText = this.r('FLTL_01912', 'Message:');
|
||||||
|
label.style.textAlign = 'right';
|
||||||
|
label.style.paddingRight = '5px';
|
||||||
|
}),
|
||||||
|
createElement('div', div => {
|
||||||
|
div.style.display = 'flex';
|
||||||
|
div.style.alignItems = 'center';
|
||||||
|
div.appendChild(dropMSGVariablesElement);
|
||||||
|
addButton = createElement('input', input => {
|
||||||
|
input.type = 'button';
|
||||||
|
input.style.marginLeft = "10px";
|
||||||
|
input.style.height = "24px";
|
||||||
|
input.value = this.r('FLTL_00083', 'Add');
|
||||||
|
})
|
||||||
|
addButton.addEventListener('click', () => {
|
||||||
|
const text = dropMSGVariables.selected?.Name;
|
||||||
|
if (!text) return;
|
||||||
|
textMessage.focus();
|
||||||
|
const startPos = textMessage.selectionStart;
|
||||||
|
const endPos = textMessage.selectionEnd;
|
||||||
|
textMessage.value = textMessage.value.substring(0, startPos) + text + textMessage.value.substring(endPos, textMessage.value.length);
|
||||||
|
textMessage.selectionStart = textMessage.selectionEnd = startPos + text.length;
|
||||||
|
});
|
||||||
|
div.appendChild(addButton);
|
||||||
|
}),
|
||||||
|
createElement('label'),
|
||||||
|
textMessage
|
||||||
|
),
|
||||||
|
resolve,
|
||||||
|
buttons: [
|
||||||
|
{ key: 'ok', text: this.r('FLTL_02582', 'Save Work Order and Send'), trigger: () => resolve('ok') },
|
||||||
|
{ key: 'cancel', text: this.r('FLTL_00499', 'Cancel'), trigger: () => resolve('cancel') }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
var names = "";
|
||||||
|
for (var i = 0; i < item.Contacts.length; i++) {
|
||||||
|
var c = item.Contacts[i];
|
||||||
|
if (c.OptOut || c.OptOut_BC) continue;
|
||||||
|
var mp = $.trim(c.MobilePhone);
|
||||||
|
var email = $.trim(c.Email);
|
||||||
|
if ((c.ContactPreference == "0" && (checkPhoneNumber(mp))
|
||||||
|
|| (c.ContactPreference == "1" && isEmail(email)))) {
|
||||||
|
if (names == "")
|
||||||
|
names = c.Name;
|
||||||
|
else
|
||||||
|
names += ";" + c.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
el.txtPhoneNumber.value = names;
|
||||||
|
el.chkSend.querySelector('input').checked = item.StatusAutoText;
|
||||||
|
el.chkSend.querySelector('input').dispatchEvent(new Event('change'));
|
||||||
|
el.txtMessage.value = item.StatusMessage;
|
||||||
|
dropMSGVariables.select(dropMSGVariables.source[0].Name);
|
||||||
|
|
||||||
|
sPopup.show().then(mask => {
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (next !== 'ok') {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof this.onSave === 'function') {
|
if (typeof this.onSave === 'function') {
|
||||||
this.onSave(item, this._var.el.dropStatus.selected);
|
const sendStatusInfo = (item.Status > 0 && item.CustomerId > 0 && item.StatusMessage && el.chkSend) ? {
|
||||||
|
SendUpdateToCustomer: el.chkSend.querySelector('input').checked,
|
||||||
|
IncludeStatusLink: el.chkLink.querySelector('input').checked,
|
||||||
|
PhoneEmails: el.txtPhoneNumber.value,
|
||||||
|
Comment: el.txtMessage.value,
|
||||||
|
} : null;
|
||||||
|
this.onSave(item, sendStatusInfo);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
popup.loading = false;
|
popup.loading = false;
|
||||||
@@ -692,6 +873,7 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
if (typeof option.requestWorkOrderParams === 'function') {
|
if (typeof option.requestWorkOrderParams === 'function') {
|
||||||
popup.loading = true;
|
popup.loading = true;
|
||||||
const data = await option.requestWorkOrderParams()
|
const data = await option.requestWorkOrderParams()
|
||||||
|
dropAssetStatus.source = data.AssetAvailabilities;
|
||||||
if (!isNaN(data.AssetId) && data.AssetId > 0 && typeof option.requestAssetInfo === 'function') {
|
if (!isNaN(data.AssetId) && data.AssetId > 0 && typeof option.requestAssetInfo === 'function') {
|
||||||
const it = await option.requestAssetInfo(data.AssetId);
|
const it = await option.requestAssetInfo(data.AssetId);
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
@@ -712,6 +894,7 @@ export default class AddWorkOrder extends OptionBase {
|
|||||||
dropAssignedTo.source = [{ IID: '', DisplayName: '' }, ...data];
|
dropAssignedTo.source = [{ IID: '', DisplayName: '' }, ...data];
|
||||||
// dropStatus.onSelected(dropStatus.selected);
|
// dropStatus.onSelected(dropStatus.selected);
|
||||||
}
|
}
|
||||||
|
dropAssetStatus.select(it.CustomStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
popup.loading = false;
|
popup.loading = false;
|
||||||
|
|||||||
@@ -69,9 +69,16 @@ export default class InspectionWizard extends OptionBase {
|
|||||||
requestTemplates: option.requestTemplates
|
requestTemplates: option.requestTemplates
|
||||||
});
|
});
|
||||||
this._var.templateSelector = templateSelector;
|
this._var.templateSelector = templateSelector;
|
||||||
assetSelector.onSelected = asset => {
|
assetSelector.onSelected = async asset => {
|
||||||
this._var.asset = asset;
|
this._var.asset = asset;
|
||||||
this._var.template = null;
|
this._var.template = null;
|
||||||
|
if (typeof option.getAssetUncompletedInspection === 'function') {
|
||||||
|
const report = await option.getAssetUncompletedInspection(asset.Id);
|
||||||
|
if (report) {
|
||||||
|
showAlert(assetSelector.title, this.r('FLTL_03601', 'The following inspection needs to be certified: ') + report.Value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
this._changePage(1);
|
this._changePage(1);
|
||||||
templateSelector.assetId = asset.Id;
|
templateSelector.assetId = asset.Id;
|
||||||
};
|
};
|
||||||
@@ -98,12 +105,19 @@ export default class InspectionWizard extends OptionBase {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: this.r('FLTL_01973', 'Next'),
|
text: this.r('FLTL_01973', 'Next'),
|
||||||
trigger: () => {
|
trigger: async () => {
|
||||||
const asset = assetSelector.currentAsset;
|
const asset = assetSelector.currentAsset;
|
||||||
if (asset == null) {
|
if (asset == null) {
|
||||||
showAlert(assetSelector.title, this.r('FLTL_02269', 'Please select an Asset.'));
|
showAlert(assetSelector.title, this.r('FLTL_02269', 'Please select an Asset.'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (typeof option.getAssetUncompletedInspection === 'function') {
|
||||||
|
const report = await option.getAssetUncompletedInspection(asset.Id);
|
||||||
|
if (report) {
|
||||||
|
showAlert(assetSelector.title, this.r('FLTL_03601', 'The following inspection needs to be certified: ') + report.Value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
this._var.asset = asset;
|
this._var.asset = asset;
|
||||||
this._var.template = null;
|
this._var.template = null;
|
||||||
this._changePage(1);
|
this._changePage(1);
|
||||||
|
|||||||
@@ -320,3 +320,31 @@
|
|||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wo-send-status-msg {
|
||||||
|
width: 460px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(80px, auto) 1fr;
|
||||||
|
grid-auto-rows: minmax(32px, auto);
|
||||||
|
|
||||||
|
.ui-text {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
fill: rgb(123, 28, 33);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity .12s ease;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:active,
|
||||||
|
&:hover {
|
||||||
|
outline: none;
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
lib/ui.js
97
lib/ui.js
@@ -8,7 +8,7 @@ import { createTab } from "./ui/tab";
|
|||||||
import { Dropdown } from "./ui/dropdown";
|
import { Dropdown } from "./ui/dropdown";
|
||||||
import { Grid } from "./ui/grid/grid";
|
import { Grid } from "./ui/grid/grid";
|
||||||
import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, GridIconColumn, GridTextColumn, GridDateColumn } from './ui/grid/column';
|
import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, GridIconColumn, GridTextColumn, GridDateColumn } from './ui/grid/column';
|
||||||
import { Popup, createPopup, resolvePopup, showAlert, showConfirm } from "./ui/popup";
|
import { Popup, createPopup, resolvePopup, showAlert, showInput, showConfirm } from "./ui/popup";
|
||||||
import { createPicture, createAudio, createVideo, createFile, createVideoList } from './ui/media';
|
import { createPicture, createAudio, createVideo, createFile, createVideoList } from './ui/media';
|
||||||
import { validation, convertCssStyle } from './ui/extension';
|
import { validation, convertCssStyle } from './ui/extension';
|
||||||
import { createDateInput, toDateValue, getFormatter, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date';
|
import { createDateInput, toDateValue, getFormatter, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date';
|
||||||
@@ -64,6 +64,96 @@ class OptionBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知选项
|
||||||
|
* @typedef NotifyOption
|
||||||
|
* @property {string | HTMLElement} message - 内容,支持自定义元素
|
||||||
|
* @property {HTMLElement} [parent] - 目标父元素
|
||||||
|
* @property {string} [title] - 标题
|
||||||
|
* @property {"success" | "warning" | "error"} [type] - 提示类型
|
||||||
|
* @property {boolean} [persistent] - 是否持续显示,默认为 `false`,3 秒后自动关闭
|
||||||
|
* @property {number} [timeout] - 超时时间,默认 3 秒
|
||||||
|
* @property {(auto: boolean) => void} [onDismissed] - 关闭时触发的函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {HTMLDivElement} wrapper
|
||||||
|
* @param {(auto: boolean) => void} [onDismissed]
|
||||||
|
* @param {boolean} [auto]
|
||||||
|
*/
|
||||||
|
function closeNotify(wrapper, onDismissed, auto) {
|
||||||
|
wrapper.classList.remove('active');
|
||||||
|
setTimeout(() => {
|
||||||
|
wrapper.remove();
|
||||||
|
if (typeof onDismissed === 'function') {
|
||||||
|
onDismissed(auto);
|
||||||
|
}
|
||||||
|
}, 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {NotifyOption} opts
|
||||||
|
*/
|
||||||
|
function notify(opts) {
|
||||||
|
opts ||= {};
|
||||||
|
let timer;
|
||||||
|
const close = createIcon('fa-light', 'times');
|
||||||
|
close.classList.add('ui-notify-close');
|
||||||
|
close.addEventListener('click', () => {
|
||||||
|
timer && clearTimeout(timer);
|
||||||
|
closeNotify(wrapper, opts.onDismissed);
|
||||||
|
});
|
||||||
|
let type;
|
||||||
|
let typeClass;
|
||||||
|
opts.type ||= 'success';
|
||||||
|
switch (opts.type) {
|
||||||
|
case 'warning':
|
||||||
|
type = 'exclamation-circle';
|
||||||
|
typeClass = 'warning';
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
type = 'times-circle';
|
||||||
|
typeClass = 'error';
|
||||||
|
break;
|
||||||
|
case 'success':
|
||||||
|
type = 'check-circle';
|
||||||
|
typeClass = 'success';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = opts.type;
|
||||||
|
typeClass = 'success';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const icon = createIcon('fa-solid', type);
|
||||||
|
icon.classList.add('ui-notify-type', typeClass);
|
||||||
|
const wrapper = createElement('div', 'ui-notify',
|
||||||
|
utility.nullOrEmpty(opts.title) ?
|
||||||
|
createElement('div', 'ui-notify-single',
|
||||||
|
icon,
|
||||||
|
createElement('span', 'ui-notify-message', opts.message),
|
||||||
|
close
|
||||||
|
) :
|
||||||
|
createElement('div', 'ui-notify-content',
|
||||||
|
createElement('div', 'ui-notify-header',
|
||||||
|
icon,
|
||||||
|
createElement('h2', 'ui-notify-title', opts.title),
|
||||||
|
close
|
||||||
|
),
|
||||||
|
createElement('span', 'ui-notify-message', opts.message)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (!opts.persistent) {
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
closeNotify(wrapper, opts.onDismissed, true);
|
||||||
|
}, opts.timeout || 3000);
|
||||||
|
}
|
||||||
|
(opts.parent ?? document.body).appendChild(wrapper);
|
||||||
|
setTimeout(() => wrapper.classList.add('active'), 10);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createElement,
|
createElement,
|
||||||
// icon
|
// icon
|
||||||
@@ -95,6 +185,7 @@ export {
|
|||||||
createPopup,
|
createPopup,
|
||||||
resolvePopup,
|
resolvePopup,
|
||||||
showAlert,
|
showAlert,
|
||||||
|
showInput,
|
||||||
showConfirm,
|
showConfirm,
|
||||||
// dateSelector
|
// dateSelector
|
||||||
createDateInput,
|
createDateInput,
|
||||||
@@ -119,5 +210,7 @@ export {
|
|||||||
requestAnimationFrame,
|
requestAnimationFrame,
|
||||||
offset,
|
offset,
|
||||||
// base classes
|
// base classes
|
||||||
OptionBase
|
OptionBase,
|
||||||
|
// notify
|
||||||
|
notify
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { createIcon } from "./icon";
|
|||||||
function fillCheckbox(container, type = 'fa-regular', label, tabindex = -1, charactor = 'check', title) {
|
function fillCheckbox(container, type = 'fa-regular', label, tabindex = -1, charactor = 'check', title) {
|
||||||
const checkIcon = createIcon(type, charactor);
|
const checkIcon = createIcon(type, charactor);
|
||||||
checkIcon.classList.add('ui-check-icon');
|
checkIcon.classList.add('ui-check-icon');
|
||||||
const indeterminateIcon = createIcon(type, 'grip-lines');
|
const indeterminateIcon = createIcon(type, 'minus');
|
||||||
indeterminateIcon.classList.add('ui-indeterminate-icon')
|
indeterminateIcon.classList.add('ui-indeterminate-icon')
|
||||||
container.appendChild(
|
container.appendChild(
|
||||||
createElement('layer', layer => {
|
createElement('layer', layer => {
|
||||||
|
|||||||
@@ -22,3 +22,106 @@
|
|||||||
text-indent: var(--text-indent);
|
text-indent: var(--text-indent);
|
||||||
line-height: var(--line-height);
|
line-height: var(--line-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-loading {
|
||||||
|
position: absolute;
|
||||||
|
@include inset(0, 0, 0, 0);
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: visibility 0s linear .12s, opacity .12s ease;
|
||||||
|
background-color: var(--loading-bg-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
>div {
|
||||||
|
width: 15px;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: loading-dot 1s infinite linear alternate;
|
||||||
|
/*background-color: var(--loading-fore-color);
|
||||||
|
border-radius: var(--loading-border-radius);
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
width: var(--loading-size);
|
||||||
|
height: var(--loading-size);
|
||||||
|
padding: 20px;
|
||||||
|
animation: loading-spinner 1.2s infinite linear;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notify {
|
||||||
|
padding: 10px 10px 12px;
|
||||||
|
position: fixed;
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 600px;
|
||||||
|
box-shadow: 0 3.2px 7.2px 0 rgba(0, 0, 0, .13), 0 0.6px 1.8px 0 rgba(0, 0, 0, .11);
|
||||||
|
transition: transform .12s ease, opacity .12s ease;
|
||||||
|
transform-origin: right;
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-notify-single {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
>.ui-notify-message {
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notify-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notify-type {
|
||||||
|
padding: 3px;
|
||||||
|
fill: var(--green-color);
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
fill: var(--orange-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
fill: var(--red-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notify-title {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notify-close {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 6px;
|
||||||
|
transition: background-color .12s ease, fill .12s ease;
|
||||||
|
fill: var(--red-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--red-color);
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notify-message {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,11 @@ $listMaxHeight: 210px;
|
|||||||
@include outline();
|
@include outline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .ui-drop-text::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
> input.ui-drop-text {
|
> input.ui-drop-text {
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
|
|
||||||
@@ -133,7 +138,6 @@ $listMaxHeight: 210px;
|
|||||||
color: var(--color);
|
color: var(--color);
|
||||||
|
|
||||||
@include outborder();
|
@include outborder();
|
||||||
|
|
||||||
// &:focus {
|
// &:focus {
|
||||||
// box-shadow: 0 0 3px 1px rgba(0, 0, 0, .2);
|
// box-shadow: 0 0 3px 1px rgba(0, 0, 0, .2);
|
||||||
// }
|
// }
|
||||||
@@ -193,6 +197,11 @@ $listMaxHeight: 210px;
|
|||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .ui-check-wrapper {
|
> .ui-check-wrapper {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
@import "./functions/func.scss";
|
@import "./functions/func.scss";
|
||||||
|
|
||||||
|
.ui-grid-container {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.ui-grid {
|
.ui-grid {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -308,6 +311,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ui-grid-row-level.level-1>td:first-child {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,9 +343,15 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--row-active-bg-color);
|
background-color: var(--row-active-bg-color);
|
||||||
|
|
||||||
>td.sticky {
|
>td {
|
||||||
|
&.sticky {
|
||||||
background-color: var(--row-active-bg-color);
|
background-color: var(--row-active-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.col-hover {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
@@ -494,6 +507,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.col-hover {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,46 +519,68 @@
|
|||||||
.ui-grid-hover-holder {
|
.ui-grid-hover-holder {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
line-height: var(--line-height);
|
// line-height: var(--line-height);
|
||||||
padding: var(--spacing-cell);
|
// padding: var(--spacing-cell);
|
||||||
background-color: var(--cell-hover-bg-color);
|
// background-color: var(--cell-hover-bg-color);
|
||||||
white-space: pre;
|
// white-space: pre;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: visibility 0s linear .12s, opacity .12s ease;
|
transition: visibility 0s linear .12s, opacity .12s ease;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 3.2px 7.2px 0 rgba(0, 0, 0, .13), 0 0.6px 1.8px 0 rgba(0, 0, 0, .11);
|
||||||
|
margin-top: -40px;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
>.ui-grid-loading {
|
>.ui-grid-hover-pointer {
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0 5px 15px 2px rgba(0, 0, 0, .3);
|
||||||
|
border: 1px solid #fff;
|
||||||
|
z-index: -1;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include inset(0, 0, 0, 0);
|
left: var(--pointer-left, calc(50% - 8px));
|
||||||
visibility: hidden;
|
bottom: -8px;
|
||||||
opacity: 0;
|
transform: rotate(-45deg);
|
||||||
transition: visibility 0s linear .12s, opacity .12s ease;
|
transform-origin: center;
|
||||||
background-color: var(--loading-bg-color);
|
}
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 2;
|
|
||||||
|
|
||||||
>div {
|
>.ui-grid-hover-curtain {
|
||||||
background-color: var(--loading-fore-color);
|
position: absolute;
|
||||||
border-radius: var(--loading-border-radius);
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
>svg {
|
>.ui-grid-hover-content {
|
||||||
width: var(--loading-size);
|
font-size: var(--font-smaller-size);
|
||||||
height: var(--loading-size);
|
line-height: 1rem;
|
||||||
padding: 20px;
|
white-space: normal;
|
||||||
animation: loading-spinner 1.2s infinite linear;
|
overflow: hidden;
|
||||||
|
margin: 8px;
|
||||||
|
height: calc(100% - 16px);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ui-grid-hover-down {
|
||||||
|
margin-top: 40px;
|
||||||
|
|
||||||
|
>.ui-grid-hover-pointer {
|
||||||
|
bottom: unset;
|
||||||
|
top: -8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ui-grid-hover-no>.ui-grid-hover-pointer {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -717,6 +756,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ui-popup-mask .ui-popup-container .ui-popup-footer {
|
.ui-popup-mask .ui-popup-container .ui-popup-footer {
|
||||||
>.ui-sort-layout {
|
>.ui-sort-layout {
|
||||||
|
|||||||
@@ -58,19 +58,20 @@ $buttonHeight: 28px;
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.ui-popup-header-title {
|
.ui-popup-header-title {
|
||||||
|
flex-grow: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding: 5px 0 3px 12px;
|
padding: 5px 0 3px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.ui-popup-header-title,
|
.ui-popup-header-title,
|
||||||
.ui-popup-move {
|
.ui-popup-move {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.ui-popup-header-title.no-move {
|
.ui-popup-header-title.no-move {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ $buttonHeight: 28px;
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
line-height: $headerLineHeight;
|
line-height: $headerLineHeight;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 100px;
|
min-height: 80px;
|
||||||
|
|
||||||
>.ui-popup-loading {
|
>.ui-popup-loading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -144,6 +145,16 @@ $buttonHeight: 28px;
|
|||||||
display: flex;
|
display: flex;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
|
||||||
|
&.message-wrapper-input {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
>.ui-text {
|
||||||
|
min-height: 50px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
>svg {
|
>svg {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
@@ -204,7 +215,7 @@ $buttonHeight: 28px;
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 4px 10px 16px 2px;
|
padding: 4px 26px 26px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-popup-body,
|
.ui-popup-body,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: calc(50% - 8px);
|
left: var(--pointer-left, calc(50% - 8px));
|
||||||
bottom: -8px;
|
bottom: -8px;
|
||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
|
|||||||
@@ -9,6 +9,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes loading-dot {
|
||||||
|
0% {
|
||||||
|
box-shadow: 20px 0 #0067c0, -20px 0 #0067c022;
|
||||||
|
background: #0067c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
33% {
|
||||||
|
box-shadow: 20px 0 #0067c0, -20px 0 #0067c022;
|
||||||
|
background: #0067c022;
|
||||||
|
}
|
||||||
|
|
||||||
|
66% {
|
||||||
|
box-shadow: 20px 0 #0067c022, -20px 0 #0067c0;
|
||||||
|
background: #0067c022;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 20px 0 #0067c022, -20px 0 #0067c0;
|
||||||
|
background: #0067c0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/*color-scheme: light dark;*/
|
/*color-scheme: light dark;*/
|
||||||
|
|
||||||
@@ -25,6 +47,8 @@
|
|||||||
--switch-active-bg-color: #33c559;
|
--switch-active-bg-color: #33c559;
|
||||||
|
|
||||||
--red-color: red;
|
--red-color: red;
|
||||||
|
--orange-color: orange;
|
||||||
|
--green-color: #33c559;
|
||||||
--title-color: #fff;
|
--title-color: #fff;
|
||||||
--title-bg-color: rgb(68, 114, 196);
|
--title-bg-color: rgb(68, 114, 196);
|
||||||
--title-ctrlbg-color: rgb(68, 114, 196);
|
--title-ctrlbg-color: rgb(68, 114, 196);
|
||||||
|
|||||||
@@ -405,6 +405,8 @@ export class Dropdown {
|
|||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
|
this._var.allChecked = false;
|
||||||
|
source.forEach(it => delete it.__checked);
|
||||||
if (itemlist.length === 0) {
|
if (itemlist.length === 0) {
|
||||||
this._var.selectedList = null;
|
this._var.selectedList = null;
|
||||||
this._var.label.innerText = r('none', '( None )');
|
this._var.label.innerText = r('none', '( None )');
|
||||||
@@ -608,7 +610,7 @@ export class Dropdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_contains(it, item, valuekey, textkey) {
|
_contains(it, item, valuekey, textkey) {
|
||||||
if (item.children?.length > 0) {
|
if (Array.isArray(item.children)) {
|
||||||
for (let t of item.children) {
|
for (let t of item.children) {
|
||||||
if (it === getValue(t, valuekey, textkey)) {
|
if (it === getValue(t, valuekey, textkey)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -644,8 +646,19 @@ export class Dropdown {
|
|||||||
createElement('span', span => {
|
createElement('span', span => {
|
||||||
// events
|
// events
|
||||||
span.className = 'ui-expandor';
|
span.className = 'ui-expandor';
|
||||||
|
if (Array.isArray(item.children) && item.children.length > 0) {
|
||||||
|
span.classList.add('active');
|
||||||
|
function hideChildren(children) {
|
||||||
|
for (let c of children) {
|
||||||
|
c.__visible = false;
|
||||||
|
if (Array.isArray(c.children)) {
|
||||||
|
hideChildren(c.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
createIcon('fa-light', 'caret-down')
|
createIcon('fa-solid', 'caret-down')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
li.appendChild(wrapper);
|
li.appendChild(wrapper);
|
||||||
@@ -700,16 +713,17 @@ export class Dropdown {
|
|||||||
const textkey = this._var.options.textKey;
|
const textkey = this._var.options.textKey;
|
||||||
const template = this._var.options.htmlTemplate;
|
const template = this._var.options.htmlTemplate;
|
||||||
const htmlkey = this._var.options.htmlKey;
|
const htmlkey = this._var.options.htmlKey;
|
||||||
|
const source = this.source;
|
||||||
if (checkbox.getAttribute('isall') === '1') {
|
if (checkbox.getAttribute('isall') === '1') {
|
||||||
const allchecked = this._var.allChecked = checkbox.checked;
|
const allchecked = this._var.allChecked = checkbox.checked;
|
||||||
const boxes = this._var.container.querySelectorAll('input.dataitem');
|
const boxes = this._var.container.querySelectorAll('input.dataitem');
|
||||||
boxes.forEach(box => box.checked = allchecked);
|
boxes.forEach(box => box.checked = allchecked);
|
||||||
|
source.forEach(it => it.__checked = allchecked ? 1 : 0);
|
||||||
list = [];
|
list = [];
|
||||||
} else {
|
} else {
|
||||||
item.__checked = checkbox.indeterminate ? 2 : checkbox.checked ? 1 : 0;
|
item.__checked = checkbox.indeterminate ? 2 : checkbox.checked ? 1 : 0;
|
||||||
const all = this._var.container.querySelector('input[isall="1"]');
|
const all = this._var.container.querySelector('input[isall="1"]');
|
||||||
if (checkbox.checked) {
|
if (checkbox.checked) {
|
||||||
const source = this.source;
|
|
||||||
if (source.some(it => it.__checked) == null) {
|
if (source.some(it => it.__checked) == null) {
|
||||||
this._var.allChecked = true;
|
this._var.allChecked = true;
|
||||||
if (all != null) {
|
if (all != null) {
|
||||||
@@ -726,7 +740,7 @@ export class Dropdown {
|
|||||||
if (all != null) {
|
if (all != null) {
|
||||||
all.checked = false;
|
all.checked = false;
|
||||||
}
|
}
|
||||||
list = this.source.filter(it => String(getValue(it, valuekey, textkey)) !== val);
|
list = source.filter(it => String(getValue(it, valuekey, textkey)) !== val);
|
||||||
} else {
|
} else {
|
||||||
list = this.selectedList.filter(it => String(getValue(it, valuekey, textkey)) !== val);
|
list = this.selectedList.filter(it => String(getValue(it, valuekey, textkey)) !== val);
|
||||||
}
|
}
|
||||||
@@ -739,7 +753,7 @@ export class Dropdown {
|
|||||||
}
|
}
|
||||||
this._var.selectedList = list;
|
this._var.selectedList = list;
|
||||||
if (typeof this.onSelectedList === 'function') {
|
if (typeof this.onSelectedList === 'function') {
|
||||||
this.onSelectedList(itemlist);
|
this.onSelectedList(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
391
lib/ui/grid/column.d.ts
vendored
Normal file
391
lib/ui/grid/column.d.ts
vendored
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
import { Grid, GridItem, GridItemWrapper, GridSourceItem } from "./grid";
|
||||||
|
import { Dropdown, DropdownOptions } from "../dropdown";
|
||||||
|
|
||||||
|
/** 列类型枚举 */
|
||||||
|
declare enum GridColumnType {
|
||||||
|
/** 通用列 */
|
||||||
|
Common = 0,
|
||||||
|
/** 单行文本框列 */
|
||||||
|
Input = 1,
|
||||||
|
/** 下拉选择列 */
|
||||||
|
Dropdown = 2,
|
||||||
|
/** 复选框列 */
|
||||||
|
Checkbox = 3,
|
||||||
|
/** 图标列 */
|
||||||
|
Icon = 4,
|
||||||
|
/** 多行文本列 */
|
||||||
|
Text = 5,
|
||||||
|
/** 日期选择列 */
|
||||||
|
Date = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列定义接口 */
|
||||||
|
export interface GridColumnDefinition {
|
||||||
|
/** 列关键字,默认以该关键字从行数据中提取单元格值,行数据的关键字属性值里包含 DisplayValue 则优先显示此值 */
|
||||||
|
key?: string;
|
||||||
|
/** 列的类型,可以为 {@linkcode GridColumn} 的子类,或者内置类型 {@linkcode GridColumnType} */
|
||||||
|
type?: GridColumnType | typeof GridColumn;
|
||||||
|
/** 列标题文本 */
|
||||||
|
caption?: string;
|
||||||
|
/** 列标题的元素样式 */
|
||||||
|
captionStyle?: { [key: string]: string };
|
||||||
|
/** 大于 0 则设置为该宽度,否则根据列内容自动调整列宽 */
|
||||||
|
width?: number;
|
||||||
|
/** 列对齐方式 */
|
||||||
|
align?: "left" | "center" | "right";
|
||||||
|
/**
|
||||||
|
* 列是否可用(可编辑),允许以下类型<br/><br/>
|
||||||
|
* `boolean` 则直接使用该值<br/><br/>
|
||||||
|
* `string` 则以该值为关键字从行数据中取值作为判断条件<br/><br/>
|
||||||
|
* `(item: GridItem) => boolean` 则调用该函数(上下文为列定义对象),以返回值作为判断条件<br/><br/>
|
||||||
|
*/
|
||||||
|
enabled?: boolean | string | ((item: GridItem) => boolean);
|
||||||
|
/**
|
||||||
|
* 单元格取值采用该方法返回的值
|
||||||
|
* @param item 行数据对象
|
||||||
|
* @param editing 是否处于编辑状态
|
||||||
|
* @param body Grid 控件的 `<tbody>` 部分
|
||||||
|
*/
|
||||||
|
filter?: (item: GridItem, editing: boolean, body?: HTMLElement) => any;
|
||||||
|
/** 单元格以该值填充内容,忽略filter与关键字属性 */
|
||||||
|
text?: string;
|
||||||
|
/** 列是否可见 */
|
||||||
|
visible?: boolean;
|
||||||
|
/** 列是否允许调整宽度 */
|
||||||
|
resizable?: boolean;
|
||||||
|
/** 列是否允许排序 */
|
||||||
|
sortable?: boolean;
|
||||||
|
/** 列是否允许重排顺序 */
|
||||||
|
orderable?: boolean;
|
||||||
|
/** 列为复选框类型时是否在列头增加全选复选框 */
|
||||||
|
allcheck?: boolean;
|
||||||
|
/** 单元格css样式对象(仅在重建行元素时读取) */
|
||||||
|
css?: { [key: string]: string };
|
||||||
|
/** 根据返回值填充单元格样式(填充行列数据时读取) */
|
||||||
|
styleFilter?: (item: GridItem) => { [key: string]: string };
|
||||||
|
/** 根据返回值设置单元格背景色 */
|
||||||
|
bgFilter?: (item: GridItem) => string;
|
||||||
|
/** 给单元格元素附加事件(事件函数上下文为数据行对象) */
|
||||||
|
events?: { [event: string]: Function };
|
||||||
|
/** 根据返回值设置单元格元素的附加属性,允许直接设置对象也支持函数返回对象 */
|
||||||
|
attrs?: { [key: string]: string } | ((item: GridItem) => { [key: string]: string });
|
||||||
|
/** 是否允许进行列头过滤 */
|
||||||
|
allowFilter?: boolean;
|
||||||
|
/** 自定义列过滤器的数据源(函数上下文为Grid) */
|
||||||
|
filterSource?: Array<GridItem> | ((col: GridColumnDefinition) => Array<GridItem>);
|
||||||
|
/** 自定义列排序函数 */
|
||||||
|
sortFilter?: (a: GridItem, b: GridItem) => -1 | 0 | 1;
|
||||||
|
/** 列为下拉列表类型时以该值设置下拉框的参数 */
|
||||||
|
dropOptions?: DropdownOptions;
|
||||||
|
/** 列为下拉列表类型时以该值设置下拉列表数据源,支持函数返回,也支持返回异步对象 */
|
||||||
|
source?: Array<GridSourceItem> | ((item: GridItem) => Array<GridSourceItem> | Promise<Array<GridSourceItem>>);
|
||||||
|
/** 下拉列表数据源是否缓存结果(即行数据未发生变化时仅从source属性获取一次值) */
|
||||||
|
sourceCache?: boolean;
|
||||||
|
/** 列为图标类型时以该值设置图标样式(函数上下文为列定义对象),默认值 `fa-light` */
|
||||||
|
iconType?: "fa-light" | "fa-regular" | "fa-solid";
|
||||||
|
/** 列为图标类型时以该值作为单元格元素的额外样式类型(函数上下文为列定义对象) */
|
||||||
|
iconClassName?: string | ((item: GridItem) => string);
|
||||||
|
/** 列为日期类型时以该值作为最小可选日期值 */
|
||||||
|
dateMin?: string;
|
||||||
|
/** 列为日期类型时以该值作为最大可选日期值 */
|
||||||
|
dateMax?: string;
|
||||||
|
/** 列为日期类型时自定义日期转字符串函数 */
|
||||||
|
dateValueFormatter?: string | ((date: Date) => string);
|
||||||
|
/** 以返回值额外设置单元格的tooltip(函数上下文为列定义对象) */
|
||||||
|
tooltip?: string | ((item: GridItem) => string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列头复选框改变时触发
|
||||||
|
* @param this 上下文为 Grid 对象
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @param flag 是否选中
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onAllChecked?: (this: Grid, col: GridColumnDefinition, flag: boolean) => void;
|
||||||
|
/**
|
||||||
|
* 单元格发生变化时触发
|
||||||
|
* @param this 上下文为 Grid 对象
|
||||||
|
* @param item 数据行对象
|
||||||
|
* @param value 修改后的值
|
||||||
|
* @param oldValue 修改前的值
|
||||||
|
* @param e 列修改事件传递过来的任意对象
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onChanged?: (this: Grid, item: GridItem, value: boolean | string | number, oldValue: boolean | string | number, e?: any) => void;
|
||||||
|
/**
|
||||||
|
* 文本单元格在输入完成时触发的事件
|
||||||
|
* @param this 上下文为 Grid 对象
|
||||||
|
* @param item 数据行对象
|
||||||
|
* @param value 修改后的文本框值
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onInputEnded?: (this: Grid, item: GridItem, value: string) => void;
|
||||||
|
/**
|
||||||
|
* 列过滤点击OK时触发的事件
|
||||||
|
* @param this 上下文为 Grid 对象
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @param selected 选中的过滤项
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onFilterOk?: (this: Grid, col: GridColumnDefinition, selected: Array<GridItem>) => void;
|
||||||
|
/**
|
||||||
|
* 列过滤后触发的事件
|
||||||
|
* @param this 上下文为 Grid 对象
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onFiltered?: (this: Grid, col: GridColumnDefinition) => void;
|
||||||
|
/**
|
||||||
|
* 列为下拉框类型时在下拉列表展开时触发的事件
|
||||||
|
* @param this 上下文为列定义对象
|
||||||
|
* @param item 数据行对象
|
||||||
|
* @param drop 下拉框对象
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onDropExpanded?: (this: GridColumnDefinition, item: GridItem, drop: Dropdown) => void;
|
||||||
|
/**
|
||||||
|
* 列为下拉框类型时在下拉列表关闭时触发的事件
|
||||||
|
* @param this 上下文为列定义对象
|
||||||
|
* @param item 数据行对象
|
||||||
|
* @param drop 下拉框对象
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onDropCollapsed?: (this: GridColumnDefinition, item: GridItem, drop: Dropdown) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列定义基类 */
|
||||||
|
export class GridColumn {
|
||||||
|
/** @ignore */
|
||||||
|
constructor();
|
||||||
|
/**
|
||||||
|
* 标记该类型是否支持列头批量操作
|
||||||
|
*/
|
||||||
|
static get headerEditing(): boolean;
|
||||||
|
/**
|
||||||
|
* 创建显示单元格时调用的方法
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @returns 返回创建的单元格元素
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static create(col: GridColumnDefinition): HTMLElement;
|
||||||
|
/**
|
||||||
|
* 创建编辑单元格时调用的方法<br/><br/>
|
||||||
|
* 元素修改后设置行包装对象的 `__editing` 后,支持在离开编辑状态时及时触发 {@linkcode leaveEdit} 方法<br/>
|
||||||
|
* 更多例子参考代码中 {@linkcode GridDropdownColumn} 的实现。
|
||||||
|
* @param trigger 编辑事件回调函数,e 参数会传递给 {@linkcode getValue} 方法
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @param container 父容器元素
|
||||||
|
* @param vals 行包装对象,其 `values` 属性为行数据对象
|
||||||
|
* @returns 返回创建的编辑状态的单元格元素
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
|
||||||
|
/**
|
||||||
|
* 创建列头时调用的方法
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @returns 返回创建的列头元素
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static createCaption?(col: GridColumnDefinition): HTMLElement;
|
||||||
|
/**
|
||||||
|
* 设置单元格值时调用的方法
|
||||||
|
* @param element 单元格元素
|
||||||
|
* @param val 待设置的单元格值
|
||||||
|
* @param vals 行包装对象
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @param grid {@linkcode Grid} 对象
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static setValue(element: HTMLElement, val: string | boolean | number, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
|
||||||
|
/**
|
||||||
|
* 获取编辑状态单元格值时调用的方法
|
||||||
|
* @param e 由 {@linkcode createEdit} 方法中 `trigger` 函数传递来的对象
|
||||||
|
* @param col 列定义对象
|
||||||
|
* @returns 返回单元格的值
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static getValue(e: any, col: GridColumnDefinition): string | boolean | number;
|
||||||
|
/**
|
||||||
|
* 设置单元格样式时调用的方法
|
||||||
|
* @param element 单元格元素
|
||||||
|
* @param style 样式对象
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static setStyle(element: HTMLElement, style: { [key: string]: string }): void;
|
||||||
|
/**
|
||||||
|
* 设置单元格可用性时调用的方法
|
||||||
|
* @param element 单元格元素
|
||||||
|
* @param enabled 启用值,为false时代表禁用
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static setEnabled(element: HTMLElement, enabled?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 单元格离开编辑元素时触发,需要由行包装对象的 `__editing` 来确定是否触发。
|
||||||
|
* @param element 单元格元素
|
||||||
|
* @param container 父容器元素
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
static leaveEdit?(element: HTMLElement, container: HTMLElement): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 单行文本列 */
|
||||||
|
export class GridInputColumn extends GridColumn {
|
||||||
|
/**
|
||||||
|
* 设置该类型是否支持触发 {@linkcode GridColumnDefinition.onInputEnded} 方法<br/>
|
||||||
|
* 该属性返回 `true` 后,在任意事件中修改行包装对象的 `__editing` 值,则会在行列元素变动时及时触发 {@linkcode GridColumnDefinition.onInputEnded} 方法,避免例如文本框还未触发 `onchange` 事件就被移除元素而导致的问题<br/>
|
||||||
|
* 更多例子参考代码中 {@linkcode GridInputColumn} 的实现
|
||||||
|
*/
|
||||||
|
static get editing(): boolean;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.createEdit
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.getValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static getValue(e: any): string;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setEnabled
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setEnabled(element: HTMLElement, enabled?: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 多行文本列 */
|
||||||
|
export class GridTextColumn extends GridInputColumn {
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridInputColumn.createEdit
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridInputColumn.setValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 下拉选择列 */
|
||||||
|
export class GridDropdownColumn extends GridColumn {
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.createEdit
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition): void;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.getValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static getValue(e: any, col: GridColumnDefinition): string;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setEnabled
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setEnabled(element: HTMLElement, enabled?: boolean): void;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.leaveEdit
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static leaveEdit?(element: HTMLElement, container: HTMLElement): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 复选框列 */
|
||||||
|
export class GridCheckboxColumn extends GridColumn {
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.createEdit
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static createEdit(trigger: (e: any) => void): HTMLElement;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setValue(element: HTMLElement, val: boolean): void;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.getValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static getValue(e: any): boolean;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setEnabled
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setEnabled(element: HTMLElement, enabled?: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 单选框列 */
|
||||||
|
export class GridRadioboxColumn extends GridCheckboxColumn {
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridCheckboxColumn.createEdit
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static createEdit(trigger: (e: any) => void): HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 图标列 */
|
||||||
|
export class GridIconColumn extends GridColumn {
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.create
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static create(): HTMLElement;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition): void;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setEnabled
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setEnabled(element: HTMLElement, enabled?: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 日期选择列 */
|
||||||
|
export class GridDateColumn extends GridColumn {
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.createEdit
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
|
||||||
|
/**
|
||||||
|
* 设置单元格值时调用的方法<br/><br/>
|
||||||
|
* 支持以下几种数据类型<br/><br/>
|
||||||
|
* `"2024-01-26"`<br/>
|
||||||
|
* `"1/26/2024"`<br/>
|
||||||
|
* `"638418240000000000"`<br/>
|
||||||
|
* `new Date('2024-01-26')`<br/>
|
||||||
|
* @param element 单元格元素
|
||||||
|
* @param val 待设置的单元格值
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setValue(element: HTMLElement, val: string | number): void;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.getValue
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static getValue(e: any): string | number;
|
||||||
|
/**
|
||||||
|
* @inheritdoc GridColumn.setEnabled
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
static setEnabled(element: HTMLElement, enabled?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 格式化日期对象为 M/d/yyyy 格式的字符串
|
||||||
|
* @param date 日期对象
|
||||||
|
* @returns 返回格式化后的字符串
|
||||||
|
*/
|
||||||
|
static formatDate(date: Date): string;
|
||||||
|
}
|
||||||
@@ -368,6 +368,11 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
col.onDropExpanded.call(col, wrapper.values, drop);
|
col.onDropExpanded.call(col, wrapper.values, drop);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
drop.onCollapsed = () => {
|
||||||
|
if (typeof col.onDropCollapsed === 'function') {
|
||||||
|
col.onDropCollapsed.call(col, wrapper.values, drop);
|
||||||
|
}
|
||||||
|
};
|
||||||
return drop.create();
|
return drop.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
325
lib/ui/grid/grid.d.ts
vendored
Normal file
325
lib/ui/grid/grid.d.ts
vendored
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
import { GridColumnDefinition } from "./column"
|
||||||
|
/**
|
||||||
|
* 单元格点击回调函数
|
||||||
|
*
|
||||||
|
* @param {number} index - 点击的行索引
|
||||||
|
* @param {number} colIndex - 点击的列索引
|
||||||
|
* @returns {boolean} 返回 `false` 则取消事件冒泡
|
||||||
|
*/
|
||||||
|
declare function cellClickedCallback(index: number, colIndex: number): boolean;
|
||||||
|
|
||||||
|
/** 列数据接口 */
|
||||||
|
interface GridItem {
|
||||||
|
/** 值 */
|
||||||
|
Value: any;
|
||||||
|
/** 显示值 */
|
||||||
|
DisplayValue: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列数据行包装接口 */
|
||||||
|
interface GridItemWrapper {
|
||||||
|
/** 真实数据对象 */
|
||||||
|
values: { [key: string]: GridItem | any };
|
||||||
|
/** 下拉数据源缓存对象 */
|
||||||
|
source: { [key: string]: Array<GridSourceItem> };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 下拉框列数据源接口 */
|
||||||
|
interface GridSourceItem {
|
||||||
|
/** 值 */
|
||||||
|
value: string;
|
||||||
|
/** 显示文本 */
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Grid 语言资源接口 */
|
||||||
|
interface GridLanguages {
|
||||||
|
/**
|
||||||
|
* “所有”文本,默认值 `( All )` */
|
||||||
|
all: string;
|
||||||
|
/** “确定”文本,默认值 `OK` */
|
||||||
|
ok: string;
|
||||||
|
/** “重置”文本,默认值 `Reset` */
|
||||||
|
reset: string;
|
||||||
|
cancel: string;
|
||||||
|
/** “空”文本,默认值 `( Null )` */
|
||||||
|
null: string;
|
||||||
|
addLevel: string;
|
||||||
|
deleteLevel: string;
|
||||||
|
copyLevel: string;
|
||||||
|
asc: string;
|
||||||
|
desc: string;
|
||||||
|
column: string;
|
||||||
|
order: string;
|
||||||
|
sort: string;
|
||||||
|
requirePrompt: string;
|
||||||
|
duplicatePrompt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Grid 列排序定义接口 */
|
||||||
|
interface GridColumnSortDefinition {
|
||||||
|
/** 排序列的关键字 */
|
||||||
|
column: string;
|
||||||
|
/** 升序或降序 */
|
||||||
|
order: "asc" | "desc";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列排序枚举 */
|
||||||
|
declare enum GridColumnDirection {
|
||||||
|
/** 倒序 */
|
||||||
|
Descending = -1,
|
||||||
|
/** 升序 */
|
||||||
|
Ascending = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列事件枚举 */
|
||||||
|
declare enum GridColumnEvent {
|
||||||
|
/** 重排事件 */
|
||||||
|
Reorder = "reorder",
|
||||||
|
/** 宽调整事件 */
|
||||||
|
Resize = "resize",
|
||||||
|
/** 排序事件 */
|
||||||
|
Sort = "sort"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Grid 控件基础类 */
|
||||||
|
export class Grid {
|
||||||
|
/** 列类型枚举 */
|
||||||
|
static ColumnTypes: {
|
||||||
|
/** 通用列(只读) */
|
||||||
|
Common: 0,
|
||||||
|
/** 单行文本列 */
|
||||||
|
Input: 1,
|
||||||
|
/** 下拉选择列 */
|
||||||
|
Dropdown: 2,
|
||||||
|
/** 复选框列 */
|
||||||
|
Checkbox: 3,
|
||||||
|
/** 图标列 */
|
||||||
|
Icon: 4,
|
||||||
|
/** 多行文本列 */
|
||||||
|
Text: 5,
|
||||||
|
/** 日期选择列 */
|
||||||
|
Date: 6,
|
||||||
|
/**
|
||||||
|
* 判断列是否为复选框列
|
||||||
|
* @param type 列类型
|
||||||
|
*/
|
||||||
|
isCheckbox(type: number): boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 列定义的数组 */
|
||||||
|
columns: Array<GridColumnDefinition>;
|
||||||
|
/** 多语言资源对象 */
|
||||||
|
langs?: GridLanguages;
|
||||||
|
/** 行数大于等于该值则启用虚模式,默认值 `100` */
|
||||||
|
virtualCount?: number;
|
||||||
|
/** 表格行高,默认值 `36` */
|
||||||
|
rowHeight?: number;
|
||||||
|
/** 文本行高,默认值 `24` */
|
||||||
|
lineHeight?: number;
|
||||||
|
/** 列表底部留出额外行的空白,默认值 `0` */
|
||||||
|
extraRows?: number;
|
||||||
|
/** 过滤条件列表的行高,默认值 `30` */
|
||||||
|
filterRowHeight?: number;
|
||||||
|
/** 列表高度值,为 0 时列表始终显示全部内容(自增高),为非数字或者小于 0 则根据容器高度来确定虚模式的渲染行数,默认值 `null` */
|
||||||
|
height?: number;
|
||||||
|
/** 是否允许多选,默认值 `false` */
|
||||||
|
multiSelect?: boolean;
|
||||||
|
/** 为 false 时只有点击在单元格内才会选中行,默认值 `true` */
|
||||||
|
fullrowClick?: boolean;
|
||||||
|
/** 单元格 tooltip 是否禁用,默认值 `false` */
|
||||||
|
tooltipDisabled?: boolean;
|
||||||
|
/** 列头是否显示,默认值 `true` */
|
||||||
|
headerVisible?: boolean;
|
||||||
|
/** 监听事件的窗口载体,默认值 `window` */
|
||||||
|
window?: Window
|
||||||
|
/** 排序列的索引,默认值 `-1` */
|
||||||
|
sortIndex?: number;
|
||||||
|
/** 排序方式,正数升序,负数倒序,默认值 `1` */
|
||||||
|
sortDirection?: GridColumnDirection;
|
||||||
|
/** 排序列 */
|
||||||
|
sortArray?: Array<GridColumnSortDefinition>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grid 控件构造函数
|
||||||
|
* @param container Grid 控件所在的父容器,可以是 string 表示选择器,也可以是 HTMLElement 对象<br/><br/>
|
||||||
|
* <i>构造时可以不进行赋值,但是调用 init 函数时则必须进行赋值</i>
|
||||||
|
* @param getText (可选参数)获取多语言文本的函数代理
|
||||||
|
*/
|
||||||
|
constructor(container: string | HTMLElement, getText?: (id: string, def?: string) => string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 即将选中行时触发,返回 false、null、undefined、0 等则取消选中动作
|
||||||
|
* @param index 即将选中的行索引
|
||||||
|
* @param colIndex 即将选中的列索引
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
willSelect?: (index: number, colIndex: number) => boolean;
|
||||||
|
/**
|
||||||
|
* 单元格单击时触发,colIndex 为 -1 则表示点击的是行的空白处,返回 false 则取消事件冒泡
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
cellClicked?: typeof cellClickedCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选中行发生变化时触发的事件
|
||||||
|
* @param index 选中的行索引
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onSelectedRowChanged?: (index?: number) => void;
|
||||||
|
/**
|
||||||
|
* 单元格双击时触发的事件,colIndex 为 -1 则表示点击的是行的空白处
|
||||||
|
* @param index 双击的行索引
|
||||||
|
* @param colIndex 双击的列索引
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onCellDblClicked?: (index: number, colIndex: number) => void;
|
||||||
|
/**
|
||||||
|
* 行双击时触发的事件
|
||||||
|
* @param index 双击的行索引
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onRowDblClicked?: (index: number) => void;
|
||||||
|
/**
|
||||||
|
* 列发生变化时触发的事件
|
||||||
|
* @param type 事件类型<br/><br/>
|
||||||
|
* "reorder" 为发生列重排事件,此时 value 为目标列索引<br/>
|
||||||
|
* "resize" 为发生列宽调整事件,此时 value 为列宽度值<br/>
|
||||||
|
* "sort" 为发生列排序事件,此时 value 为 1(升序)或 -1(倒序)
|
||||||
|
* @param colIndex 发生变化事件的列索引
|
||||||
|
* @param value 变化的值
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onColumnChanged?: (type: GridColumnEvent, colIndex: number, value: number | GridColumnDirection) => void;
|
||||||
|
/**
|
||||||
|
* 列滚动时触发的事件
|
||||||
|
* @param e 滚动事件对象
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onBodyScrolled?: (e: Event) => void;
|
||||||
|
/**
|
||||||
|
* 多列排序后触发的事件
|
||||||
|
* @param array 排序列定义数组
|
||||||
|
* @eventProperty
|
||||||
|
*/
|
||||||
|
onSorted?: (array?: Array<GridColumnSortDefinition>) => void;
|
||||||
|
|
||||||
|
/** 返回所有数据的数据(未过滤) */
|
||||||
|
get allSource(): Array<GridItem>;
|
||||||
|
/** 获取数据数组(已过滤) */
|
||||||
|
get source(): Array<GridItem>;
|
||||||
|
/** 设置数据,并刷新列表 */
|
||||||
|
set source(list: Array<GridItem>);
|
||||||
|
/** 获取列表是否为只读,默认值 `false` */
|
||||||
|
get readonly(): boolean;
|
||||||
|
/** 设置列表是否为只读 */
|
||||||
|
set readonly(flag: boolean);
|
||||||
|
/** 获取当前选中的行索引的数组 */
|
||||||
|
get selectedIndexes(): Array<number>;
|
||||||
|
/** 设置当前选中的行索引的数组,并刷新列表 */
|
||||||
|
set selectedIndexes(indexes: Array<number>);
|
||||||
|
/** 获取 Grid 当前是否处于加载状态 */
|
||||||
|
get loading(): boolean;
|
||||||
|
/** 使 Grid 进入加载状态 */
|
||||||
|
set loading(flag: boolean);
|
||||||
|
/** 获取 Grid 当前滚动的偏移量 */
|
||||||
|
get scrollTop(): number;
|
||||||
|
/** 设置 Grid 滚动偏移量 */
|
||||||
|
set scrollTop(top: number);
|
||||||
|
|
||||||
|
/** 获取 Grid 的页面元素 */
|
||||||
|
get element(): HTMLElement;
|
||||||
|
/** 获取当前 Grid 是否已发生改变 */
|
||||||
|
get changed(): boolean;
|
||||||
|
/** 获取当前是否为虚模式状态 */
|
||||||
|
get virtual(): boolean;
|
||||||
|
/** 获取当前排序的列关键字,为 null 则当前无排序列 */
|
||||||
|
get sortKey(): string | undefined;
|
||||||
|
/** 获取当前选中行的索引,为 -1 则当前没有选中行 */
|
||||||
|
get selectedIndex(): number | -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化Grid控件
|
||||||
|
* @param container 父容器元素,若未传值则采用构造方法中传入的父容器元素
|
||||||
|
*/
|
||||||
|
init(container?: HTMLElement): void;
|
||||||
|
/**
|
||||||
|
* 设置数据列表,该方法为 set source 属性的语法糖
|
||||||
|
* @param source 待设置的数据列表
|
||||||
|
*/
|
||||||
|
setData(source: Array<GridItem>): void;
|
||||||
|
/**
|
||||||
|
* 设置单行数据
|
||||||
|
* @param index 行索引
|
||||||
|
* @param item 待设置的行数据值
|
||||||
|
*/
|
||||||
|
setItem(index: number, item: GridItem): void;
|
||||||
|
/**
|
||||||
|
* 添加行数据
|
||||||
|
* @param item 待添加的行数据值
|
||||||
|
* @param index 待添加的行索引
|
||||||
|
* @returns 返回已添加的行数据
|
||||||
|
*/
|
||||||
|
addItem(item: GridItem, index?: number): GridItem;
|
||||||
|
/**
|
||||||
|
* 批量添加行数据
|
||||||
|
* @param array 待添加的行数据数组
|
||||||
|
* @param index 待添加的行索引
|
||||||
|
* @returns 返回已添加的行数据数组
|
||||||
|
*/
|
||||||
|
addItems(array: Array<GridItem>, index?: number): Array<GridItem>
|
||||||
|
/**
|
||||||
|
* 删除行数据
|
||||||
|
* @param index 待删除的行索引
|
||||||
|
* @returns 返回已删除的行数据
|
||||||
|
*/
|
||||||
|
removeItem(index: number): GridItem;
|
||||||
|
/**
|
||||||
|
* 批量删除行数据
|
||||||
|
* @param indexes 待删除的行索引数组,未传值时删除所有行
|
||||||
|
* @returns 返回已删除的行数据数组
|
||||||
|
*/
|
||||||
|
removeItems(indexes?: Array<number>): Array<GridItem>;
|
||||||
|
/**
|
||||||
|
* 滚动到指定行的位置
|
||||||
|
* @param index 待滚动至的行索引
|
||||||
|
*/
|
||||||
|
scrollToIndex(index: number): void;
|
||||||
|
/**
|
||||||
|
* 调整 Grid 元素的大小,一般需要在宽度变化时(如页面大小发生变化时)调用
|
||||||
|
* @param force 是否强制 {@linkcode reload},默认只有待渲染的行数发生变化时才会调用
|
||||||
|
* @param keep 是否保持当前滚动位置
|
||||||
|
*/
|
||||||
|
resize(force?: boolean, keep?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 重新计算需要渲染的行,并载入元素,一般需要在高度变化时调用
|
||||||
|
* @param keep 是否保持当前滚动位置
|
||||||
|
*/
|
||||||
|
reload(keep?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 重新填充Grid单元格数据
|
||||||
|
*/
|
||||||
|
refresh(): void;
|
||||||
|
/**
|
||||||
|
* 把所有行重置为未修改的状态
|
||||||
|
*/
|
||||||
|
resetChange(): void;
|
||||||
|
/**
|
||||||
|
* 根据当前排序字段进行列排序
|
||||||
|
* @param reload 为 true 则在列排序后调用 {@linkcode Grid.reload} 方法
|
||||||
|
*/
|
||||||
|
sortColumn(reload?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 根据当前排序列数组进行多列排序
|
||||||
|
* @param reload 为 true 则在多列排序后调用 {@linkcode Grid.reload} 方法
|
||||||
|
*/
|
||||||
|
sort(reload?: boolean): void;
|
||||||
|
/**
|
||||||
|
* 清除列头复选框的选中状态
|
||||||
|
*/
|
||||||
|
clearHeaderCheckbox(): void;
|
||||||
|
/**
|
||||||
|
* 显示多列排序设置面板
|
||||||
|
*/
|
||||||
|
showSortPanel(): void;
|
||||||
|
}
|
||||||
@@ -285,6 +285,7 @@ let r = lang;
|
|||||||
* @property {Function} [onFilterOk] - 列过滤点击 `OK` 时触发的事件
|
* @property {Function} [onFilterOk] - 列过滤点击 `OK` 时触发的事件
|
||||||
* @property {Function} [onFiltered] - 列过滤后触发的事件
|
* @property {Function} [onFiltered] - 列过滤后触发的事件
|
||||||
* @property {Function} [onDropExpanded] - 列为下拉框类型时在下拉列表展开时触发的事件
|
* @property {Function} [onDropExpanded] - 列为下拉框类型时在下拉列表展开时触发的事件
|
||||||
|
* @property {Function} [onDropCollapsed] - 列为下拉框类型时在下拉列表关闭时触发的事件
|
||||||
* @interface
|
* @interface
|
||||||
* @example
|
* @example
|
||||||
* [
|
* [
|
||||||
@@ -410,6 +411,15 @@ let r = lang;
|
|||||||
* @this GridColumnDefinition
|
* @this GridColumnDefinition
|
||||||
* @memberof GridColumnDefinition
|
* @memberof GridColumnDefinition
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* 列为下拉框类型时在下拉列表关闭时触发的事件
|
||||||
|
* @name onDropCollapsed
|
||||||
|
* @event
|
||||||
|
* @param {GridRowItem} item - 行数据对象
|
||||||
|
* @param {Dropdown} drop - 下拉框对象
|
||||||
|
* @this GridColumnDefinition
|
||||||
|
* @memberof GridColumnDefinition
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断列是否始终编辑的回调函数
|
* 判断列是否始终编辑的回调函数
|
||||||
@@ -613,6 +623,12 @@ export class Grid {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
parent: null,
|
parent: null,
|
||||||
|
/**
|
||||||
|
* Grid 包裹元素
|
||||||
|
* @type {HTMLDivElement}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
container: null,
|
||||||
/**
|
/**
|
||||||
* Grid 元素 - `div.ui-grid`
|
* Grid 元素 - `div.ui-grid`
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLDivElement}
|
||||||
@@ -818,7 +834,7 @@ export class Grid {
|
|||||||
*/
|
*/
|
||||||
footer: null,
|
footer: null,
|
||||||
/**
|
/**
|
||||||
* 加载状态元素引用 - div.ui-grid-loading
|
* 加载状态元素引用 - div.ui-loading
|
||||||
* @type {HTMLDivElement}
|
* @type {HTMLDivElement}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@@ -862,6 +878,13 @@ export class Grid {
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
langs = {};
|
langs = {};
|
||||||
|
/**
|
||||||
|
* 区域字符串
|
||||||
|
* @type {string}
|
||||||
|
* @default "en"
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
lgid = 'en';
|
||||||
/**
|
/**
|
||||||
* 行数大于等于该值则启用虚模式
|
* 行数大于等于该值则启用虚模式
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@@ -1124,6 +1147,7 @@ export class Grid {
|
|||||||
* @param {string} getText.{returns} 返回的多语言
|
* @param {string} getText.{returns} 返回的多语言
|
||||||
* @property {GridColumnDefinition[]} columns - 列定义的数组
|
* @property {GridColumnDefinition[]} columns - 列定义的数组
|
||||||
* @property {GridLanguages} [langs] - 多语言资源对象
|
* @property {GridLanguages} [langs] - 多语言资源对象
|
||||||
|
* @property {string} [lgid=en] - 区域字符串
|
||||||
* @property {number} [virtualCount=100] - 行数大于等于该值则启用虚模式
|
* @property {number} [virtualCount=100] - 行数大于等于该值则启用虚模式
|
||||||
* @property {boolean} [autoResize=true] - 未设置宽度的列自动调整列宽
|
* @property {boolean} [autoResize=true] - 未设置宽度的列自动调整列宽
|
||||||
* @property {number} [rowHeight=36] - 表格行高,修改后同时需要在 `.ui-grid` 所在父容器重写 `--line-height` 的值以配合显示
|
* @property {number} [rowHeight=36] - 表格行高,修改后同时需要在 `.ui-grid` 所在父容器重写 `--line-height` 的值以配合显示
|
||||||
@@ -1274,10 +1298,23 @@ export class Grid {
|
|||||||
if (!Array.isArray(list)) {
|
if (!Array.isArray(list)) {
|
||||||
throw new Error('source is not an Array.')
|
throw new Error('source is not an Array.')
|
||||||
}
|
}
|
||||||
|
list = list.reduce((array, item) => {
|
||||||
|
array.push({
|
||||||
|
__level: 0,
|
||||||
|
values: item
|
||||||
|
});
|
||||||
|
if (Array.isArray(item.__children)) {
|
||||||
|
array.push(...item.__children.map(c => ({
|
||||||
|
__level: 1,
|
||||||
|
values: c
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}, []);
|
||||||
list = list.map((it, index) => {
|
list = list.map((it, index) => {
|
||||||
return {
|
return {
|
||||||
__index: index,
|
__index: index,
|
||||||
values: it
|
...it
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this._var.source = list;
|
this._var.source = list;
|
||||||
@@ -1335,9 +1372,11 @@ export class Grid {
|
|||||||
if (flag === false) {
|
if (flag === false) {
|
||||||
this._var.refs.loading.style.visibility = 'hidden';
|
this._var.refs.loading.style.visibility = 'hidden';
|
||||||
this._var.refs.loading.style.opacity = 0;
|
this._var.refs.loading.style.opacity = 0;
|
||||||
|
this._var.el.style.overflow = '';
|
||||||
} else {
|
} else {
|
||||||
this._var.refs.loading.style.visibility = 'visible';
|
this._var.refs.loading.style.visibility = 'visible';
|
||||||
this._var.refs.loading.style.opacity = 1;
|
this._var.refs.loading.style.opacity = 1;
|
||||||
|
this._var.el.style.overflow = 'hidden';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1402,6 +1441,8 @@ export class Grid {
|
|||||||
this._var.parent = container;
|
this._var.parent = container;
|
||||||
this._var.isFirefox = /Firefox\//i.test(navigator.userAgent);
|
this._var.isFirefox = /Firefox\//i.test(navigator.userAgent);
|
||||||
this._var.enabledDict = {};
|
this._var.enabledDict = {};
|
||||||
|
const c = createElement('div', 'ui-grid-container');
|
||||||
|
this._var.container = c;
|
||||||
const grid = createElement('div', 'ui-grid');
|
const grid = createElement('div', 'ui-grid');
|
||||||
grid.setAttribute('tabindex', 0);
|
grid.setAttribute('tabindex', 0);
|
||||||
grid.addEventListener('keydown', e => {
|
grid.addEventListener('keydown', e => {
|
||||||
@@ -1506,7 +1547,8 @@ export class Grid {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
container.replaceChildren(grid);
|
c.appendChild(grid);
|
||||||
|
container.replaceChildren(c);
|
||||||
const sizer = createElement('span', 'ui-grid-sizer');
|
const sizer = createElement('span', 'ui-grid-sizer');
|
||||||
grid.appendChild(sizer);
|
grid.appendChild(sizer);
|
||||||
this._var.refs.sizer = sizer;
|
this._var.refs.sizer = sizer;
|
||||||
@@ -1525,7 +1567,11 @@ export class Grid {
|
|||||||
wrapper.appendChild(table);
|
wrapper.appendChild(table);
|
||||||
// tooltip
|
// tooltip
|
||||||
if (!this.tooltipDisabled) {
|
if (!this.tooltipDisabled) {
|
||||||
const holder = createElement('div', 'ui-grid-hover-holder');
|
const holder = createElement('div', 'ui-grid-hover-holder ui-grid-hover ui-tooltip-color',
|
||||||
|
// createElement('div', 'ui-grid-hover-pointer ui-grid-hover ui-tooltip-color'),
|
||||||
|
createElement('div', 'ui-grid-hover-curtain ui-grid-hover ui-tooltip-color'),
|
||||||
|
createElement('div', 'ui-grid-hover-content ui-grid-hover')
|
||||||
|
);
|
||||||
holder.addEventListener('mousedown', e => {
|
holder.addEventListener('mousedown', e => {
|
||||||
const holder = e.currentTarget;
|
const holder = e.currentTarget;
|
||||||
const row = Number(holder.dataset.row);
|
const row = Number(holder.dataset.row);
|
||||||
@@ -1542,8 +1588,8 @@ export class Grid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loading
|
// loading
|
||||||
const loading = createElement('div', 'ui-grid-loading',
|
const loading = createElement('div', 'ui-loading',
|
||||||
createElement('div', null, createIcon('fa-regular', 'spinner-third'))
|
createElement('div')
|
||||||
);
|
);
|
||||||
this._var.refs.loading = loading;
|
this._var.refs.loading = loading;
|
||||||
grid.appendChild(loading);
|
grid.appendChild(loading);
|
||||||
@@ -1958,7 +2004,8 @@ export class Grid {
|
|||||||
const gridWrapper = createElement('div', 'ui-sort-panel-grid');
|
const gridWrapper = createElement('div', 'ui-sort-panel-grid');
|
||||||
content.append(buttonWrapper, gridWrapper);
|
content.append(buttonWrapper, gridWrapper);
|
||||||
const columnSource = this.columns.filter(c => c.sortable !== false); // ticket 56389, && c.visible !== false
|
const columnSource = this.columns.filter(c => c.sortable !== false); // ticket 56389, && c.visible !== false
|
||||||
columnSource.sort((a, b) => a.caption > b.caption ? 1 : -1);
|
const lgid = this.lgid;
|
||||||
|
columnSource.sort((a, b) => String(a.caption).localeCompare(b.caption, lgid));
|
||||||
grid.columns = [
|
grid.columns = [
|
||||||
{
|
{
|
||||||
width: 80,
|
width: 80,
|
||||||
@@ -2421,8 +2468,12 @@ export class Grid {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
let working;
|
let working;
|
||||||
let url;
|
let url;
|
||||||
|
let path = ScriptPath;
|
||||||
|
if (nullOrEmpty(path) && typeof consts !== 'undefined') {
|
||||||
|
path = consts.modulePath;
|
||||||
|
}
|
||||||
if (typeof module === 'string') {
|
if (typeof module === 'string') {
|
||||||
url = `${ScriptPath}${module}`;
|
url = `${path}${module}`;
|
||||||
} else {
|
} else {
|
||||||
url = URL.createObjectURL(new Blob([`let wasm,WASM_VECTOR_LEN=0,cachegetUint8Memory0=null;function getUint8Memory0(){return null!==cachegetUint8Memory0&&cachegetUint8Memory0.buffer===wasm.memory.buffer||(cachegetUint8Memory0=new Uint8Array(wasm.memory.buffer)),cachegetUint8Memory0}let cachegetInt32Memory0=null;function getInt32Memory0(){return null!==cachegetInt32Memory0&&cachegetInt32Memory0.buffer===wasm.memory.buffer||(cachegetInt32Memory0=new Int32Array(wasm.memory.buffer)),cachegetInt32Memory0}function passArray8ToWasm0(e,t){const a=t(1*e.length);return getUint8Memory0().set(e,a/1),WASM_VECTOR_LEN=e.length,a}function getArrayU8FromWasm0(e,t){return getUint8Memory0().subarray(e/1,e/1+t)}function encode_raw(e,t){var a=passArray8ToWasm0(t,wasm.__wbindgen_malloc),r=WASM_VECTOR_LEN;wasm[e+"_encode_raw"](8,a,r);var s=getInt32Memory0()[2],n=getInt32Memory0()[3],m=getArrayU8FromWasm0(s,n).slice();return wasm.__wbindgen_free(s,1*n),m}self.addEventListener("message",e=>{const t=e.data.type;if("init"===t)if("function"==typeof WebAssembly.instantiateStreaming){const t={},a=fetch(e.data.path+"wasm_flate_bg.wasm");WebAssembly.instantiateStreaming(a,t).then(({instance:e})=>{wasm=e.exports,self.postMessage({type:"init",result:0})}).catch(e=>a.then(t=>{"application/wasm"!==t.headers.get("Content-Type")?self.postMessage({type:"init",error:"\`WebAssembly.instantiateStreaming\` failed because your server does not serve wasm with \`application/wasm\` MIME type. Original error: "+e.message}):self.postMessage({type:"init",error:e.message})}))}else self.postMessage({type:"init",error:"no \`WebAssembly.instantiateStreaming\`"});else if("compress"===t)if(null==wasm)self.postMessage({error:"no \`wasm\` instance"});else{let t=encode_raw("${compressed ?? 'deflate'}",e.data.data);self.postMessage(t,[t.buffer])}});`]));
|
url = URL.createObjectURL(new Blob([`let wasm,WASM_VECTOR_LEN=0,cachegetUint8Memory0=null;function getUint8Memory0(){return null!==cachegetUint8Memory0&&cachegetUint8Memory0.buffer===wasm.memory.buffer||(cachegetUint8Memory0=new Uint8Array(wasm.memory.buffer)),cachegetUint8Memory0}let cachegetInt32Memory0=null;function getInt32Memory0(){return null!==cachegetInt32Memory0&&cachegetInt32Memory0.buffer===wasm.memory.buffer||(cachegetInt32Memory0=new Int32Array(wasm.memory.buffer)),cachegetInt32Memory0}function passArray8ToWasm0(e,t){const a=t(1*e.length);return getUint8Memory0().set(e,a/1),WASM_VECTOR_LEN=e.length,a}function getArrayU8FromWasm0(e,t){return getUint8Memory0().subarray(e/1,e/1+t)}function encode_raw(e,t){var a=passArray8ToWasm0(t,wasm.__wbindgen_malloc),r=WASM_VECTOR_LEN;wasm[e+"_encode_raw"](8,a,r);var s=getInt32Memory0()[2],n=getInt32Memory0()[3],m=getArrayU8FromWasm0(s,n).slice();return wasm.__wbindgen_free(s,1*n),m}self.addEventListener("message",e=>{const t=e.data.type;if("init"===t)if("function"==typeof WebAssembly.instantiateStreaming){const t={},a=fetch(e.data.path+"wasm_flate_bg.wasm");WebAssembly.instantiateStreaming(a,t).then(({instance:e})=>{wasm=e.exports,self.postMessage({type:"init",result:0})}).catch(e=>a.then(t=>{"application/wasm"!==t.headers.get("Content-Type")?self.postMessage({type:"init",error:"\`WebAssembly.instantiateStreaming\` failed because your server does not serve wasm with \`application/wasm\` MIME type. Original error: "+e.message}):self.postMessage({type:"init",error:e.message})}))}else self.postMessage({type:"init",error:"no \`WebAssembly.instantiateStreaming\`"});else if("compress"===t)if(null==wasm)self.postMessage({error:"no \`wasm\` instance"});else{let t=encode_raw("${compressed ?? 'deflate'}",e.data.data);self.postMessage(t,[t.buffer])}});`]));
|
||||||
}
|
}
|
||||||
@@ -2469,7 +2520,7 @@ export class Grid {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
working = true;
|
working = true;
|
||||||
worker.postMessage({ type: 'init', path: ScriptPath });
|
worker.postMessage({ type: 'init', path });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2515,6 +2566,7 @@ export class Grid {
|
|||||||
direction = 1;
|
direction = 1;
|
||||||
}
|
}
|
||||||
const editing = col.sortAsText !== true;
|
const editing = col.sortAsText !== true;
|
||||||
|
const lgid = this.lgid;
|
||||||
const comparer = (a, b) => {
|
const comparer = (a, b) => {
|
||||||
a = this._getItemSortProp(a, editing, col);
|
a = this._getItemSortProp(a, editing, col);
|
||||||
b = this._getItemSortProp(b, editing, col);
|
b = this._getItemSortProp(b, editing, col);
|
||||||
@@ -2541,8 +2593,9 @@ export class Grid {
|
|||||||
b = b.join(', ');
|
b = b.join(', ');
|
||||||
}
|
}
|
||||||
if (typeof a === 'string' && typeof b === 'string') {
|
if (typeof a === 'string' && typeof b === 'string') {
|
||||||
a = a.toLowerCase();
|
// a = a.toLowerCase();
|
||||||
b = b.toLowerCase();
|
// b = b.toLowerCase();
|
||||||
|
return a.localeCompare(b, lgid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (a == null && b != null) {
|
if (a == null && b != null) {
|
||||||
@@ -2558,8 +2611,9 @@ export class Grid {
|
|||||||
b = b.join(', ');
|
b = b.join(', ');
|
||||||
}
|
}
|
||||||
if (typeof a === 'string' && typeof b === 'string') {
|
if (typeof a === 'string' && typeof b === 'string') {
|
||||||
a = a.toLowerCase();
|
// a = a.toLowerCase();
|
||||||
b = b.toLowerCase();
|
// b = b.toLowerCase();
|
||||||
|
return a.localeCompare(b, lgid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return a === b ? 0 : (a > b ? 1 : -1);
|
return a === b ? 0 : (a > b ? 1 : -1);
|
||||||
@@ -2727,10 +2781,15 @@ export class Grid {
|
|||||||
}
|
}
|
||||||
th.appendChild(wrapper);
|
th.appendChild(wrapper);
|
||||||
if (!readonly && col.enabled !== false && col.allcheck && alwaysEditing) {
|
if (!readonly && col.enabled !== false && col.allcheck && alwaysEditing) {
|
||||||
const check = createCheckbox({
|
let check;
|
||||||
|
if (typeof type.createEdit === 'function') {
|
||||||
|
check = type.createEdit(e => this._onColumnAllChecked(col, e.target.checked), col);
|
||||||
|
} else {
|
||||||
|
check = createCheckbox({
|
||||||
switch: col.switch,
|
switch: col.switch,
|
||||||
onchange: e => this._onColumnAllChecked(col, e.target.checked)
|
onchange: e => this._onColumnAllChecked(col, e.target.checked)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
wrapper.appendChild(check);
|
wrapper.appendChild(check);
|
||||||
}
|
}
|
||||||
let caption;
|
let caption;
|
||||||
@@ -2752,7 +2811,7 @@ export class Grid {
|
|||||||
if (col.captionTooltip != null) {
|
if (col.captionTooltip != null) {
|
||||||
const help = createIcon('fa-solid', 'question-circle');
|
const help = createIcon('fa-solid', 'question-circle');
|
||||||
wrapper.appendChild(help);
|
wrapper.appendChild(help);
|
||||||
setTooltip(help, col.captionTooltip, false, this._var.parent);
|
setTooltip(help, col.captionTooltip, false, this._var.container);
|
||||||
}
|
}
|
||||||
// order arrow
|
// order arrow
|
||||||
if (col.sortable) {
|
if (col.sortable) {
|
||||||
@@ -3041,6 +3100,12 @@ export class Grid {
|
|||||||
} else if (row.classList.contains('selected')) {
|
} else if (row.classList.contains('selected')) {
|
||||||
row.classList.remove('selected');
|
row.classList.remove('selected');
|
||||||
}
|
}
|
||||||
|
if (vals.__level !== 0) {
|
||||||
|
row.classList.add('ui-grid-row-level');
|
||||||
|
row.classList.add(`level-${vals.__level}`);
|
||||||
|
} else {
|
||||||
|
row.classList.remove('ui-grid-row-level');
|
||||||
|
}
|
||||||
const stateChanged = virtualRow.editing !== selected;
|
const stateChanged = virtualRow.editing !== selected;
|
||||||
virtualRow.editing = selected;
|
virtualRow.editing = selected;
|
||||||
// data
|
// data
|
||||||
@@ -3105,7 +3170,7 @@ export class Grid {
|
|||||||
if (col.text != null) {
|
if (col.text != null) {
|
||||||
val = col.text;
|
val = col.text;
|
||||||
} else if (typeof col.filter === 'function') {
|
} else if (typeof col.filter === 'function') {
|
||||||
val = col.filter(item, selected, this._var.refs.body, startIndex + i);
|
val = col.filter(item, !this.readonly && selected, this._var.refs.body, startIndex + i);
|
||||||
} else {
|
} else {
|
||||||
val = item[col.key];
|
val = item[col.key];
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
@@ -3604,7 +3669,7 @@ export class Grid {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {("autoResize" | "style" | "resizing" | "dragging" | "filterSource" | "filterHeight" | "filterTop")} name
|
* @param {("autoResize" | "style" | "resizing" | "dragging" | "filterSource" | "filterHeight" | "filterTop" | "filterSearched")} name
|
||||||
* @returns {any}
|
* @returns {any}
|
||||||
*/
|
*/
|
||||||
_get(key, name) {
|
_get(key, name) {
|
||||||
@@ -3618,7 +3683,7 @@ export class Grid {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {("autoResize" | "style" | "filterSource" | "filterHeight" | "filterTop")} name
|
* @param {("autoResize" | "style" | "filterSource" | "filterHeight" | "filterTop" | "filterSearched")} name
|
||||||
* @param {any} value
|
* @param {any} value
|
||||||
*/
|
*/
|
||||||
_set(key, name, value) {
|
_set(key, name, value) {
|
||||||
@@ -3703,7 +3768,7 @@ export class Grid {
|
|||||||
if (e.parentElement.classList.contains('ui-switch')) {
|
if (e.parentElement.classList.contains('ui-switch')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return /^(input|label|layer|svg|use)$/i.test(e.tagName);
|
return /^(i|input|label|layer|svg|use)$/i.test(e.tagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3815,6 +3880,7 @@ export class Grid {
|
|||||||
panel.style.height = '';
|
panel.style.height = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._set(col.key, 'filterSearched', false);
|
||||||
// search
|
// search
|
||||||
let searchbox;
|
let searchbox;
|
||||||
if (col.allowSearch !== false) {
|
if (col.allowSearch !== false) {
|
||||||
@@ -3884,6 +3950,7 @@ export class Grid {
|
|||||||
const type = this._var.colTypes[col.key];
|
const type = this._var.colTypes[col.key];
|
||||||
const isDateColumn = type === GridDateColumn || type instanceof GridDateColumn;
|
const isDateColumn = type === GridDateColumn || type instanceof GridDateColumn;
|
||||||
const filterAsValue = col.filterAsValue;
|
const filterAsValue = col.filterAsValue;
|
||||||
|
const lgid = this.lgid;
|
||||||
array.sort((itemA, itemB) => {
|
array.sort((itemA, itemB) => {
|
||||||
let a = itemA.Value;
|
let a = itemA.Value;
|
||||||
let b = itemB.Value;
|
let b = itemB.Value;
|
||||||
@@ -3901,8 +3968,9 @@ export class Grid {
|
|||||||
b = itemB.DisplayValue;
|
b = itemB.DisplayValue;
|
||||||
}
|
}
|
||||||
if (typeof a === 'string' && typeof b === 'string') {
|
if (typeof a === 'string' && typeof b === 'string') {
|
||||||
a = a.toLowerCase();
|
// a = a.toLowerCase();
|
||||||
b = b.toLowerCase();
|
// b = b.toLowerCase();
|
||||||
|
return a.localeCompare(b, lgid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return a > b ? 1 : (a < b ? -1 : 0);
|
return a > b ? 1 : (a < b ? -1 : 0);
|
||||||
@@ -3920,7 +3988,7 @@ export class Grid {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
this._fillFilterList(col, itemlist, array, itemall);
|
this._fillFilterList(col, itemlist, array, itemall);
|
||||||
itemall.querySelector('input').checked = ![...itemlist.querySelectorAll('.filter-content input')].some(i => !i.checked);
|
itemall.querySelector('input').checked = array.find(i => !i.__checked) == null;
|
||||||
panel.appendChild(itemlist);
|
panel.appendChild(itemlist);
|
||||||
if (searchbox != null) {
|
if (searchbox != null) {
|
||||||
searchbox.addEventListener('input', e => {
|
searchbox.addEventListener('input', e => {
|
||||||
@@ -3937,6 +4005,7 @@ export class Grid {
|
|||||||
}
|
}
|
||||||
return String(displayValue).toLowerCase().includes(key);
|
return String(displayValue).toLowerCase().includes(key);
|
||||||
});
|
});
|
||||||
|
this._set(col.key, 'filterSearched', items.length !== array.length);
|
||||||
this._fillFilterList(col, itemlist, items, itemall);
|
this._fillFilterList(col, itemlist, items, itemall);
|
||||||
this._set(col.key, 'filterTop', -1);
|
this._set(col.key, 'filterTop', -1);
|
||||||
itemlist.dispatchEvent(new Event('scroll'));
|
itemlist.dispatchEvent(new Event('scroll'));
|
||||||
@@ -3949,7 +4018,16 @@ export class Grid {
|
|||||||
ok.className = 'button';
|
ok.className = 'button';
|
||||||
ok.innerText = this.langs.ok;
|
ok.innerText = this.langs.ok;
|
||||||
ok.addEventListener('click', () => {
|
ok.addEventListener('click', () => {
|
||||||
const array = this._get(col.key, 'filterSource').filter(i => i.__checked !== false);
|
const filterSource = this._get(col.key, 'filterSource');
|
||||||
|
const filterSearched = this._get(col.key, 'filterSearched');
|
||||||
|
if (!filterSearched && filterSource.find(i => i.__checked === false) == null) {
|
||||||
|
// all checked, equals to 'Reset'
|
||||||
|
delete col.filterValues;
|
||||||
|
this._var.colAttrs.__filtered = this.columns.some(c => c.filterValues != null);
|
||||||
|
filter.replaceChildren(createIcon('fa-solid', this.filterIcon));
|
||||||
|
filter.classList.remove('active');
|
||||||
|
} else {
|
||||||
|
const array = filterSource.filter(i => i.__checked !== false);
|
||||||
if (typeof col.onFilterOk === 'function') {
|
if (typeof col.onFilterOk === 'function') {
|
||||||
col.onFilterOk.call(this, col, array);
|
col.onFilterOk.call(this, col, array);
|
||||||
} else {
|
} else {
|
||||||
@@ -3961,12 +4039,13 @@ export class Grid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._var.colAttrs.__filtered = true;
|
this._var.colAttrs.__filtered = true;
|
||||||
|
filter.replaceChildren(createIcon('fa-solid', this.filteredIcon));
|
||||||
|
filter.classList.add('active');
|
||||||
|
}
|
||||||
this._refreshSource();
|
this._refreshSource();
|
||||||
if (typeof col.onFiltered === 'function') {
|
if (typeof col.onFiltered === 'function') {
|
||||||
col.onFiltered.call(this, col);
|
col.onFiltered.call(this, col);
|
||||||
}
|
}
|
||||||
filter.replaceChildren(createIcon('fa-solid', this.filteredIcon));
|
|
||||||
filter.classList.add('active');
|
|
||||||
this._onCloseFilter();
|
this._onCloseFilter();
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
@@ -3975,7 +4054,7 @@ export class Grid {
|
|||||||
reset.innerText = this.langs.reset;
|
reset.innerText = this.langs.reset;
|
||||||
reset.addEventListener('click', () => {
|
reset.addEventListener('click', () => {
|
||||||
delete col.filterValues;
|
delete col.filterValues;
|
||||||
this._var.colAttrs.__filtered = this.columns.some(c => c.filterValues != null)
|
this._var.colAttrs.__filtered = this.columns.some(c => c.filterValues != null);
|
||||||
this._refreshSource();
|
this._refreshSource();
|
||||||
if (typeof col.onFiltered === 'function') {
|
if (typeof col.onFiltered === 'function') {
|
||||||
col.onFiltered.call(this, col);
|
col.onFiltered.call(this, col);
|
||||||
@@ -3998,34 +4077,39 @@ export class Grid {
|
|||||||
* @private
|
* @private
|
||||||
* @param {GridColumnDefinition} col
|
* @param {GridColumnDefinition} col
|
||||||
* @param {HTMLDivElement} list
|
* @param {HTMLDivElement} list
|
||||||
* @param {ValueItem[]} array
|
* @param {ValueItem[]} source
|
||||||
* @param {HTMLDivElement} all
|
* @param {HTMLDivElement} all
|
||||||
*/
|
*/
|
||||||
_fillFilterList(col, list, array, all) {
|
_fillFilterList(col, list, source, all) {
|
||||||
list.querySelector('.filter-holder')?.remove();
|
list.querySelector('.filter-holder')?.remove();
|
||||||
list.querySelector('.filter-content')?.remove();
|
list.querySelector('.filter-content')?.remove();
|
||||||
const rowHeight = this.filterRowHeight;
|
const rowHeight = this.filterRowHeight;
|
||||||
const height = array.length * rowHeight;
|
const height = source.length * rowHeight;
|
||||||
this._set(col.key, 'filterHeight', height);
|
this._set(col.key, 'filterHeight', height);
|
||||||
const holder = createElement('div', 'filter-holder');
|
const holder = createElement('div', 'filter-holder');
|
||||||
holder.style.height = `${height}px`;
|
holder.style.height = `${height}px`;
|
||||||
const content = createElement('div', 'filter-content');
|
const content = createElement('div', 'filter-content');
|
||||||
content.style.top = `${rowHeight}px`;
|
content.style.top = `${rowHeight}px`;
|
||||||
this._set(col.key, 'filterSource', array);
|
this._set(col.key, 'filterSource', source);
|
||||||
const propKey = GridColumnTypeEnum.isAlwaysEditing(col.type) ? 'Value' : 'DisplayValue';
|
const propKey = GridColumnTypeEnum.isAlwaysEditing(col.type) ? 'Value' : 'DisplayValue';
|
||||||
const nullValue = col.filterAllowNull ? null : '';
|
const nullValue = col.filterAllowNull ? null : '';
|
||||||
const allSelected = !Array.isArray(col.filterValues);
|
const allSelected = !Array.isArray(col.filterValues);
|
||||||
for (let item of array) {
|
for (let item of source) {
|
||||||
|
if (item.__checked == null) {
|
||||||
let v = item.Value ?? nullValue;
|
let v = item.Value ?? nullValue;
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
v = Object.prototype.hasOwnProperty.call(item, propKey) ? item[propKey] : item;
|
v = Object.prototype.hasOwnProperty.call(item, propKey) ? item[propKey] : item;
|
||||||
}
|
}
|
||||||
item.__checked = allSelected || col.filterValues.some(it => Array.isArray(it) ? it.includes(v) : it === v);
|
item.__checked = allSelected || col.filterValues.some(it => Array.isArray(it) ? it.includes(v) : it === v);
|
||||||
}
|
}
|
||||||
if (array.length > 12) {
|
|
||||||
array = array.slice(0, 12);
|
|
||||||
}
|
}
|
||||||
this._doFillFilterList(col, content, array, all);
|
let array;
|
||||||
|
if (source.length > 12) {
|
||||||
|
array = source.slice(0, 12);
|
||||||
|
} else {
|
||||||
|
array = source;
|
||||||
|
}
|
||||||
|
this._doFillFilterList(col, content, array, source, all);
|
||||||
list.append(holder, content);
|
list.append(holder, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4034,9 +4118,10 @@ export class Grid {
|
|||||||
* @param {GridColumnDefinition} col
|
* @param {GridColumnDefinition} col
|
||||||
* @param {HTMLDivElement} content
|
* @param {HTMLDivElement} content
|
||||||
* @param {ValueItem[]} array
|
* @param {ValueItem[]} array
|
||||||
|
* @param {ValueItem[]} source
|
||||||
* @param {HTMLDivElement} all
|
* @param {HTMLDivElement} all
|
||||||
*/
|
*/
|
||||||
_doFillFilterList(col, content, array, all) {
|
_doFillFilterList(col, content, array, source, all) {
|
||||||
for (let item of array) {
|
for (let item of array) {
|
||||||
const div = createElement('div', 'filter-item');
|
const div = createElement('div', 'filter-item');
|
||||||
const title = Object.prototype.hasOwnProperty.call(item, 'DisplayValue') ? item.DisplayValue : item;
|
const title = Object.prototype.hasOwnProperty.call(item, 'DisplayValue') ? item.DisplayValue : item;
|
||||||
@@ -4053,7 +4138,7 @@ export class Grid {
|
|||||||
title,
|
title,
|
||||||
onchange: e => {
|
onchange: e => {
|
||||||
item.__checked = e.target.checked;
|
item.__checked = e.target.checked;
|
||||||
all.querySelector('input').checked = ![...content.querySelectorAll('input')].some(i => !i.checked);
|
all.querySelector('input').checked = source.find(i => !i.__checked) == null;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
content.appendChild(div);
|
content.appendChild(div);
|
||||||
@@ -4083,15 +4168,16 @@ export class Grid {
|
|||||||
if (this._get(col.key, 'filterTop') !== top) {
|
if (this._get(col.key, 'filterTop') !== top) {
|
||||||
this._set(col.key, 'filterTop', top);
|
this._set(col.key, 'filterTop', top);
|
||||||
const startIndex = top / rowHeight;
|
const startIndex = top / rowHeight;
|
||||||
let array = this._get(col.key, 'filterSource');
|
let source = this._get(col.key, 'filterSource');
|
||||||
if (startIndex + 12 < array.length) {
|
let array;
|
||||||
array = array.slice(startIndex, startIndex + 12);
|
if (startIndex + 12 < source.length) {
|
||||||
|
array = source.slice(startIndex, startIndex + 12);
|
||||||
} else {
|
} else {
|
||||||
array = array.slice(-12);
|
array = source.slice(-12);
|
||||||
}
|
}
|
||||||
const content = list.querySelector('.filter-content');
|
const content = list.querySelector('.filter-content');
|
||||||
content.replaceChildren();
|
content.replaceChildren();
|
||||||
this._doFillFilterList(col, content, array, list.querySelector('.filter-all'));
|
this._doFillFilterList(col, content, array, source, list.querySelector('.filter-all'));
|
||||||
content.style.top = `${top + rowHeight}px`;
|
content.style.top = `${top + rowHeight}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4341,7 +4427,7 @@ export class Grid {
|
|||||||
*/
|
*/
|
||||||
_onGridMouseMove(e, holder) {
|
_onGridMouseMove(e, holder) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.target.classList.contains('ui-grid-hover-holder')) {
|
if (e.target.classList.contains('ui-grid-hover')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [parent, target] = this._getRowTarget(e.target);
|
let [parent, target] = this._getRowTarget(e.target);
|
||||||
@@ -4364,8 +4450,10 @@ export class Grid {
|
|||||||
holder.dataset.col === col) {
|
holder.dataset.col === col) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const type = this._var.colTypes[this.columns[col]?.key];
|
const key = this.columns[col]?.key ?? col;
|
||||||
if (type?.canEdit && this._var.virtualRows[row]?.editing) {
|
const type = this._var.colTypes[key];
|
||||||
|
const virtualRow = this._var.virtualRows[row];
|
||||||
|
if (type?.canEdit && virtualRow?.editing) {
|
||||||
delete holder.dataset.row;
|
delete holder.dataset.row;
|
||||||
delete holder.dataset.col;
|
delete holder.dataset.col;
|
||||||
if (holder.classList.contains('active')) {
|
if (holder.classList.contains('active')) {
|
||||||
@@ -4391,7 +4479,7 @@ export class Grid {
|
|||||||
element.scrollHeight > element.offsetHeight) {
|
element.scrollHeight > element.offsetHeight) {
|
||||||
holder.dataset.row = row;
|
holder.dataset.row = row;
|
||||||
holder.dataset.col = col;
|
holder.dataset.col = col;
|
||||||
holder.innerText = element.innerText;
|
holder.querySelector('.ui-grid-hover-content').innerText = element.innerText;
|
||||||
const top = (parent.classList.contains('ui-grid-total-row') ? this._var.refs.footer.parentElement.offsetTop + 1 : target.offsetTop) + this._var.refs.table.offsetTop;
|
const top = (parent.classList.contains('ui-grid-total-row') ? this._var.refs.footer.parentElement.offsetTop + 1 : target.offsetTop) + this._var.refs.table.offsetTop;
|
||||||
let left = target.offsetLeft;
|
let left = target.offsetLeft;
|
||||||
let width = holder.offsetWidth;
|
let width = holder.offsetWidth;
|
||||||
@@ -4402,8 +4490,13 @@ export class Grid {
|
|||||||
if (left > maxleft) {
|
if (left > maxleft) {
|
||||||
left = maxleft;
|
left = maxleft;
|
||||||
}
|
}
|
||||||
const height = target.offsetHeight;
|
// const height = target.offsetHeight;
|
||||||
holder.style.cssText = `top: ${top}px; left: ${left}px; max-width: ${this._var.wrapClientWidth}px; min-height: ${height - 2}px`;
|
holder.style.cssText = `top: ${top}px; left: ${left}px; max-width: ${this._var.wrapClientWidth}px`; // ; min-height: ${height - 2}px
|
||||||
|
if (top > this.rowHeight * 2) {
|
||||||
|
holder.classList.remove('ui-grid-hover-down');
|
||||||
|
} else {
|
||||||
|
holder.classList.add('ui-grid-hover-down');
|
||||||
|
}
|
||||||
holder.classList.add('active');
|
holder.classList.add('active');
|
||||||
} else if (holder.classList.contains('active')) {
|
} else if (holder.classList.contains('active')) {
|
||||||
delete holder.dataset.row;
|
delete holder.dataset.row;
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ const ResizeMods = {
|
|||||||
topLeft: 8 | 4
|
topLeft: 8 | 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const IconStyle = 'fa-light';
|
||||||
|
|
||||||
// const Cursors = {
|
// const Cursors = {
|
||||||
// [ResizeMods.right]: 'ew-resize',
|
// [ResizeMods.right]: 'ew-resize',
|
||||||
// [ResizeMods.bottom]: 'ns-resize',
|
// [ResizeMods.bottom]: 'ns-resize',
|
||||||
@@ -110,7 +112,7 @@ export class Popup {
|
|||||||
this._var.bounds = r;
|
this._var.bounds = r;
|
||||||
container.classList.add('ui-popup-collapse');
|
container.classList.add('ui-popup-collapse');
|
||||||
if (collapse != null) {
|
if (collapse != null) {
|
||||||
changeIcon(collapse, 'fa-regular', 'expand-alt');
|
changeIcon(collapse, IconStyle, 'expand-alt');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isNaN(r.width) && r.width > 0) {
|
if (!isNaN(r.width) && r.width > 0) {
|
||||||
@@ -122,7 +124,7 @@ export class Popup {
|
|||||||
container.classList.remove('ui-popup-collapse');
|
container.classList.remove('ui-popup-collapse');
|
||||||
this._var.bounds = null;
|
this._var.bounds = null;
|
||||||
if (collapse != null) {
|
if (collapse != null) {
|
||||||
changeIcon(collapse, 'fa-regular', 'compress-alt');
|
changeIcon(collapse, IconStyle, 'compress-alt');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (css.length > 0) {
|
if (css.length > 0) {
|
||||||
@@ -259,7 +261,7 @@ export class Popup {
|
|||||||
const icons = createElement('div', icons => {
|
const icons = createElement('div', icons => {
|
||||||
icons.className = 'ui-popup-header-icons';
|
icons.className = 'ui-popup-header-icons';
|
||||||
if (option.collapsable === true) {
|
if (option.collapsable === true) {
|
||||||
const collapse = createIcon('fa-regular', 'compress-alt');
|
const collapse = createIcon(IconStyle, 'compress-alt');
|
||||||
collapse.tabIndex = tabIndex + 2;
|
collapse.tabIndex = tabIndex + 2;
|
||||||
collapse.classList.add('icon-expand');
|
collapse.classList.add('icon-expand');
|
||||||
collapse.addEventListener('keypress', e => {
|
collapse.addEventListener('keypress', e => {
|
||||||
@@ -275,13 +277,13 @@ export class Popup {
|
|||||||
this._var.bounds = null;
|
this._var.bounds = null;
|
||||||
}
|
}
|
||||||
container.classList.remove('ui-popup-collapse');
|
container.classList.remove('ui-popup-collapse');
|
||||||
changeIcon(collapse, 'fa-regular', 'compress-alt');
|
changeIcon(collapse, IconStyle, 'compress-alt');
|
||||||
} else {
|
} else {
|
||||||
const rect = this.rect;
|
const rect = this.rect;
|
||||||
this._var.bounds = rect;
|
this._var.bounds = rect;
|
||||||
container.style.cssText += `width: 160px; height: 40px`;
|
container.style.cssText += `width: 160px; height: 40px`;
|
||||||
container.classList.add('ui-popup-collapse');
|
container.classList.add('ui-popup-collapse');
|
||||||
changeIcon(collapse, 'fa-regular', 'expand-alt');
|
changeIcon(collapse, IconStyle, 'expand-alt');
|
||||||
}
|
}
|
||||||
if (typeof option.onResizeEnded === 'function') {
|
if (typeof option.onResizeEnded === 'function') {
|
||||||
option.onResizeEnded.call(this);
|
option.onResizeEnded.call(this);
|
||||||
@@ -290,7 +292,7 @@ export class Popup {
|
|||||||
icons.appendChild(collapse);
|
icons.appendChild(collapse);
|
||||||
}
|
}
|
||||||
if (option.closable !== false) {
|
if (option.closable !== false) {
|
||||||
const cancel = createIcon('fa-regular', 'times');
|
const cancel = createIcon(IconStyle, 'times');
|
||||||
cancel.tabIndex = tabIndex + 3;
|
cancel.tabIndex = tabIndex + 3;
|
||||||
cancel.addEventListener('keypress', e => {
|
cancel.addEventListener('keypress', e => {
|
||||||
if (e.key === ' ' || e.key === 'Enter') {
|
if (e.key === ' ' || e.key === 'Enter') {
|
||||||
@@ -304,7 +306,7 @@ export class Popup {
|
|||||||
header.appendChild(icons);
|
header.appendChild(icons);
|
||||||
}),
|
}),
|
||||||
createElement('div', 'ui-popup-body', content, createElement('div', 'ui-popup-loading',
|
createElement('div', 'ui-popup-body', content, createElement('div', 'ui-popup-loading',
|
||||||
createElement('div', null, createIcon('fa-regular', 'spinner-third'))
|
createElement('div', null, createIcon(IconStyle, 'spinner-third'))
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
if (Array.isArray(option.buttons) && option.buttons.length > 0) {
|
if (Array.isArray(option.buttons) && option.buttons.length > 0) {
|
||||||
@@ -315,6 +317,9 @@ export class Popup {
|
|||||||
if (b.className != null) {
|
if (b.className != null) {
|
||||||
button.classList.add(b.className);
|
button.classList.add(b.className);
|
||||||
}
|
}
|
||||||
|
if (b.isPrimary) {
|
||||||
|
button.classList.add('primary');
|
||||||
|
}
|
||||||
if (b.tabIndex > 0) {
|
if (b.tabIndex > 0) {
|
||||||
button.tabIndex = b.tabIndex;
|
button.tabIndex = b.tabIndex;
|
||||||
} else {
|
} else {
|
||||||
@@ -560,6 +565,7 @@ export function resolvePopup(wrapper, callback, removable, zIndex) {
|
|||||||
const buttons = [...wrapper.querySelectorAll('.dialog-func>input[type="button"]')].reverse().map(b => ({
|
const buttons = [...wrapper.querySelectorAll('.dialog-func>input[type="button"]')].reverse().map(b => ({
|
||||||
tabIndex: b.tabIndex,
|
tabIndex: b.tabIndex,
|
||||||
text: b.value,
|
text: b.value,
|
||||||
|
isPrimary: b.dataset.primary != null,
|
||||||
trigger: b.onclick == null ? null : (popup => (b.onclick.call(popup), false))
|
trigger: b.onclick == null ? null : (popup => (b.onclick.call(popup), false))
|
||||||
}));
|
}));
|
||||||
const popup = new Popup({
|
const popup = new Popup({
|
||||||
@@ -594,16 +600,54 @@ export function showAlert(title, message, iconType = 'info', parent = document.b
|
|||||||
),
|
),
|
||||||
resolve,
|
resolve,
|
||||||
buttons: [
|
buttons: [
|
||||||
{ text: r('ok', 'OK') }
|
{ text: r('ok', 'OK'), isPrimary: true }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
popup.show(parent).then(mask => {
|
popup.show(parent).then(mask => {
|
||||||
const button = mask.querySelector('.ui-popup-container .ui-popup-footer .ui-popup-button:last-child');
|
const button = mask.querySelector('.ui-popup-container .ui-popup-footer .ui-popup-button');
|
||||||
button?.focus();
|
button?.focus();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function showInput(title, multiline, message, def, placeholder, buttons, parent = document.body) {
|
||||||
|
const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0));
|
||||||
|
if (tabIndex < 0) {
|
||||||
|
tabIndex = 0;
|
||||||
|
}
|
||||||
|
const input = multiline ?
|
||||||
|
createElement('textarea', text => {
|
||||||
|
text.className = 'ui-text';
|
||||||
|
}) :
|
||||||
|
createElement('input', input => {
|
||||||
|
input.type = 'text';
|
||||||
|
input.className = 'ui-input';
|
||||||
|
});
|
||||||
|
input.tabIndex = tabIndex + 4;
|
||||||
|
if (!nullOrEmpty(placeholder)) {
|
||||||
|
input.placeholder = placeholder;
|
||||||
|
}
|
||||||
|
if (!nullOrEmpty(def)) {
|
||||||
|
input.value = def;
|
||||||
|
}
|
||||||
|
const popup = new Popup({
|
||||||
|
title,
|
||||||
|
content: createElement('div', 'message-wrapper message-wrapper-input',
|
||||||
|
nullOrEmpty(message) ? '' : createElement('span', span => span.innerText = message),
|
||||||
|
input
|
||||||
|
),
|
||||||
|
resolve: r => resolve(r.result === 'yes' ? input.value : null),
|
||||||
|
buttons: buttons ?? [
|
||||||
|
{ key: 'yes', text: r('yes', 'Yes'), isPrimary: true },
|
||||||
|
{ key: 'no', text: r('no', 'No') }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
popup.show(parent).then(() => input.focus());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function showConfirm(title, content, buttons, iconType = 'question', parent = document.body) {
|
export function showConfirm(title, content, buttons, iconType = 'question', parent = document.body) {
|
||||||
const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
|
const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
@@ -621,6 +665,7 @@ export function showConfirm(title, content, buttons, iconType = 'question', pare
|
|||||||
buttons: buttons?.map((b, i) => {
|
buttons: buttons?.map((b, i) => {
|
||||||
return {
|
return {
|
||||||
text: b.text,
|
text: b.text,
|
||||||
|
isPrimary: b.isPrimary,
|
||||||
trigger: p => {
|
trigger: p => {
|
||||||
let result;
|
let result;
|
||||||
if (typeof b.trigger === 'function') {
|
if (typeof b.trigger === 'function') {
|
||||||
@@ -633,12 +678,12 @@ export function showConfirm(title, content, buttons, iconType = 'question', pare
|
|||||||
};
|
};
|
||||||
}) ??
|
}) ??
|
||||||
[
|
[
|
||||||
{ key: 'yes', text: r('yes', 'Yes') },
|
{ key: 'yes', text: r('yes', 'Yes'), isPrimary: true },
|
||||||
{ key: 'no', text: r('no', 'No') }
|
{ key: 'no', text: r('no', 'No') }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
popup.show(parent).then(mask => {
|
popup.show(parent).then(mask => {
|
||||||
const button = mask.querySelector('.ui-popup-container .ui-popup-footer .ui-popup-button:last-child');
|
const button = mask.querySelector('.ui-popup-container .ui-popup-footer .ui-popup-button:first-child');
|
||||||
button?.focus();
|
button?.focus();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { global } from '../utility';
|
|||||||
|
|
||||||
const pointerHeight = 12;
|
const pointerHeight = 12;
|
||||||
|
|
||||||
export function setTooltip(container, content, flag = false, parent = null) {
|
export function setTooltip(container, content, flag = false, parent = null, maxLeft = null) {
|
||||||
const isParent = parent instanceof HTMLElement;
|
const isParent = parent instanceof HTMLElement;
|
||||||
if (isParent) {
|
if (isParent) {
|
||||||
const tipid = container.dataset.tipId;
|
const tipid = container.dataset.tipId;
|
||||||
@@ -54,6 +54,7 @@ export function setTooltip(container, content, flag = false, parent = null) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!flag || c.scrollWidth > c.offsetWidth) {
|
if (!flag || c.scrollWidth > c.offsetWidth) {
|
||||||
|
wrapper.style.cssText += 'left: -9999px; top: -9999px; display: block';
|
||||||
tid = setTimeout(() => {
|
tid = setTimeout(() => {
|
||||||
let p;
|
let p;
|
||||||
let left;
|
let left;
|
||||||
@@ -75,7 +76,6 @@ export function setTooltip(container, content, flag = false, parent = null) {
|
|||||||
top -= p.scrollTop;
|
top -= p.scrollTop;
|
||||||
p = p.parentElement;
|
p = p.parentElement;
|
||||||
}
|
}
|
||||||
wrapper.style.display = '';
|
|
||||||
const offsetHeight = wrapper.offsetHeight;
|
const offsetHeight = wrapper.offsetHeight;
|
||||||
const offsetWidth = wrapper.offsetWidth;
|
const offsetWidth = wrapper.offsetWidth;
|
||||||
if (isParent) {
|
if (isParent) {
|
||||||
@@ -150,6 +150,15 @@ export function setTooltip(container, content, flag = false, parent = null) {
|
|||||||
left = lastWidth - offsetWidth - 1;
|
left = lastWidth - offsetWidth - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (typeof maxLeft === 'function') {
|
||||||
|
const max = maxLeft(offsetWidth);
|
||||||
|
if (left > max) {
|
||||||
|
wrapper.style.setProperty('--pointer-left', 'calc(100% - 20px)');
|
||||||
|
left = max;
|
||||||
|
} else {
|
||||||
|
wrapper.style.setProperty('--pointer-left', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
// wrapper.style.left = `${left}px`;
|
// wrapper.style.left = `${left}px`;
|
||||||
// wrapper.style.top = `${top}px`;
|
// wrapper.style.top = `${top}px`;
|
||||||
// wrapper.style.visibility = 'visible';
|
// wrapper.style.visibility = 'visible';
|
||||||
|
|||||||
@@ -63,8 +63,9 @@ async function refreshLgres(template, lgres) {
|
|||||||
lgres = await doRefreshLgres(template);
|
lgres = await doRefreshLgres(template);
|
||||||
}
|
}
|
||||||
const ver = Number(consts.resver);
|
const ver = Number(consts.resver);
|
||||||
if (isNaN(lgres.ver) || isNaN(ver) || ver > lgres.ver) {
|
const currentVer = Number(lgres.ver);
|
||||||
console.log(`found new language res version: ${lgres.ver} => ${ver}`);
|
if (isNaN(currentVer) || isNaN(ver) || ver > currentVer) {
|
||||||
|
console.log(`found new language res version: ${lgres.ver} => ${consts.resver}`);
|
||||||
lgres = await doRefreshLgres(template);
|
lgres = await doRefreshLgres(template);
|
||||||
}
|
}
|
||||||
Object.defineProperty(lgres, 'r', {
|
Object.defineProperty(lgres, 'r', {
|
||||||
|
|||||||
@@ -37,13 +37,25 @@ export function post(url, data, options = {}) {
|
|||||||
options.customHeaders['Content-Type'] = 'application/json';
|
options.customHeaders['Content-Type'] = 'application/json';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fetch(combineUrl(url), {
|
const opts = {
|
||||||
method: options.method || 'POST',
|
method: options.method || 'POST',
|
||||||
headers: options.customHeaders,
|
headers: options.customHeaders,
|
||||||
body: data,
|
body: data,
|
||||||
signal: options.signal,
|
signal: options.signal,
|
||||||
cache: 'no-cache'
|
cache: 'no-cache'
|
||||||
|
};
|
||||||
|
if (options.diagnostic) {
|
||||||
|
const started = new Date().getTime();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch(combineUrl(url), opts).then(response => {
|
||||||
|
resolve({
|
||||||
|
time: new Date().getTime() - started,
|
||||||
|
response
|
||||||
});
|
});
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return fetch(combineUrl(url), opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function upload(url, data, options = {}) {
|
export function upload(url, data, options = {}) {
|
||||||
|
|||||||
Reference in New Issue
Block a user