Compare commits
44 Commits
dc2b67a33c
...
master
Author | SHA1 | Date | |
---|---|---|---|
eec9d6045c
|
|||
a3f0288c92
|
|||
6b1e74790b
|
|||
adbf4750cc
|
|||
5baf00de64
|
|||
1a7aa1ab66
|
|||
d296dd01fd
|
|||
f5b6ce360e | |||
614a983aa8 | |||
ea7f4f538a | |||
190e43c814 | |||
a946012a33 | |||
f676ec76db | |||
4e8be83625 | |||
d91630212f | |||
5cbbcf8d81 | |||
b6fe3e34f5 | |||
0ff48a0ac4 | |||
03e3b4a70f | |||
c78e445a24 | |||
6458427c75 | |||
87e3d6c81b | |||
fd8899c597 | |||
f46e25952c | |||
18f8cc322d | |||
e07342a257 | |||
f54eb3ac24 | |||
70ab06190f | |||
39378e9963 | |||
3ea7ce62bb | |||
e75e763938 | |||
168cae3ce1 | |||
6fb7c3c769 | |||
baec3a3959 | |||
6de4987360 | |||
4096979d5d | |||
1ed5b39308 | |||
975d54fee1 | |||
a28b56b191 | |||
20a8fbae02 | |||
5e53d04174 | |||
38bad864e0 | |||
7940edbea2 | |||
c21f62b7b8 |
35
README.md
@ -1,3 +1,36 @@
|
|||||||
# [ui-lib].Grid
|
# [ui-lib].Grid
|
||||||
|
|
||||||
UI Mordern Gridview Library
|
UI Mordern Gridview Library
|
||||||
|
|
||||||
|
## 1.0.7
|
||||||
|
* 调整: [(event) onBodyScrolled(e: Event, index: number, count: number)](Grid.html#event:onBodyScrolled) - 列滚动时触发的事件,增加显示行起始索引与一屏呈现行数
|
||||||
|
* 新增: [`currentItem: GridRowItem | null`](Grid.html#currentItem) - 获取当前选中的行对象
|
||||||
|
|
||||||
|
## 1.0.6
|
||||||
|
* 新增: [`col.switch`](GridColumnDefinition.html) - 列复选框样式改为 `ui-switch`
|
||||||
|
|
||||||
|
## 1.0.5
|
||||||
|
* 新增: [`col`.filterTemplate: (this: GridColumnDefinition, item: ValueItem) => HTMLElement](GridColumnDefinition.html) - 列头过滤项的模板函数
|
||||||
|
|
||||||
|
## 1.0.4
|
||||||
|
* 调整: `Dropdown` 组件支持虚模式
|
||||||
|
* 新增: `ui-switch` 样式(iOS 切换组件)
|
||||||
|
* 新增: `Dropdown` 组件增加 `ignoreAll` 参数
|
||||||
|
|
||||||
|
## 1.0.3
|
||||||
|
* 调整: [showSortPanel(callback?: Function, layout?: boolean)](Grid.html#showSortPanel) 现支持输入搜索列,已添加的列不会重复显示在下拉数据源中,增加回调函数与 layout 更新复选框。
|
||||||
|
* 新增: [onRowChanged(action: "update" | "add" | "remove", items: GridRowItem[], indexes: number | number[])](Grid.html#onRowChanged) - 行发生变化时触发的事件
|
||||||
|
### bugs
|
||||||
|
* 修复: 清空多列排序后列头箭头没有清除的异常。
|
||||||
|
|
||||||
|
## 1.0.2
|
||||||
|
* 新增: [export(compressed: string | boolean, module?: string) : Promise<GridExportData>](Grid.html#export) - 导出已压缩的数据源
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
* 新增: [total: GridRowItem](Grid.html#total) - 获取或设置合计行数据
|
||||||
|
* 新增: [showSortPanel()](Grid.html#showSortPanel) - 显示多列排序设置面板
|
||||||
|
* 新增: [setItem(index: number, item: GridRowItem)](Grid.html#setItem) - 设置单行数据
|
||||||
|
* 新增: [addItem(item: GridRowItem, index?: number): GridRowItem](Grid.html#addItem) - 添加行数据
|
||||||
|
* 新增: [addItems(array: GridRowItem[], index?: number) : GridRowItem[]](Grid.html#addItems) - 批量添加行数据
|
||||||
|
* 新增: [removeItem(index: number) : GridRowItem](Grid.html#removeItem) - 删除行数据
|
||||||
|
* 新增: [removeItems(indexes?: number[]): GridRowItem[]](Grid.html#removeItems) - 批量删除行数据
|
||||||
|
2629
fonts/fa-light.svg
Before Width: | Height: | Size: 651 KiB |
2629
fonts/fa-regular.svg
Before Width: | Height: | Size: 600 KiB |
2629
fonts/fa-solid.svg
Before Width: | Height: | Size: 510 KiB |
19
index.html
@ -1,19 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>UI Lib</title>
|
|
||||||
<script src="/amrnb.js"></script>
|
|
||||||
<script type="module" src="/main.js"></script>
|
|
||||||
<script src="/dist/ui.min.js"></script>
|
|
||||||
<script src="/dist/utility.min.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="container"></div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -2,9 +2,11 @@ import "./app/communications/style.scss";
|
|||||||
import CustomerCommunication from "./app/communications/customer";
|
import CustomerCommunication from "./app/communications/customer";
|
||||||
import InternalComment from "./app/communications/internal";
|
import InternalComment from "./app/communications/internal";
|
||||||
import CustomerRecordComment from "./app/communications/comments";
|
import CustomerRecordComment from "./app/communications/comments";
|
||||||
|
import { createHideMessageTitleButton } from "./app/communications/lib";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CustomerCommunication,
|
CustomerCommunication,
|
||||||
InternalComment,
|
InternalComment,
|
||||||
CustomerRecordComment
|
CustomerRecordComment,
|
||||||
|
createHideMessageTitleButton
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { createElement, setTooltip, createIcon } from "../../ui";
|
import { createElement, setTooltip, createIcon, requestAnimationFrame } from "../../ui";
|
||||||
import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility";
|
import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility";
|
||||||
import { createBox, appendMedia } from "./lib";
|
import { createBox, appendMedia, createHideMessageTitleButton, createHideMessageCommentTail } from "./lib";
|
||||||
|
|
||||||
let r = lang;
|
let r = lang;
|
||||||
|
|
||||||
@ -12,6 +12,8 @@ export default class CustomerRecordComment {
|
|||||||
const getText = opt?.getText;
|
const getText = opt?.getText;
|
||||||
if (typeof getText === 'function') {
|
if (typeof getText === 'function') {
|
||||||
r = getText;
|
r = getText;
|
||||||
|
} else if (typeof GetTextByKey === 'function') {
|
||||||
|
r = GetTextByKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +27,20 @@ export default class CustomerRecordComment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} flag
|
||||||
|
*/
|
||||||
|
set messageHidden(flag) {
|
||||||
|
const el = this._var.container.querySelector('.msgadminsetting');
|
||||||
|
if (el == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._var.option.showCommentHidden = flag;
|
||||||
|
// TODO: 是否与参数 flag 无关
|
||||||
|
el.classList.remove('iconview');
|
||||||
|
el.classList.add('iconnotview');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} flag
|
* @param {boolean} flag
|
||||||
*/
|
*/
|
||||||
@ -55,8 +71,10 @@ export default class CustomerRecordComment {
|
|||||||
createElement('div', null,
|
createElement('div', null,
|
||||||
createElement('div', div => {
|
createElement('div', div => {
|
||||||
div.className = 'title-module';
|
div.className = 'title-module';
|
||||||
div.innerText = r('P_CR_COMMENTS', 'Comments');
|
div.innerText = r('FLTL_00584', 'Comments');
|
||||||
})
|
},
|
||||||
|
createHideMessageTitleButton(this, 'showCommentHidden')
|
||||||
|
)
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
createElement('button', button => {
|
createElement('button', button => {
|
||||||
@ -79,7 +97,7 @@ export default class CustomerRecordComment {
|
|||||||
);
|
);
|
||||||
// enter box
|
// enter box
|
||||||
const enter = createElement('textarea', 'ui-text');
|
const enter = createElement('textarea', 'ui-text');
|
||||||
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
|
enter.placeholder = r('FLTL_01154', 'Enter Comment Here');
|
||||||
enter.maxLength = this._var.option.maxLength ??= 3000;
|
enter.maxLength = this._var.option.maxLength ??= 3000;
|
||||||
enter.addEventListener('input', () => {
|
enter.addEventListener('input', () => {
|
||||||
const val = this.text;
|
const val = this.text;
|
||||||
@ -102,8 +120,8 @@ export default class CustomerRecordComment {
|
|||||||
button.style.display = 'none';
|
button.style.display = 'none';
|
||||||
}
|
}
|
||||||
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
||||||
// setTooltip(button, r('P_M3_SENDMESSAGE', 'Send Message'));
|
// setTooltip(button, r('FLTL_02692', 'Send Message'));
|
||||||
setTooltip(button, r('P_CU_POSTNOTE', '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);
|
||||||
@ -120,13 +138,20 @@ export default class CustomerRecordComment {
|
|||||||
return this._var.container = container;
|
return this._var.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
load(data) {
|
load(data, func, hisFunc, keep) {
|
||||||
const children = [];
|
const children = [];
|
||||||
if (data?.length > 0) {
|
if (data?.length > 0) {
|
||||||
|
const lastVisible = this._var.option.showCommentHidden;
|
||||||
for (let comment of data) {
|
for (let comment of data) {
|
||||||
const div = createElement('div', 'item-div');
|
const div = createElement('div', 'item-div');
|
||||||
|
if (comment.Hidden) {
|
||||||
|
div.classList.add('hidden-content');
|
||||||
|
if (!lastVisible) {
|
||||||
|
div.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
// if (sendto !== '') {
|
// if (sendto !== '') {
|
||||||
// sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + `\n${sendto}`;
|
// sendto = r('FLTL_02716', 'Sent To :') + `\n${sendto}`;
|
||||||
// }
|
// }
|
||||||
div.appendChild(createElement('div', div => {
|
div.appendChild(createElement('div', div => {
|
||||||
div.className = 'item-poster';
|
div.className = 'item-poster';
|
||||||
@ -143,17 +168,19 @@ export default class CustomerRecordComment {
|
|||||||
}
|
}
|
||||||
div.append(
|
div.append(
|
||||||
content,
|
content,
|
||||||
createElement('div', div => {
|
createHideMessageCommentTail(
|
||||||
div.className = 'item-time';
|
this, 'showCommentHidden',
|
||||||
div.innerText = comment.SubmitLocalDateStr;
|
comment, 'SubmitLocalDateStr',
|
||||||
})
|
func, hisFunc)
|
||||||
);
|
);
|
||||||
children.push(div);
|
children.push(div);
|
||||||
}
|
}
|
||||||
children[0].style.marginTop = '0';
|
children[0].style.marginTop = '0';
|
||||||
}
|
}
|
||||||
|
if (this._var.message.children.length > 0) {
|
||||||
|
this._var.lastTop = this._var.message.scrollTop;
|
||||||
|
}
|
||||||
this._var.message.replaceChildren(...children);
|
this._var.message.replaceChildren(...children);
|
||||||
this._var.message.scrollTop = this._var.message.scrollHeight
|
requestAnimationFrame(() => this._var.message.scrollTop = keep ? this._var.lastTop : this._var.message.scrollHeight);
|
||||||
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Grid, Dropdown, createElement, createCheckbox, Popup, showAlert } from "../../ui";
|
import { Grid, Dropdown, createElement, createCheckbox, Popup, showAlert, requestAnimationFrame } from "../../ui";
|
||||||
import { isEmail, nullOrEmpty, r as lang } from "../../utility";
|
import { isEmail, nullOrEmpty, r as lang } from "../../utility";
|
||||||
|
|
||||||
let r = lang;
|
let r = lang;
|
||||||
@ -11,6 +11,8 @@ export class Contact {
|
|||||||
const getText = option?.getText;
|
const getText = option?.getText;
|
||||||
if (typeof getText === 'function') {
|
if (typeof getText === 'function') {
|
||||||
r = getText;
|
r = getText;
|
||||||
|
} else if (typeof GetTextByKey === 'function') {
|
||||||
|
r = GetTextByKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,9 +29,9 @@ export class Contact {
|
|||||||
});
|
});
|
||||||
const preferences = new Dropdown({ tabIndex: tabIndex + 2 });
|
const preferences = new Dropdown({ tabIndex: tabIndex + 2 });
|
||||||
preferences.source = [
|
preferences.source = [
|
||||||
{ value: '0', text: r('P_CR_TEXT', 'Text') },
|
{ value: '0', text: r('FLTL_02915', 'Text') },
|
||||||
{ value: '1', text: r('P_CR_EMAIL', 'Email') },
|
{ value: '1', text: r('FLTL_01089', 'Email') },
|
||||||
{ value: '2', text: r('P_CR_PHONE', 'Phone') }
|
{ value: '2', text: r('FLTL_02194', 'Phone') }
|
||||||
];
|
];
|
||||||
const contactEmail = createElement('input', input => {
|
const contactEmail = createElement('input', input => {
|
||||||
input.type = 'email';
|
input.type = 'email';
|
||||||
@ -55,7 +57,7 @@ export class Contact {
|
|||||||
const buttons = [];
|
const buttons = [];
|
||||||
if (this._var.option.company) {
|
if (this._var.option.company) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
text: c == null ? r('P_WO_ADDCONTACTRECORD', 'Add Contact Record') : r('P_WO_EDITCONTACTRECORD', 'Edit Contact Record'),
|
text: c == null ? r('FLTL_00100', 'Add Contact Record') : r('FLTL_01042', 'Edit Contact Record'),
|
||||||
// tabIndex: tabIndex + 7,
|
// tabIndex: tabIndex + 7,
|
||||||
trigger: () => {
|
trigger: () => {
|
||||||
const item = this.prepare();
|
const item = this.prepare();
|
||||||
@ -71,7 +73,7 @@ export class Contact {
|
|||||||
}
|
}
|
||||||
buttons.push(
|
buttons.push(
|
||||||
{
|
{
|
||||||
text: r('P_WO_WORKORDERONLY', 'Work Order Only'),
|
text: r('FLTL_03348', 'Work Order Only'),
|
||||||
// tabIndex: tabIndex + 8,
|
// tabIndex: tabIndex + 8,
|
||||||
trigger: () => {
|
trigger: () => {
|
||||||
const item = this.prepare();
|
const item = this.prepare();
|
||||||
@ -86,39 +88,39 @@ export class Contact {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: r('P_WO_CANCEL', 'Cancel'),
|
text: r('FLTL_00499', 'Cancel'),
|
||||||
// tabIndex: tabIndex + 9
|
// tabIndex: tabIndex + 9
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const popup = new Popup({
|
const popup = new Popup({
|
||||||
onMasking: this._var.option.onMasking,
|
onMasking: this._var.option.onMasking,
|
||||||
title: c == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact'),
|
title: c == null ? r('FLTL_00099', 'Add Contact') : r('FLTL_01041', 'Edit Contact'),
|
||||||
content: createElement('div', wrapper => {
|
content: createElement('div', wrapper => {
|
||||||
wrapper.className = 'setting-wrapper';
|
wrapper.className = 'setting-wrapper';
|
||||||
wrapper.style.width = '500px';
|
wrapper.style.width = '500px';
|
||||||
},
|
},
|
||||||
createElement('div', 'setting-item',
|
createElement('div', 'setting-item',
|
||||||
createElement('span', 'setting-label setting-required', r('P_CR_CONTACTNAME_COLON', 'Contact Name:')),
|
createElement('span', 'setting-label setting-required', r('FLTL_00640', 'Contact Name:')),
|
||||||
contactName
|
contactName
|
||||||
),
|
),
|
||||||
createElement('div', 'setting-item',
|
createElement('div', 'setting-item',
|
||||||
createElement('span', 'setting-label', r('P_CR_CONTACTPREFERENCES_COLON', 'Contact Preferences:')),
|
createElement('span', 'setting-label', r('FLTL_00643', 'Contact Preferences:')),
|
||||||
preferences.create()
|
preferences.create()
|
||||||
),
|
),
|
||||||
createElement('div', 'setting-item',
|
createElement('div', 'setting-item',
|
||||||
createElement('span', 'setting-label', r('P_CR_EMAILADDRESS_COLON', 'Email Address:')),
|
createElement('span', 'setting-label', r('FLTL_01092', 'Email Address:')),
|
||||||
contactEmail
|
contactEmail
|
||||||
),
|
),
|
||||||
createElement('div', 'setting-item',
|
createElement('div', 'setting-item',
|
||||||
createElement('span', 'setting-label', r('P_WO_MOBILE_COLON', 'Mobile:')),
|
createElement('span', 'setting-label', r('FLTL_01932', 'Mobile:')),
|
||||||
contactMobile
|
contactMobile
|
||||||
),
|
),
|
||||||
createElement('div', 'setting-item',
|
createElement('div', 'setting-item',
|
||||||
createElement('span', 'setting-label', r('P_CR_OPTOUT_COLON', 'Opt Out:')),
|
createElement('span', 'setting-label', r('FLTL_02089', 'Opt Out:')),
|
||||||
checkOpt
|
checkOpt
|
||||||
),
|
),
|
||||||
createElement('div', 'setting-item',
|
createElement('div', 'setting-item',
|
||||||
createElement('span', 'setting-label', r('P_CR_NOTES_COLON', 'Notes:')),
|
createElement('span', 'setting-label', r('FLTL_02017', 'Notes:')),
|
||||||
contactNotes
|
contactNotes
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -143,7 +145,7 @@ export class Contact {
|
|||||||
contactNotes
|
contactNotes
|
||||||
};
|
};
|
||||||
const result = await popup.show(parent);
|
const result = await popup.show(parent);
|
||||||
setTimeout(() => contactName.focus());
|
requestAnimationFrame(() => contactName.focus());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,24 +156,24 @@ export class Contact {
|
|||||||
const phone = this._var.refs.contactMobile.value;
|
const phone = this._var.refs.contactMobile.value;
|
||||||
const opt = this._var.refs.checkOpt.querySelector('input').checked;
|
const opt = this._var.refs.checkOpt.querySelector('input').checked;
|
||||||
const notes = this._var.refs.contactNotes.value;
|
const notes = this._var.refs.contactNotes.value;
|
||||||
const title = this._var.option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact');
|
const title = this._var.option.contact == null ? r('FLTL_00099', 'Add Contact') : r('FLTL_01041', 'Edit Contact');
|
||||||
if (nullOrEmpty(name)) {
|
if (nullOrEmpty(name)) {
|
||||||
showAlert(title, r('P_CR_CONTACTNAMECANNOTBEEMPTY', 'Contact Name cannot be empty.'), 'warn')
|
showAlert(title, r('FLTL_00639', 'Contact Name cannot be empty.'), 'warn')
|
||||||
.then(() => this._var.refs.contactName.focus());
|
.then(() => this._var.refs.contactName.focus());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ((pref == 0 || pref == 2) && nullOrEmpty(phone)) {
|
if ((pref == 0 || pref == 2) && nullOrEmpty(phone)) {
|
||||||
showAlert(title, r('P_CR_MOBILECANNOTBEEMPTY', 'Mobile cannot be empty.'), 'warn')
|
showAlert(title, r('FLTL_01929', 'Mobile cannot be empty.'), 'warn')
|
||||||
.then(() => this._var.refs.contactMobile.focus());
|
.then(() => this._var.refs.contactMobile.focus());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (pref == 1 && nullOrEmpty(email)) {
|
if (pref == 1 && nullOrEmpty(email)) {
|
||||||
showAlert(title, r('P_CU_EMAILCANNOTBEEMPTY', 'Email cannot be empty.'), 'warn')
|
showAlert(title, r('FLTL_01094', 'Email cannot be empty.'), 'warn')
|
||||||
.then(() => this._var.refs.contactEmail.focus());
|
.then(() => this._var.refs.contactEmail.focus());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!nullOrEmpty(email) && !isEmail(email)) {
|
if (!nullOrEmpty(email) && !isEmail(email)) {
|
||||||
showAlert(title, r('P_CR_EMAILISNOTAVALIDEMAILADDRESS', 'The email address is invalid.'), 'warn')
|
showAlert(title, r('FLTL_02952', 'The email address is invalid.'), 'warn')
|
||||||
.then(() => this._var.refs.contactEmail.focus());
|
.then(() => this._var.refs.contactEmail.focus());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -204,6 +206,8 @@ export class CustomerRecordContact {
|
|||||||
const getText = option?.getText;
|
const getText = option?.getText;
|
||||||
if (typeof getText === 'function') {
|
if (typeof getText === 'function') {
|
||||||
r = getText;
|
r = getText;
|
||||||
|
} else if (typeof GetTextByKey === 'function') {
|
||||||
|
r = GetTextByKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +223,7 @@ export class CustomerRecordContact {
|
|||||||
),
|
),
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: r('P_WO_OK', 'OK'),
|
text: r('FLTL_02057', 'OK'),
|
||||||
key: 'ok',
|
key: 'ok',
|
||||||
trigger: () => {
|
trigger: () => {
|
||||||
if (typeof this._var.option.onOk === 'function') {
|
if (typeof this._var.option.onOk === 'function') {
|
||||||
@ -227,7 +231,7 @@ export class CustomerRecordContact {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ text: r('P_WO_CANCEL', 'Cancel'), key: 'cancel' }
|
{ text: r('FLTL_00499', 'Cancel'), key: 'cancel' }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
const result = await popup.show(parent);
|
const result = await popup.show(parent);
|
||||||
@ -240,12 +244,12 @@ export class CustomerRecordContact {
|
|||||||
width: 40,
|
width: 40,
|
||||||
// enabled: item => !nullOrEmpty(item.ID)
|
// enabled: item => !nullOrEmpty(item.ID)
|
||||||
},
|
},
|
||||||
{ key: 'Name', caption: r("P_CR_CONTACTNAME", "Contact Name"), width: 100 },
|
{ key: 'Name', caption: r('FLTL_00637', 'Contact Name'), width: 100 },
|
||||||
{ key: 'Email', caption: r("P_CR_CONTACTEMAIL", "Contact Email"), css: { 'width': 180, 'text-align': 'left' } },
|
{ key: 'Email', caption: r('FLTL_00633', 'Contact Email'), css: { 'width': 180, 'text-align': 'left' } },
|
||||||
{ key: 'MobilePhoneDisplayText', caption: r("P_CR_CONTACTMOBILE", "Contact Mobile"), width: 130 },
|
{ key: 'MobilePhoneDisplayText', caption: r('FLTL_00636', 'Contact Mobile'), width: 130 },
|
||||||
{ key: 'ContactPreferenceStr', caption: r("P_CR_CONTACTPREFERENCES", "Contact Preferences"), width: 100 },
|
{ key: 'ContactPreferenceStr', caption: r('FLTL_00642', 'Contact Preferences'), width: 100 },
|
||||||
{ key: 'OptOut', caption: r("P_CR_OPTOUT", "Opt Out"), type: Grid.ColumnTypes.Checkbox, width: 70, enabled: false, align: 'center' },
|
{ key: 'OptOut', caption: r('FLTL_02084', 'Opt Out'), type: Grid.ColumnTypes.Checkbox, width: 70, enabled: false, align: 'center' },
|
||||||
{ key: 'Notes', caption: r("P_CR_NOTES", "Notes"), width: 120 }
|
{ key: 'Notes', caption: r('FLTL_02012', 'Notes'), width: 120 }
|
||||||
];
|
];
|
||||||
grid.init();
|
grid.init();
|
||||||
grid.source = this._var.option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
|
grid.source = this._var.option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) });
|
||||||
|
@ -11,6 +11,8 @@ export default class Follower {
|
|||||||
const getText = option?.getText;
|
const getText = option?.getText;
|
||||||
if (typeof getText === 'function') {
|
if (typeof getText === 'function') {
|
||||||
r = getText;
|
r = getText;
|
||||||
|
} else if (typeof GetTextByKey === 'function') {
|
||||||
|
r = GetTextByKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +24,7 @@ export default class Follower {
|
|||||||
onMasking: this._var.option.onMasking,
|
onMasking: this._var.option.onMasking,
|
||||||
title,
|
title,
|
||||||
content: createElement('div', 'follower-wrapper',
|
content: createElement('div', 'follower-wrapper',
|
||||||
createElement('div', div => div.innerText = r('P_CR_WHODOYOUWANTTORECEIVECUSTOMERNOTIFICATIONS', 'Who do you want to receive customer notifications?')),
|
createElement('div', div => div.innerText = r('FLTL_03300', 'Who do you want to receive customer notifications?')),
|
||||||
createElement('input', search => {
|
createElement('input', search => {
|
||||||
search.type = 'text';
|
search.type = 'text';
|
||||||
search.tabIndex = tabIndex + 3;
|
search.tabIndex = tabIndex + 3;
|
||||||
@ -41,7 +43,7 @@ export default class Follower {
|
|||||||
),
|
),
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: r('P_WO_OK', 'OK'),
|
text: r('FLTL_02057', 'OK'),
|
||||||
key: 'ok',
|
key: 'ok',
|
||||||
trigger: () => {
|
trigger: () => {
|
||||||
if (typeof this._var.option.onOk === 'function') {
|
if (typeof this._var.option.onOk === 'function') {
|
||||||
@ -49,7 +51,7 @@ export default class Follower {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ text: r('P_WO_CANCEL', 'Cancel'), key: 'cancel' }
|
{ text: r('FLTL_00499', 'Cancel'), key: 'cancel' }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
const result = await popup.show(parent);
|
const result = await popup.show(parent);
|
||||||
@ -57,18 +59,18 @@ export default class Follower {
|
|||||||
// grid
|
// grid
|
||||||
const grid = new Grid(gridContainer);
|
const grid = new Grid(gridContainer);
|
||||||
grid.columns = [
|
grid.columns = [
|
||||||
{ key: 'DisplayName', caption: r('P_WO_CONTACTNAME', 'Contact Name'), width: 240 },
|
{ key: 'DisplayName', caption: r('FLTL_00637', 'Contact Name'), width: 240 },
|
||||||
{ key: 'ContactTypeName', caption: r('P_WO_CONTACTTYPE', 'Contact Type'), width: 120 },
|
{ key: 'ContactTypeName', caption: r('FLTL_00644', 'Contact Type'), width: 120 },
|
||||||
{
|
{
|
||||||
key: 'Text',
|
key: 'Text',
|
||||||
caption: r('P_CR_TEXT', 'Text'),
|
caption: r('FLTL_02915', 'Text'),
|
||||||
type: Grid.ColumnTypes.Checkbox,
|
type: Grid.ColumnTypes.Checkbox,
|
||||||
width: 60,
|
width: 60,
|
||||||
enabled: item => !nullOrEmpty(item.Mobile)
|
enabled: item => !nullOrEmpty(item.Mobile)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Email',
|
key: 'Email',
|
||||||
caption: r('P_CR_EMAIL', 'Email'),
|
caption: r('FLTL_01089', 'Email'),
|
||||||
type: Grid.ColumnTypes.Checkbox,
|
type: Grid.ColumnTypes.Checkbox,
|
||||||
width: 70,
|
width: 70,
|
||||||
// enabled: item => !nullOrEmpty(item.ID)
|
// enabled: item => !nullOrEmpty(item.ID)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { createElement, setTooltip, createIcon } from "../../ui";
|
import AddWorkOrder from "../../element/addWorkorder";
|
||||||
|
import { createElement, setTooltip, createIcon, requestAnimationFrame } from "../../ui";
|
||||||
import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility";
|
import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility";
|
||||||
import { createBox, appendMedia } from "./lib";
|
import { createBox, appendMedia } from "./lib";
|
||||||
import { fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib";
|
import { fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName, createHideMessageTitleButton, createHideMessageCommentTail } from "./lib";
|
||||||
|
|
||||||
let r = lang;
|
let r = lang;
|
||||||
|
|
||||||
@ -19,6 +20,8 @@ export default class InternalComment {
|
|||||||
const getText = opt?.getText;
|
const getText = opt?.getText;
|
||||||
if (typeof getText === 'function') {
|
if (typeof getText === 'function') {
|
||||||
r = getText;
|
r = getText;
|
||||||
|
} else if (typeof GetTextByKey === 'function') {
|
||||||
|
r = GetTextByKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +35,20 @@ export default class InternalComment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} flag
|
||||||
|
*/
|
||||||
|
set messageHidden(flag) {
|
||||||
|
const el = this._var.container.querySelector('.msgadminsetting');
|
||||||
|
if (el == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._var.option.showMessageHidden = flag;
|
||||||
|
// TODO: 是否与参数 flag 无关
|
||||||
|
el.classList.remove('iconview');
|
||||||
|
el.classList.add('iconnotview');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {any} contacts
|
* @param {any} contacts
|
||||||
*/
|
*/
|
||||||
@ -99,24 +116,38 @@ export default class InternalComment {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._var.enter.disabled = flag === true;
|
this._var.enter.disabled = flag === true;
|
||||||
|
this._var.container.querySelector('.button-call-log').style.display = flag === true ? 'none' : '';
|
||||||
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
|
this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : '';
|
||||||
this._var.container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
|
this._var.container.querySelector('.button-post-note').style.display = flag === true ? 'none' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} flag
|
||||||
|
*/
|
||||||
|
set noCallLog(flag) {
|
||||||
|
this._var.option.noCallLog = flag;
|
||||||
|
if (this._var.container == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._var.container.querySelector('.button-call-log').style.display = flag === true ? 'none' : '';
|
||||||
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
const option = this._var.option;
|
const option = this._var.option;
|
||||||
const container = createBox(
|
const container = createBox(
|
||||||
createElement('div', null,
|
createElement('div', null,
|
||||||
createElement('div', div => {
|
createElement('div', div => {
|
||||||
div.className = 'title-module';
|
div.className = 'title-module';
|
||||||
div.innerText = r('P_WO_INTERNALCOMMENTS', 'Internal Comments');
|
div.innerText = r('FLTL_01613', 'Internal Comments');
|
||||||
})
|
},
|
||||||
|
createHideMessageTitleButton(this, 'showMessageHidden')
|
||||||
|
)
|
||||||
), []
|
), []
|
||||||
);
|
);
|
||||||
const readonly = option.readonly;
|
const readonly = option.readonly;
|
||||||
// enter box
|
// enter box
|
||||||
const enter = createElement('textarea', 'ui-text');
|
const enter = createElement('textarea', 'ui-text');
|
||||||
enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here');
|
enter.placeholder = r('FLTL_01154', 'Enter Comment Here');
|
||||||
enter.maxLength = option.maxLength ??= 3000;
|
enter.maxLength = option.maxLength ??= 3000;
|
||||||
enter.addEventListener('input', () => {
|
enter.addEventListener('input', () => {
|
||||||
const val = this.text;
|
const val = this.text;
|
||||||
@ -196,6 +227,24 @@ export default class InternalComment {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
createElement('div', 'prompt-count'),
|
createElement('div', 'prompt-count'),
|
||||||
|
createElement('button', button => {
|
||||||
|
button.className = 'roundbtn button-call-log';
|
||||||
|
button.style.backgroundImage = 'url(' + AddWorkOrder.IconWorkOrder + ')';
|
||||||
|
button.style.backgroundSize = '80% 80%';
|
||||||
|
button.style.backgroundPosition = 'center';
|
||||||
|
button.style.backgroundRepeat = 'no-repeat';
|
||||||
|
button.style.verticalAlign = 'top';//firefox图片需要设置verticalAlign
|
||||||
|
if (readonly === true || option.noCallLog === true) {
|
||||||
|
button.style.display = 'none';
|
||||||
|
}
|
||||||
|
setTooltip(button, r('FLTL_00491', 'Call Log'));
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
if (typeof option.onAddCallLog === 'function') {
|
||||||
|
this.loading = true;
|
||||||
|
option.onAddCallLog();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
createElement('button', button => {
|
createElement('button', button => {
|
||||||
button.className = 'roundbtn button-send-message';
|
button.className = 'roundbtn button-send-message';
|
||||||
button.style.backgroundColor = 'rgb(19, 150, 204)';
|
button.style.backgroundColor = 'rgb(19, 150, 204)';
|
||||||
@ -203,7 +252,7 @@ export default class InternalComment {
|
|||||||
button.style.display = 'none';
|
button.style.display = 'none';
|
||||||
}
|
}
|
||||||
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
||||||
setTooltip(button, r('P_M3_SENDMESSAGE', 'Send Message'));
|
setTooltip(button, r('FLTL_02692', 'Send Message'));
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
const val = this.text;
|
const val = this.text;
|
||||||
if (nullOrEmpty(val?.trim())) {
|
if (nullOrEmpty(val?.trim())) {
|
||||||
@ -223,7 +272,7 @@ export default class InternalComment {
|
|||||||
button.style.display = 'none';
|
button.style.display = 'none';
|
||||||
}
|
}
|
||||||
button.appendChild(createIcon('fa-solid', 'comment-alt-lines'));
|
button.appendChild(createIcon('fa-solid', 'comment-alt-lines'));
|
||||||
setTooltip(button, r('P_CU_POSTNOTE', 'Post Note'));
|
setTooltip(button, r('FLTL_02301', 'Post Note'));
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
const val = this.text;
|
const val = this.text;
|
||||||
if (nullOrEmpty(val?.trim())) {
|
if (nullOrEmpty(val?.trim())) {
|
||||||
@ -245,7 +294,7 @@ export default class InternalComment {
|
|||||||
return this._var.container = container;
|
return this._var.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
load(data) {
|
load(data, func, hisFunc, keep) {
|
||||||
const children = [];
|
const children = [];
|
||||||
if (data?.length > 0) {
|
if (data?.length > 0) {
|
||||||
this._var.comments = data;
|
this._var.comments = data;
|
||||||
@ -256,8 +305,15 @@ export default class InternalComment {
|
|||||||
this._var.contactsUpdated = true;
|
this._var.contactsUpdated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const lastVisible = this._var.option.showMessageHidden;
|
||||||
for (let comment of data) {
|
for (let comment of data) {
|
||||||
const div = createElement('div', 'item-div');
|
const div = createElement('div', 'item-div');
|
||||||
|
if (comment.Hidden) {
|
||||||
|
div.classList.add('hidden-content');
|
||||||
|
if (!lastVisible) {
|
||||||
|
div.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
const sendto = getMessageSendTo(comment, null, null, r)
|
const sendto = getMessageSendTo(comment, null, null, r)
|
||||||
div.appendChild(createElement('div', div => {
|
div.appendChild(createElement('div', div => {
|
||||||
div.className = 'item-poster';
|
div.className = 'item-poster';
|
||||||
@ -268,7 +324,13 @@ export default class InternalComment {
|
|||||||
}));
|
}));
|
||||||
const content = createElement('div', 'item-content');
|
const content = createElement('div', 'item-content');
|
||||||
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.Message)), mmsParts));
|
content.appendChild(createElement('span', span => {
|
||||||
|
if (comment.MessageType === 2) {
|
||||||
|
span.innerHTML = comment.Message;
|
||||||
|
} else {
|
||||||
|
span.innerHTML = escapeHtml(escapeEmoji(comment.Message));
|
||||||
|
}
|
||||||
|
}, mmsParts));
|
||||||
if (comment.MMSParts?.length > 0) {
|
if (comment.MMSParts?.length > 0) {
|
||||||
mmsParts.style.display = '';
|
mmsParts.style.display = '';
|
||||||
for (let kv of comment.MMSParts) {
|
for (let kv of comment.MMSParts) {
|
||||||
@ -277,10 +339,10 @@ export default class InternalComment {
|
|||||||
}
|
}
|
||||||
// if (comment.FollowUp?.length > 0) {
|
// if (comment.FollowUp?.length > 0) {
|
||||||
// div.classList.add('item-sent');
|
// div.classList.add('item-sent');
|
||||||
// const sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
|
// const sendto = r('FLTL_02716', 'Sent To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
|
||||||
// content.appendChild(createElement('div', div => {
|
// content.appendChild(createElement('div', div => {
|
||||||
// div.className = 'item-status';
|
// div.className = 'item-status';
|
||||||
// div.innerText = r('P_WO_SENT', 'Sent');
|
// div.innerText = r('FLTL_02711', 'Sent');
|
||||||
// setTooltip(div, sendto);
|
// setTooltip(div, sendto);
|
||||||
// }));
|
// }));
|
||||||
// }
|
// }
|
||||||
@ -300,17 +362,19 @@ export default class InternalComment {
|
|||||||
}
|
}
|
||||||
div.append(
|
div.append(
|
||||||
content,
|
content,
|
||||||
createElement('div', div => {
|
createHideMessageCommentTail(
|
||||||
div.className = 'item-time';
|
this, 'showMessageHidden',
|
||||||
div.innerText = comment.TimeStr;
|
comment, 'TimeStr',
|
||||||
})
|
func, hisFunc)
|
||||||
);
|
);
|
||||||
children.push(div);
|
children.push(div);
|
||||||
}
|
}
|
||||||
children[0].style.marginTop = '0';
|
children[0].style.marginTop = '0';
|
||||||
}
|
}
|
||||||
|
if (this._var.message.children.length > 0) {
|
||||||
|
this._var.lastTop = this._var.message.scrollTop;
|
||||||
|
}
|
||||||
this._var.message.replaceChildren(...children);
|
this._var.message.replaceChildren(...children);
|
||||||
this._var.message.scrollTop = this._var.message.scrollHeight
|
requestAnimationFrame(() => this._var.message.scrollTop = keep ? this._var.lastTop : this._var.message.scrollHeight);
|
||||||
// setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -155,12 +155,12 @@ export function insertFile(container, file, r) {
|
|||||||
type = type.substring(type.lastIndexOf('.'));
|
type = type.substring(type.lastIndexOf('.'));
|
||||||
}
|
}
|
||||||
if (fileSupported.indexOf(type) < 0) {
|
if (fileSupported.indexOf(type) < 0) {
|
||||||
showAlert(r('P_WO_ERROR', 'Error'), r('P_CU_TYPENOTSUPPORTED', 'File type "{type}" is now not supported.').replace('{type}', type));
|
showAlert(r('FLTL_01165', 'Error'), r('FLTL_01385', 'File type "{type}" is now not supported.').replace('{type}', type));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isImage = /^image\//.test(type);
|
const isImage = /^image\//.test(type);
|
||||||
if (!isImage && file.size > MaxAttachmentSize.limit) {
|
if (!isImage && file.size > MaxAttachmentSize.limit) {
|
||||||
showAlert(r('P_WO_ERROR', 'Error'), r('P_WO_ATTACHMENTSIZEEXCEEDSTHEMAXIMUMTIPS', `Attachment size exceeds the maximum allowed to be sent (${MaxAttachmentSize.text})`), 'warn');
|
showAlert(r('FLTL_01165', 'Error'), r('FLTL_00407', `Attachment size exceeds the maximum allowed to be sent (${MaxAttachmentSize.text})`), 'warn');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fn = file.name;
|
const fn = file.name;
|
||||||
@ -202,12 +202,12 @@ function getStatusText(status, dict) {
|
|||||||
|
|
||||||
export function getMessageStatus(comm, r, _var) {
|
export function getMessageStatus(comm, r, _var) {
|
||||||
const messageStatus = {
|
const messageStatus = {
|
||||||
0: r('P_CU_PENDING', 'Pending'),
|
0: r('FLTL_02186', 'Pending'),
|
||||||
1: r('P_WO_SENT', 'Sent'),
|
1: r('FLTL_02711', 'Sent'),
|
||||||
5: r('P_CU_DELIVERYCONFIRMED', 'Delivery Confirmed'),
|
5: r('FLTL_00864', 'Delivery Confirmed'),
|
||||||
6: r('P_CU_RESENT', 'Resent'),
|
6: r('FLTL_02478', 'Resent'),
|
||||||
9: r('P_MA_FAILED', 'Failed'),
|
9: r('FLTL_01224', 'Failed'),
|
||||||
9999: r('P_CU_UNKNOWN', 'Unknown')
|
9999: r('FLTL_03152', 'Unknown')
|
||||||
};
|
};
|
||||||
const knownStatus = [0, 1, 5, 6, 9, 10, 412];
|
const knownStatus = [0, 1, 5, 6, 9, 10, 412];
|
||||||
const okStatus = [1, 5, 6];
|
const okStatus = [1, 5, 6];
|
||||||
@ -269,7 +269,7 @@ export function getMessageStatus(comm, r, _var) {
|
|||||||
if (statusUpdatable !== false) {
|
if (statusUpdatable !== false) {
|
||||||
tip.appendChild(createElement('div', b => {
|
tip.appendChild(createElement('div', b => {
|
||||||
b.className = 'tip-function-button';
|
b.className = 'tip-function-button';
|
||||||
// setTooltip(b, r('P_CU_UPDATESTATUS', 'Update Status'));
|
// setTooltip(b, r('FLTL_03174', 'Update Status'));
|
||||||
b.addEventListener('click', async () => {
|
b.addEventListener('click', async () => {
|
||||||
for (let p of comm.Participator) {
|
for (let p of comm.Participator) {
|
||||||
switch (p.Status) {
|
switch (p.Status) {
|
||||||
@ -292,7 +292,7 @@ export function getMessageStatus(comm, r, _var) {
|
|||||||
const gridContainer = createElement('div', 'status-grid');
|
const gridContainer = createElement('div', 'status-grid');
|
||||||
const popup = new Popup({
|
const popup = new Popup({
|
||||||
onMasking: _var.option.onMasking,
|
onMasking: _var.option.onMasking,
|
||||||
title: r('P_CU_UPDATESTATUS', 'Update Status'),
|
title: r('FLTL_03174', 'Update Status'),
|
||||||
content: createElement('div', wrapper => {
|
content: createElement('div', wrapper => {
|
||||||
wrapper.className = 'update-status-wrapper';
|
wrapper.className = 'update-status-wrapper';
|
||||||
wrapper.style.width = '500px';
|
wrapper.style.width = '500px';
|
||||||
@ -301,7 +301,7 @@ export function getMessageStatus(comm, r, _var) {
|
|||||||
),
|
),
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: r('P_WO_OK', 'OK'),
|
text: r('FLTL_02057', 'OK'),
|
||||||
key: 'ok',
|
key: 'ok',
|
||||||
trigger: () => {
|
trigger: () => {
|
||||||
const changed = msgs.filter(m => {
|
const changed = msgs.filter(m => {
|
||||||
@ -329,7 +329,7 @@ export function getMessageStatus(comm, r, _var) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: r('P_WO_CANCEL', 'Cancel'),
|
text: r('FLTL_00499', 'Cancel'),
|
||||||
key: 'cancel'
|
key: 'cancel'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -341,22 +341,22 @@ export function getMessageStatus(comm, r, _var) {
|
|||||||
grid.columns = [
|
grid.columns = [
|
||||||
{
|
{
|
||||||
key: 'CustomerNumber',
|
key: 'CustomerNumber',
|
||||||
caption: r('P_JS_NUMBER', 'Number'),
|
caption: r('FLTL_02026', 'Number'),
|
||||||
width: 150
|
width: 150
|
||||||
},
|
},
|
||||||
/*{
|
/*{
|
||||||
key: 'customerName',
|
key: 'customerName',
|
||||||
caption: r('P_WOS_CUSTOMERNAME', 'Customer Name'),
|
caption: r('FLTL_00742', 'Customer Name'),
|
||||||
width: 120
|
width: 120
|
||||||
},*/
|
},*/
|
||||||
{
|
{
|
||||||
key: 'statusText',
|
key: 'statusText',
|
||||||
caption: r('P_CU_CURRENTSTATUS', 'Current Status'),
|
caption: r('FLTL_00725', 'Current Status'),
|
||||||
width: 155
|
width: 155
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'statusChanged',
|
key: 'statusChanged',
|
||||||
caption: r('P_CU_REVISEDSTATUS', 'Revised Status'),
|
caption: r('FLTL_02511', 'Revised Status'),
|
||||||
width: 155,
|
width: 155,
|
||||||
type: Grid.ColumnTypes.Dropdown,
|
type: Grid.ColumnTypes.Dropdown,
|
||||||
source: [
|
source: [
|
||||||
@ -404,7 +404,7 @@ export function getMessageSendTo(comm, contacts, followers, r) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sendto !== '') {
|
if (sendto !== '') {
|
||||||
sendto = r('P_CU_SENDTO_COLON', 'Sent to :') + `\n${sendto}`;
|
sendto = r('FLTL_02716', 'Sent to :') + `\n${sendto}`;
|
||||||
}
|
}
|
||||||
return sendto;
|
return sendto;
|
||||||
}
|
}
|
||||||
@ -420,4 +420,99 @@ export function updateCustomerName(messages, contacts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createHideMessageTitleButton(This, optionName) {
|
||||||
|
const option = This._var.option;
|
||||||
|
return createElement('span', span => {
|
||||||
|
if (option.userIsAdmin) {
|
||||||
|
if (option[optionName]) {
|
||||||
|
span.className = 'msgadminsetting sbutton iconview';
|
||||||
|
} else {
|
||||||
|
span.className = 'msgadminsetting sbutton iconnotview';
|
||||||
|
}
|
||||||
|
span.style.padding = '0px 0px 0px 5px';
|
||||||
|
setTooltip(span, option?.getText('FLTL_01860', 'Manage Messages'));
|
||||||
|
span.addEventListener('click', function () {
|
||||||
|
const container = This._var.container;
|
||||||
|
if (!option[optionName]) {
|
||||||
|
this.classList.remove('iconnotview');
|
||||||
|
this.classList.add('iconview');
|
||||||
|
option[optionName] = true;
|
||||||
|
container.querySelectorAll('.msgsetting').forEach(x => x.style.display = '');
|
||||||
|
container.querySelectorAll('.msgHistory').forEach(h => h.style.display = h.getAttribute('ModifyCount') > 0 ? '' : 'none');
|
||||||
|
container.querySelectorAll('.hidden-content').forEach(c => c.style.display = '');
|
||||||
|
} else {
|
||||||
|
this.classList.remove('iconview');
|
||||||
|
this.classList.add('iconnotview');
|
||||||
|
option[optionName] = false;
|
||||||
|
container.querySelectorAll('.msgsetting').forEach(x => x.style.display = 'none');
|
||||||
|
container.querySelectorAll('.msgHistory').forEach(h => h.style.display = 'none');
|
||||||
|
container.querySelectorAll('.hidden-content').forEach(c => c.style.display = 'none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createHideMessageCommentTail(This, optionName, comment, commentTime, func, hisFunc) {
|
||||||
|
const option = This._var.option;
|
||||||
|
const showTooltip = option?.getText('FLTL_03267', 'Visible');
|
||||||
|
const notShowTooltip = option?.getText('FLTL_02006', 'Not Visible');
|
||||||
|
return createElement('div', div => {
|
||||||
|
div.className = 'item-time';
|
||||||
|
div.style.display = 'flex';
|
||||||
|
div.style.alignItems = 'center';
|
||||||
|
},
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'msgsetting sbutton ' + (comment.Hidden ? 'iconnotview' : 'iconview');
|
||||||
|
span.style.padding = '0';
|
||||||
|
span.style.fontSize = '12px';
|
||||||
|
setTooltip(span, comment.Hidden ? notShowTooltip : showTooltip);
|
||||||
|
span.style.display = option[optionName] ? '' : 'none';
|
||||||
|
span.addEventListener('click', function () {
|
||||||
|
if (this.classList.contains('iconview')) {
|
||||||
|
func(comment.Id, true);
|
||||||
|
this.classList.remove('iconview');
|
||||||
|
this.classList.add('iconnotview');
|
||||||
|
setTooltip(this, notShowTooltip);
|
||||||
|
} else {
|
||||||
|
func(comment.Id, false);
|
||||||
|
this.classList.remove('iconnotview');
|
||||||
|
this.classList.add('iconview');
|
||||||
|
setTooltip(this, showTooltip);
|
||||||
|
}
|
||||||
|
if (isNaN(comment.ModifyCount)) {
|
||||||
|
comment.ModifyCount = 1;
|
||||||
|
} else {
|
||||||
|
comment.ModifyCount += 1;
|
||||||
|
}
|
||||||
|
const x = This._var.container.querySelector('.history-span-' + comment.Id);
|
||||||
|
if (x != null) {
|
||||||
|
x.setAttribute('ModifyCount', comment.ModifyCount);
|
||||||
|
x.style.display = (option[optionName] && comment.ModifyCount > 0) ? '' : 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
createElement('span', span => {
|
||||||
|
span.className = 'msgHistory history-span-' + comment.Id;
|
||||||
|
span.setAttribute('ModifyCount', comment.ModifyCount ?? 0);
|
||||||
|
span.style.display = (option[optionName] && comment.ModifyCount > 0) ? '' : 'none';
|
||||||
|
setTooltip(span, option?.getText('FLTL_01508', 'Hidden History'));
|
||||||
|
const icon = createIcon('fa-light', 'wave-sine');
|
||||||
|
icon.style.height = '12px';
|
||||||
|
icon.style.width = '12px';
|
||||||
|
icon.style.margin = '0 5px 0 0';
|
||||||
|
icon.style.cursor = 'pointer';
|
||||||
|
icon.style.border = '1px solid';
|
||||||
|
icon.style.borderRadius = '6px';
|
||||||
|
icon.style.borderColor = '#000';
|
||||||
|
icon.style.display = 'block';
|
||||||
|
span.appendChild(icon);
|
||||||
|
span.addEventListener('click', () => hisFunc(comment.Id));
|
||||||
|
}),
|
||||||
|
createElement('span', span => {
|
||||||
|
span.innerText = comment[commentTime];
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
>.ui-grid {
|
>.ui-grid {
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
|
max-height: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,12 +90,39 @@
|
|||||||
|
|
||||||
>div {
|
>div {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
>.title-company {
|
||||||
|
line-height: 1rem;
|
||||||
|
padding: 2px 10px;
|
||||||
|
// background-color: rgba(0, 0, 0, .15);
|
||||||
|
|
||||||
|
>.title-company-name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.title-company-selector {
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
fill: rgb(123, 28, 33);
|
||||||
|
margin: 0 5px 3px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.title-functions {
|
>.title-functions {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 4px;
|
padding: 0 4px;
|
||||||
|
|
||||||
>label {
|
>label {
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import "./element/style.scss";
|
import "./element/style.scss";
|
||||||
import ScheduleItem from "./element/schedule";
|
import ScheduleItem from "./element/schedule";
|
||||||
|
import AddWorkOrder from "./element/addWorkorder";
|
||||||
|
import InspectionWizard from "./element/inspectionWizard";
|
||||||
|
import Signature from "./element/signature";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ScheduleItem
|
ScheduleItem,
|
||||||
|
AddWorkOrder,
|
||||||
|
InspectionWizard,
|
||||||
|
Signature
|
||||||
}
|
}
|
734
lib/element/addworkorder.js
Normal file
260
lib/element/assetSelector.js
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
import { Dropdown, Grid, OptionBase, createCheckbox, createElement, createIcon } from "../ui";
|
||||||
|
|
||||||
|
export default class AssetSelector extends OptionBase {
|
||||||
|
_var = {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLElement}
|
||||||
|
*/
|
||||||
|
container: null,
|
||||||
|
el: {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLInputElement}
|
||||||
|
*/
|
||||||
|
inputSearch: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLLabelElement}
|
||||||
|
*/
|
||||||
|
checkHidden: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Dropdown}
|
||||||
|
*/
|
||||||
|
dropAssetGroup: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Dropdown}
|
||||||
|
*/
|
||||||
|
dropJobsite: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Dropdown}
|
||||||
|
*/
|
||||||
|
dropJobsiteCode: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Grid}
|
||||||
|
*/
|
||||||
|
grid: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected;
|
||||||
|
|
||||||
|
constructor(opt = {
|
||||||
|
assetFullcontrol: false,
|
||||||
|
ignoreHidden: false,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {(flag: boolean) => void}
|
||||||
|
*/
|
||||||
|
loading: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {() => void}
|
||||||
|
*/
|
||||||
|
close: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {(hidden: boolean, search?: string, groups?: string[], jobsites?: number[], codes?: string[]) => Promise<any[]>}
|
||||||
|
*/
|
||||||
|
requestAssets: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {() => Promise<any>}
|
||||||
|
*/
|
||||||
|
requestAddAsset: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {() => Promise<{AssetGroups: any[], Codes: any[], Jobsites: any[]}>}
|
||||||
|
*/
|
||||||
|
requestAssetParams: null,
|
||||||
|
dataSource: {
|
||||||
|
AssetGroups: [],
|
||||||
|
Codes: [],
|
||||||
|
Jobsites: []
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
super(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() { return this.r('FLTL_02645', 'Select Asset') }
|
||||||
|
|
||||||
|
get currentAsset() { return this._var.el.grid.currentItem }
|
||||||
|
|
||||||
|
loading(flag) {
|
||||||
|
if (typeof this._option.loading === 'function') {
|
||||||
|
this._option.loading(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
const requestAssets = this._option.requestAssets;
|
||||||
|
if (typeof requestAssets !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const el = this._var.el;
|
||||||
|
this.loading(true);
|
||||||
|
requestAssets(
|
||||||
|
el.checkHidden.querySelector('input[type="checkbox"]')?.checked,
|
||||||
|
el.inputSearch.value,
|
||||||
|
el.dropAssetGroup.selected?.Key,
|
||||||
|
el.dropJobsite.selected?.ID,
|
||||||
|
el.dropJobsiteCode.selected?.value
|
||||||
|
).then(data => {
|
||||||
|
el.grid.source = data;
|
||||||
|
}).finally(() => this.loading(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
select(item) {
|
||||||
|
if (typeof this.onSelected !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (item == null) {
|
||||||
|
item = this._var.el.grid.currentItem;
|
||||||
|
}
|
||||||
|
if (item == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.onSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
const option = this._option;
|
||||||
|
const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3;
|
||||||
|
const inputSearch = createElement('input', input => {
|
||||||
|
input.type = 'text';
|
||||||
|
input.placeholder = this.r('FLTL_02606', 'Search');
|
||||||
|
input.tabIndex = tabIndex + 2;
|
||||||
|
input.className = 'ui-input';
|
||||||
|
input.addEventListener('keypress', e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const checkHidden = createCheckbox({
|
||||||
|
tabIndex: tabIndex + 3,
|
||||||
|
label: this.r('FLTL_02768', 'Show Hidden'),
|
||||||
|
onchange: () => this.refresh()
|
||||||
|
});
|
||||||
|
if (option.ignoreHidden) {
|
||||||
|
checkHidden.style.display = 'none';
|
||||||
|
}
|
||||||
|
const dropAssetGroup = new Dropdown({
|
||||||
|
tabIndex: tabIndex + 4,
|
||||||
|
search: true,
|
||||||
|
valueKey: 'Key',
|
||||||
|
textKey: 'Value'
|
||||||
|
});
|
||||||
|
dropAssetGroup.onSelected = () => this.refresh();
|
||||||
|
const dropJobsite = new Dropdown({
|
||||||
|
tabIndex: tabIndex + 5,
|
||||||
|
search: true,
|
||||||
|
valueKey: 'ID',
|
||||||
|
textKey: 'Name'
|
||||||
|
});
|
||||||
|
dropJobsite.onSelected = () => this.refresh();
|
||||||
|
const dropJobsiteCode = new Dropdown({
|
||||||
|
tabIndex: tabIndex + 6,
|
||||||
|
search: true
|
||||||
|
});
|
||||||
|
dropJobsiteCode.onSelected = () => this.refresh();
|
||||||
|
const gridContent = createElement('div', div => {
|
||||||
|
div.className = 'popup-selector-content';
|
||||||
|
div.style.height = '400px';
|
||||||
|
});
|
||||||
|
const grid = new Grid(gridContent, this.r);
|
||||||
|
grid.columns = [
|
||||||
|
{ key: 'VIN', caption: this.r('FLTL_03260', 'VIN'), width: 170 },
|
||||||
|
{ key: 'DisplayName', caption: this.r('FLTL_01966', 'Name'), width: 190 },
|
||||||
|
{ key: 'MakeName', caption: this.r('FLTL_01832', 'Make'), width: 110, allowFilter: true },
|
||||||
|
{ key: 'ModelName', caption: this.r('FLTL_01933', 'Model'), width: 110, allowFilter: true },
|
||||||
|
{ key: 'TypeName', caption: this.r('FLTL_03112', 'Type'), width: 110, allowFilter: true },
|
||||||
|
{ key: 'AcquisitionType', caption: this.r('FLTL_00072', 'Acquisition Type'), width: 130, allowFilter: true },
|
||||||
|
{ key: 'AssetGroups', caption: this.r('FLTL_00318', 'Asset Group'), width: 150 },
|
||||||
|
{ key: 'Jobsites', caption: this.r('FLTL_01666', 'Jobsite'), width: 180, filter: it => it.Jobsites?.map(s => s.Key) }
|
||||||
|
];
|
||||||
|
grid.onRowDblClicked = index => {
|
||||||
|
const item = grid.source[index];
|
||||||
|
this.select(item);
|
||||||
|
if (typeof this._option.close === 'function') {
|
||||||
|
this._option.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
grid.init();
|
||||||
|
grid.element.tabIndex = tabIndex + 7;
|
||||||
|
this._var.el = {
|
||||||
|
inputSearch,
|
||||||
|
checkHidden,
|
||||||
|
dropAssetGroup,
|
||||||
|
dropJobsite,
|
||||||
|
dropJobsiteCode,
|
||||||
|
grid
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = createElement('div', 'popup-selector',
|
||||||
|
createElement('div', 'popup-selector-header',
|
||||||
|
createElement('img', img => img.src = '../img/modules/devices.png'),
|
||||||
|
createElement('h3', h3 => h3.innerText = this.title),
|
||||||
|
option.assetFullcontrol ? createElement('button', button => {
|
||||||
|
button.className = 'ui-popup-button';
|
||||||
|
button.tabIndex = tabIndex + 1;
|
||||||
|
button.innerText = this.r('FLTL_00091', 'Add Asset');
|
||||||
|
button.addEventListener('click', async () => {
|
||||||
|
if (typeof option.requestAddAsset === 'function') {
|
||||||
|
this.loading(true);
|
||||||
|
const asset = await option.requestAddAsset();
|
||||||
|
this.loading(false);
|
||||||
|
if (asset != null) {
|
||||||
|
this.select(asset);
|
||||||
|
if (typeof this._option.close === 'function') {
|
||||||
|
this._option.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}) : ''
|
||||||
|
),
|
||||||
|
createElement('div', 'popup-selector-function',
|
||||||
|
createElement('div', 'search-box',
|
||||||
|
inputSearch,
|
||||||
|
createElement('span', span => {
|
||||||
|
span.addEventListener('click', () => this.refresh());
|
||||||
|
},
|
||||||
|
createIcon('fa-light', 'search')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
checkHidden,
|
||||||
|
createElement('span', span => span.innerText = this.r('FLTL_00318', 'Asset Group')),
|
||||||
|
dropAssetGroup.create(),
|
||||||
|
createElement('span', span => span.innerText = this.r('FLTL_01666', 'Jobsite')),
|
||||||
|
dropJobsite.create(),
|
||||||
|
createElement('span', span => span.innerText = this.r('FLTL_01669', 'Jobsite Code')),
|
||||||
|
dropJobsiteCode.create()
|
||||||
|
),
|
||||||
|
gridContent
|
||||||
|
);
|
||||||
|
this._var.container = container;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
const option = this._option;
|
||||||
|
if (option.dataSource == null) {
|
||||||
|
if (typeof option.requestAssetParams === 'function') {
|
||||||
|
this.loading(true);
|
||||||
|
const data = await option.requestAssetParams();
|
||||||
|
option.dataSource = data;
|
||||||
|
} else {
|
||||||
|
option.dataSource = { AssetGroups: [], Jobsites: [], Codes: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._var.el.dropAssetGroup.source = [{ Key: '-1', Value: '' }, ...option.dataSource.AssetGroups];
|
||||||
|
this._var.el.dropJobsite.source = [{ ID: '-1', Name: '' }, ...option.dataSource.Jobsites];
|
||||||
|
this._var.el.dropJobsiteCode.source = [{ value: '-1', text: '' }, ...option.dataSource.Codes.map(c => ({ value: c, text: c }))];
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
189
lib/element/inspectionWizard.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import { OptionBase, Popup, createElement, showAlert } from "../ui";
|
||||||
|
import AssetSelector from "./assetselector";
|
||||||
|
import TemplateSelector from "./templateselector";
|
||||||
|
|
||||||
|
export default class InspectionWizard extends OptionBase {
|
||||||
|
_var = {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
index: 0,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLElement[]}
|
||||||
|
*/
|
||||||
|
containers: [],
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {AssetSelector}
|
||||||
|
*/
|
||||||
|
assetSelector: null,
|
||||||
|
asset: null,
|
||||||
|
template: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Popup}
|
||||||
|
*/
|
||||||
|
popup: null
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event
|
||||||
|
* @type {(this: InspectionWizard, asset: any, template: any) => void}
|
||||||
|
*/
|
||||||
|
onSelected;
|
||||||
|
|
||||||
|
constructor(opt = {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {(assetId: number, search: string) => Promise<any[]>}
|
||||||
|
*/
|
||||||
|
requestTemplates: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {(hidden: boolean, search?: string, groups?: string[], jobsites?: number[], codes?: string[]) => Promise<any[]>}
|
||||||
|
*/
|
||||||
|
requestAssets: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {() => Promise<{AssetGroups: any[], Codes: any[], Jobsites: any[]}>}
|
||||||
|
*/
|
||||||
|
requestAssetParams: null
|
||||||
|
}) {
|
||||||
|
super(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(asset) {
|
||||||
|
const option = this._option;
|
||||||
|
const loading = flag => this._var.popup.loading = flag;
|
||||||
|
const assetSelector = new AssetSelector({
|
||||||
|
ignoreHidden: true,
|
||||||
|
loading,
|
||||||
|
requestAssets: option.requestAssets,
|
||||||
|
requestAssetParams: option.requestAssetParams
|
||||||
|
});
|
||||||
|
this._var.assetSelector = assetSelector;
|
||||||
|
const templateSelector = new TemplateSelector({
|
||||||
|
loading,
|
||||||
|
requestTemplates: option.requestTemplates
|
||||||
|
});
|
||||||
|
this._var.templateSelector = templateSelector;
|
||||||
|
assetSelector.onSelected = asset => {
|
||||||
|
this._var.asset = asset;
|
||||||
|
this._var.template = null;
|
||||||
|
this._changePage(1);
|
||||||
|
templateSelector.assetId = asset.Id;
|
||||||
|
};
|
||||||
|
templateSelector.onSelected = template => {
|
||||||
|
this._var.template = template;
|
||||||
|
this._select();
|
||||||
|
this._var.popup.close();
|
||||||
|
}
|
||||||
|
this._var.containers = [
|
||||||
|
assetSelector.create(),
|
||||||
|
templateSelector.create()
|
||||||
|
];
|
||||||
|
const popup = new Popup({
|
||||||
|
title: this.r('FLTL_00121', 'Add Inspection'),
|
||||||
|
content: createElement('div', null, ...this._var.containers),
|
||||||
|
persistent: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: this.r('FLTL_00447', 'Back'),
|
||||||
|
trigger: () => {
|
||||||
|
this._changePage(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.r('FLTL_01973', 'Next'),
|
||||||
|
trigger: () => {
|
||||||
|
const asset = assetSelector.currentAsset;
|
||||||
|
if (asset == null) {
|
||||||
|
showAlert(assetSelector.title, this.r('FLTL_02269', 'Please select an Asset.'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this._var.asset = asset;
|
||||||
|
this._var.template = null;
|
||||||
|
this._changePage(1);
|
||||||
|
templateSelector.assetId = asset.Id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.r('FLTL_02057', 'OK'),
|
||||||
|
trigger: () => {
|
||||||
|
const template = templateSelector.currentTemplate;
|
||||||
|
if (template == null) {
|
||||||
|
showAlert(templateSelector.title, this.r('FLTL_02261', 'Please select a template.'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this._var.template = template;
|
||||||
|
this._select();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ text: this.r('FLTL_00499', 'Cancel') }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
this._var.popup = popup;
|
||||||
|
popup.create();
|
||||||
|
if (asset) {
|
||||||
|
this._changePage(1);
|
||||||
|
this._var.asset = asset;
|
||||||
|
templateSelector.assetId = asset.Id;
|
||||||
|
const footerButtons = popup.container.querySelectorAll('.ui-popup-footer>.ui-popup-button');
|
||||||
|
footerButtons[0].style.display = 'none';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this._changePage(0);
|
||||||
|
popup.rect = { width: 1220 };
|
||||||
|
const mask = await popup.show();
|
||||||
|
assetSelector.init();
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
show(asset) {
|
||||||
|
if (this._var.popup == null) {
|
||||||
|
return this.create(asset);
|
||||||
|
}
|
||||||
|
if (asset) {
|
||||||
|
this._var.asset = asset;
|
||||||
|
this._var.template = null;
|
||||||
|
this._changePage(1);
|
||||||
|
this._var.templateSelector._var.el.inputSearch.value = '';
|
||||||
|
this._var.templateSelector.assetId = asset.Id;
|
||||||
|
const footerButtons = this._var.popup.container.querySelectorAll('.ui-popup-footer>.ui-popup-button');
|
||||||
|
footerButtons[0].style.display = 'none';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._changePage(0);
|
||||||
|
const selector = this._var.assetSelector;
|
||||||
|
selector._var.el.inputSearch.value = '';
|
||||||
|
selector._var.el.dropAssetGroup.select('-1');
|
||||||
|
selector._var.el.dropJobsite.select('-1');
|
||||||
|
selector._var.el.dropJobsiteCode.select('-1');
|
||||||
|
selector.refresh();
|
||||||
|
}
|
||||||
|
return this._var.popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
_select() {
|
||||||
|
if (typeof this.onSelected === 'function') {
|
||||||
|
this.onSelected(this._var.asset, this._var.template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_changePage(index) {
|
||||||
|
if (isNaN(index) || index < 0 || index >= 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._var.index = index;
|
||||||
|
this._var.containers[0].style.display = index === 0 ? '' : 'none';
|
||||||
|
this._var.containers[1].style.display = index === 1 ? '' : 'none';
|
||||||
|
const footerButtons = this._var.popup.container.querySelectorAll('.ui-popup-footer>.ui-popup-button');
|
||||||
|
footerButtons[0].style.display = index === 0 ? 'none' : '';
|
||||||
|
footerButtons[1].style.display = index === 1 ? 'none' : '';
|
||||||
|
footerButtons[2].style.display = index === 0 ? 'none' : '';
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,10 @@
|
|||||||
import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup, Dropdown, validation } from "../ui";
|
import { createElement, createCheckbox, createRadiobox, Dropdown, validation, toDateValue, OptionBase } from "../ui";
|
||||||
import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../utility";
|
|
||||||
|
|
||||||
let r = lang;
|
export default class ScheduleItem extends OptionBase {
|
||||||
|
|
||||||
function datepicker(element) {
|
|
||||||
if (typeof $?.fn?.datepicker === 'function') {
|
|
||||||
$(element).datepicker({
|
|
||||||
autoHide: true,
|
|
||||||
format: 'm/dd/yyyy'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ScheduleItem {
|
|
||||||
_var = {};
|
_var = {};
|
||||||
|
|
||||||
constructor(opt) {
|
constructor(opt = {}) {
|
||||||
this._var.option = opt ?? {};
|
super(opt);
|
||||||
const getText = opt?.getText;
|
|
||||||
if (typeof getText === 'function') {
|
|
||||||
r = getText;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get checkOccurOnce() { return this._var.container.querySelector('.schedule-id-box-occur-once>input'); }
|
get checkOccurOnce() { return this._var.container.querySelector('.schedule-id-box-occur-once>input'); }
|
||||||
@ -79,7 +62,8 @@ export default class ScheduleItem {
|
|||||||
|
|
||||||
getDateString(s) {
|
getDateString(s) {
|
||||||
const d = this.getDateTime(s);
|
const d = this.getDateTime(s);
|
||||||
return String(d.getMonth() + 1).padStart(2, '0') + '/' + String(d.getDate()).padStart(2, '0') + '/' + String(d.getFullYear());
|
// return String(d.getMonth() + 1).padStart(2, '0') + '/' + String(d.getDate()).padStart(2, '0') + '/' + String(d.getFullYear());
|
||||||
|
return toDateValue(d, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setParameters(p) {
|
setParameters(p) {
|
||||||
@ -101,19 +85,12 @@ export default class ScheduleItem {
|
|||||||
this._var.container.querySelector('.schedule-id-6>input').checked = schedule.Saturday;
|
this._var.container.querySelector('.schedule-id-6>input').checked = schedule.Saturday;
|
||||||
this._var.container.querySelector('.schedule-id-7>input').checked = schedule.Sunday;
|
this._var.container.querySelector('.schedule-id-7>input').checked = schedule.Sunday;
|
||||||
this._var.container.querySelector('.schedule-id-dayofmonth').value = String(schedule.DayOfMonth);
|
this._var.container.querySelector('.schedule-id-dayofmonth').value = String(schedule.DayOfMonth);
|
||||||
const start = this.getDateString(schedule.StartDate);
|
this._var.container.querySelector('.schedule-id-duration-start').value = this.getDateString(schedule.StartDate);
|
||||||
const end = this.getDateString(schedule.EndDate);
|
this._var.container.querySelector('.schedule-id-duration-end').value = this.getDateString(schedule.EndDate);
|
||||||
if (typeof $?.fn?.datepicker === 'function') {
|
|
||||||
$(this._var.container.querySelector('.schedule-id-duration-start')).datepicker('setDate', new Date(start));
|
|
||||||
$(this._var.container.querySelector('.schedule-id-duration-end')).datepicker('setDate', new Date(end));
|
|
||||||
} else {
|
|
||||||
this._var.container.querySelector('.schedule-id-duration-start').value = start;
|
|
||||||
this._var.container.querySelector('.schedule-id-duration-end').value = end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
const option = this._var.option;
|
const option = this._option;
|
||||||
const drop = new Dropdown({ selected: '0' });
|
const drop = new Dropdown({ selected: '0' });
|
||||||
this.dropFrequency = drop;
|
this.dropFrequency = drop;
|
||||||
drop.source = [
|
drop.source = [
|
||||||
@ -184,7 +161,7 @@ export default class ScheduleItem {
|
|||||||
}),
|
}),
|
||||||
validation(
|
validation(
|
||||||
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-once', i.maxLength = 5 }),
|
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-once', i.maxLength = 5 }),
|
||||||
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
|
/^([1-9]|[01][0-9]|[2][0-3]):([1-9]|[0-5][0-9])$/
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
createElement('div', 'schedule-item-line schedule-item-line-occur-every',
|
createElement('div', 'schedule-item-line schedule-item-line-occur-every',
|
||||||
@ -205,14 +182,14 @@ export default class ScheduleItem {
|
|||||||
createElement('span', span => span.innerText = 'Starting at'),
|
createElement('span', span => span.innerText = 'Starting at'),
|
||||||
validation(
|
validation(
|
||||||
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-starting', i.maxLength = 5 }),
|
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-starting', i.maxLength = 5 }),
|
||||||
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
|
/^([1-9]|[01][0-9]|[2][0-3]):([1-9]|[0-5][0-9])$/
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
createElement('div', 'scheldule-item-line',
|
createElement('div', 'scheldule-item-line',
|
||||||
createElement('span', span => span.innerText = 'Ending at'),
|
createElement('span', span => span.innerText = 'Ending at'),
|
||||||
validation(
|
validation(
|
||||||
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-ending', i.maxLength = 5 }),
|
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-ending', i.maxLength = 5 }),
|
||||||
/^([01][0-9]|[2][0-3]):[0-5][0-9]$/
|
/^([1-9]|[01][0-9]|[2][0-3]):([1-9]|[0-5][0-9])$/
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -222,19 +199,15 @@ export default class ScheduleItem {
|
|||||||
createElement('legend', legend => legend.innerText = 'Duration'),
|
createElement('legend', legend => legend.innerText = 'Duration'),
|
||||||
createElement('div', 'schedule-item-line schedule-item-line-duration',
|
createElement('div', 'schedule-item-line schedule-item-line-duration',
|
||||||
createElement('span', span => span.innerText = 'Start date'),
|
createElement('span', span => span.innerText = 'Start date'),
|
||||||
datepicker(
|
validation(
|
||||||
validation(
|
createElement('input', i => { i.type = 'date', i.className = 'ui-input schedule-id-duration-start', i.maxLength = 10, i.required = true, i.min = '1753-01-01', i.max = '9999-12-31' }),
|
||||||
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-start', i.maxLength = 10 }),
|
/^[1-9][0-9]{3}-(1[0-2]|0[1-9])-(3[0-1]|[1-2][0-9]|0[1-9])$/
|
||||||
/^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
createElement('div', 'schedule-item-placeholder'),
|
createElement('div', 'schedule-item-placeholder'),
|
||||||
createElement('span', span => span.innerText = 'End date'),
|
createElement('span', span => span.innerText = 'End date'),
|
||||||
datepicker(
|
validation(
|
||||||
validation(
|
createElement('input', i => { i.type = 'date', i.className = 'ui-input schedule-id-duration-end', i.maxLength = 10, i.required = true, i.min = '1753-01-01', i.max = '9999-12-31' }),
|
||||||
createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-end', i.maxLength = 10 }),
|
/^[1-9][0-9]{3}-(1[0-2]|0[1-9])-(3[0-1]|[1-2][0-9]|0[1-9])$/
|
||||||
/^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
createElement('div', 'schedule-item-line',
|
createElement('div', 'schedule-item-line',
|
||||||
|
151
lib/element/signature.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import { OptionBase, Popup, createElement } from "../ui";
|
||||||
|
|
||||||
|
export default class Signature extends OptionBase {
|
||||||
|
_var = {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLCanvasElement}
|
||||||
|
*/
|
||||||
|
canvas: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Popup}
|
||||||
|
*/
|
||||||
|
popup: null,
|
||||||
|
isDrawing: false,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {{x: number, y: number}[]}
|
||||||
|
*/
|
||||||
|
points: [],
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {{x: number, y: number}[][]}
|
||||||
|
*/
|
||||||
|
allPoints: []
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event
|
||||||
|
* @type {(this: Signature, singature: string) => void}
|
||||||
|
*/
|
||||||
|
onSignatured;
|
||||||
|
|
||||||
|
constructor(opt = {}) {
|
||||||
|
super(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
async show() {
|
||||||
|
const popup = new Popup({
|
||||||
|
title: this.r('FLTL_02770', 'Signature'),
|
||||||
|
content: this._var.canvas = createElement('canvas', canvas => {
|
||||||
|
canvas.style.width = '100%';
|
||||||
|
canvas.style.height = '100%';
|
||||||
|
}),
|
||||||
|
resolve: result => {
|
||||||
|
if (result.result === 'ok' && this._var.allPoints.length > 0) {
|
||||||
|
if (typeof this.onSignatured === 'function') {
|
||||||
|
const data = this._var.canvas.toDataURL('image/png');
|
||||||
|
this.onSignatured(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof this.onSignatured === 'function') {
|
||||||
|
this.onSignatured();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: this.r('FLTL_02057', 'OK'),
|
||||||
|
trigger: () => {
|
||||||
|
if (this._var.allPoints.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.r('FLTL_02479', 'Reset'),
|
||||||
|
trigger: () => {
|
||||||
|
const ctx = this._var.canvas.getContext('2d');
|
||||||
|
ctx.clearRect(0, 0, this._var.canvas.width, this._var.canvas.height);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ text: this.r('FLTL_00499', 'Cancel') }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
this._var.popup = popup;
|
||||||
|
popup.create();
|
||||||
|
popup.container.classList.add('ui-popup-signature');
|
||||||
|
const mask = await popup.show();
|
||||||
|
this.init();
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
const canvas = this._var.canvas;
|
||||||
|
canvas.width = canvas.clientWidth;
|
||||||
|
canvas.height = canvas.clientHeight;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
|
||||||
|
canvas.addEventListener('mousedown', e => this._down(ctx, e.offsetX, e.offsetY), false);
|
||||||
|
canvas.addEventListener('mousemove', e => this._move(ctx, e.offsetX, e.offsetY), false);
|
||||||
|
canvas.addEventListener('mouseup', () => this._up(ctx), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
* @param {number} x1
|
||||||
|
* @param {number} y1
|
||||||
|
* @param {number} x2
|
||||||
|
* @param {number} y2
|
||||||
|
*/
|
||||||
|
_draw(ctx, x1, y1, x2, y2) {
|
||||||
|
ctx.moveTo(x1, y1);
|
||||||
|
ctx.lineTo(x2, y2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
*/
|
||||||
|
_down(ctx, x, y) {
|
||||||
|
this._var.isDrawing = true;
|
||||||
|
this._var.points.push({ x, y });
|
||||||
|
ctx.beginPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
*/
|
||||||
|
_move(ctx, x, y) {
|
||||||
|
if (this._var.isDrawing) {
|
||||||
|
const lastPoint = this._var.points.at(-1);
|
||||||
|
this._draw(ctx, lastPoint.x, lastPoint.y, x, y);
|
||||||
|
this._var.points.push({ x, y });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
*/
|
||||||
|
_up(ctx) {
|
||||||
|
if (this._var.isDrawing) {
|
||||||
|
this._var.isDrawing = false;
|
||||||
|
ctx.closePath();
|
||||||
|
this._var.allPoints.push(this._var.points);
|
||||||
|
this._var.points = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
@import "../ui/css/functions/func.scss";
|
||||||
|
|
||||||
.schedule-item-container {
|
.schedule-item-container {
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
@ -18,6 +20,12 @@
|
|||||||
.ui-input {
|
.ui-input {
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
text-indent: 0;
|
||||||
|
|
||||||
|
&.validation-error,
|
||||||
|
&:invalid {
|
||||||
|
color: #0000004d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.schedule-item-monthly {
|
.schedule-item-monthly {
|
||||||
@ -88,4 +96,227 @@
|
|||||||
.ui-drop-wrapper>.ui-drop-header {
|
.ui-drop-wrapper>.ui-drop-header {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.open-wo-container,
|
||||||
|
.popup-selector {
|
||||||
|
|
||||||
|
.ui-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
fill: rgb(123, 28, 33);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity .12s ease;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:active,
|
||||||
|
&:hover {
|
||||||
|
outline: none;
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-icon.disabled>.ui-icon {
|
||||||
|
cursor: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.open-wo-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
>.open-wo-header {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
>h3 {
|
||||||
|
font-size: var(--font-header-size);
|
||||||
|
font-family: var(--header-font-family);
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.open-wo-content {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin: 10px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(130px, auto) 1fr;
|
||||||
|
grid-auto-rows: minmax(32px, auto);
|
||||||
|
align-items: center;
|
||||||
|
justify-items: start;
|
||||||
|
|
||||||
|
>.wo-line {
|
||||||
|
grid-column: 1 / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-combined {
|
||||||
|
line-height: var(--settings-line-height);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
>.ui-icon {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-title {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&.wo-title-required {
|
||||||
|
&::after {
|
||||||
|
content: '*';
|
||||||
|
color: var(--red-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-sub-line {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-asset-name,
|
||||||
|
.wo-company-name {
|
||||||
|
margin-left: 6px;
|
||||||
|
max-width: 400px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-input,
|
||||||
|
.ui-date-cell,
|
||||||
|
.ui-drop-wrapper {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-input {
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-input,
|
||||||
|
.ui-drop-wrapper {
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-odometer+.ui-drop-wrapper {
|
||||||
|
min-width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-date-cell {
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 4px;
|
||||||
|
|
||||||
|
@include outborder();
|
||||||
|
|
||||||
|
&:invalid {
|
||||||
|
color: rgba(0, 0, 0, .3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-text {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-color-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
>em {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>label {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-left: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-selector {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
>.popup-selector-header {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
>img {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>h3 {
|
||||||
|
font-size: var(--font-header-size);
|
||||||
|
font-family: var(--header-font-family);
|
||||||
|
margin: 10px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.popup-selector-function {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
>.search-box {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
>span {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
right: 4px;
|
||||||
|
top: calc(50% - 7px);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-input {
|
||||||
|
width: 200px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-drop-wrapper {
|
||||||
|
margin: 0 8px 0 4px;
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.popup-selector-content {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-opened-workorder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 650px;
|
||||||
|
|
||||||
|
>header {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.wo-grid-opened {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin: 0 10px;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
}
|
}
|
142
lib/element/templateSelector.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { Grid, OptionBase, createElement, createIcon } from "../ui";
|
||||||
|
import { nullOrEmpty } from "../utility";
|
||||||
|
|
||||||
|
export default class TemplateSelector extends OptionBase {
|
||||||
|
_var = {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
assetId: -1,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLElement}
|
||||||
|
*/
|
||||||
|
container: null,
|
||||||
|
el: {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLInputElement}
|
||||||
|
*/
|
||||||
|
inputSearch: null,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Grid}
|
||||||
|
*/
|
||||||
|
grid: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event
|
||||||
|
* @type {(this: TemplateSelector, template: any) => void}
|
||||||
|
*/
|
||||||
|
onSelected;
|
||||||
|
|
||||||
|
constructor(opt = {
|
||||||
|
/** @type {(flag: boolean) => void} */
|
||||||
|
loading: null,
|
||||||
|
/** @type {() => void} */
|
||||||
|
close: null,
|
||||||
|
/** @type {(assetId: number, search: string) => Promise<any[]>} */
|
||||||
|
requestTemplates: null
|
||||||
|
}) {
|
||||||
|
super(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
get assetId() { return this._var.assetId }
|
||||||
|
set assetId(id) {
|
||||||
|
this._var.assetId = id;
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() { return this.r('FLTL_01604', 'Inspection Templates') }
|
||||||
|
|
||||||
|
get currentTemplate() { return this._var.el.grid.currentItem }
|
||||||
|
|
||||||
|
loading(flag) {
|
||||||
|
if (typeof this._option.loading === 'function') {
|
||||||
|
this._option.loading(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
const requestTemplates = this._option.requestTemplates;
|
||||||
|
if (typeof requestTemplates !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const el = this._var.el;
|
||||||
|
this.loading(true);
|
||||||
|
requestTemplates(this._var.assetId, el.inputSearch.value)
|
||||||
|
.then(data => el.grid.source = data)
|
||||||
|
.finally(() => this.loading(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
select(item) {
|
||||||
|
if (typeof this.onSelected !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.onSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3;
|
||||||
|
const inputSearch = createElement('input', input => {
|
||||||
|
input.type = 'text';
|
||||||
|
input.placeholder = this.r('FLTL_02606', 'Search');
|
||||||
|
input.tabIndex = tabIndex + 1;
|
||||||
|
input.className = 'ui-input';
|
||||||
|
input.addEventListener('keypress', e => e.key === 'Enter' && this.refresh());
|
||||||
|
});
|
||||||
|
const gridContent = createElement('div', div => {
|
||||||
|
div.className = 'popup-selector-content';
|
||||||
|
div.style.height = '400px';
|
||||||
|
});
|
||||||
|
const grid = new Grid(gridContent, this.r);
|
||||||
|
grid.columns = [
|
||||||
|
{
|
||||||
|
key: 'IssueId',
|
||||||
|
type: Grid.ColumnTypes.Icon,
|
||||||
|
width: 30,
|
||||||
|
enabled: false,
|
||||||
|
resizable: false,
|
||||||
|
filter: it => nullOrEmpty(it.IssueId) ? '' : 'cubes'
|
||||||
|
},
|
||||||
|
{ key: 'Name', caption: this.r('FLTL_01966', 'Name'), width: 390 },
|
||||||
|
{ key: 'Notes', caption: this.r('FLTL_02012', 'Notes'), width: 630 }
|
||||||
|
];
|
||||||
|
grid.onRowDblClicked = index => {
|
||||||
|
const item = grid.source[index];
|
||||||
|
this.select(item);
|
||||||
|
if (typeof this._option.close === 'function') {
|
||||||
|
this._option.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
grid.init();
|
||||||
|
grid.element.tabIndex = tabIndex + 2;
|
||||||
|
this._var.el = {
|
||||||
|
inputSearch,
|
||||||
|
grid
|
||||||
|
};
|
||||||
|
|
||||||
|
// content
|
||||||
|
const container = createElement('div', 'popup-selector',
|
||||||
|
createElement('div', 'popup-selector-header',
|
||||||
|
createElement('h3', h3 => h3.innerText = this.r('FLTL_02261', 'Please select a template.'))
|
||||||
|
),
|
||||||
|
createElement('div', 'popup-selector-function',
|
||||||
|
createElement('div', 'search-box',
|
||||||
|
inputSearch,
|
||||||
|
createElement('span', span => {
|
||||||
|
span.addEventListener('click', () => this.refresh());
|
||||||
|
},
|
||||||
|
createIcon('fa-light', 'search')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
gridContent
|
||||||
|
);
|
||||||
|
this._var.container = container;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
72
lib/ui.js
@ -4,13 +4,65 @@ import { createElement } from "./functions";
|
|||||||
import { createIcon, changeIcon, resolveIcon } from "./ui/icon";
|
import { createIcon, changeIcon, resolveIcon } from "./ui/icon";
|
||||||
import { createCheckbox, createRadiobox, resolveCheckbox } from "./ui/checkbox";
|
import { createCheckbox, createRadiobox, resolveCheckbox } from "./ui/checkbox";
|
||||||
import { setTooltip, resolveTooltip } from "./ui/tooltip";
|
import { setTooltip, resolveTooltip } from "./ui/tooltip";
|
||||||
|
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, showAlert, showConfirm } from "./ui/popup";
|
import { Popup, createPopup, resolvePopup, showAlert, showConfirm } from "./ui/popup";
|
||||||
import { createPicture, createAudio, createVideo, createFile } 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, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date';
|
import { createDateInput, toDateValue, getFormatter, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date';
|
||||||
|
import * as utility from './utility';
|
||||||
|
|
||||||
|
function requestAnimationFrame(callback) {
|
||||||
|
if (typeof utility.global.requestAnimationFrame === 'function') {
|
||||||
|
utility.global.requestAnimationFrame(callback);
|
||||||
|
} else {
|
||||||
|
setTimeout(callback, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollLeft() {
|
||||||
|
const n = document.documentElement;
|
||||||
|
return (utility.global.scrollX || n.scrollLeft) - (n.clientLeft || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollTop() {
|
||||||
|
const n = document.documentElement;
|
||||||
|
return (utility.global.scrollY || n.scrollTop) - (n.clientTop || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {HTMLElement} e
|
||||||
|
*/
|
||||||
|
function offset(e) {
|
||||||
|
const rect = e.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
top: rect.top + scrollTop(),
|
||||||
|
left: rect.left + scrollLeft(),
|
||||||
|
height: rect.height,
|
||||||
|
width: rect.width
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionBase {
|
||||||
|
_option;
|
||||||
|
|
||||||
|
r;
|
||||||
|
|
||||||
|
constructor(opt) {
|
||||||
|
this._option = opt;
|
||||||
|
const getText = opt.getText;
|
||||||
|
if (typeof getText === 'function') {
|
||||||
|
this.r = getText;
|
||||||
|
} else if (typeof GetTextByKey === 'function') {
|
||||||
|
this.r = GetTextByKey;
|
||||||
|
} else {
|
||||||
|
this.r = utility.r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createElement,
|
createElement,
|
||||||
@ -25,6 +77,8 @@ export {
|
|||||||
// tooltip
|
// tooltip
|
||||||
setTooltip,
|
setTooltip,
|
||||||
resolveTooltip,
|
resolveTooltip,
|
||||||
|
// tab
|
||||||
|
createTab,
|
||||||
// dropdown
|
// dropdown
|
||||||
Dropdown,
|
Dropdown,
|
||||||
// grid
|
// grid
|
||||||
@ -39,11 +93,13 @@ export {
|
|||||||
// popup
|
// popup
|
||||||
Popup,
|
Popup,
|
||||||
createPopup,
|
createPopup,
|
||||||
|
resolvePopup,
|
||||||
showAlert,
|
showAlert,
|
||||||
showConfirm,
|
showConfirm,
|
||||||
// dateSelector
|
// dateSelector
|
||||||
createDateInput,
|
createDateInput,
|
||||||
toDateValue,
|
toDateValue,
|
||||||
|
getFormatter,
|
||||||
formatDate,
|
formatDate,
|
||||||
setDateValue,
|
setDateValue,
|
||||||
getDateValue,
|
getDateValue,
|
||||||
@ -53,7 +109,15 @@ export {
|
|||||||
createAudio,
|
createAudio,
|
||||||
createVideo,
|
createVideo,
|
||||||
createFile,
|
createFile,
|
||||||
|
createVideoList,
|
||||||
// extension
|
// extension
|
||||||
validation,
|
validation,
|
||||||
convertCssStyle
|
convertCssStyle,
|
||||||
|
// utility
|
||||||
|
utility,
|
||||||
|
// functions
|
||||||
|
requestAnimationFrame,
|
||||||
|
offset,
|
||||||
|
// base classes
|
||||||
|
OptionBase
|
||||||
}
|
}
|
||||||
|
4
lib/ui/checkbox.d.ts
vendored
@ -10,10 +10,14 @@ interface CheckboxOptions {
|
|||||||
name?: string;
|
name?: string;
|
||||||
/** 焦点索引 */
|
/** 焦点索引 */
|
||||||
tabIndex?: Number;
|
tabIndex?: Number;
|
||||||
|
/** 是否为 switch 样式 */
|
||||||
|
switch?: boolean;
|
||||||
/** 样式分类,可以是 ['`fa-light`', '`fa-regular`', '`fa-solid`'] 其中之一 */
|
/** 样式分类,可以是 ['`fa-light`', '`fa-regular`', '`fa-solid`'] 其中之一 */
|
||||||
type?: string;
|
type?: string;
|
||||||
/** 标签 */
|
/** 标签 */
|
||||||
label?: string | HTMLElement;
|
label?: string | HTMLElement;
|
||||||
|
/** 标签提示文本 */
|
||||||
|
title?: string;
|
||||||
/** 是否已选中 */
|
/** 是否已选中 */
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
/** 图片高度 */
|
/** 图片高度 */
|
||||||
|
@ -2,7 +2,11 @@ import './css/checkbox.scss';
|
|||||||
import { createElement } from "../functions";
|
import { createElement } from "../functions";
|
||||||
import { createIcon } from "./icon";
|
import { createIcon } from "./icon";
|
||||||
|
|
||||||
function fillCheckbox(container, type = 'fa-regular', label, tabindex = -1, charactor = 'check') {
|
function fillCheckbox(container, type = 'fa-regular', label, tabindex = -1, charactor = 'check', title) {
|
||||||
|
const checkIcon = createIcon(type, charactor);
|
||||||
|
checkIcon.classList.add('ui-check-icon');
|
||||||
|
const indeterminateIcon = createIcon(type, 'grip-lines');
|
||||||
|
indeterminateIcon.classList.add('ui-indeterminate-icon')
|
||||||
container.appendChild(
|
container.appendChild(
|
||||||
createElement('layer', layer => {
|
createElement('layer', layer => {
|
||||||
layer.className = 'ui-check-inner';
|
layer.className = 'ui-check-inner';
|
||||||
@ -18,13 +22,18 @@ function fillCheckbox(container, type = 'fa-regular', label, tabindex = -1, char
|
|||||||
if (tabindex >= 0) {
|
if (tabindex >= 0) {
|
||||||
layer.tabIndex = tabindex;
|
layer.tabIndex = tabindex;
|
||||||
}
|
}
|
||||||
}, createIcon(type, charactor))
|
}, checkIcon, indeterminateIcon)
|
||||||
);
|
);
|
||||||
if (label instanceof Element) {
|
if (label instanceof Element) {
|
||||||
container.appendChild(label);
|
container.appendChild(label);
|
||||||
} else if (label != null && String(label).length > 0) {
|
} else if (label != null && String(label).length > 0) {
|
||||||
container.appendChild(
|
container.appendChild(
|
||||||
createElement('span', span => span.innerText = label)
|
createElement('span', span => {
|
||||||
|
span.innerText = label;
|
||||||
|
if (title != null) {
|
||||||
|
span.title = title;
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,17 +61,20 @@ export function createRadiobox(opts = {}) {
|
|||||||
if (opts.className) {
|
if (opts.className) {
|
||||||
container.classList.add(opts.className);
|
container.classList.add(opts.className);
|
||||||
}
|
}
|
||||||
fillCheckbox(container, opts.type, opts.label, opts.tabIndex, 'circle');
|
fillCheckbox(container, opts.type, opts.label, opts.tabIndex, 'circle', opts.title);
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCheckbox(opts = {}) {
|
export function createCheckbox(opts = {}) {
|
||||||
const container = createElement('label', 'ui-check-wrapper',
|
const container = createElement('label', opts.switch ? 'ui-switch' : 'ui-check-wrapper',
|
||||||
createElement('input', input => {
|
createElement('input', input => {
|
||||||
input.setAttribute('type', 'checkbox');
|
input.setAttribute('type', 'checkbox');
|
||||||
if (opts.checked === true) {
|
if (opts.checked === true) {
|
||||||
input.checked = true;
|
input.checked = true;
|
||||||
}
|
}
|
||||||
|
if (opts.indeterminate === true) {
|
||||||
|
input.indeterminate = true;
|
||||||
|
}
|
||||||
if (opts.enabled === false) {
|
if (opts.enabled === false) {
|
||||||
input.disabled = true;
|
input.disabled = true;
|
||||||
}
|
}
|
||||||
@ -81,7 +93,23 @@ export function createCheckbox(opts = {}) {
|
|||||||
if (opts.enabled === false) {
|
if (opts.enabled === false) {
|
||||||
container.classList.add('disabled');
|
container.classList.add('disabled');
|
||||||
}
|
}
|
||||||
if (opts.checkedNode != null && opts.uncheckedNode != null) {
|
if (opts.switch) {
|
||||||
|
const label = opts.label;
|
||||||
|
if (label instanceof Element) {
|
||||||
|
container.appendChild(label);
|
||||||
|
} else {
|
||||||
|
container.appendChild(
|
||||||
|
createElement('span', span => {
|
||||||
|
if (label != null && String(label).length > 0) {
|
||||||
|
span.innerText = label;
|
||||||
|
}
|
||||||
|
if (opts.title != null) {
|
||||||
|
span.title = opts.title;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (opts.checkedNode != null && opts.uncheckedNode != null) {
|
||||||
container.classList.add('ui-check-image-wrapper');
|
container.classList.add('ui-check-image-wrapper');
|
||||||
let height = opts.imageHeight;
|
let height = opts.imageHeight;
|
||||||
if (isNaN(height) || height <= 0) {
|
if (isNaN(height) || height <= 0) {
|
||||||
@ -92,7 +120,7 @@ export function createCheckbox(opts = {}) {
|
|||||||
opts.uncheckedNode.classList.add('unchecked');
|
opts.uncheckedNode.classList.add('unchecked');
|
||||||
container.appendChild(opts.uncheckedNode);
|
container.appendChild(opts.uncheckedNode);
|
||||||
} else {
|
} else {
|
||||||
fillCheckbox(container, opts.type, opts.label, opts.tabIndex);
|
fillCheckbox(container, opts.type, opts.label, opts.tabIndex, undefined, opts.title);
|
||||||
}
|
}
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
@ -144,7 +172,7 @@ export function resolveCheckbox(container = document.body, legacy) {
|
|||||||
label.className = 'ui-check-wrapper';
|
label.className = 'ui-check-wrapper';
|
||||||
}
|
}
|
||||||
label.replaceChildren();
|
label.replaceChildren();
|
||||||
fillCheckbox(label, 'fa-regular', text, chk.tabIndex);
|
fillCheckbox(label, 'fa-regular', text, chk.tabIndex, undefined, label.title);
|
||||||
label.insertBefore(chk, label.firstChild);
|
label.insertBefore(chk, label.firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,7 +189,10 @@ export function resolveCheckbox(container = document.body, legacy) {
|
|||||||
fillCheckbox(box,
|
fillCheckbox(box,
|
||||||
box.dataset.type,
|
box.dataset.type,
|
||||||
box.dataset.label,
|
box.dataset.label,
|
||||||
box.dataset.tabIndex)
|
box.dataset.tabIndex,
|
||||||
|
undefined,
|
||||||
|
box.title);
|
||||||
|
box.removeAttribute('title');
|
||||||
box.removeAttribute('data-type');
|
box.removeAttribute('data-type');
|
||||||
box.removeAttribute('data-label');
|
box.removeAttribute('data-label');
|
||||||
}
|
}
|
||||||
|
@ -48,4 +48,101 @@
|
|||||||
color: var(--color);
|
color: var(--color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-switch {
|
||||||
|
line-height: 1rem;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
>span:first-of-type {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 30px;
|
||||||
|
min-width: 30px;
|
||||||
|
max-width: 30px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 4px;
|
||||||
|
background-color: var(--switch-bg-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background-color .08s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 1px;
|
||||||
|
top: calc(50% - 7px);
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 7px;
|
||||||
|
box-shadow: 1px 1px 8px rgb(0 0 0 / 20%);
|
||||||
|
transition: left .08s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ui-switch-red>span:first-of-type::before {
|
||||||
|
background-color: var(--red-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
>input[type="checkbox"] {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&:checked+span:first-of-type {
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-color: var(--switch-active-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
left: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled+span:first-of-type {
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
opacity: .3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-switch-group {
|
||||||
|
line-height: 1rem;
|
||||||
|
user-select: none;
|
||||||
|
display: inline-flex;
|
||||||
|
background-color: var(--switch-bg-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
>label {
|
||||||
|
|
||||||
|
>span {
|
||||||
|
display: block;
|
||||||
|
padding: 4px 10px;
|
||||||
|
margin: 2px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>label>input[type="radio"] {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&:checked+span {
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 1px 1px 8px rgb(0 0 0 / 20%);
|
||||||
|
transition: background-color .08s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
@include outborder();
|
@include outborder();
|
||||||
|
|
||||||
&.validation-error {
|
&.validation-error,
|
||||||
|
&:invalid {
|
||||||
border-color: var(--red-color);
|
border-color: var(--red-color);
|
||||||
|
|
||||||
&:focus,
|
&:focus,
|
||||||
|
@ -30,7 +30,8 @@ $listMaxHeight: 210px;
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
// line-height: $headerHeight;
|
line-height: $headerHeight;
|
||||||
|
min-height: $headerHeight;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -152,21 +153,25 @@ $listMaxHeight: 210px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
>.ui-drop-list {
|
>.ui-drop-list {
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
max-height: $listMaxHeight;
|
max-height: $listMaxHeight;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
position: relative;
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
@include scrollbar();
|
@include scrollbar();
|
||||||
|
|
||||||
&.filtered>li:first-child {
|
&.filtered>.drop-content>.li:first-child {
|
||||||
background-color: var(--hover-bg-color);
|
background-color: var(--hover-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
>li {
|
>.drop-content {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
// display: flex;
|
// display: flex;
|
||||||
// align-items: center;
|
// align-items: center;
|
||||||
|
list-style: none;
|
||||||
line-height: $dropItemHeight;
|
line-height: $dropItemHeight;
|
||||||
height: $dropItemHeight;
|
height: $dropItemHeight;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
@ -180,10 +185,21 @@ $listMaxHeight: 210px;
|
|||||||
background-color: var(--hover-bg-color);
|
background-color: var(--hover-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
>.ui-check-wrapper {
|
>.li-wrapper {
|
||||||
height: $dropItemHeight;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
>.ui-expandor {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-check-wrapper {
|
||||||
|
height: $dropItemHeight;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -44,6 +44,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin wrap() {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
@mixin ui-check() {
|
@mixin ui-check() {
|
||||||
.ui-check-inner {
|
.ui-check-inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -87,7 +92,22 @@
|
|||||||
border-color: var(--link-color);
|
border-color: var(--link-color);
|
||||||
background-color: var(--link-color);
|
background-color: var(--link-color);
|
||||||
|
|
||||||
>svg {
|
>.ui-check-icon {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:indeterminate+.ui-check-inner {
|
||||||
|
border-color: var(--secondary-color);
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
|
||||||
|
>.ui-check-icon {
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-indeterminate-icon {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,8 @@
|
|||||||
--header-padding: 4px 12px 4px 8px;
|
--header-padding: 4px 12px 4px 8px;
|
||||||
--header-filter-padding: 4px 26px 4px 8px;
|
--header-filter-padding: 4px 26px 4px 8px;
|
||||||
--spacing-s: 4px;
|
--spacing-s: 4px;
|
||||||
--spacing-cell: 6px 4px 6px 8px;
|
--spacing-cell: 9px 4px 9px 8px;
|
||||||
|
--spacing-drop-cell: 5px 4px 5px 8px;
|
||||||
--filter-line-height: 30px;
|
--filter-line-height: 30px;
|
||||||
--filter-item-padding: 0 4px;
|
--filter-item-padding: 0 4px;
|
||||||
}
|
}
|
||||||
@ -94,6 +95,7 @@
|
|||||||
// position: relative;
|
// position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
&.sticky {
|
&.sticky {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@ -117,12 +119,22 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
&.wrap {
|
&.wrap {
|
||||||
white-space: normal;
|
@include wrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.ui-check-wrapper {
|
>.ui-check-wrapper,
|
||||||
|
>.ui-switch {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
padding: 0 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
width: 12px;
|
||||||
|
min-width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
margin-left: 4px;
|
||||||
|
fill: var(--split-border-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +214,7 @@
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
>em.bottom-border {
|
>.bottom-border {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -285,11 +297,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
>span {
|
>span {
|
||||||
padding: var(--spacing-cell);
|
margin: var(--spacing-cell);
|
||||||
display: block;
|
display: block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
|
||||||
|
&.wrap {
|
||||||
|
@include wrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,11 +372,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
>input[type="text"],
|
>input[type="text"],
|
||||||
|
>input[type="date"],
|
||||||
>textarea {
|
>textarea {
|
||||||
border: none;
|
border: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
max-height: none !important;
|
||||||
|
|
||||||
@include outline();
|
@include outline();
|
||||||
|
|
||||||
@ -383,10 +401,12 @@
|
|||||||
@include scrollbar();
|
@include scrollbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-check-wrapper {
|
.ui-check-wrapper,
|
||||||
|
.ui-switch {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: var(--row-height);
|
height: var(--row-height);
|
||||||
|
padding: 0 8px;
|
||||||
|
|
||||||
.ui-check-inner {
|
.ui-check-inner {
|
||||||
|
|
||||||
@ -395,6 +415,30 @@
|
|||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
>span:first-of-type {
|
||||||
|
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-drop-span {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
>span {
|
||||||
|
margin: var(--spacing-cell);
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.wrap>span {
|
||||||
|
@include wrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-drop-wrapper {
|
.ui-drop-wrapper {
|
||||||
@ -408,18 +452,14 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
>.ui-drop-text {
|
>.ui-drop-text {
|
||||||
padding: var(--spacing-cell);
|
padding: var(--spacing-drop-cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-date-cell {
|
.ui-date-cell {
|
||||||
line-height: 22px;
|
height: var(--row-height);
|
||||||
box-sizing: border-box;
|
text-indent: 4px;
|
||||||
padding: var(--spacing-cell);
|
|
||||||
border: none;
|
|
||||||
font-size: var(--font-size);
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:invalid {
|
&:invalid {
|
||||||
color: rgba(0, 0, 0, .3);
|
color: rgba(0, 0, 0, .3);
|
||||||
@ -432,7 +472,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: var(--spacing-s);
|
// padding: var(--spacing-s);
|
||||||
|
|
||||||
>svg {
|
>svg {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
@ -580,6 +620,7 @@
|
|||||||
|
|
||||||
.ui-check-wrapper {
|
.ui-check-wrapper {
|
||||||
height: var(--filter-line-height);
|
height: var(--filter-line-height);
|
||||||
|
line-height: var(--filter-line-height);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.ui-check-inner+* {
|
.ui-check-inner+* {
|
||||||
@ -594,11 +635,14 @@
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
||||||
>button {
|
>.button {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
padding-inline: 6px;
|
||||||
|
text-align: center;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
height: var(--filter-line-height);
|
height: var(--filter-line-height);
|
||||||
|
line-height: var(--filter-line-height);
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -624,8 +668,10 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
>button {
|
>.button {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
|
padding-inline: 6px;
|
||||||
|
text-align: center;
|
||||||
border: none;
|
border: none;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
color: var(--title-color);
|
color: var(--title-color);
|
||||||
@ -633,6 +679,7 @@
|
|||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: var(--title-bg-color);
|
background-color: var(--title-bg-color);
|
||||||
@ -671,6 +718,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-popup-mask .ui-popup-container .ui-popup-footer {
|
||||||
|
>.ui-sort-layout {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*@media (prefers-color-scheme: dark) {
|
/*@media (prefers-color-scheme: dark) {
|
||||||
.ui-grid {
|
.ui-grid {
|
||||||
--cell-hover-bg-color: yellow;
|
--cell-hover-bg-color: yellow;
|
||||||
|
@ -65,4 +65,235 @@
|
|||||||
.ui-media-video {
|
.ui-media-video {
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-media-video-container {
|
||||||
|
position: relative;
|
||||||
|
background-color: #000;
|
||||||
|
min-height: 200px;
|
||||||
|
height: 100%;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
--icon-size: 16px;
|
||||||
|
|
||||||
|
>.ui-video-content {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
>.ui-video-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
>video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-video-waiting {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, .3);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: opacity .2s;
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
fill: #eee;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
top: calc(50% - 15px);
|
||||||
|
left: calc(50% - 15px);
|
||||||
|
animation: spinner 1.2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spinner {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(359deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-video-control {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 70px;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
background: linear-gradient(transparent, #000);
|
||||||
|
// calc(100% - 100px), rgba(50, 50, 50, .7) calc(100% - 65px)
|
||||||
|
transition: opacity .5s;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-video-icon {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--icon-size);
|
||||||
|
height: var(--icon-size);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
width: var(--icon-size);
|
||||||
|
height: var(--icon-size);
|
||||||
|
display: block;
|
||||||
|
fill: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-video-bar {
|
||||||
|
position: absolute;
|
||||||
|
height: 4px;
|
||||||
|
padding: 4px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
>.seek-buffers {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
>.ui-video-buffer {
|
||||||
|
background-color: #adadad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-video-duration,
|
||||||
|
>.seek-buffers>.ui-video-buffer,
|
||||||
|
>.ui-video-progress {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 2px;
|
||||||
|
height: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-video-duration {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #4e4e4e;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-video-progress {
|
||||||
|
width: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '\a0';
|
||||||
|
position: absolute;
|
||||||
|
right: -6px;
|
||||||
|
top: -5px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 7px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.play-icon {
|
||||||
|
left: 20px;
|
||||||
|
bottom: 41px;
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pause {
|
||||||
|
>svg:first-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
>svg:last-child {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-video-time-label {
|
||||||
|
position: absolute;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
left: 48px;
|
||||||
|
bottom: 40px;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.fullscreen-icon {
|
||||||
|
right: 20px;
|
||||||
|
bottom: 41px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-video-volume-container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 40px;
|
||||||
|
right: 50px;
|
||||||
|
|
||||||
|
>.volume-icon {
|
||||||
|
right: 0;
|
||||||
|
bottom: 1px;
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.muted {
|
||||||
|
>svg:first-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
>svg:last-child {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.volume-bar {
|
||||||
|
width: 60px;
|
||||||
|
right: 30px;
|
||||||
|
bottom: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-fullscreen>.ui-video-volume-container {
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.seek-bar {
|
||||||
|
left: 16px;
|
||||||
|
right: 16px;
|
||||||
|
bottom: 16px;
|
||||||
|
|
||||||
|
>.seek-progress::after {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active>.seek-bar>.seek-progress::after,
|
||||||
|
>.seek-bar:hover>.seek-progress::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -62,7 +62,7 @@ $buttonHeight: 28px;
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding: 10px 0 6px 12px;
|
padding: 5px 0 3px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.ui-popup-header-title,
|
>.ui-popup-header-title,
|
||||||
@ -70,9 +70,13 @@ $buttonHeight: 28px;
|
|||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
>.ui-popup-header-title.no-move {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
>.ui-popup-header-icons {
|
>.ui-popup-header-icons {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: 10px 12px 6px 0;
|
padding: 5px 12px 3px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
>svg {
|
>svg {
|
||||||
@ -141,8 +145,9 @@ $buttonHeight: 28px;
|
|||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
|
||||||
>svg {
|
>svg {
|
||||||
width: 40px;
|
width: 30px;
|
||||||
height: 40px;
|
height: 30px;
|
||||||
|
fill: unset;
|
||||||
|
|
||||||
+span {
|
+span {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
@ -200,6 +205,10 @@ $buttonHeight: 28px;
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 4px 10px 16px 2px;
|
padding: 4px 10px 16px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-popup-body,
|
||||||
|
.ui-popup-footer {
|
||||||
|
|
||||||
.ui-popup-button {
|
.ui-popup-button {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
@ -208,7 +217,7 @@ $buttonHeight: 28px;
|
|||||||
line-height: $buttonHeight;
|
line-height: $buttonHeight;
|
||||||
color: var(--title-color);
|
color: var(--title-color);
|
||||||
border-radius: var(--corner-radius);
|
border-radius: var(--corner-radius);
|
||||||
padding: 4px 16px;
|
padding: 1px 16px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: 70px;
|
min-width: 70px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -223,6 +232,11 @@ $buttonHeight: 28px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include outline();
|
@include outline();
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: .4;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
lib/ui/css/tab.scss
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
.ui-tab-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
--header-line-height: 24px;
|
||||||
|
--header-padding: 6px 6px 0;
|
||||||
|
--header-hover-color: #b9b9b9;
|
||||||
|
--header-selected-color: rgb(173, 84, 12);
|
||||||
|
|
||||||
|
>.ui-tab-header {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
line-height: var(--header-line-height);
|
||||||
|
padding: var(--header-padding);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
>.ui-tab-title {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--header-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: var(--border-color);
|
||||||
|
border-bottom-color: var(--bg-color);
|
||||||
|
color: var(--header-selected-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.ui-tab-page {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: 6px;
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,8 @@
|
|||||||
--disabled-color: #aaa;
|
--disabled-color: #aaa;
|
||||||
--disabled-bg-color: #e9e9e9;
|
--disabled-bg-color: #e9e9e9;
|
||||||
--disabled-border-color: #d9d9d9;
|
--disabled-border-color: #d9d9d9;
|
||||||
|
--switch-bg-color: #eae9eb;
|
||||||
|
--switch-active-bg-color: #33c559;
|
||||||
|
|
||||||
--red-color: red;
|
--red-color: red;
|
||||||
--title-color: #fff;
|
--title-color: #fff;
|
||||||
@ -35,12 +37,15 @@
|
|||||||
|
|
||||||
--border-radius: 2px;
|
--border-radius: 2px;
|
||||||
--text-indent: 4px;
|
--text-indent: 4px;
|
||||||
--line-height: 24px;
|
--line-height: 18px;
|
||||||
|
--settings-line-height: 32px;
|
||||||
|
|
||||||
--font-size: .8125rem; // 13px
|
--font-size: .8125rem; // 13px
|
||||||
--font-smaller-size: .75rem; // 12px
|
--font-smaller-size: .75rem; // 12px
|
||||||
--font-larger-size: .875rem; // 14px
|
--font-larger-size: .875rem; // 14px
|
||||||
|
--font-header-size: 1.5rem; // 24px
|
||||||
--font-family: "Franklin Gothic Book", "San Francisco", "Segoe UI", "Open Sans", "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei UI", sans-serif;
|
--font-family: "Franklin Gothic Book", "San Francisco", "Segoe UI", "Open Sans", "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei UI", sans-serif;
|
||||||
|
--header-font-family: Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@media (prefers-color-scheme: dark) {
|
/*@media (prefers-color-scheme: dark) {
|
||||||
|
14
lib/ui/date.d.ts
vendored
@ -10,8 +10,9 @@ export function createDateInput(min?: string, max?: string, element?: HTMLInputE
|
|||||||
/**
|
/**
|
||||||
* 将日期转换成 yyyy-MM-dd 格式的字符串
|
* 将日期转换成 yyyy-MM-dd 格式的字符串
|
||||||
* @param dt 日期值
|
* @param dt 日期值
|
||||||
|
* @param local 是否视日期为本地时间
|
||||||
*/
|
*/
|
||||||
export function toDateValue(dt: Date): string;
|
export function toDateValue(dt: Date, local?: boolean): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化日期字符串
|
* 格式化日期字符串
|
||||||
@ -23,14 +24,15 @@ export function toDateValue(dt: Date): string;
|
|||||||
* `new Date('2024-01-26')`<br/>
|
* `new Date('2024-01-26')`<br/>
|
||||||
* @returns 格式化为 M/d/yyyy 的日期字符串
|
* @returns 格式化为 M/d/yyyy 的日期字符串
|
||||||
*/
|
*/
|
||||||
export function formatDate(date: Date | number | string): string;
|
export function formatDate(date: Date | number | string, formatter?: string): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置显示日期
|
* 设置显示日期
|
||||||
* @param element 要设置显示日期的元素
|
* @param element 要设置显示日期的元素
|
||||||
* @param val 日期值,支持格式参见 {@linkcode formatDate}
|
* @param val 日期值,支持格式参见 {@linkcode formatDate}
|
||||||
|
* @param formatter 日期格式化字符串(仅设置显示元素时调用)
|
||||||
*/
|
*/
|
||||||
export function setDateValue(element: HTMLElement, val: Date | number | string): void;
|
export function setDateValue(element: HTMLElement, val: Date | number | string, formatter?: string): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从日期选择框获取日期值
|
* 从日期选择框获取日期值
|
||||||
@ -38,7 +40,7 @@ export function setDateValue(element: HTMLElement, val: Date | number | string):
|
|||||||
* @param formatter 自定义格式化函数,传入参数为 `Date` 类型
|
* @param formatter 自定义格式化函数,传入参数为 `Date` 类型
|
||||||
* @returns 默认返回日期 `ticks` 的字符串
|
* @returns 默认返回日期 `ticks` 的字符串
|
||||||
*/
|
*/
|
||||||
export function getDateValue(element: HTMLInputElement, formatter?: (date: Date) => string): string;
|
export function getDateValue(element: HTMLInputElement, formatter?: string | ((date: Date) => string)): string;
|
||||||
|
|
||||||
/** 日期选择框类 */
|
/** 日期选择框类 */
|
||||||
export class DateSelector {
|
export class DateSelector {
|
||||||
@ -85,6 +87,10 @@ export class DateSelector {
|
|||||||
maxDate?: string,
|
maxDate?: string,
|
||||||
/** 是否启用 */
|
/** 是否启用 */
|
||||||
enabled?: boolean,
|
enabled?: boolean,
|
||||||
|
/** 焦点索引 */
|
||||||
|
tabIndex?: number,
|
||||||
|
/** 类名 */
|
||||||
|
className?: string,
|
||||||
/**
|
/**
|
||||||
* 自定义格式化函数,用于获取日期值时调用
|
* 自定义格式化函数,用于获取日期值时调用
|
||||||
* @param date 日期值
|
* @param date 日期值
|
||||||
|
301
lib/ui/date.js
@ -1,4 +1,5 @@
|
|||||||
import { createElement } from "../functions";
|
import { createElement } from "../functions";
|
||||||
|
import { isPositive, nullOrEmpty } from "../utility";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建或转换日期选择框
|
* 创建或转换日期选择框
|
||||||
@ -22,9 +23,13 @@ export function createDateInput(min, max, element) {
|
|||||||
date.type = 'date';
|
date.type = 'date';
|
||||||
if (min != null) {
|
if (min != null) {
|
||||||
date.min = min;
|
date.min = min;
|
||||||
|
} else {
|
||||||
|
date.min = '1753-01-01';
|
||||||
}
|
}
|
||||||
if (max != null) {
|
if (max != null) {
|
||||||
date.max = max;
|
date.max = max;
|
||||||
|
} else {
|
||||||
|
date.max = '9999-12-31';
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
@ -32,26 +37,140 @@ export function createDateInput(min, max, element) {
|
|||||||
/**
|
/**
|
||||||
* 将日期转换为 `yyyy-MM-dd` 格式的字符串
|
* 将日期转换为 `yyyy-MM-dd` 格式的字符串
|
||||||
* @param {Date} dt 要转换的日期值
|
* @param {Date} dt 要转换的日期值
|
||||||
|
* @param {boolean} [local] 是否视日期为本地时间
|
||||||
* @returns 返回 `yyyy-MM-dd` 格式的字符串
|
* @returns 返回 `yyyy-MM-dd` 格式的字符串
|
||||||
*/
|
*/
|
||||||
export function toDateValue(dt) {
|
export function toDateValue(dt, local) {
|
||||||
if (isNaN(dt)) {
|
if (isNaN(dt)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const month = String(dt.getMonth() + 1).padStart(2, '0');
|
const year = local ? dt.getFullYear() : dt.getUTCFullYear();
|
||||||
const date = String(dt.getDate()).padStart(2, '0');
|
const month = String((local ? dt.getMonth() : dt.getUTCMonth()) + 1).padStart(2, '0');
|
||||||
return `${dt.getFullYear()}-${month}-${date}`;
|
const date = String(local ? dt.getDate() : dt.getUTCDate()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${date}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveDate(s) {
|
/**
|
||||||
if (s instanceof Date) {
|
* 获取日期格式器
|
||||||
return s;
|
* @param {Date} date - 待格式的日期
|
||||||
}
|
* @param {boolean} [utc=true] - 是否按 UTC 时间格式化
|
||||||
const ticks = Number(s);
|
* @returns {any} 返回格式化工具对象
|
||||||
if (!isNaN(ticks) && ticks > 0) {
|
*/
|
||||||
return new Date((ticks - 621355968e9) / 1e4);
|
export function getFormatter(date, utc) {
|
||||||
}
|
const prefix = utc !== false ? 'getUTC' : 'get';
|
||||||
return new Date(s);
|
const r = {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {number} 返回日数字
|
||||||
|
*/
|
||||||
|
j: () => date[`${prefix}Date`](),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回格式化成两位的日字符串
|
||||||
|
*/
|
||||||
|
d: () => String(r.j()).padStart(2, '0'),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {number} 返回月数字
|
||||||
|
*/
|
||||||
|
n: () => date[`${prefix}Month`]() + 1,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回格式化成两位的月字符串
|
||||||
|
*/
|
||||||
|
m: () => String(r.n()).padStart(2, '0'),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {number} 返回年数字
|
||||||
|
*/
|
||||||
|
Y: () => date[`${prefix}FullYear`](),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回年后两位
|
||||||
|
*/
|
||||||
|
y: () => String(r.Y()).slice(-2),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {number} 返回日期当月总天数
|
||||||
|
*/
|
||||||
|
t: () => new Date(r.Y(), r.n(), 0).getDate(),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {('1' | '0')} 返回是否为闰年
|
||||||
|
*/
|
||||||
|
L() {
|
||||||
|
const y = r.Y();
|
||||||
|
return t % 4 === 0 && t % 100 !== 0 || t % 400 === 0 ? 1 : 0;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {number} 返回小时数字
|
||||||
|
*/
|
||||||
|
G: () => date[`${prefix}Hours`](),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {number} 返回 12 小时制的数字
|
||||||
|
*/
|
||||||
|
g: () => r.G() % 12 || 12,
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回格式化成两位的小时字符串
|
||||||
|
*/
|
||||||
|
H: () => String(r.G()).padStart(2, '0'),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回格式化成两位的 12 小时制的字符串
|
||||||
|
*/
|
||||||
|
h: () => String(r.g()).padStart(2, '0'),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回上下午
|
||||||
|
*/
|
||||||
|
A: () => ['AM', 'PM'][r.G() < 12 ? 0 : 1],
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回小写的上下午
|
||||||
|
*/
|
||||||
|
a: () => r.A().toLowerCase(),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回格式化成两位的分钟字符串
|
||||||
|
*/
|
||||||
|
i: () => String(date[`${prefix}Minutes`]()).padStart(2, '0'),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回格式化成两位的秒字符串
|
||||||
|
*/
|
||||||
|
s: () => String(date[`${prefix}Seconds`]()).padStart(2, '0'),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回格式化成六位的毫秒字符串
|
||||||
|
*/
|
||||||
|
u: () => String(1e3 * date[`${prefix}Milliseconds`]()).padStart(6, '0'),
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回时区描述字符串
|
||||||
|
*/
|
||||||
|
e: () => /\((.*)\)/.exec(String(date))[1] || 'Coordinated Universal Time',
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回时区偏移字符串
|
||||||
|
*/
|
||||||
|
O() {
|
||||||
|
const t = date.getTimezoneOffset();
|
||||||
|
const r = Math.abs(t);
|
||||||
|
return (t > 0 ? '-' : '+') + String(100 * Math.floor(r / 60) + r % 60).padStart(4, '0');
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @returns {string} 返回 +08:00 这种格式的时区偏移字符串
|
||||||
|
*/
|
||||||
|
P() {
|
||||||
|
const t = r.O();
|
||||||
|
return t.substring(0, 3) + ':' + t.substring(3, 5);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,45 +178,109 @@ function resolveDate(s) {
|
|||||||
* @param {Date | number | string} date - 需要格式化的日期值,支持的格式如下:
|
* @param {Date | number | string} date - 需要格式化的日期值,支持的格式如下:
|
||||||
*
|
*
|
||||||
* * `"2024-01-26"`
|
* * `"2024-01-26"`
|
||||||
|
* * `"2024/1/26"`
|
||||||
* * `"2024-01-26T00:00:00"`
|
* * `"2024-01-26T00:00:00"`
|
||||||
* * `"1/26/2024"`
|
* * `"1/26/2024"`
|
||||||
* * `"638418240000000000"`
|
* * `"638418240000000000"`
|
||||||
* * `new Date('2024-01-26')`
|
* * `new Date('2024-01-26')`
|
||||||
|
* @param {string} [formatter] - 格式化格式,默认为 `"m/d/Y"`
|
||||||
|
*
|
||||||
|
* * Y - 年,例如 `2024`
|
||||||
|
* * y - 年的后两位,例如 `"24"`
|
||||||
|
* * n - 月,例如 `2`
|
||||||
|
* * m - 格式化成两位的月,例如 `"02"`
|
||||||
|
* * j - 日,例如 `4`
|
||||||
|
* * d - 格式化成两位的日,例如 `"04"`
|
||||||
|
* * t - 日期当月总天数,例如 `29`
|
||||||
|
* * L - 是否为闰年,例如 `1`
|
||||||
|
* * G - 小时,例如 `15`
|
||||||
|
* * g - 12 小时制的小时,例如 `3`
|
||||||
|
* * H - 格式化成两位的小时,例如 `"15"`
|
||||||
|
* * h - 格式化成两位的 12 小时制的小时,例如 `"03"`
|
||||||
|
* * A - 上下午,例如 `"PM"`
|
||||||
|
* * a - 小写的上下午,例如 `"pm"`
|
||||||
|
* * i - 格式化成两位的分钟,例如 `"39"`
|
||||||
|
* * s - 格式化成两位的秒,例如 `"20"`
|
||||||
|
* * u - 格式化成六位的毫秒,例如 `"023040"`
|
||||||
|
* * e - 时区描述字符串,例如 `"China Standard Time"`
|
||||||
|
* * O - 时区偏移字符串,例如 `"+0800"`
|
||||||
|
* * P - `"+08:00"` 这种格式的时区偏移字符串
|
||||||
* @returns {string} 返回格式化后的日期字符串
|
* @returns {string} 返回格式化后的日期字符串
|
||||||
*/
|
*/
|
||||||
export function formatDate(date) {
|
export function formatDate(date, formatter) {
|
||||||
date = resolveDate(date);
|
formatter ??= 'm/d/Y';
|
||||||
if (date instanceof Date && !isNaN(date)) {
|
if (date === '') {
|
||||||
return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
|
return '';
|
||||||
}
|
}
|
||||||
return '';
|
if (isNaN(date)) {
|
||||||
|
let e = /^(\d{4})-(\d{2})-(\d{2})/.exec(date);
|
||||||
|
if (e == null) {
|
||||||
|
e = /^(\d{4})\/(\d{1,2})\/(\d{1,2})/.exec(date);
|
||||||
|
}
|
||||||
|
if (e != null) {
|
||||||
|
date = new Date(e[1], parseInt(e[2]) - 1, e[3]);
|
||||||
|
} else {
|
||||||
|
e = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(date);
|
||||||
|
if (e != null) {
|
||||||
|
date = new Date(e[3], parseInt(e[1]) - 1, e[2]);
|
||||||
|
} else {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (date instanceof Date) {
|
||||||
|
const f = getFormatter(date, false);
|
||||||
|
return formatter.replace(/\\?(.?)/gi, (k, v) => f[k] ? f[k]() : v);
|
||||||
|
}
|
||||||
|
const ticks = Number(date);
|
||||||
|
if (!isNaN(ticks) && ticks > 0) {
|
||||||
|
date = new Date((ticks - 621355968e9) / 1e4);
|
||||||
|
const f = getFormatter(date);
|
||||||
|
return formatter.replace(/\\?(.?)/gi, (k, v) => f[k] ? f[k]() : v);
|
||||||
|
}
|
||||||
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置显示日期
|
* 设置显示日期
|
||||||
* @param {HTMLElement} element - 要设置显示日期的元素
|
* @param {HTMLElement} element - 要设置显示日期的元素
|
||||||
* @param {Date | number | string} val - 日期值,支持格式参见 {@linkcode formatDate}
|
* @param {Date | number | string} val - 日期值,支持格式参见 {@linkcode formatDate}
|
||||||
|
* @param {string} [formatter] - 日期格式化字符串(仅设置显示元素时调用)
|
||||||
*/
|
*/
|
||||||
export function setDateValue(element, val) {
|
export function setDateValue(element, val, formatter) {
|
||||||
if (element.tagName === 'INPUT') {
|
if (element.tagName === 'INPUT') {
|
||||||
if (val === '') {
|
if (val === '') {
|
||||||
element.value = '';
|
element.value = '';
|
||||||
} else if (isNaN(val)) {
|
} else if (isNaN(val)) {
|
||||||
if (/^\d{4}-\d{2}-\d{2}/.test(val)) {
|
if (/^\d{4}-\d{2}-\d{2}/.test(val)) {
|
||||||
element.value = String(val).substring(0, 10);
|
element.value = String(val).substring(0, 10);
|
||||||
} else if (/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(val)) {
|
} else if (/^\d{4}\/\d{2}\/\d{2}/.test(val)) {
|
||||||
element.value = toDateValue(new Date(val));
|
element.value = String(val).replace('/', '-').substring(0, 10);
|
||||||
} else {
|
} else {
|
||||||
element.value = '';
|
const e = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(val);
|
||||||
|
if (e != null) {
|
||||||
|
const month = e[1].padStart(2, '0');
|
||||||
|
const day = e[2].padStart(2, '0');
|
||||||
|
const year = e[3];
|
||||||
|
element.value = `${year}-${month}-${day}`;
|
||||||
|
} else {
|
||||||
|
element.value = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!(val instanceof Date)) {
|
if (val instanceof Date) {
|
||||||
val = new Date((val - 621355968e9) / 1e4);
|
element.value = toDateValue(val, true);
|
||||||
|
} else {
|
||||||
|
const ticks = Number(val);
|
||||||
|
if (!isNaN(ticks) && ticks > 0) {
|
||||||
|
element.value = toDateValue(new Date((ticks - 621355968e9) / 1e4));
|
||||||
|
} else {
|
||||||
|
element.value = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
element.value = toDateValue(val);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
element.innerText = formatDate(val);
|
element.innerText = formatDate(val, formatter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,18 +294,25 @@ export function setDateValue(element, val) {
|
|||||||
/**
|
/**
|
||||||
* 从日期选择框获取日期值
|
* 从日期选择框获取日期值
|
||||||
* @param {HTMLInputElement} element - 要获取的日期选择框
|
* @param {HTMLInputElement} element - 要获取的日期选择框
|
||||||
* @param {DateFormatterCallback} [formatter] - 自定义格式化函数,传入参数为 `Date` 类型
|
* @param {string | DateFormatterCallback} [formatter] - 自定义格式化字符串或函数,传入参数为 `Date` 类型
|
||||||
* @returns {string | any} 默认返回日期 `ticks` 的字符串
|
* @returns {string | any} 默认返回日期 `ticks` 的字符串
|
||||||
*/
|
*/
|
||||||
export function getDateValue(element, formatter) {
|
export function getDateValue(element, formatter) {
|
||||||
const date = element?.valueAsDate;
|
const date = element?.valueAsDate;
|
||||||
if (date instanceof Date && !isNaN(date)) {
|
if (date instanceof Date && !isNaN(date)) {
|
||||||
const year = date.getFullYear();
|
const year = date.getUTCFullYear();
|
||||||
if (year < 1900 || year > 9999) {
|
if (year < 1900 || year > 9999) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if (typeof formatter === 'function') {
|
if (formatter != null) {
|
||||||
return formatter(date);
|
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getUTCDate()).padStart(2, '0');
|
||||||
|
// 使外部 formatter 不需要再处理 `getUTCDate` 亦或是 `getDate`
|
||||||
|
const localDate = new Date(`${year}-${month}-${day}T00:00:00`);
|
||||||
|
if (typeof formatter === 'function') {
|
||||||
|
return formatter(localDate);
|
||||||
|
}
|
||||||
|
return formatDate(localDate, formatter);
|
||||||
}
|
}
|
||||||
return String(date.getTime() * 1e4 + 621355968e9);
|
return String(date.getTime() * 1e4 + 621355968e9);
|
||||||
}
|
}
|
||||||
@ -135,7 +325,43 @@ export function getDateValue(element, formatter) {
|
|||||||
*/
|
*/
|
||||||
export class DateSelector {
|
export class DateSelector {
|
||||||
_var = {
|
_var = {
|
||||||
options: null
|
/**
|
||||||
|
* @type {HTMLInputElement}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
el: null,
|
||||||
|
options: {
|
||||||
|
/**
|
||||||
|
* @type {boolean?}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
enabled: true,
|
||||||
|
/**
|
||||||
|
* @type {string?}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
minDate: null,
|
||||||
|
/**
|
||||||
|
* @type {string?}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
maxDate: null,
|
||||||
|
/**
|
||||||
|
* @type {number?}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
tabIndex: null,
|
||||||
|
/**
|
||||||
|
* @type {string?}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
className: null,
|
||||||
|
/**
|
||||||
|
* @type {DateFormatterCallback?}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
valueFormatter: null
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +388,7 @@ export class DateSelector {
|
|||||||
* minDate: '2020-01-01',
|
* minDate: '2020-01-01',
|
||||||
* maxDate: '2024-02-01',
|
* maxDate: '2024-02-01',
|
||||||
* valueFormatter: (date) => {
|
* valueFormatter: (date) => {
|
||||||
* return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; // 2024/1/31
|
* return `${date.getUTCFullYear()}/${date.getUTCMonth() + 1}/${date.getUTCDate()}`; // 2024/1/31
|
||||||
* }
|
* }
|
||||||
* };
|
* };
|
||||||
* const dateSelectorFrom = new DateSelector(opts);
|
* const dateSelectorFrom = new DateSelector(opts);
|
||||||
@ -185,6 +411,10 @@ export class DateSelector {
|
|||||||
create(element) {
|
create(element) {
|
||||||
const opts = this._var.options;
|
const opts = this._var.options;
|
||||||
const el = createDateInput(opts.minDate, opts.maxDate, element);
|
const el = createDateInput(opts.minDate, opts.maxDate, element);
|
||||||
|
isPositive(opts.tabIndex) && el.setAttribute('tabindex', opts.tabIndex);
|
||||||
|
if (!nullOrEmpty(opts.className)) {
|
||||||
|
el.classList.add(opts.className);
|
||||||
|
}
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
el.disabled = opts.enabled === false;
|
el.disabled = opts.enabled === false;
|
||||||
}
|
}
|
||||||
@ -246,9 +476,14 @@ export class DateSelector {
|
|||||||
this._var.options.maxDate = date;
|
this._var.options.maxDate = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {Date} date
|
||||||
|
* @returns {Date | any}
|
||||||
|
*/
|
||||||
_getDate(date) {
|
_getDate(date) {
|
||||||
if (date instanceof Date && !isNaN(date)) {
|
if (date instanceof Date && !isNaN(date)) {
|
||||||
const year = date.getFullYear();
|
const year = date.getUTCFullYear();
|
||||||
if (year < 1900 || year > 9999) {
|
if (year < 1900 || year > 9999) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
6
lib/ui/dropdown.d.ts
vendored
@ -16,6 +16,8 @@ export interface DropdownOptions {
|
|||||||
valueKey?: string;
|
valueKey?: string;
|
||||||
/** 源码显示的关键字,默认值 `html` */
|
/** 源码显示的关键字,默认值 `html` */
|
||||||
htmlKey?: string;
|
htmlKey?: string;
|
||||||
|
/** 源码显示的模板函数 */
|
||||||
|
htmlTemplate?: (item: DropdownItem) => HTMLElement;
|
||||||
/** 最大输入长度,默认值 `500` */
|
/** 最大输入长度,默认值 `500` */
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
/** 是否允许多选 */
|
/** 是否允许多选 */
|
||||||
@ -117,8 +119,10 @@ export class Dropdown {
|
|||||||
* 选中某个条目
|
* 选中某个条目
|
||||||
* @param selected 选中的值
|
* @param selected 选中的值
|
||||||
* @param silence 是否静默选中,即不触发 {@linkcode onSelected} 事件
|
* @param silence 是否静默选中,即不触发 {@linkcode onSelected} 事件
|
||||||
|
* @param ignoreCase 是否不区分大小写
|
||||||
|
* @returns 是否选中
|
||||||
*/
|
*/
|
||||||
select(selected: string, silence?: boolean): void;
|
select(selected: string, silence?: boolean, ignoreCase?: boolean): boolean | undefined;
|
||||||
/**
|
/**
|
||||||
* 选中条目列表
|
* 选中条目列表
|
||||||
* @param selectedlist 选中的值的列表
|
* @param selectedlist 选中的值的列表
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// import { r, global, contains, isPositive, nullOrEmpty } from "../utility";
|
|
||||||
import './css/dropdown.scss';
|
import './css/dropdown.scss';
|
||||||
import { r } from "../utility/lgres";
|
import { r as lang } from "../utility/lgres";
|
||||||
import { contains, nullOrEmpty } from "../utility/strings";
|
import { contains, nullOrEmpty } from "../utility/strings";
|
||||||
import { global, isPositive } from "../utility";
|
import { global, isPositive, throttle } from "../utility";
|
||||||
import { createElement } from "../functions";
|
import { createElement } from "../functions";
|
||||||
import { createCheckbox } from "./checkbox";
|
import { createCheckbox } from "./checkbox";
|
||||||
import { createIcon } from "./icon"
|
import { createIcon } from "./icon"
|
||||||
@ -10,6 +9,7 @@ import { createIcon } from "./icon"
|
|||||||
const SymbolDropdown = Symbol.for('ui-dropdown');
|
const SymbolDropdown = Symbol.for('ui-dropdown');
|
||||||
const DropdownItemHeight = 30;
|
const DropdownItemHeight = 30;
|
||||||
|
|
||||||
|
let r = lang;
|
||||||
let dropdownGlobal = global[SymbolDropdown];
|
let dropdownGlobal = global[SymbolDropdown];
|
||||||
|
|
||||||
if (dropdownGlobal == null) {
|
if (dropdownGlobal == null) {
|
||||||
@ -52,8 +52,13 @@ if (dropdownGlobal == null) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectItems(label, itemlist, htmlkey, textkey) {
|
function selectItems(label, itemlist, template, htmlkey, textkey) {
|
||||||
const htmls = itemlist.map(it => it[htmlkey]);
|
let htmls;
|
||||||
|
if (typeof template === 'function') {
|
||||||
|
htmls = itemlist.map(it => template.call(this, it));
|
||||||
|
} else {
|
||||||
|
htmls = itemlist.map(it => it[htmlkey]);
|
||||||
|
}
|
||||||
if (htmls.some(it => it instanceof HTMLElement)) {
|
if (htmls.some(it => it instanceof HTMLElement)) {
|
||||||
label.replaceChildren(...htmls.filter(it => it != null).map(it => it.cloneNode(true)));
|
label.replaceChildren(...htmls.filter(it => it != null).map(it => it.cloneNode(true)));
|
||||||
} else {
|
} else {
|
||||||
@ -82,12 +87,24 @@ function filterSource(searchkeys, textkey, key, source) {
|
|||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getValue(it, valuekey, textkey) {
|
||||||
|
if (it == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const value = it[valuekey];
|
||||||
|
if (value == null || value === '') {
|
||||||
|
return it[textkey];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下拉列表参数对象
|
* 下拉列表参数对象
|
||||||
* @typedef DropdownOptions
|
* @typedef DropdownOptions
|
||||||
* @property {string} [textKey=text] - 文本关键字
|
* @property {string} [textKey=text] - 文本关键字
|
||||||
* @property {string} [valueKey=value] - 值关键字
|
* @property {string} [valueKey=value] - 值关键字
|
||||||
* @property {string} [htmlKey=html] - 源码显示的关键字
|
* @property {string} [htmlKey=html] - 源码显示的关键字
|
||||||
|
* @property {Function} [htmlTemplate] - 模板创建函数
|
||||||
* @property {number} [maxLength=500] - 最大输入长度
|
* @property {number} [maxLength=500] - 最大输入长度
|
||||||
* @property {boolean} [multiSelect] - 是否允许多选
|
* @property {boolean} [multiSelect] - 是否允许多选
|
||||||
* @property {string} [selected] - 选中值
|
* @property {string} [selected] - 选中值
|
||||||
@ -124,12 +141,18 @@ export class Dropdown {
|
|||||||
onCollapsed;
|
onCollapsed;
|
||||||
|
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
options.searchPlaceholder ??= r('searchHolder', 'Search...');
|
|
||||||
options.textKey ??= 'text';
|
options.textKey ??= 'text';
|
||||||
options.valueKey ??= 'value';
|
options.valueKey ??= 'value';
|
||||||
options.htmlKey ??= 'html';
|
options.htmlKey ??= 'html';
|
||||||
options.maxLength ??= 500;
|
options.maxLength ??= 500;
|
||||||
this._var.options = options;
|
this._var.options = options;
|
||||||
|
const getText = options.getText;
|
||||||
|
if (typeof getText === 'function') {
|
||||||
|
r = getText;
|
||||||
|
} else if (typeof GetTextByKey === 'function') {
|
||||||
|
r = GetTextByKey;
|
||||||
|
}
|
||||||
|
options.searchPlaceholder ??= r('searchHolder', 'Search...');
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
@ -138,6 +161,9 @@ export class Dropdown {
|
|||||||
// wrapper
|
// wrapper
|
||||||
const wrapper = createElement('div', 'ui-drop-wrapper');
|
const wrapper = createElement('div', 'ui-drop-wrapper');
|
||||||
const dropId = String(Math.random()).substring(2);
|
const dropId = String(Math.random()).substring(2);
|
||||||
|
if (options.wrapper instanceof HTMLElement) {
|
||||||
|
options.wrapper.dataset.dropId = dropId;
|
||||||
|
}
|
||||||
wrapper.dataset.dropId = dropId;
|
wrapper.dataset.dropId = dropId;
|
||||||
dropdownGlobal[dropId] = this;
|
dropdownGlobal[dropId] = this;
|
||||||
this._var.wrapper = wrapper;
|
this._var.wrapper = wrapper;
|
||||||
@ -156,6 +182,7 @@ export class Dropdown {
|
|||||||
const source = this.source;
|
const source = this.source;
|
||||||
const count = source.length;
|
const count = source.length;
|
||||||
const valuekey = this._var.options.valueKey;
|
const valuekey = this._var.options.valueKey;
|
||||||
|
const textkey = this._var.options.textKey;
|
||||||
let index = source?.indexOf(this._var.selected);
|
let index = source?.indexOf(this._var.selected);
|
||||||
if (isNaN(index) || index < -1) {
|
if (isNaN(index) || index < -1) {
|
||||||
index = -1;
|
index = -1;
|
||||||
@ -177,7 +204,7 @@ export class Dropdown {
|
|||||||
index = count - 1;
|
index = count - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const target = source[index]?.[valuekey];
|
const target = getValue(source[index], valuekey, textkey);
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
this.select(target);
|
this.select(target);
|
||||||
}
|
}
|
||||||
@ -204,7 +231,9 @@ export class Dropdown {
|
|||||||
let label;
|
let label;
|
||||||
if (options.input) {
|
if (options.input) {
|
||||||
label = createElement('input', 'ui-drop-text');
|
label = createElement('input', 'ui-drop-text');
|
||||||
label.setAttribute('type', 'text');
|
label.type = 'text';
|
||||||
|
label.autocomplete = 'off';
|
||||||
|
label.draggable = false;
|
||||||
options.placeholder && label.setAttribute('placeholder', options.placeholder);
|
options.placeholder && label.setAttribute('placeholder', options.placeholder);
|
||||||
isPositive(options.maxLength) && label.setAttribute('maxlength', options.maxLength);
|
isPositive(options.maxLength) && label.setAttribute('maxlength', options.maxLength);
|
||||||
isPositive(options.tabIndex) && label.setAttribute('tabindex', options.tabIndex);
|
isPositive(options.tabIndex) && label.setAttribute('tabindex', options.tabIndex);
|
||||||
@ -240,6 +269,8 @@ export class Dropdown {
|
|||||||
|
|
||||||
get multiSelect() { return this._var.options.multiSelect }
|
get multiSelect() { return this._var.options.multiSelect }
|
||||||
|
|
||||||
|
get ignoreAll() { return this._var.options.ignoreAll }
|
||||||
|
|
||||||
get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }
|
get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }
|
||||||
|
|
||||||
set disabled(flag) {
|
set disabled(flag) {
|
||||||
@ -271,7 +302,14 @@ export class Dropdown {
|
|||||||
if (!Array.isArray(list)) {
|
if (!Array.isArray(list)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._var.source = list;
|
const valuekey = this._var.options.valueKey;
|
||||||
|
function reduceItems(list, id, level = 0) {
|
||||||
|
if (!Array.isArray(list)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return list.reduce((array, item) => [...array, { __p: id, __level: level, ...item }, ...reduceItems(item.children, item[valuekey], level + 1)], []);
|
||||||
|
}
|
||||||
|
this._var.source = reduceItems(list);
|
||||||
if (this._expanded) {
|
if (this._expanded) {
|
||||||
setTimeout(() => this._dropdown(), 120);
|
setTimeout(() => this._dropdown(), 120);
|
||||||
}
|
}
|
||||||
@ -281,18 +319,22 @@ export class Dropdown {
|
|||||||
|
|
||||||
get selectedList() { return this._var.selectedList || [] }
|
get selectedList() { return this._var.selectedList || [] }
|
||||||
|
|
||||||
select(selected, silence) {
|
select(selected, silence, ignoreCase) {
|
||||||
if (typeof selected !== 'string') {
|
if (typeof selected !== 'string') {
|
||||||
selected = String(selected);
|
selected = String(selected);
|
||||||
}
|
}
|
||||||
|
if (ignoreCase) {
|
||||||
|
selected = selected.toLowerCase();
|
||||||
|
}
|
||||||
if (this._var.lastSelected === selected) {
|
if (this._var.lastSelected === selected) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
this._var.lastSelected = selected;
|
this._var.lastSelected = selected;
|
||||||
const valuekey = this._var.options.valueKey;
|
const valuekey = this._var.options.valueKey;
|
||||||
const textkey = this._var.options.textKey;
|
const textkey = this._var.options.textKey;
|
||||||
|
const template = this._var.options.htmlTemplate;
|
||||||
const htmlkey = this._var.options.htmlKey;
|
const htmlkey = this._var.options.htmlKey;
|
||||||
let item = this.source.find(it => String(it[valuekey]) === selected);
|
let item = this.source.find(it => (ignoreCase ? String(getValue(it, valuekey, textkey)).toLowerCase() : String(getValue(it, valuekey, textkey))) === selected);
|
||||||
if (this._var.options.input) {
|
if (this._var.options.input) {
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = { [valuekey]: selected };
|
item = { [valuekey]: selected };
|
||||||
@ -308,7 +350,12 @@ export class Dropdown {
|
|||||||
this._var.label.innerText = ' ';
|
this._var.label.innerText = ' ';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const html = item[htmlkey];
|
let html;
|
||||||
|
if (typeof template === 'function') {
|
||||||
|
html = template.call(this, item);
|
||||||
|
} else {
|
||||||
|
html = item[htmlkey];
|
||||||
|
}
|
||||||
if (html instanceof HTMLElement) {
|
if (html instanceof HTMLElement) {
|
||||||
this._var.label.replaceChildren(html.cloneNode(true));
|
this._var.label.replaceChildren(html.cloneNode(true));
|
||||||
} else if (typeof html === 'string') {
|
} else if (typeof html === 'string') {
|
||||||
@ -321,27 +368,35 @@ export class Dropdown {
|
|||||||
this._var.label.innerText = text;
|
this._var.label.innerText = text;
|
||||||
}
|
}
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
const val = selected.replace(/"/g, '\\"');
|
for (let li of this._var.container.querySelectorAll('li[data-value]')) {
|
||||||
const li = this._var.container.querySelector(`li[data-value="${val}"]`);
|
if ((ignoreCase ? li.dataset.value.toLowerCase() : li.dataset.value) === selected) {
|
||||||
if (li != null) {
|
li.classList.add('selected');
|
||||||
li.classList.add('selected');
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// const val = selected.replace(/"/g, '\\"');
|
||||||
|
// const li = this._var.container.querySelector(`li[data-value="${val}"]`);
|
||||||
|
// if (li != null) {
|
||||||
|
// li.classList.add('selected');
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._var.selected = item;
|
this._var.selected = item;
|
||||||
if (!silence && typeof this.onSelected === 'function') {
|
if (!silence && typeof this.onSelected === 'function') {
|
||||||
this.onSelected(item);
|
this.onSelected(item);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectlist(selectedlist, silence) {
|
selectlist(selectedlist, silence) {
|
||||||
const source = this.source;
|
const source = this.source;
|
||||||
const valuekey = this._var.options.valueKey;
|
const valuekey = this._var.options.valueKey;
|
||||||
const textkey = this._var.options.textKey;
|
const textkey = this._var.options.textKey;
|
||||||
|
const template = this._var.options.htmlTemplate;
|
||||||
const htmlkey = this._var.options.htmlKey;
|
const htmlkey = this._var.options.htmlKey;
|
||||||
const itemlist = selectedlist.map(a => {
|
const itemlist = selectedlist.map(a => {
|
||||||
const v = typeof a === 'string' ? a : String(a);
|
const v = typeof a === 'string' ? a : String(a);
|
||||||
let item = source.find(it => String(it[valuekey]) === v);
|
let item = source.find(it => String(getValue(it, valuekey, textkey)) === v);
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = {
|
item = {
|
||||||
[valuekey]: v,
|
[valuekey]: v,
|
||||||
@ -352,10 +407,10 @@ export class Dropdown {
|
|||||||
});
|
});
|
||||||
if (itemlist.length === 0) {
|
if (itemlist.length === 0) {
|
||||||
this._var.selectedList = null;
|
this._var.selectedList = null;
|
||||||
this._var.label.innerText = none;
|
this._var.label.innerText = r('none', '( None )');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
selectItems(this._var.label, itemlist, htmlkey, textkey);
|
selectItems(this._var.label, itemlist, template, htmlkey, textkey);
|
||||||
this._var.selectedList = itemlist;
|
this._var.selectedList = itemlist;
|
||||||
if (!silence && typeof this.onSelectedList === 'function') {
|
if (!silence && typeof this.onSelectedList === 'function') {
|
||||||
this.onSelectedList(itemlist);
|
this.onSelectedList(itemlist);
|
||||||
@ -373,7 +428,8 @@ export class Dropdown {
|
|||||||
if (!options.input && options.search) {
|
if (!options.input && options.search) {
|
||||||
const search = createElement('div', 'ui-drop-search');
|
const search = createElement('div', 'ui-drop-search');
|
||||||
const input = createElement('input');
|
const input = createElement('input');
|
||||||
input.setAttribute('type', 'text');
|
input.type = 'text';
|
||||||
|
input.className = 'ui-input';
|
||||||
isPositive(options.tabIndex) && input.setAttribute('tabindex', options.tabIndex);
|
isPositive(options.tabIndex) && input.setAttribute('tabindex', options.tabIndex);
|
||||||
!nullOrEmpty(options.searchPlaceholder) && input.setAttribute('placeholder', options.searchPlaceholder);
|
!nullOrEmpty(options.searchPlaceholder) && input.setAttribute('placeholder', options.searchPlaceholder);
|
||||||
input.addEventListener('input', e => {
|
input.addEventListener('input', e => {
|
||||||
@ -385,7 +441,8 @@ export class Dropdown {
|
|||||||
panel.appendChild(search);
|
panel.appendChild(search);
|
||||||
}
|
}
|
||||||
// list
|
// list
|
||||||
const list = createElement('ul', 'ui-drop-list');
|
const list = createElement('div', 'ui-drop-list');
|
||||||
|
list.addEventListener('scroll', e => throttle(this._onlistscroll, 10, this, list, e.target.scrollTop), { passive: true });
|
||||||
if (!this.multiSelect) {
|
if (!this.multiSelect) {
|
||||||
list.addEventListener('click', e => {
|
list.addEventListener('click', e => {
|
||||||
let li = e.target;
|
let li = e.target;
|
||||||
@ -457,45 +514,148 @@ export class Dropdown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel.classList.add('active');
|
panel.classList.add('active');
|
||||||
|
this._var.dropTop = 0;
|
||||||
|
panel.querySelector('.ui-drop-list').dispatchEvent(new Event('scroll'));
|
||||||
} else {
|
} else {
|
||||||
panel.classList.remove('active');
|
panel.classList.remove('active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onlistscroll(list, top) {
|
||||||
|
const offset = (this.multiSelect && !this.ignoreAll) ? DropdownItemHeight : 0;
|
||||||
|
top -= (top % (DropdownItemHeight * 2)) + offset;
|
||||||
|
if (top < 0) {
|
||||||
|
top = 0;
|
||||||
|
} else {
|
||||||
|
let bottomTop = this._var.dropHeight - (20 * DropdownItemHeight);
|
||||||
|
if (bottomTop < 0) {
|
||||||
|
bottomTop = 0;
|
||||||
|
}
|
||||||
|
if (top > bottomTop) {
|
||||||
|
top = bottomTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this._var.dropTop !== top) {
|
||||||
|
this._var.dropTop = top;
|
||||||
|
const startIndex = top / DropdownItemHeight;
|
||||||
|
let array = this._var.currentSource;
|
||||||
|
if (startIndex + 20 < array.length) {
|
||||||
|
array = array.slice(startIndex, startIndex + 20);
|
||||||
|
} else {
|
||||||
|
array = array.slice(-20);
|
||||||
|
}
|
||||||
|
const content = list.querySelector('.drop-content');
|
||||||
|
content.replaceChildren();
|
||||||
|
this._dofilllist(content, array);
|
||||||
|
content.style.top = `${top + offset}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_filllist(source) {
|
_filllist(source) {
|
||||||
const list = this._var.container.querySelector('.ui-drop-list');
|
const list = this._var.container.querySelector('.ui-drop-list');
|
||||||
list.replaceChildren();
|
list.replaceChildren();
|
||||||
const multiselect = this.multiSelect;
|
const height = source.length * DropdownItemHeight;
|
||||||
const allchecked = this._var.allChecked;
|
this._var.dropHeight = height;
|
||||||
if (multiselect) {
|
this._var.currentSource = source;
|
||||||
|
const holder = createElement('div', 'drop-holder');
|
||||||
|
holder.style.height = `${height}px`;
|
||||||
|
const content = createElement('div', 'drop-content');
|
||||||
|
if (this.multiSelect && !this.ignoreAll) {
|
||||||
list.appendChild(
|
list.appendChild(
|
||||||
createElement('li', null,
|
createElement('li', null,
|
||||||
createCheckbox({
|
createCheckbox({
|
||||||
label: r('allItem', '( All )'),
|
label: r('allItem', '( All )'),
|
||||||
checked: allchecked,
|
checked: this._var.allChecked,
|
||||||
customAttributes: { 'isall': '1' },
|
customAttributes: { 'isall': '1' },
|
||||||
onchange: e => this._triggerselect(e.target)
|
onchange: e => this._triggerselect(e.target)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
content.style.top = `${DropdownItemHeight}px`;
|
||||||
|
} else {
|
||||||
|
content.style.top = '0px';
|
||||||
}
|
}
|
||||||
// TODO: virtual mode
|
const multiselect = this.multiSelect;
|
||||||
const valuekey = this._var.options.valueKey;
|
const valuekey = this._var.options.valueKey;
|
||||||
const textkey = this._var.options.textKey;
|
const textkey = this._var.options.textKey;
|
||||||
|
const allchecked = this._var.allChecked;
|
||||||
|
const selectedlist = this.selectedList;
|
||||||
|
source.forEach((item, i) => {
|
||||||
|
let val = getValue(item, valuekey, textkey);
|
||||||
|
if (typeof val !== 'string') {
|
||||||
|
val = String(val);
|
||||||
|
}
|
||||||
|
if (multiselect) {
|
||||||
|
const selected = selectedlist.some(s => String(getValue(s, valuekey, textkey)) === val);
|
||||||
|
if (allchecked || selected) {
|
||||||
|
item.__checked = 1;
|
||||||
|
} else {
|
||||||
|
const indeterminate = selectedlist.some(s => this._contains(String(getValue(s, valuekey, textkey)), item, valuekey, textkey));
|
||||||
|
if (indeterminate) {
|
||||||
|
item.__checked = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (source.length > 20) {
|
||||||
|
source = source.slice(0, 20);
|
||||||
|
}
|
||||||
|
const scrolled = this._dofilllist(content, source);
|
||||||
|
list.append(holder, content);
|
||||||
|
if (scrolled != null) {
|
||||||
|
setTimeout(() => list.scrollTop = scrolled, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_contains(it, item, valuekey, textkey) {
|
||||||
|
if (item.children?.length > 0) {
|
||||||
|
for (let t of item.children) {
|
||||||
|
if (it === getValue(t, valuekey, textkey)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this._contains(it, t, valuekey, textkey)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dofilllist(content, array) {
|
||||||
|
const multiselect = this.multiSelect;
|
||||||
|
const valuekey = this._var.options.valueKey;
|
||||||
|
const textkey = this._var.options.textKey;
|
||||||
|
const template = this._var.options.htmlTemplate;
|
||||||
const htmlkey = this._var.options.htmlKey;
|
const htmlkey = this._var.options.htmlKey;
|
||||||
const selected = this.selected;
|
const selected = this.selected;
|
||||||
const selectedlist = this.selectedList;
|
|
||||||
let scrolled;
|
let scrolled;
|
||||||
source.slice(0, 200).forEach((item, i) => {
|
array.forEach((item, i) => {
|
||||||
let val = item[valuekey];
|
let val = getValue(item, valuekey, textkey);
|
||||||
if (typeof val !== 'string') {
|
if (typeof val !== 'string') {
|
||||||
val = String(val);
|
val = String(val);
|
||||||
}
|
}
|
||||||
const li = createElement('li');
|
const li = createElement('li');
|
||||||
li.dataset.value = val;
|
li.dataset.value = val;
|
||||||
li.setAttribute('title', item[textkey]);
|
li.title = item[textkey];
|
||||||
|
if (item.__level > 0) {
|
||||||
|
li.style.marginLeft = `${item.__level * 24}px`;
|
||||||
|
}
|
||||||
|
const wrapper = createElement('span', 'li-wrapper',
|
||||||
|
createElement('span', span => {
|
||||||
|
// events
|
||||||
|
span.className = 'ui-expandor';
|
||||||
|
},
|
||||||
|
createIcon('fa-light', 'caret-down')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
li.appendChild(wrapper);
|
||||||
let label;
|
let label;
|
||||||
const html = item[htmlkey];
|
let html;
|
||||||
|
if (typeof template === 'function') {
|
||||||
|
html = template.call(this, item);
|
||||||
|
} else {
|
||||||
|
html = item[htmlkey];
|
||||||
|
}
|
||||||
if (html instanceof HTMLElement) {
|
if (html instanceof HTMLElement) {
|
||||||
label = html;
|
label = html;
|
||||||
} else if (typeof html === 'string') {
|
} else if (typeof html === 'string') {
|
||||||
@ -503,77 +663,79 @@ export class Dropdown {
|
|||||||
label.innerHTML = html;
|
label.innerHTML = html;
|
||||||
}
|
}
|
||||||
if (multiselect) {
|
if (multiselect) {
|
||||||
const selected = selectedlist.some(s => String(s[valuekey]) === val);
|
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
label = createElement('span');
|
label = createElement('span');
|
||||||
label.innerText = item[textkey];
|
label.innerText = item[textkey];
|
||||||
}
|
}
|
||||||
const box = createCheckbox({
|
const box = createCheckbox({
|
||||||
label,
|
label,
|
||||||
checked: allchecked || selected,
|
checked: item.__checked === 1,
|
||||||
|
indeterminate: item.__checked === 2,
|
||||||
customAttributes: {
|
customAttributes: {
|
||||||
'class': 'dataitem',
|
'class': 'dataitem',
|
||||||
'data-value': val
|
'data-value': val
|
||||||
},
|
},
|
||||||
onchange: e => this._triggerselect(e.target)
|
onchange: e => this._triggerselect(e.target, item)
|
||||||
});
|
});
|
||||||
li.appendChild(box);
|
wrapper.appendChild(box);
|
||||||
} else {
|
} else {
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
li.innerText = item[textkey];
|
label = createElement('span');
|
||||||
} else {
|
label.innerHTML = item[textkey];
|
||||||
li.appendChild(label);
|
|
||||||
}
|
}
|
||||||
|
wrapper.appendChild(label);
|
||||||
if (selected != null && String(selected[valuekey]) === val) {
|
if (selected != null && String(selected[valuekey]) === val) {
|
||||||
scrolled = DropdownItemHeight * i;
|
scrolled = DropdownItemHeight * i;
|
||||||
li.classList.add('selected');
|
li.classList.add('selected');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.appendChild(li);
|
content.appendChild(li);
|
||||||
});
|
});
|
||||||
if (scrolled != null) {
|
return scrolled;
|
||||||
setTimeout(() => list.scrollTop = scrolled, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_triggerselect(checkbox) {
|
_triggerselect(checkbox, item) {
|
||||||
let list;
|
let list;
|
||||||
const valuekey = this._var.options.valueKey;
|
const valuekey = this._var.options.valueKey;
|
||||||
const textkey = this._var.options.textKey;
|
const textkey = this._var.options.textKey;
|
||||||
|
const template = this._var.options.htmlTemplate;
|
||||||
const htmlkey = this._var.options.htmlKey;
|
const htmlkey = this._var.options.htmlKey;
|
||||||
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);
|
||||||
list = [];
|
list = [];
|
||||||
} else if (checkbox.checked) {
|
|
||||||
if (this._var.container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
|
|
||||||
this._var.allChecked = true;
|
|
||||||
this._var.container.querySelector('input[isall="1"]').checked = true;
|
|
||||||
list = [];
|
|
||||||
} else {
|
|
||||||
const source = this.source;
|
|
||||||
list = [...this._var.container.querySelectorAll('input.dataitem:checked')]
|
|
||||||
.map(c => {
|
|
||||||
const v = c.dataset.value;
|
|
||||||
return source.find(it => String(it[valuekey]) === v);
|
|
||||||
})
|
|
||||||
.filter(it => it != null);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const val = checkbox.dataset.value;
|
item.__checked = checkbox.indeterminate ? 2 : checkbox.checked ? 1 : 0;
|
||||||
if (this._var.allChecked) {
|
const all = this._var.container.querySelector('input[isall="1"]');
|
||||||
this._var.allChecked = false;
|
if (checkbox.checked) {
|
||||||
this._var.container.querySelector('input[isall="1"]').checked = false;
|
const source = this.source;
|
||||||
list = this.source.filter(it => String(it[valuekey]) !== val);
|
if (source.some(it => it.__checked) == null) {
|
||||||
|
this._var.allChecked = true;
|
||||||
|
if (all != null) {
|
||||||
|
all.checked = true;
|
||||||
|
}
|
||||||
|
list = [];
|
||||||
|
} else {
|
||||||
|
list = source.filter(it => it.__checked);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
list = this.selectedList.filter(it => String(it[valuekey]) !== val);
|
const val = checkbox.dataset.value;
|
||||||
|
if (this._var.allChecked) {
|
||||||
|
this._var.allChecked = false;
|
||||||
|
if (all != null) {
|
||||||
|
all.checked = false;
|
||||||
|
}
|
||||||
|
list = this.source.filter(it => String(getValue(it, valuekey, textkey)) !== val);
|
||||||
|
} else {
|
||||||
|
list = this.selectedList.filter(it => String(getValue(it, valuekey, textkey)) !== val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this._var.allChecked) {
|
if (this._var.allChecked) {
|
||||||
this._var.label.innerText = r('allItem', '( All )');
|
this._var.label.innerText = r('allItem', '( All )');
|
||||||
} else {
|
} else {
|
||||||
selectItems(this._var.label, list, htmlkey, textkey);
|
selectItems(this._var.label, list, template, htmlkey, textkey);
|
||||||
}
|
}
|
||||||
this._var.selectedList = list;
|
this._var.selectedList = list;
|
||||||
if (typeof this.onSelectedList === 'function') {
|
if (typeof this.onSelectedList === 'function') {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { global } from "../../utility";
|
import { global } from "../../utility";
|
||||||
import { createElement } from "../../functions";
|
import { createElement } from "../../functions";
|
||||||
import { createIcon } from "../icon";
|
import { createIcon } from "../icon";
|
||||||
import { createCheckbox } from "../checkbox";
|
import { createCheckbox, createRadiobox } from "../checkbox";
|
||||||
// import { setTooltip } from "../tooltip";
|
// import { setTooltip } from "../tooltip";
|
||||||
import { Dropdown } from "../dropdown";
|
import { Dropdown } from "../dropdown";
|
||||||
import { convertCssStyle } from "../extension";
|
import { convertCssStyle } from "../extension";
|
||||||
@ -36,6 +36,22 @@ export class GridColumn {
|
|||||||
* @see 更多例子参考 {@linkcode GridInputColumn} {@linkcode GridDateColumn} 中的代码实现
|
* @see 更多例子参考 {@linkcode GridInputColumn} {@linkcode GridDateColumn} 中的代码实现
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记该类型是否可编辑
|
||||||
|
* @member
|
||||||
|
* @name GridColumn.canEdit
|
||||||
|
* @readonly
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记该类型是否支持列头批量操作
|
||||||
|
* @member
|
||||||
|
* @name GridColumn.headerEditing
|
||||||
|
* @readonly
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建显示单元格时调用的方法
|
* 创建显示单元格时调用的方法
|
||||||
* @param {GridColumnDefinition} col - 列定义对象
|
* @param {GridColumnDefinition} col - 列定义对象
|
||||||
@ -55,10 +71,11 @@ export class GridColumn {
|
|||||||
* 更多例子参考代码中 {@linkcode GridDropdownColumn} 的实现。
|
* 更多例子参考代码中 {@linkcode GridDropdownColumn} 的实现。
|
||||||
* @method
|
* @method
|
||||||
* @name GridColumn.createEdit
|
* @name GridColumn.createEdit
|
||||||
* @param {Function} trigger - 编辑事件回调函数,`e` 参数会传递给 [getValue]{@linkcode GridColumn.getValue} 方法
|
* @param {Function} trigger - 编辑事件回调函数
|
||||||
|
* @param {any} trigger.e - 该参数会传递给 [getValue]{@linkcode GridColumn.getValue} 方法
|
||||||
* @param {GridColumnDefinition} col - 列定义对象
|
* @param {GridColumnDefinition} col - 列定义对象
|
||||||
* @param {HTMLElement} container - 父容器元素
|
* @param {HTMLElement} [container] - 父容器元素
|
||||||
* @param {GridItemWrapper} wrapper - 行包装对象,其 `values` 属性为行数据对象
|
* @param {GridItemWrapper} [wrapper] - 行包装对象,其 `values` 属性为行数据对象
|
||||||
* @returns {HTMLElement} 返回创建的编辑状态的单元格元素
|
* @returns {HTMLElement} 返回创建的编辑状态的单元格元素
|
||||||
* @virtual
|
* @virtual
|
||||||
*/
|
*/
|
||||||
@ -72,6 +89,15 @@ export class GridColumn {
|
|||||||
* @virtual
|
* @virtual
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用于判断文本大小的元素
|
||||||
|
* @method
|
||||||
|
* @name GridColumn.getElement
|
||||||
|
* @param {HTMLElement} element - 单元格主内容元素
|
||||||
|
* @returns {HTMLElement} 返回文本元素
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取编辑状态单元格值时调用的方法
|
* 获取编辑状态单元格值时调用的方法
|
||||||
* @method
|
* @method
|
||||||
@ -122,6 +148,7 @@ export class GridColumn {
|
|||||||
* 设置单元格可用性时调用的方法
|
* 设置单元格可用性时调用的方法
|
||||||
* @param {HTMLElement} element - 单元格元素
|
* @param {HTMLElement} element - 单元格元素
|
||||||
* @param {boolean} enabled - 启用值,为 `false` 时代表禁用
|
* @param {boolean} enabled - 启用值,为 `false` 时代表禁用
|
||||||
|
* @param {boolean} editing - 是否处于编辑状态
|
||||||
* @virtual
|
* @virtual
|
||||||
*/
|
*/
|
||||||
static setEnabled(element, enabled) {
|
static setEnabled(element, enabled) {
|
||||||
@ -132,7 +159,16 @@ export class GridColumn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单元格离开编辑元素时触发,需要由行包装对象的 `__editing` 来确定是否触发。
|
* 单元格编辑状态发生改变时调用的方法
|
||||||
|
* @method
|
||||||
|
* @name GridColumn.setEditing
|
||||||
|
* @param {HTMLElement} element - 单元格元素
|
||||||
|
* @param {boolean} editing - 是否处于编辑状态
|
||||||
|
* @virtual
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元格离开编辑元素时调用的方法,需要由行包装对象的 `__editing` 来确定是否触发。
|
||||||
* @method
|
* @method
|
||||||
* @name GridColumn.leaveEdit
|
* @name GridColumn.leaveEdit
|
||||||
* @param {HTMLElement} element - 单元格元素
|
* @param {HTMLElement} element - 单元格元素
|
||||||
@ -143,7 +179,33 @@ export class GridColumn {
|
|||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
static toString() { return '[object Column]' }
|
static toString() { return 'GridCommon' }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
* @param {string} key
|
||||||
|
* @param {GridItemWrapper} wrapper
|
||||||
|
* @param {any} value
|
||||||
|
*/
|
||||||
|
static _changeValue(key, wrapper, value) {
|
||||||
|
const val = wrapper.values[key] ?? null;
|
||||||
|
const hasValue = val != null && Object.prototype.hasOwnProperty.call(val, 'Value');
|
||||||
|
if (wrapper.__editing == null) {
|
||||||
|
wrapper.__editing = {
|
||||||
|
[key]: hasValue ? val.Value : val
|
||||||
|
}
|
||||||
|
} else if (!Object.prototype.hasOwnProperty.call(wrapper.__editing, key)) {
|
||||||
|
wrapper.__editing[key] = hasValue ? val.Value : val;
|
||||||
|
}
|
||||||
|
if (hasValue) {
|
||||||
|
val.Value = value;
|
||||||
|
if (Object.prototype.hasOwnProperty.call(val, 'DisplayValue')) {
|
||||||
|
val.DisplayValue = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wrapper.values[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,27 +217,21 @@ export class GridColumn {
|
|||||||
*/
|
*/
|
||||||
export class GridInputColumn extends GridColumn {
|
export class GridInputColumn extends GridColumn {
|
||||||
static get editing() { return true };
|
static get editing() { return true };
|
||||||
|
static get canEdit() { return true };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {Function} _trigger
|
* @param {Function} trigger
|
||||||
* @param {GridColumnDefinition} col
|
* @param {GridColumnDefinition} col
|
||||||
* @param {HTMLElement} _container
|
* @param {HTMLElement} _container
|
||||||
* @param {GridItemWrapper} wrapper
|
* @param {GridItemWrapper} wrapper
|
||||||
* @returns {HTMLElement}
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
static createEdit(_trigger, col, _container, wrapper) {
|
static createEdit(trigger, col, _container, wrapper) {
|
||||||
const input = createElement('input');
|
const input = createElement('input');
|
||||||
input.setAttribute('type', 'text');
|
input.setAttribute('type', 'text');
|
||||||
input.addEventListener('input', () => {
|
input.addEventListener('input', () => super._changeValue(col.key, wrapper, input.value));
|
||||||
if (wrapper.__editing == null) {
|
input.addEventListener('change', trigger);
|
||||||
wrapper.__editing = {
|
|
||||||
[col.key]: true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wrapper.__editing[col.key] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +264,11 @@ export class GridInputColumn extends GridColumn {
|
|||||||
super.setEnabled(element, enabled);
|
super.setEnabled(element, enabled);
|
||||||
element.disabled = enabled === false;
|
element.disabled = enabled === false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
static toString() { return 'GridInput' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -221,23 +282,16 @@ export class GridInputColumn extends GridColumn {
|
|||||||
export class GridTextColumn extends GridInputColumn {
|
export class GridTextColumn extends GridInputColumn {
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {Function} _trigger
|
* @param {Function} trigger
|
||||||
* @param {GridColumnDefinition} col
|
* @param {GridColumnDefinition} col
|
||||||
* @param {HTMLElement} _container
|
* @param {HTMLElement} _container
|
||||||
* @param {GridItemWrapper} wrapper
|
* @param {GridItemWrapper} wrapper
|
||||||
* @returns {HTMLElement}
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
static createEdit(_trigger, col, _container, wrapper) {
|
static createEdit(trigger, col, _container, wrapper) {
|
||||||
const input = createElement('textarea');
|
const input = createElement('textarea');
|
||||||
input.addEventListener('input', () => {
|
input.addEventListener('input', () => super._changeValue(col.key, wrapper, input.value));
|
||||||
if (wrapper.__editing == null) {
|
input.addEventListener('change', trigger);
|
||||||
wrapper.__editing = {
|
|
||||||
[col.key]: true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wrapper.__editing[col.key] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,6 +315,11 @@ export class GridTextColumn extends GridInputColumn {
|
|||||||
// TODO: bad performance
|
// TODO: bad performance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
static toString() { return 'GridText' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const SymbolDropdown = Symbol.for('ui-dropdown');
|
const SymbolDropdown = Symbol.for('ui-dropdown');
|
||||||
@ -273,6 +332,16 @@ const SymbolDropdown = Symbol.for('ui-dropdown');
|
|||||||
* @hideconstructor
|
* @hideconstructor
|
||||||
*/
|
*/
|
||||||
export class GridDropdownColumn extends GridColumn {
|
export class GridDropdownColumn extends GridColumn {
|
||||||
|
static get canEdit() { return true };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
* @returns {HTMLElement}
|
||||||
|
*/
|
||||||
|
static create() {
|
||||||
|
return createElement('span', 'ui-drop-span', createElement('span'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {Function} trigger
|
* @param {Function} trigger
|
||||||
@ -302,6 +371,18 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
return drop.create();
|
return drop.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @returns {HTMLElement}
|
||||||
|
*/
|
||||||
|
static getElement(element) {
|
||||||
|
if (element.tagName === 'DIV') {
|
||||||
|
return element.children[0].children[0];
|
||||||
|
}
|
||||||
|
return element.children[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {HTMLElement} element
|
* @param {HTMLElement} element
|
||||||
@ -363,7 +444,7 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
if (data != null) {
|
if (data != null) {
|
||||||
val = data[opts?.textKey ?? 'text'];
|
val = data[opts?.textKey ?? 'text'];
|
||||||
}
|
}
|
||||||
super.setValue(element, val);
|
element.children[0].innerText = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -372,8 +453,9 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
* @param {any} val
|
* @param {any} val
|
||||||
* @param {GridItemWrapper} wrapper
|
* @param {GridItemWrapper} wrapper
|
||||||
* @param {GridColumnDefinition} col
|
* @param {GridColumnDefinition} col
|
||||||
|
* @param {Grid} grid
|
||||||
*/
|
*/
|
||||||
static setValue(element, val, wrapper, col) {
|
static setValue(element, val, wrapper, col, grid) {
|
||||||
if (element.tagName !== 'DIV') {
|
if (element.tagName !== 'DIV') {
|
||||||
let source = this._getSource(wrapper, col);
|
let source = this._getSource(wrapper, col);
|
||||||
if (source instanceof Promise) {
|
if (source instanceof Promise) {
|
||||||
@ -387,19 +469,42 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
if (drop == null) {
|
if (drop == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const ignoreCase = col.dropRestrictCase !== true;
|
||||||
if (drop.source == null || drop.source.length === 0) {
|
if (drop.source == null || drop.source.length === 0) {
|
||||||
let source = this._getSource(wrapper, col);
|
let source = this._getSource(wrapper, col);
|
||||||
if (source instanceof Promise) {
|
if (source instanceof Promise) {
|
||||||
source.then(s => {
|
source.then(s => {
|
||||||
drop.source = s;
|
drop.source = s;
|
||||||
drop.select(val, true);
|
drop.select(val, true, ignoreCase);
|
||||||
})
|
})
|
||||||
return;
|
return;
|
||||||
} else if (source != null) {
|
} else if (source != null) {
|
||||||
drop.source = source;
|
drop.source = source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drop.select(val, true);
|
if (typeof val === 'string' && val !== '') {
|
||||||
|
const lVal = String(val).toLowerCase();
|
||||||
|
const item = drop.source.find(s => {
|
||||||
|
let v = s[col.dropOptions?.valueKey ?? 'value'];
|
||||||
|
if (ignoreCase) {
|
||||||
|
return String(v).toLowerCase() === lVal;
|
||||||
|
}
|
||||||
|
return v === val;
|
||||||
|
});
|
||||||
|
if (item == null) {
|
||||||
|
let text;
|
||||||
|
if (col.text == null && typeof col.filter === 'function') {
|
||||||
|
text = col.filter(wrapper.values, false, grid._var.refs.body);
|
||||||
|
} else {
|
||||||
|
text = val;
|
||||||
|
}
|
||||||
|
drop.source.push({
|
||||||
|
[col.dropOptions?.textKey ?? 'text']: text,
|
||||||
|
[col.dropOptions?.valueKey ?? 'value']: val
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop.select(val, true, ignoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -409,7 +514,10 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
* @returns {any}
|
* @returns {any}
|
||||||
*/
|
*/
|
||||||
static getValue(e, col) {
|
static getValue(e, col) {
|
||||||
return e[col.dropOptions?.valueKey ?? 'value'];
|
return {
|
||||||
|
value: e[col.dropOptions?.valueKey ?? 'value'],
|
||||||
|
text: e[col.dropOptions?.textKey ?? 'text']
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -458,6 +566,11 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
drop.onCollapsed();
|
drop.onCollapsed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
static toString() { return 'GridDropdown' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -469,14 +582,18 @@ export class GridDropdownColumn extends GridColumn {
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class GridCheckboxColumn extends GridColumn {
|
export class GridCheckboxColumn extends GridColumn {
|
||||||
|
static get canEdit() { return true };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {Function} trigger
|
* @param {Function} trigger
|
||||||
|
* @param {GridColumnDefinition} col
|
||||||
* @returns {HTMLElement}
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
static createEdit(trigger) {
|
static createEdit(trigger, col) {
|
||||||
const check = createCheckbox({
|
const check = createCheckbox({
|
||||||
onchange: typeof trigger === 'function' ? trigger : null
|
switch: col.switch,
|
||||||
|
onchange: trigger
|
||||||
});
|
});
|
||||||
return check;
|
return check;
|
||||||
}
|
}
|
||||||
@ -487,7 +604,8 @@ export class GridCheckboxColumn extends GridColumn {
|
|||||||
* @param {boolean} val
|
* @param {boolean} val
|
||||||
*/
|
*/
|
||||||
static setValue(element, val) {
|
static setValue(element, val) {
|
||||||
element.querySelector('input').checked = val;
|
// element.querySelector('input').checked = val;
|
||||||
|
element.children[0].checked = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -517,8 +635,44 @@ export class GridCheckboxColumn extends GridColumn {
|
|||||||
*/
|
*/
|
||||||
static setEnabled(element, enabled) {
|
static setEnabled(element, enabled) {
|
||||||
super.setEnabled(element, enabled);
|
super.setEnabled(element, enabled);
|
||||||
element.querySelector('input').disabled = enabled === false;
|
// element.querySelector('input').disabled = enabled === false;
|
||||||
|
element.children[0].disabled = enabled === false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
static toString() { return 'GridCheckbox' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单选框列
|
||||||
|
* @class
|
||||||
|
* @static
|
||||||
|
* @extends GridCheckboxColumn
|
||||||
|
* @hideconstructor
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
export class GridRadioboxColumn extends GridCheckboxColumn {
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
* @param {Function} trigger
|
||||||
|
* @param {GridColumnDefinition} _col
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {HTMLElement}
|
||||||
|
*/
|
||||||
|
static createEdit(trigger, _col, index) {
|
||||||
|
const check = createRadiobox({
|
||||||
|
name: `r_${index}`,
|
||||||
|
onchange: trigger
|
||||||
|
});
|
||||||
|
return check;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
static toString() { return 'GridRadiobox' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -590,6 +744,11 @@ export class GridIconColumn extends GridColumn {
|
|||||||
element.classList.remove('disabled');
|
element.classList.remove('disabled');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
static toString() { return 'GridIcon' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -601,16 +760,17 @@ export class GridIconColumn extends GridColumn {
|
|||||||
*/
|
*/
|
||||||
export class GridDateColumn extends GridColumn {
|
export class GridDateColumn extends GridColumn {
|
||||||
static get editing() { return true };
|
static get editing() { return true };
|
||||||
|
static get canEdit() { return true };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {Function} _trigger
|
* @param {Function} trigger
|
||||||
* @param {GridColumnDefinition} col
|
* @param {GridColumnDefinition} col
|
||||||
* @param {HTMLElement} _container
|
* @param {HTMLElement} _container
|
||||||
* @param {GridItemWrapper} wrapper
|
* @param {GridItemWrapper} wrapper
|
||||||
* @returns {HTMLElement}
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
static createEdit(_trigger, col, _container, wrapper) {
|
static createEdit(trigger, col, _container, wrapper) {
|
||||||
let enabled = col.enabled;
|
let enabled = col.enabled;
|
||||||
if (typeof enabled === 'string') {
|
if (typeof enabled === 'string') {
|
||||||
enabled = wrapper.values[enabled];
|
enabled = wrapper.values[enabled];
|
||||||
@ -621,15 +781,8 @@ export class GridDateColumn extends GridColumn {
|
|||||||
return super.create();
|
return super.create();
|
||||||
}
|
}
|
||||||
const date = createDateInput(col.dateMin, col.dateMax);
|
const date = createDateInput(col.dateMin, col.dateMax);
|
||||||
date.addEventListener('change', () => {
|
date.addEventListener('change', () => super._changeValue(col.key, wrapper, date.value));
|
||||||
if (wrapper.__editing == null) {
|
date.addEventListener('blur', trigger);
|
||||||
wrapper.__editing = {
|
|
||||||
[col.key]: true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wrapper.__editing[col.key] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,9 +790,11 @@ export class GridDateColumn extends GridColumn {
|
|||||||
* @ignore
|
* @ignore
|
||||||
* @param {HTMLElement} element
|
* @param {HTMLElement} element
|
||||||
* @param {(string | number | Date)} val
|
* @param {(string | number | Date)} val
|
||||||
|
* @param {GridItemWrapper} _wrapper
|
||||||
|
* @param {GridColumnDefinition} col
|
||||||
*/
|
*/
|
||||||
static setValue(element, val) {
|
static setValue(element, val, _wrapper, col) {
|
||||||
setDateValue(element, val);
|
setDateValue(element, val, col.dateDisplayFormatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -649,7 +804,13 @@ export class GridDateColumn extends GridColumn {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
static getValue(e, col) {
|
static getValue(e, col) {
|
||||||
return getDateValue(e.target, col.dateValueFormatter);
|
if (e.target.tagName === 'INPUT') {
|
||||||
|
return {
|
||||||
|
value: getDateValue(e.target, col.dateValueFormatter),
|
||||||
|
text: getDateValue(e.target, col.dateDisplayFormatter)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return e.target.innerText;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -683,9 +844,36 @@ export class GridDateColumn extends GridColumn {
|
|||||||
* * `"1/26/2024"`
|
* * `"1/26/2024"`
|
||||||
* * `"638418240000000000"`
|
* * `"638418240000000000"`
|
||||||
* * `new Date('2024-01-26')`
|
* * `new Date('2024-01-26')`
|
||||||
|
* @param {string} [formatter] - 格式化格式,默认为 `"m/d/Y"`
|
||||||
|
*
|
||||||
|
* * Y - 年,例如 `2024`
|
||||||
|
* * y - 年的后两位,例如 `"24"`
|
||||||
|
* * n - 月,例如 `2`
|
||||||
|
* * m - 格式化成两位的月,例如 `"02"`
|
||||||
|
* * j - 日,例如 `4`
|
||||||
|
* * d - 格式化成两位的日,例如 `"04"`
|
||||||
|
* * t - 日期当月总天数,例如 `29`
|
||||||
|
* * L - 是否为闰年,例如 `1`
|
||||||
|
* * G - 小时,例如 `15`
|
||||||
|
* * g - 12 小时制的小时,例如 `3`
|
||||||
|
* * H - 格式化成两位的小时,例如 `"15"`
|
||||||
|
* * h - 格式化成两位的 12 小时制的小时,例如 `"03"`
|
||||||
|
* * A - 上下午,例如 `"PM"`
|
||||||
|
* * a - 小写的上下午,例如 `"pm"`
|
||||||
|
* * i - 格式化成两位的分钟,例如 `"39"`
|
||||||
|
* * s - 格式化成两位的秒,例如 `"20"`
|
||||||
|
* * u - 格式化成六位的毫秒,例如 `"023040"`
|
||||||
|
* * e - 时区描述字符串,例如 `"China Standard Time"`
|
||||||
|
* * O - 时区偏移字符串,例如 `"+0800"`
|
||||||
|
* * P - `"+08:00"` 这种格式的时区偏移字符串
|
||||||
* @returns {string} 格式化为 M/d/yyyy 的日期字符串
|
* @returns {string} 格式化为 M/d/yyyy 的日期字符串
|
||||||
*/
|
*/
|
||||||
static formatDate(date) {
|
static formatDate(date, formatter) {
|
||||||
return formatDate(date);
|
return formatDate(date, formatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
static toString() { return 'GridDate' }
|
||||||
}
|
}
|
1403
lib/ui/grid/grid.js
3
lib/ui/icon.d.ts
vendored
@ -3,8 +3,9 @@
|
|||||||
* @param type 样式分类,可以是 ['`fa-light`', '`fa-regular`', '`fa-solid`'] 其中之一
|
* @param type 样式分类,可以是 ['`fa-light`', '`fa-regular`', '`fa-solid`'] 其中之一
|
||||||
* @param id 图标 id
|
* @param id 图标 id
|
||||||
* @param style 样式表对象
|
* @param style 样式表对象
|
||||||
|
* @param action 点击回调事件
|
||||||
*/
|
*/
|
||||||
export function createIcon(type: string, id: string, style?: { [key: string]: string }): SVGSVGElement
|
export function createIcon(type: string, id: string, style?: { [key: string]: string } | ((icon: SVGSVGElement) => { [key: string]: string }), action?: (this: SVGSVGElement, ev: MouseEvent) => any): SVGSVGElement
|
||||||
/**
|
/**
|
||||||
* 修改矢量图标
|
* 修改矢量图标
|
||||||
* @param svg 矢量图标元素
|
* @param svg 矢量图标元素
|
||||||
|
1663
lib/ui/icon.js
8
lib/ui/media.d.ts
vendored
@ -23,4 +23,10 @@ export function createVideo(url: string): HTMLVideoElement
|
|||||||
* @param url 文件 url
|
* @param url 文件 url
|
||||||
* @param icon 图标,默认为 `file-alt`
|
* @param icon 图标,默认为 `file-alt`
|
||||||
*/
|
*/
|
||||||
export function createFile(url: string, icon?: string): HTMLDivElement
|
export function createFile(url: string, icon?: string): HTMLDivElement
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建联动视频元素
|
||||||
|
* @param urls 视频 url 数组
|
||||||
|
*/
|
||||||
|
export function createVideoList(urls: string[]): HTMLDivElement
|
321
lib/ui/media.js
@ -1,7 +1,7 @@
|
|||||||
import "./css/media.scss";
|
import "./css/media.scss";
|
||||||
import { createElement } from "../functions";
|
import { createElement } from "../functions";
|
||||||
import { createIcon } from "./icon";
|
import { createIcon } from "./icon";
|
||||||
import { get } from "../utility";
|
import { get } from "../utility/request";
|
||||||
|
|
||||||
export function createPicture(url) {
|
export function createPicture(url) {
|
||||||
return createElement('a', a => {
|
return createElement('a', a => {
|
||||||
@ -62,14 +62,24 @@ function playPcm(samples, ended) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTimeLabel(time) {
|
function getTimeLabel(time) {
|
||||||
time = Math.round(time);
|
// time = Math.round(time);
|
||||||
return String(Math.floor(time / 60)).padStart(2, '0') + ':' + String(time % 60).padStart(2, '0');
|
// return String(Math.floor(time / 60)).padStart(2, '0') + ':' + String(time % 60).padStart(2, '0');
|
||||||
|
if (isNaN(time) || time < 0) {
|
||||||
|
return '0:00';
|
||||||
|
}
|
||||||
|
time = Math.floor(time);
|
||||||
|
const m = Math.floor(time / 60);
|
||||||
|
const h = Math.floor(m / 60);
|
||||||
|
if (h > 0) {
|
||||||
|
return h + ':' + String(m % 60).padStart(2, '0') + ':' + String(time % 60).padStart(2, '0');
|
||||||
|
}
|
||||||
|
return m + ':' + String(time % 60).padStart(2, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAudio(mime, url) {
|
export function createAudio(mime, url) {
|
||||||
if ((mime === 'audio/amr' || mime === '.amr') && typeof AMR !== 'undefined') {
|
if ((mime === 'audio/amr' || mime === '.amr') && typeof AMR !== 'undefined') {
|
||||||
const timestamp = createElement('span', 'ui-media-timestamp');
|
const timestamp = createElement('span', 'ui-media-timestamp');
|
||||||
timestamp.textContent = '00:00 / 00:00';
|
timestamp.textContent = '0:00 / 0:00';
|
||||||
let context;
|
let context;
|
||||||
let timer;
|
let timer;
|
||||||
return createElement('div', 'ui-media-audio',
|
return createElement('div', 'ui-media-audio',
|
||||||
@ -80,7 +90,7 @@ export function createAudio(mime, url) {
|
|||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
context.close();
|
context.close();
|
||||||
context = null;
|
context = null;
|
||||||
timestamp.textContent = '00:00 / 00:00';
|
timestamp.textContent = '0:00 / 0:00';
|
||||||
button.className = 'play';
|
button.className = 'play';
|
||||||
button.replaceChildren(createIcon('fa-solid', 'play'));
|
button.replaceChildren(createIcon('fa-solid', 'play'));
|
||||||
return;
|
return;
|
||||||
@ -92,7 +102,7 @@ export function createAudio(mime, url) {
|
|||||||
.then(r => playPcm(r, ctx => {
|
.then(r => playPcm(r, ctx => {
|
||||||
context = null;
|
context = null;
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
timestamp.textContent = '00:00 / ' + getTimeLabel(ctx.duration);
|
timestamp.textContent = '0:00 / ' + getTimeLabel(ctx.duration);
|
||||||
button.className = 'play';
|
button.className = 'play';
|
||||||
button.replaceChildren(createIcon('fa-solid', 'play'));
|
button.replaceChildren(createIcon('fa-solid', 'play'));
|
||||||
}))
|
}))
|
||||||
@ -139,4 +149,303 @@ export function createFile(url, icon = 'file-alt') {
|
|||||||
a.innerText = 'Click here to view the file';
|
a.innerText = 'Click here to view the file';
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建联动视频元素
|
||||||
|
* @param {string[]} urls - 视频 url 数组
|
||||||
|
* @param {any} [options] - 播放参数
|
||||||
|
* @param {boolean} options.[autoPlay] - 是否自动播放
|
||||||
|
* @param {boolean} options.[autoFullScreen] - 是否自动全屏
|
||||||
|
* @param {boolean} options.[autoLoop] - 是否循环播放
|
||||||
|
* @param {Function} options.[onLoaded] - 视频加载完成回调
|
||||||
|
* @param {Function} [callback] - 视频元素处理回调函数
|
||||||
|
* @returns {HTMLDivElement} 返回联动视频元素
|
||||||
|
*/
|
||||||
|
export function createVideoList(urls, options, callback) {
|
||||||
|
if (!Array.isArray(urls)) {
|
||||||
|
urls = [urls];
|
||||||
|
}
|
||||||
|
const length = urls.length;
|
||||||
|
const container = createElement('div', 'ui-media-video-container');
|
||||||
|
let seekBufferBar;
|
||||||
|
let seekProgressBar;
|
||||||
|
let playIcon;
|
||||||
|
let timeLabel;
|
||||||
|
let volumeProgressBar;
|
||||||
|
let volumeIcon;
|
||||||
|
|
||||||
|
const videos = Array(length);
|
||||||
|
const waiting = Array(length);
|
||||||
|
let prepared = 0;
|
||||||
|
let ended = 0;
|
||||||
|
|
||||||
|
let playing;
|
||||||
|
let duration = -1;
|
||||||
|
let durationLabel;
|
||||||
|
let seekBarWidth;
|
||||||
|
let mousing;
|
||||||
|
const controller = createElement('div', 'ui-video-control active',
|
||||||
|
createElement('div', seek => {
|
||||||
|
seek.className = 'ui-video-bar seek-bar';
|
||||||
|
seek.addEventListener('mousedown', e => {
|
||||||
|
if (prepared < length || waiting.find(p => p) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const width = seekBarWidth = seek.offsetWidth;
|
||||||
|
const currentTime = Math.min(Math.max(e.offsetX * duration / width, 0), duration);
|
||||||
|
videos.forEach(video => typeof video.fastSeek === 'function' ?
|
||||||
|
video.fastSeek(currentTime) :
|
||||||
|
(video.currentTime = currentTime));
|
||||||
|
mousing = true;
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
seek.addEventListener('mousemove', e => {
|
||||||
|
if (mousing) {
|
||||||
|
if (!e.buttons) {
|
||||||
|
mousing = false;
|
||||||
|
} else {
|
||||||
|
const v = Math.min(Math.max(e.offsetX / seekBarWidth, 0), 1);
|
||||||
|
seekProgressBar.style.width = `${(v * 100).toFixed(2)}%`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
seek.addEventListener('mouseup', e => {
|
||||||
|
if (mousing) {
|
||||||
|
mousing = false;
|
||||||
|
const currentTime = Math.min(Math.max(e.offsetX * duration / seekBarWidth, 0), duration);
|
||||||
|
videos.forEach(video => typeof video.fastSeek === 'function' ?
|
||||||
|
video.fastSeek(currentTime) :
|
||||||
|
(video.currentTime = currentTime));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createElement('div', 'ui-video-duration seek-duration'),
|
||||||
|
seekBufferBar = createElement('div', 'seek-buffers'),
|
||||||
|
seekProgressBar = createElement('div', 'ui-video-progress seek-progress')
|
||||||
|
),
|
||||||
|
playIcon = createElement('div', 'ui-video-icon play-icon',
|
||||||
|
createIcon('fa-solid', 'play'),
|
||||||
|
createIcon('fa-solid', 'pause')
|
||||||
|
),
|
||||||
|
timeLabel = createElement('div', 'ui-video-time-label'),
|
||||||
|
createElement('div', 'ui-video-volume-container',
|
||||||
|
createElement('div', volume => {
|
||||||
|
volume.className = 'ui-video-bar volume-bar';
|
||||||
|
volume.addEventListener('mousedown', e => {
|
||||||
|
const v = Math.min(Math.max(e.offsetX / 60, 0), 1);
|
||||||
|
const video = videos[0];
|
||||||
|
if (video != null) {
|
||||||
|
video.volume = v;
|
||||||
|
}
|
||||||
|
volumeIcon.classList[v === 0 ? 'add' : 'remove']('muted');
|
||||||
|
mousing = true;
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
volume.addEventListener('mousemove', e => {
|
||||||
|
if (mousing) {
|
||||||
|
if (!e.buttons) {
|
||||||
|
mousing = false;
|
||||||
|
} else {
|
||||||
|
const v = Math.min(Math.max(e.offsetX / 60, 0), 1);
|
||||||
|
volumeProgressBar.style.width = `${(v * 100).toFixed(2)}%`;
|
||||||
|
const video = videos[0];
|
||||||
|
if (video != null) {
|
||||||
|
video.volume = v;
|
||||||
|
}
|
||||||
|
volumeIcon.classList[v === 0 ? 'add' : 'remove']('muted');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
volume.addEventListener('mouseup', e => {
|
||||||
|
if (mousing) {
|
||||||
|
mousing = false;
|
||||||
|
const v = Math.min(Math.max(e.offsetX / 60, 0), 1);
|
||||||
|
const video = videos[0];
|
||||||
|
if (video != null) {
|
||||||
|
video.volume = v;
|
||||||
|
}
|
||||||
|
volumeIcon.classList[v === 0 ? 'add' : 'remove']('muted');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createElement('div', 'ui-video-duration video-duration'),
|
||||||
|
volumeProgressBar = createElement('div', 'ui-video-progress video-progress')
|
||||||
|
),
|
||||||
|
volumeIcon = createElement('div', 'ui-video-icon volume-icon',
|
||||||
|
createIcon('fa-solid', 'volume'),
|
||||||
|
createIcon('fa-solid', 'volume-mute')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
controller.addEventListener('mousedown', () => {
|
||||||
|
if (prepared < length || waiting.find(p => p) != null) {
|
||||||
|
// not prepared.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (playing) {
|
||||||
|
videos.forEach(video => video.pause());
|
||||||
|
} else {
|
||||||
|
ended = 0;
|
||||||
|
videos.forEach(video => video.play().catch(() => { }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
volumeIcon.addEventListener('mousedown', e => {
|
||||||
|
if (volumeIcon.classList.contains('disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const video = videos[0];
|
||||||
|
if (video == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (volumeIcon.classList.contains('muted')) {
|
||||||
|
video.volume = 1;
|
||||||
|
volumeIcon.classList.remove('muted');
|
||||||
|
} else {
|
||||||
|
video.volume = 0;
|
||||||
|
volumeIcon.classList.add('muted');
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
if (length === 1) {
|
||||||
|
controller.append(createElement('div', icon => {
|
||||||
|
icon.className = 'ui-video-icon fullscreen-icon';
|
||||||
|
icon.addEventListener('mousedown', e => {
|
||||||
|
if (prepared < length) {
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const video = videos[0];
|
||||||
|
if (video != null) {
|
||||||
|
if (document.fullscreenElement == null) {
|
||||||
|
video.requestFullscreen().catch(() => { });
|
||||||
|
} else if (typeof document.exitFullscreen === 'function') {
|
||||||
|
document.exitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createIcon('fa-regular', 'expand')
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
controller.classList.add('no-fullscreen');
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = createElement('div', 'ui-video-content');
|
||||||
|
container.append(content, controller);
|
||||||
|
|
||||||
|
urls.forEach((url, i) => {
|
||||||
|
const video = createElement('video');
|
||||||
|
videos[i] = video;
|
||||||
|
video.src = url;
|
||||||
|
video.addEventListener('durationchange', () => {
|
||||||
|
const d = video.duration;
|
||||||
|
if (d > duration) {
|
||||||
|
duration = d;
|
||||||
|
durationLabel = getTimeLabel(d);
|
||||||
|
timeLabel.innerText = '0:00 / ' + durationLabel;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
video.addEventListener('loadeddata', () => {
|
||||||
|
if (i === 0) {
|
||||||
|
volumeProgressBar.style.width = `${(video.volume * 100).toFixed(2)}%`;
|
||||||
|
} else {
|
||||||
|
video.volume = 0;
|
||||||
|
}
|
||||||
|
prepared += 1;
|
||||||
|
if (prepared >= length) {
|
||||||
|
if (options?.autoPlay) {
|
||||||
|
// auto play
|
||||||
|
videos.forEach(v => v.play().catch(() => { }));
|
||||||
|
if (options?.autoFullScreen && length === 1 && document.fullscreenElement == null) {
|
||||||
|
video.requestFullscreen().catch(() => { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof options?.onLoaded === 'function') {
|
||||||
|
options.onLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
video.addEventListener('progress', () => {
|
||||||
|
const buffered = video.buffered;
|
||||||
|
for (let i = 0; i < buffered.length; ++i) {
|
||||||
|
let buffer = seekBufferBar.children[i];
|
||||||
|
if (buffer == null) {
|
||||||
|
seekBufferBar.append(buffer = createElement('div', 'ui-video-buffer'));
|
||||||
|
}
|
||||||
|
const start = buffered.start(i) * 100 / duration;
|
||||||
|
const end = buffered.end(i) * 100 / duration;
|
||||||
|
buffer.style.left = `${start.toFixed(2)}%`;
|
||||||
|
buffer.style.width = `${(end - start).toFixed(2)}%`;
|
||||||
|
}
|
||||||
|
for (let i = seekBufferBar.children.length - 1; i >= buffered.length; i -= 1) {
|
||||||
|
seekBufferBar.children[i].remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
video.addEventListener('canplaythrough', () => {
|
||||||
|
const w = video.parentElement.querySelector('.ui-video-waiting');
|
||||||
|
if (w != null) {
|
||||||
|
w.style.opacity = 0;
|
||||||
|
}
|
||||||
|
if (waiting[i]) {
|
||||||
|
waiting[i] = false;
|
||||||
|
if (waiting.find(p => p) == null) {
|
||||||
|
videos.forEach(v => v.play().catch(() => { }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
video.addEventListener('pause', () => {
|
||||||
|
playing = false;
|
||||||
|
controller.classList.add('active');
|
||||||
|
playIcon.classList.remove('pause');
|
||||||
|
});
|
||||||
|
video.addEventListener('playing', () => {
|
||||||
|
playing = true;
|
||||||
|
controller.classList.remove('active');
|
||||||
|
playIcon.classList.add('pause');
|
||||||
|
});
|
||||||
|
video.addEventListener('waiting', () => {
|
||||||
|
waiting[i] = true;
|
||||||
|
const w = video.parentElement.querySelector('.ui-video-waiting');
|
||||||
|
if (w != null) {
|
||||||
|
w.style.opacity = 1;
|
||||||
|
}
|
||||||
|
videos.forEach(v => v.pause());
|
||||||
|
});
|
||||||
|
video.addEventListener('ended', () => {
|
||||||
|
ended += 1;
|
||||||
|
if (ended >= length) {
|
||||||
|
if (options?.autoLoop) {
|
||||||
|
videos.forEach(v => v.play().catch(() => { }));
|
||||||
|
} else {
|
||||||
|
playing = false;
|
||||||
|
controller.classList.add('active');
|
||||||
|
seekProgressBar.style.width = '0';
|
||||||
|
playIcon.classList.remove('pause');
|
||||||
|
timeLabel.innerText = '0:00 / ' + durationLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (i === 0) {
|
||||||
|
video.addEventListener('timeupdate', () => {
|
||||||
|
seekProgressBar.style.width = `${(video.currentTime * 100 / duration).toFixed(2)}%`;
|
||||||
|
timeLabel.innerText = getTimeLabel(video.currentTime) + ' / ' + durationLabel;
|
||||||
|
});
|
||||||
|
video.addEventListener('volumechange', () => {
|
||||||
|
volumeProgressBar.style.width = `${(video.volume * 100).toFixed(2)}%`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const wrapper = createElement('div', 'ui-video-wrapper',
|
||||||
|
video,
|
||||||
|
createElement('div', 'ui-video-waiting',
|
||||||
|
createIcon('fa-regular', 'spinner-third')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(wrapper);
|
||||||
|
}
|
||||||
|
content.append(wrapper);
|
||||||
|
});
|
||||||
|
|
||||||
|
return container;
|
||||||
}
|
}
|
28
lib/ui/popup.d.ts
vendored
@ -7,12 +7,16 @@ interface PopupOptions {
|
|||||||
/** 弹出框标题,可以是文本或者 html 元素 */
|
/** 弹出框标题,可以是文本或者 html 元素 */
|
||||||
title: string | HTMLElement;
|
title: string | HTMLElement;
|
||||||
|
|
||||||
|
/** 是否持久化显示 */
|
||||||
|
persistent?: boolean;
|
||||||
/** 是否包含遮罩层,默认为 `true` */
|
/** 是否包含遮罩层,默认为 `true` */
|
||||||
mask?: boolean;
|
mask?: boolean;
|
||||||
/** 遮罩层 z-index */
|
/** 遮罩层 z-index */
|
||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
/** 是否在获取焦点时修改 z-index */
|
/** 是否在获取焦点时修改 z-index */
|
||||||
changeZIndex?: boolean;
|
changeZIndex?: boolean;
|
||||||
|
/** 是否允许关闭 */
|
||||||
|
closable?: boolean;
|
||||||
/** 是否允许移动 */
|
/** 是否允许移动 */
|
||||||
movable?: boolean;
|
movable?: boolean;
|
||||||
/** 是否允许修改大小 */
|
/** 是否允许修改大小 */
|
||||||
@ -59,15 +63,31 @@ interface PopupOptions {
|
|||||||
/**
|
/**
|
||||||
* 弹出框关闭时的回调函数
|
* 弹出框关闭时的回调函数
|
||||||
*/
|
*/
|
||||||
resolve?: () => void;
|
resolve?: (this: Popup, result: { result: any, popup: Popup }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Popup {
|
export class Popup {
|
||||||
constructor(opts?: PopupOptions);
|
constructor(opts?: PopupOptions);
|
||||||
|
|
||||||
|
get container(): HTMLElement;
|
||||||
|
|
||||||
|
get title(): string;
|
||||||
|
set title(title: string);
|
||||||
|
|
||||||
|
get loading(): boolean;
|
||||||
|
set loading(flag: boolean);
|
||||||
|
|
||||||
|
get rect(): { collapsed: boolean, left: number, top: number, width: number, height: number };
|
||||||
|
set rect(r: { collapsed?: boolean, left?: number, top?: number, width?: number, height?: number });
|
||||||
|
|
||||||
|
close(result?: any, animation?: boolean): void;
|
||||||
|
create(): HTMLDivElement;
|
||||||
|
show(parent?: HTMLElement, hidden?: boolean): Promise<HTMLElement> | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PopupButton {
|
interface PopupButton {
|
||||||
tabIndex: number;
|
className?: string;
|
||||||
|
tabIndex?: number;
|
||||||
key: string;
|
key: string;
|
||||||
text: string;
|
text: string;
|
||||||
trigger: (this: Popup) => boolean | Promise<boolean>;
|
trigger: (this: Popup) => boolean | Promise<boolean>;
|
||||||
@ -83,12 +103,14 @@ interface PopupIconTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PopupButtonResult {
|
interface PopupButtonResult {
|
||||||
key: string;
|
result: string;
|
||||||
popup: Popup;
|
popup: Popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPopup(title: string | HTMLElement, content: string | HTMLElement, ...buttons: PopupButton[]): Popup
|
export function createPopup(title: string | HTMLElement, content: string | HTMLElement, ...buttons: PopupButton[]): Popup
|
||||||
|
|
||||||
|
export function resolvePopup(wrapper: string | HTMLElement, callback?: Function, removable?: boolean, zIndex?: number): Popup
|
||||||
|
|
||||||
export function showAlert(title: string | HTMLElement, message: string, iconType?: keyof PopupIconTypes, parent?: HTMLElement): Promise<void>
|
export function showAlert(title: string | HTMLElement, message: string, iconType?: keyof PopupIconTypes, parent?: HTMLElement): Promise<void>
|
||||||
|
|
||||||
export function showConfirm(title: string | HTMLElement, content: string | HTMLElement, buttons: PopupButton[], iconType?: keyof PopupIconTypes, parent?: HTMLElement): Promise<PopupButtonResult>
|
export function showConfirm(title: string | HTMLElement, content: string | HTMLElement, buttons: PopupButton[], iconType?: keyof PopupIconTypes, parent?: HTMLElement): Promise<PopupButtonResult>
|
212
lib/ui/popup.js
@ -1,9 +1,10 @@
|
|||||||
import "./css/popup.scss";
|
import "./css/popup.scss";
|
||||||
import { r } from "../utility/lgres";
|
import { r as lang } from "../utility/lgres";
|
||||||
import { nullOrEmpty } from "../utility/strings";
|
import { nullOrEmpty } from "../utility/strings";
|
||||||
import { global } from "../utility";
|
import { global } from "../utility";
|
||||||
import { createElement } from "../functions";
|
import { createElement } from "../functions";
|
||||||
import { createIcon, changeIcon } from "./icon";
|
import { createIcon, changeIcon } from "./icon";
|
||||||
|
import { requestAnimationFrame } from "../ui";
|
||||||
|
|
||||||
const ResizeMods = {
|
const ResizeMods = {
|
||||||
right: 1,
|
right: 1,
|
||||||
@ -51,6 +52,30 @@ export class Popup {
|
|||||||
|
|
||||||
get container() { return this._var.mask.querySelector('.ui-popup-container') }
|
get container() { return this._var.mask.querySelector('.ui-popup-container') }
|
||||||
|
|
||||||
|
get title() { return this._var.option.title }
|
||||||
|
set title(title) {
|
||||||
|
const element = this._var.mask?.querySelector('.ui-popup-container .ui-popup-header .ui-popup-header-title');
|
||||||
|
if (element != null) {
|
||||||
|
element.innerText = title;
|
||||||
|
}
|
||||||
|
this._var.option.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
|
||||||
|
set loading(flag) {
|
||||||
|
let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
|
||||||
|
if (loading == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (flag === false) {
|
||||||
|
loading.style.visibility = 'hidden';
|
||||||
|
loading.style.opacity = 0;
|
||||||
|
} else {
|
||||||
|
loading.style.visibility = 'visible';
|
||||||
|
loading.style.opacity = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get rect() {
|
get rect() {
|
||||||
const container = this.container;
|
const container = this.container;
|
||||||
if (container == null) {
|
if (container == null) {
|
||||||
@ -105,25 +130,41 @@ export class Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(animation = true) {
|
close(result = null, animation = true) {
|
||||||
|
const option = this._var.option;
|
||||||
const mask = this._var.mask;
|
const mask = this._var.mask;
|
||||||
|
const doClose = () => {
|
||||||
|
if (option.persistent) {
|
||||||
|
mask.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
mask.remove();
|
||||||
|
this._var.mask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (animation) {
|
if (animation) {
|
||||||
mask.classList.add('ui-popup-active');
|
mask.classList.add('ui-popup-active');
|
||||||
mask.style.opacity = 0;
|
mask.style.opacity = 0;
|
||||||
setTimeout(() => { mask.remove(); }, 120);
|
setTimeout(() => { doClose(); }, 120);
|
||||||
} else {
|
} else {
|
||||||
mask.remove();
|
doClose();
|
||||||
}
|
}
|
||||||
if (typeof this._var.option.onMasking === 'function') {
|
if (typeof option.onMasking === 'function') {
|
||||||
this._var.option.onMasking.call(this, false);
|
option.onMasking.call(this, false);
|
||||||
}
|
}
|
||||||
if (typeof this._var.option.resolve === 'function') {
|
if (typeof option.resolve === 'function') {
|
||||||
this._var.option.resolve();
|
option.resolve.call(this, {
|
||||||
|
result,
|
||||||
|
popup: this
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Popup 面板
|
||||||
|
* @returns {HTMLDivElement} 返回遮罩元素(顶层元素)
|
||||||
|
*/
|
||||||
create() {
|
create() {
|
||||||
const mask = createElement('div', 'ui-popup-mask');
|
const mask = createElement('div', 'ui-popup-mask ui-popup-active');
|
||||||
const option = this._var.option;
|
const option = this._var.option;
|
||||||
if (option.mask === false) {
|
if (option.mask === false) {
|
||||||
mask.classList.add('ui-popup-transparent');
|
mask.classList.add('ui-popup-transparent');
|
||||||
@ -172,7 +213,11 @@ export class Popup {
|
|||||||
let title = option.title;
|
let title = option.title;
|
||||||
if (!(title instanceof HTMLElement)) {
|
if (!(title instanceof HTMLElement)) {
|
||||||
title = createElement('div', t => {
|
title = createElement('div', t => {
|
||||||
t.className = 'ui-popup-header-title';
|
if (option.movable === false) {
|
||||||
|
t.className = 'ui-popup-header-title no-move';
|
||||||
|
} else {
|
||||||
|
t.className = 'ui-popup-header-title';
|
||||||
|
}
|
||||||
t.innerText = title;
|
t.innerText = title;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -244,15 +289,17 @@ export class Popup {
|
|||||||
});
|
});
|
||||||
icons.appendChild(collapse);
|
icons.appendChild(collapse);
|
||||||
}
|
}
|
||||||
const cancel = createIcon('fa-regular', 'times');
|
if (option.closable !== false) {
|
||||||
cancel.tabIndex = tabIndex + 3;
|
const cancel = createIcon('fa-regular', 'times');
|
||||||
cancel.addEventListener('keypress', e => {
|
cancel.tabIndex = tabIndex + 3;
|
||||||
if (e.key === ' ' || e.key === 'Enter') {
|
cancel.addEventListener('keypress', e => {
|
||||||
this.close();
|
if (e.key === ' ' || e.key === 'Enter') {
|
||||||
}
|
this.close();
|
||||||
});
|
}
|
||||||
cancel.addEventListener('click', () => this.close());
|
});
|
||||||
icons.appendChild(cancel);
|
cancel.addEventListener('click', () => this.close());
|
||||||
|
icons.appendChild(cancel);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
header.appendChild(icons);
|
header.appendChild(icons);
|
||||||
}),
|
}),
|
||||||
@ -265,6 +312,9 @@ export class Popup {
|
|||||||
container.appendChild(
|
container.appendChild(
|
||||||
createElement('div', 'ui-popup-footer', ...option.buttons.map((b, i) => {
|
createElement('div', 'ui-popup-footer', ...option.buttons.map((b, i) => {
|
||||||
const button = createElement('button', 'ui-popup-button');
|
const button = createElement('button', 'ui-popup-button');
|
||||||
|
if (b.className != null) {
|
||||||
|
button.classList.add(b.className);
|
||||||
|
}
|
||||||
if (b.tabIndex > 0) {
|
if (b.tabIndex > 0) {
|
||||||
button.tabIndex = b.tabIndex;
|
button.tabIndex = b.tabIndex;
|
||||||
} else {
|
} else {
|
||||||
@ -277,14 +327,14 @@ export class Popup {
|
|||||||
if (typeof result?.then === 'function') {
|
if (typeof result?.then === 'function') {
|
||||||
result.then(r => {
|
result.then(r => {
|
||||||
if (r !== false) {
|
if (r !== false) {
|
||||||
this.close();
|
this.close(r);
|
||||||
}
|
}
|
||||||
}).catch(reason => console.warn(reason));
|
}).catch(reason => console.warn(reason));
|
||||||
} else if (result !== false) {
|
} else if (result !== false) {
|
||||||
this.close();
|
this.close(result);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.close();
|
this.close(b.key ?? i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return button;
|
return button;
|
||||||
@ -348,24 +398,33 @@ export class Popup {
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
show(parent = document.body) {
|
show(parent = document.body, hidden = false) {
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mask = this._var.mask ?? this.create();
|
let mask = this._var.mask;
|
||||||
// const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
|
if (mask == null) {
|
||||||
const exists = parent.querySelectorAll('.ui-popup-mask');
|
mask = this._var.mask = this.create();
|
||||||
let zindex = 0;
|
}
|
||||||
for (let ex of exists) {
|
if (mask.parentElement == null) {
|
||||||
let z = parseInt(ex.style.zIndex);
|
// const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
|
||||||
if (!isNaN(z) && z > zindex) {
|
const exists = parent.querySelectorAll('.ui-popup-mask');
|
||||||
zindex = z;
|
let zindex = 0;
|
||||||
|
for (let ex of exists) {
|
||||||
|
let z = parseInt(global.getComputedStyle(ex).zIndex);
|
||||||
|
if (!isNaN(z) && z > zindex) {
|
||||||
|
zindex = z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (zindex > 0) {
|
||||||
|
mask.style.zIndex = String(zindex + 1);
|
||||||
|
}
|
||||||
|
parent.appendChild(mask);
|
||||||
|
if (hidden === true) {
|
||||||
|
mask.style.display = 'none';
|
||||||
|
return Promise.resolve(mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (zindex > 0) {
|
|
||||||
mask.style.zIndex = String(zindex + 1);
|
|
||||||
}
|
|
||||||
parent.appendChild(mask);
|
|
||||||
if (this._var.option.mask === false) {
|
if (this._var.option.mask === false) {
|
||||||
// calculator position
|
// calculator position
|
||||||
const container = this.container;
|
const container = this.container;
|
||||||
@ -373,29 +432,16 @@ export class Popup {
|
|||||||
container.style.top = String((parent.offsetHeight - container.offsetHeight) / 2) + 'px';
|
container.style.top = String((parent.offsetHeight - container.offsetHeight) / 2) + 'px';
|
||||||
}
|
}
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(() => {
|
mask.style.display = '';
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
mask.classList.remove('ui-popup-active');
|
||||||
mask.style.opacity = 1;
|
mask.style.opacity = 1;
|
||||||
this.container.focus();
|
this.container.focus();
|
||||||
resolve(mask);
|
setTimeout(() => resolve(mask), 120);
|
||||||
}, 0);
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
|
|
||||||
set loading(flag) {
|
|
||||||
let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
|
|
||||||
if (loading == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (flag === false) {
|
|
||||||
loading.style.visibility = 'hidden';
|
|
||||||
loading.style.opacity = 0;
|
|
||||||
} else {
|
|
||||||
loading.style.visibility = 'visible';
|
|
||||||
loading.style.opacity = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_resize(mod, e) {
|
_resize(mod, e) {
|
||||||
if (e.buttons !== 1) {
|
if (e.buttons !== 1) {
|
||||||
return;
|
return;
|
||||||
@ -491,6 +537,43 @@ export function createPopup(title, content, ...buttons) {
|
|||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析对话框元素
|
||||||
|
* @param {HTMLElement | string} wrapper - 解析该 `.dialog` 元素
|
||||||
|
* @param {Function} [callback] - 关闭对话框时的回调
|
||||||
|
* @param {boolean} [removable] - 是否可移除
|
||||||
|
* @param {number} [zIndex] - 对话框默认 `z-index`
|
||||||
|
* @returns {Popup} 返回弹出框字典
|
||||||
|
*/
|
||||||
|
export function resolvePopup(wrapper, callback, removable, zIndex) {
|
||||||
|
if (typeof wrapper === 'string') {
|
||||||
|
wrapper = document.querySelector(wrapper);
|
||||||
|
}
|
||||||
|
if (wrapper == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!wrapper.classList.contains('dialog')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const title = wrapper.querySelector('.dialog-title>.title')?.innerText;
|
||||||
|
const content = wrapper.querySelector('.dialog-title+div');
|
||||||
|
const buttons = [...wrapper.querySelectorAll('.dialog-func>input[type="button"]')].reverse().map(b => ({
|
||||||
|
tabIndex: b.tabIndex,
|
||||||
|
text: b.value,
|
||||||
|
trigger: b.onclick == null ? null : (popup => (b.onclick.call(popup), false))
|
||||||
|
}));
|
||||||
|
const popup = new Popup({
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
persistent: !removable,
|
||||||
|
resolve: typeof callback === 'function' ? (result => callback(result)) : null,
|
||||||
|
zIndex: wrapper.zIndex ?? zIndex,
|
||||||
|
buttons
|
||||||
|
});
|
||||||
|
popup.show(document.body, true);
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
const iconTypes = {
|
const iconTypes = {
|
||||||
'info': 'info-circle',
|
'info': 'info-circle',
|
||||||
'information': 'info-circle',
|
'information': 'info-circle',
|
||||||
@ -501,6 +584,7 @@ const iconTypes = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function showAlert(title, message, iconType = 'info', parent = document.body) {
|
export function showAlert(title, message, iconType = 'info', parent = document.body) {
|
||||||
|
const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const popup = new Popup({
|
const popup = new Popup({
|
||||||
title,
|
title,
|
||||||
@ -510,7 +594,7 @@ export function showAlert(title, message, iconType = 'info', parent = document.b
|
|||||||
),
|
),
|
||||||
resolve,
|
resolve,
|
||||||
buttons: [
|
buttons: [
|
||||||
{ text: r('ok', 'OK'), trigger: resolve }
|
{ text: r('ok', 'OK') }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
popup.show(parent).then(mask => {
|
popup.show(parent).then(mask => {
|
||||||
@ -521,6 +605,7 @@ export function showAlert(title, message, iconType = 'info', parent = document.b
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const wrapper = createElement('div', 'message-wrapper');
|
const wrapper = createElement('div', 'message-wrapper');
|
||||||
if (!nullOrEmpty(iconType)) {
|
if (!nullOrEmpty(iconType)) {
|
||||||
@ -533,34 +618,23 @@ export function showConfirm(title, content, buttons, iconType = 'question', pare
|
|||||||
title,
|
title,
|
||||||
content: wrapper,
|
content: wrapper,
|
||||||
resolve,
|
resolve,
|
||||||
buttons: buttons?.map(b => {
|
buttons: buttons?.map((b, i) => {
|
||||||
return {
|
return {
|
||||||
text: b.text,
|
text: b.text,
|
||||||
trigger: p => {
|
trigger: p => {
|
||||||
let result;
|
let result;
|
||||||
if (typeof b.trigger === 'function') {
|
if (typeof b.trigger === 'function') {
|
||||||
result = b.trigger(p, b);
|
result = b.trigger(p, b);
|
||||||
if (typeof result?.then === 'function') {
|
|
||||||
return result.then(r => {
|
|
||||||
r !== false && resolve(r);
|
|
||||||
return r;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
result !== false && resolve(result);
|
|
||||||
} else {
|
} else {
|
||||||
result = {
|
result = b.key ?? i;
|
||||||
key: b.key,
|
|
||||||
popup: p
|
|
||||||
};
|
|
||||||
resolve(result);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}) ??
|
}) ??
|
||||||
[
|
[
|
||||||
{ text: r('yes', 'Yes'), trigger: p => resolve({ key: 'yes', popup: p }) },
|
{ key: 'yes', text: r('yes', 'Yes') },
|
||||||
{ text: r('no', 'No'), trigger: p => resolve({ key: 'no', popup: p }) }
|
{ key: 'no', text: r('no', 'No') }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
popup.show(parent).then(mask => {
|
popup.show(parent).then(mask => {
|
||||||
|
43
lib/ui/tab.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import "./css/tab.scss";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab 页创建参数类
|
||||||
|
* @typedef TabOption
|
||||||
|
* @property {HTMLElement} container - 父容器
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createElement } from "../functions";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Tab 页
|
||||||
|
* @param {TabOption | HTMLElement} options - 创建选项
|
||||||
|
*/
|
||||||
|
export function createTab(options) {
|
||||||
|
if (options instanceof HTMLElement) {
|
||||||
|
options = { container: options };
|
||||||
|
}
|
||||||
|
let container;
|
||||||
|
if (options?.container instanceof HTMLElement) {
|
||||||
|
container = options.container;
|
||||||
|
if (!container.classList.contains('ui-tab-container')) {
|
||||||
|
container.classList.add('ui-tab-container');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
container = createElement('div', 'ui-tab-container');
|
||||||
|
}
|
||||||
|
container.replaceChildren(
|
||||||
|
createElement('div', header => {
|
||||||
|
header.className = 'ui-tab-header';
|
||||||
|
header.addEventListener('click', e => {
|
||||||
|
const title = e.target;
|
||||||
|
if (title.classList.contains('ui-tab-title')) {
|
||||||
|
// title
|
||||||
|
header.querySelectorAll('.ui-tab-title').forEach(t => t === title ? t.classList.add('selected') : t.classList.remove('selected'));
|
||||||
|
// pages
|
||||||
|
const page = title.dataset.for;
|
||||||
|
container.querySelectorAll('[data-page]').forEach(p => p.style.display = p.dataset.page === page ? 'block' : '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import './css/tooltip.scss';
|
import './css/tooltip.scss';
|
||||||
import { createElement } from "../functions";
|
import { createElement } from "../functions";
|
||||||
// import { global } from "../utility";
|
import { global } from '../utility';
|
||||||
|
|
||||||
const pointerHeight = 12;
|
const pointerHeight = 12;
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ export function setTooltip(container, content, flag = false, parent = null) {
|
|||||||
let lastWidth = p.clientWidth;
|
let lastWidth = p.clientWidth;
|
||||||
let lastHeight = p.clientHeight;
|
let lastHeight = p.clientHeight;
|
||||||
while (p != null) {
|
while (p != null) {
|
||||||
const overflow = window.getComputedStyle(p).overflow;
|
const overflow = global.getComputedStyle(p).overflow;
|
||||||
if (overflow !== 'visible') {
|
if (overflow !== 'visible') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { getCookie, setCookie, deleteCookie } from "./utility/cookie";
|
import { getCookie, setCookie, deleteCookie } from "./utility/cookie";
|
||||||
import { init, r, lang } from "./utility/lgres";
|
import { domLoad, init, r, lang } from "./utility/lgres";
|
||||||
import { get, post, upload } from "./utility/request";
|
import { get, post, upload } from "./utility/request";
|
||||||
import { nullOrEmpty, contains, endsWith, padStart, formatUrl, escapeHtml, escapeEmoji } from "./utility/strings";
|
import { nullOrEmpty, contains, endsWith, padStart, formatUrl, escapeHtml, escapeEmoji } from "./utility/strings";
|
||||||
|
|
||||||
@ -53,6 +53,27 @@ function isPhone(text) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPasswordStrength(password) {
|
||||||
|
if (password == null || typeof password !== 'string') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (password.length < 8) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let secure = 0;
|
||||||
|
if (/[0-9]/.test(password)) { secure++ }
|
||||||
|
if (/[a-z]/.test(password)) { secure++ }
|
||||||
|
if (/[A-Z]/.test(password)) { secure++ }
|
||||||
|
if (/[^0-9a-zA-Z]/.test(password)) { secure++ }
|
||||||
|
return secure;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyPassword(password, min) {
|
||||||
|
min ??= 3;
|
||||||
|
const secure = getPasswordStrength(password);
|
||||||
|
return secure >= min;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
// cookie
|
// cookie
|
||||||
getCookie,
|
getCookie,
|
||||||
@ -83,5 +104,8 @@ export {
|
|||||||
debounce,
|
debounce,
|
||||||
truncate,
|
truncate,
|
||||||
isEmail,
|
isEmail,
|
||||||
isPhone
|
isPhone,
|
||||||
|
getPasswordStrength,
|
||||||
|
verifyPassword,
|
||||||
|
domLoad
|
||||||
}
|
}
|
2
lib/utility/cookie.d.ts
vendored
@ -1,3 +1,3 @@
|
|||||||
export function getCookie(name: string): string
|
export function getCookie(name: string): string
|
||||||
export function setCookie(name: string, value: string, expireDays?: Number): void
|
export function setCookie(name: string, value: string, expireDays?: Number, host?: string, encode?: boolean): void
|
||||||
export function deleteCookie(name: string): void
|
export function deleteCookie(name: string): void
|
@ -1,8 +1,8 @@
|
|||||||
export function setCookie(name, value, expireDays) {
|
export function setCookie(name, value, expireDays, host, encode) {
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let extra = `; domain=${location.host}; path=/`;
|
let extra = `; domain=${host ?? location.hostname}; path=/`;
|
||||||
if (expireDays != null) {
|
if (expireDays != null) {
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
d.setTime(d.getTime() + (expireDays * 24 * 60 * 60 * 1000));
|
d.setTime(d.getTime() + (expireDays * 24 * 60 * 60 * 1000));
|
||||||
@ -11,7 +11,10 @@ export function setCookie(name, value, expireDays) {
|
|||||||
if (/^(https|wss):$/.test(location.protocol)) {
|
if (/^(https|wss):$/.test(location.protocol)) {
|
||||||
extra += '; secure';
|
extra += '; secure';
|
||||||
}
|
}
|
||||||
document.cookie = `${name}=${encodeURIComponent(value)}${extra}`;
|
if (encode !== false) {
|
||||||
|
value = encodeURIComponent(value);
|
||||||
|
}
|
||||||
|
document.cookie = `${name}=${value}${extra}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCookie(name) {
|
export function getCookie(name) {
|
||||||
|
@ -23,17 +23,21 @@ function getCurrentLgId() {
|
|||||||
lgid = 'en';
|
lgid = 'en';
|
||||||
}
|
}
|
||||||
switch (lgid) {
|
switch (lgid) {
|
||||||
case 'en':
|
|
||||||
case 'en_au':
|
case 'en_au':
|
||||||
case 'fr':
|
case 'en_ca':
|
||||||
|
case 'fr_ca':
|
||||||
case 'zh_cn':
|
case 'zh_cn':
|
||||||
return lgid;
|
return lgid;
|
||||||
}
|
}
|
||||||
const lang = lgid.split('_')[0];
|
const lang = lgid.split('_')[0];
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case 'en':
|
case 'en':
|
||||||
|
case 'es':
|
||||||
case 'fr':
|
case 'fr':
|
||||||
|
case 'pt':
|
||||||
return lang;
|
return lang;
|
||||||
|
case 'zh':
|
||||||
|
return 'zh_cn';
|
||||||
}
|
}
|
||||||
return 'en';
|
return 'en';
|
||||||
}
|
}
|
||||||
@ -88,17 +92,37 @@ function applyLanguage(dom, result) {
|
|||||||
} else {
|
} else {
|
||||||
text.innerText = getLanguage(result, key, text.innerText);
|
text.innerText = getLanguage(result, key, text.innerText);
|
||||||
}
|
}
|
||||||
|
// delete text.dataset.lgid;
|
||||||
|
text.dataset.lgid = '';
|
||||||
}
|
}
|
||||||
for (let title of dom.querySelectorAll('[data-title-lgid]')) {
|
for (let title of dom.querySelectorAll('[data-title-lgid]')) {
|
||||||
const key = title.dataset.titleLgid;
|
const key = title.dataset.titleLgid;
|
||||||
title.setAttribute('title', getLanguage(result, key, title.getAttribute('title')));
|
title.setAttribute('title', getLanguage(result, key, title.getAttribute('title')));
|
||||||
|
// delete title.dataset.titleLgid;
|
||||||
|
title.dataset.titleLgid = '';
|
||||||
}
|
}
|
||||||
for (let holder of dom.querySelectorAll('[data-placeholder-lgid]')) {
|
for (let holder of dom.querySelectorAll('[data-placeholder-lgid]')) {
|
||||||
const key = holder.dataset.placeholderLgid;
|
const key = holder.dataset.placeholderLgid;
|
||||||
holder.setAttribute('placeholder', getLanguage(result, key, holder.getAttribute('placeholder')));
|
holder.setAttribute('placeholder', getLanguage(result, key, holder.getAttribute('placeholder')));
|
||||||
|
// delete holder.dataset.placeholderLgid;
|
||||||
|
holder.dataset.placeholderLgid = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function domLoad() {
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let tid = setTimeout(() => reject('timeout'), 30000);
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
clearTimeout(tid);
|
||||||
|
tid = void 0;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
export async function init(dom = document.body, options = {}) {
|
export async function init(dom = document.body, options = {}) {
|
||||||
const lgid = getCurrentLgId();
|
const lgid = getCurrentLgId();
|
||||||
let lgres = localStorage.getItem(getStorageKey(lgid));
|
let lgres = localStorage.getItem(getStorageKey(lgid));
|
||||||
@ -116,20 +140,7 @@ export async function init(dom = document.body, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (document.readyState === 'loading') {
|
await domLoad();
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
let tid = setTimeout(() => reject('timeout'), 30000);
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
clearTimeout(tid);
|
|
||||||
tid = void 0;
|
|
||||||
if (typeof options.callback === 'function') {
|
|
||||||
options.callback(result);
|
|
||||||
}
|
|
||||||
applyLanguage(dom, result);
|
|
||||||
resolve(result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (typeof options.callback === 'function') {
|
if (typeof options.callback === 'function') {
|
||||||
options.callback(result);
|
options.callback(result);
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,8 @@ export function formatUrl(msg) {
|
|||||||
path = consts.path;
|
path = consts.path;
|
||||||
} else if (typeof _network !== 'undefined') {
|
} else if (typeof _network !== 'undefined') {
|
||||||
path = _network.root;
|
path = _network.root;
|
||||||
|
} else if (typeof _net !== 'undefined') {
|
||||||
|
path = _net.root;
|
||||||
}
|
}
|
||||||
for (let r of rs) {
|
for (let r of rs) {
|
||||||
msg = msg.replaceAll(r, `<a target="_blank" href="${r}"><svg><use xlink:href="${path || ''}fonts/fa-regular.svg#link"></use></svg></a>`);
|
msg = msg.replaceAll(r, `<a target="_blank" href="${r}"><svg><use xlink:href="${path || ''}fonts/fa-regular.svg#link"></use></svg></a>`);
|
||||||
|
59
main.js
@ -1,59 +0,0 @@
|
|||||||
import './style.scss'
|
|
||||||
// import javascriptLogo from './javascript.svg'
|
|
||||||
// import { get } from './lib/utility'
|
|
||||||
// import { createPicture, createAudio, createVideo, createPdf } from './lib/ui/media'
|
|
||||||
import './lib/element/style.scss'
|
|
||||||
import ScheduleItem from './lib/element/schedule'
|
|
||||||
import { createElement } from './lib/functions';
|
|
||||||
|
|
||||||
// document.querySelector('#js-logo').src = javascriptLogo
|
|
||||||
|
|
||||||
window.consts = {
|
|
||||||
path: '/',
|
|
||||||
resver: 20231218
|
|
||||||
}
|
|
||||||
|
|
||||||
const schedule = new ScheduleItem();
|
|
||||||
document.querySelector('#container').replaceChildren(
|
|
||||||
schedule.create(),
|
|
||||||
createElement('button', button => {
|
|
||||||
button.innerText = 'Get';
|
|
||||||
button.addEventListener('click', () => console.log(schedule.getParameters()));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// document.querySelector('#container').replaceChildren(
|
|
||||||
// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/1740581frZuuFhz5WWCysxs9oGB.jpg'),
|
|
||||||
// createAudio('audio/amr', 'http://vite.tsanie.org/1055003tb0DisaMu1615PeSXKG.amr'),
|
|
||||||
// createPdf('AG-PRO COMPANIES', 'https://fleet.foresightintelligence.com/doc/mmspart/1333321JLrYhkGYqsw6QSVMx3d.pdf'),
|
|
||||||
// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/138390UGZUMWRmqBsEgPnWuW16.gif'),
|
|
||||||
// // createVideo('https://fleet.foresightintelligence.com/doc/mmspart/17359338sR5qsG7TvS7eaUdP9PL.mp4'),
|
|
||||||
// );
|
|
||||||
|
|
||||||
/*
|
|
||||||
init(null, {
|
|
||||||
template: '/res.json',
|
|
||||||
callback: result => console.log(result)
|
|
||||||
}).then(() => {
|
|
||||||
// document.querySelector('#create-icon').appendChild(createIcon('fa-solid', 'user-edit'))
|
|
||||||
resolveIcon(document.querySelector('#create-icon'))
|
|
||||||
|
|
||||||
// document.querySelector('#create-checkbox').appendChild(createCheckbox({
|
|
||||||
// label: 'Switch 1'
|
|
||||||
// }))
|
|
||||||
resolveCheckbox(document.querySelector('#create-checkbox'))
|
|
||||||
|
|
||||||
resolveTooltip(document.querySelector('#buttons'))
|
|
||||||
|
|
||||||
document.querySelector('#button-fetch').addEventListener('click', () => {
|
|
||||||
get('javascript.svg', {
|
|
||||||
// contentType: '',
|
|
||||||
customHeaders: {
|
|
||||||
'X-Auth': 'test/authentication'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(r => r.blob())
|
|
||||||
.then(blob => document.querySelector('#js-logo').src = URL.createObjectURL(blob));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
1277
package-lock.json
generated
16
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ui-lib",
|
"name": "ui-lib",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.1",
|
"version": "1.0.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
@ -28,14 +28,14 @@
|
|||||||
"jsdoc-date": "jsdoc -c jsdoc-date.json"
|
"jsdoc-date": "jsdoc -c jsdoc-date.json"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mxssfd/typedoc-theme": "^1.1.3",
|
"@mxssfd/typedoc-theme": "^1.1.6",
|
||||||
"clean-jsdoc-theme": "^4.2.17",
|
"clean-jsdoc-theme": "^4.3.0",
|
||||||
"docdash": "^2.0.2",
|
"docdash": "^2.0.2",
|
||||||
"jsdoc": "^4.0.2",
|
"jsdoc": "^4.0.3",
|
||||||
"postcss-preset-env": "^9.4.0",
|
"postcss-preset-env": "^9.6.0",
|
||||||
"sass": "^1.71.0",
|
"sass": "^1.77.8",
|
||||||
"typedoc": "^0.25.8",
|
"typedoc": "^0.26.5",
|
||||||
"vite": "^5.1.3",
|
"vite": "^5.3.4",
|
||||||
"vite-plugin-externals": "^0.6.2"
|
"vite-plugin-externals": "^0.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 430 KiB |
4948
sample/fonts/fa-light.svg
Normal file
After Width: | Height: | Size: 1.4 MiB |
4948
sample/fonts/fa-regular.svg
Normal file
After Width: | Height: | Size: 1.2 MiB |
4948
sample/fonts/fa-solid.svg
Normal file
After Width: | Height: | Size: 1.0 MiB |
33
sample/index.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>UI Lib</title>
|
||||||
|
<link href="dist/ui.min.css" rel="stylesheet" />
|
||||||
|
<script src="amrnb.js"></script>
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
|
<script src="dist/ui.min.js"></script>
|
||||||
|
<script src="dist/utility.min.js"></script>
|
||||||
|
<style type="text/css">
|
||||||
|
#container>.ui-grid {
|
||||||
|
width: 1000px;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div style="display: flex; flex-direction: column; align-items: flex-start">
|
||||||
|
<div id="container"></div>
|
||||||
|
<!--<button id="setItem">set item</button>
|
||||||
|
<button id="addItem">add item</button>
|
||||||
|
<button id="addItems">add items</button>
|
||||||
|
<button id="removeItem">remove item</button>
|
||||||
|
<button id="removeItems">remove items</button>-->
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B |
350
sample/main.js
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
// import './style.scss'
|
||||||
|
// import javascriptLogo from './javascript.svg'
|
||||||
|
// import { get } from './lib/utility'
|
||||||
|
// import { createPicture, createAudio, createVideo, createPdf } from './lib/ui/media'
|
||||||
|
// import './lib/element/style.scss'
|
||||||
|
// import ScheduleItem from './lib/element/schedule'
|
||||||
|
// import { createElement } from './lib/functions';
|
||||||
|
// import Grid from './lib/ui/grid/grid';
|
||||||
|
|
||||||
|
// document.querySelector('#js-logo').src = javascriptLogo
|
||||||
|
|
||||||
|
window.consts = {
|
||||||
|
path: '/',
|
||||||
|
resver: 20231218
|
||||||
|
}
|
||||||
|
|
||||||
|
// const DateSelector = window['lib-ui'].DateSelector;
|
||||||
|
// const formatDate = window['lib-ui'].formatDate;
|
||||||
|
|
||||||
|
// window.addEventListener('load', () => {
|
||||||
|
// DateSelector.resolve(document.querySelector('#container'), function (date) {
|
||||||
|
// console.log(`element(#${this.element.id}), date changed to: ${formatDate(date)}`);
|
||||||
|
|
||||||
|
// const value = document.querySelector('#dateFrom').value;
|
||||||
|
// console.log(`dateFrom.value = '${value}', formatted: '${formatDate(value)}'`);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
const Grid = window['lib-ui'].Grid;
|
||||||
|
const createElement = window['lib-ui'].createElement;
|
||||||
|
const toDateValue = window['lib-ui'].toDateValue;
|
||||||
|
const showConfirm = window['lib-ui'].showConfirm;
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const grid = new Grid('#container');
|
||||||
|
grid.columns = [
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
// type: Grid.ColumnTypes.Common,
|
||||||
|
caption: 'Name',
|
||||||
|
captionStyle: {
|
||||||
|
'font-style': 'italic'
|
||||||
|
},
|
||||||
|
width: 150,
|
||||||
|
allowFilter: true,
|
||||||
|
totalCss: {
|
||||||
|
'text-align': 'right'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'birthday',
|
||||||
|
type: Grid.ColumnTypes.Date,
|
||||||
|
caption: 'Birthday',
|
||||||
|
width: 120,
|
||||||
|
dateMin: '1900-01-01',
|
||||||
|
dateMax: '2025-01-01',
|
||||||
|
dateValueFormatter: toDateValue
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'age',
|
||||||
|
type: Grid.ColumnTypes.Input,
|
||||||
|
caption: 'Age',
|
||||||
|
enabled: false,
|
||||||
|
align: 'right',
|
||||||
|
filter: item => {
|
||||||
|
const ms = new Date() - new Date(item.birthday);
|
||||||
|
const age = Math.floor(ms / 1000 / 60 / 60 / 24 / 365);
|
||||||
|
return String(age);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sex',
|
||||||
|
type: Grid.ColumnTypes.Dropdown,
|
||||||
|
caption: 'Sex',
|
||||||
|
source: [
|
||||||
|
{ value: 'male', text: 'Male' },
|
||||||
|
{ value: 'female', text: 'Female' },
|
||||||
|
{ value: 'other', text: 'Other' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'active',
|
||||||
|
type: Grid.ColumnTypes.Checkbox,
|
||||||
|
caption: 'Active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'remove',
|
||||||
|
type: Grid.ColumnTypes.Icon,
|
||||||
|
text: 'times',
|
||||||
|
resizable: false,
|
||||||
|
sortable: false,
|
||||||
|
orderable: false,
|
||||||
|
tooltip: 'Remove',
|
||||||
|
events: {
|
||||||
|
onclick: function () {
|
||||||
|
showConfirm('Remove', `Are you sure you want to remove "${this.name}"?`, [
|
||||||
|
{
|
||||||
|
key: 'yes',
|
||||||
|
text: 'Yes',
|
||||||
|
trigger: () => {
|
||||||
|
console.log('yes');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'no',
|
||||||
|
text: 'No'
|
||||||
|
}
|
||||||
|
], 'question')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// grid.height = 700 - 36;
|
||||||
|
// grid.autoResize = false;
|
||||||
|
grid.multiSelect = true;
|
||||||
|
// grid.expandable = true;
|
||||||
|
// grid.expandableGenerator = item => ({
|
||||||
|
// element: createElement('div', div => {
|
||||||
|
// div.innerText = JSON.stringify(item);
|
||||||
|
// })
|
||||||
|
// });
|
||||||
|
const fnames = '李王张刘陈杨赵黄周吴徐孙胡朱高林何郭马罗梁宋郑谢韩唐冯于董萧程曹袁邓许傅沈曾彭吕苏卢蒋蔡贾丁魏薛叶阎余潘杜戴夏钟汪田任姜范方石姚谭廖邹熊金陆郝孔白崔康毛邱秦江史顾侯邵孟龙万段漕钱汤尹黎易常武乔贺赖龚文';
|
||||||
|
const names = '先帝创业未半而中道崩殂今天下三分益州疲弊此诚危急存亡之秋也然侍卫之臣不懈于内忠志之士忘身于外者盖追先帝之殊遇欲报之于陛下也诚宜开张圣听以光先帝遗德恢弘志士之气不宜妄自菲薄引喻失义以塞忠谏之路也';
|
||||||
|
// grid.source = Array.from({ length: 200 }).map(() => {
|
||||||
|
// const r = Math.random();
|
||||||
|
// const r2 = Math.random();
|
||||||
|
// const date = new Date(631152000000 + Math.floor(20 * 365 * 24 * 60 * 60 * 1000 * r));
|
||||||
|
// return {
|
||||||
|
// name: `${fnames[Math.floor(r * fnames.length)]}${names[Math.floor(r2 * names.length)]}`,
|
||||||
|
// birthday: `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`,
|
||||||
|
// sex: r > 0.5 ? 'female' : 'male',
|
||||||
|
// active: r2 > 0.5 ? true : false
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
const getCountFilter = key => (it) => {
|
||||||
|
const count = it[key];
|
||||||
|
if (isNaN(count)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// return count.toLocaleString();
|
||||||
|
return String(count).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
};
|
||||||
|
grid.columns = [
|
||||||
|
{ key: 'name', caption: '包名称', type: Grid.ColumnTypes.Input },
|
||||||
|
{ key: 'version', caption: '版本号' },
|
||||||
|
{ key: 'author', caption: '作者' },
|
||||||
|
{ key: 'count', caption: '下载量', filter: getCountFilter('count'), align: 'right' },
|
||||||
|
{ key: 'mauiName', caption: 'Maui 对应包' },
|
||||||
|
{ key: 'mauiVersion', caption: '版本号' },
|
||||||
|
{ key: 'mauiNet', caption: '.NET' },
|
||||||
|
{ key: 'mauiCount', caption: '下载量', filter: getCountFilter('mauiCount'), align: 'right' }
|
||||||
|
];
|
||||||
|
grid.source = [
|
||||||
|
{
|
||||||
|
name: 'Dynatrace.OneAgent.Xamarin',
|
||||||
|
version: '8.283.1',
|
||||||
|
author: 'Dynatrace',
|
||||||
|
count: 308681,
|
||||||
|
mauiName: 'Dynatrace.OneAgent.MAUI',
|
||||||
|
mauiVersion: '1.283.1',
|
||||||
|
mauiNet: '7.0',
|
||||||
|
mauiCount: 12351
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Esri.ArcGISRuntime.Xamarin.Forms',
|
||||||
|
version: '100.15.4',
|
||||||
|
author: 'Esri_Inc',
|
||||||
|
count: 249909,
|
||||||
|
mauiName: 'Esri.ArcGISRuntime.Maui',
|
||||||
|
mauiVersion: '200.3.0',
|
||||||
|
mauiNet: '8.0',
|
||||||
|
mauiCount: 40913
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Scandit.DataCapture.Core.Xamarin',
|
||||||
|
version: '6.22.0',
|
||||||
|
author: 'Scandit',
|
||||||
|
count: 527424,
|
||||||
|
mauiName: 'Scandit.DataCapture.Core.Maui',
|
||||||
|
mauiVersion: '6.22.0',
|
||||||
|
mauiNet: '6.0',
|
||||||
|
mauiCount: 14032
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Scandit.DataCapture.Barcode.Xamarin',
|
||||||
|
version: '6.22.0',
|
||||||
|
author: 'Scandit',
|
||||||
|
count: 417823,
|
||||||
|
mauiName: 'Scandit.DataCapture.Barcode.Maui',
|
||||||
|
mauiVersion: '6.22.0',
|
||||||
|
mauiNet: '6.0',
|
||||||
|
mauiCount: 6048
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SkiaSharp.Views.Forms',
|
||||||
|
version: '2.88.7',
|
||||||
|
author: 'Microsoft Xamarin',
|
||||||
|
count: 7229049,
|
||||||
|
mauiName: 'SkiaSharp.Views.Maui.Core',
|
||||||
|
mauiVersion: '2.88.7',
|
||||||
|
mauiNet: '7.0',
|
||||||
|
mauiCount: 558468
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mauiName: 'SkiaSharp.Views.Maui.Controls',
|
||||||
|
mauiVersion: '2.88.7',
|
||||||
|
mauiNet: '7.0',
|
||||||
|
mauiCount: 550081
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ZXing.Net.Mobile',
|
||||||
|
version: '2.4.1',
|
||||||
|
author: 'redth',
|
||||||
|
count: 7187155,
|
||||||
|
mauiName: 'ZXing.Net.Maui',
|
||||||
|
mauiVersion: '0.4.0',
|
||||||
|
mauiNet: '7.0',
|
||||||
|
mauiCount: 177002
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ZXing.Net.Mobile.Forms',
|
||||||
|
version: '2.4.1',
|
||||||
|
author: 'redth',
|
||||||
|
count: 5494785,
|
||||||
|
mauiName: 'ZXing.Net.Maui.Controls',
|
||||||
|
mauiVersion: '0.4.0',
|
||||||
|
mauiNet: '7.0',
|
||||||
|
mauiCount: 104144
|
||||||
|
}
|
||||||
|
]
|
||||||
|
grid.init();
|
||||||
|
// setTimeout(() => {
|
||||||
|
// grid.total = { name: '合计', birthday: grid.source.length };
|
||||||
|
// }, 1000);
|
||||||
|
|
||||||
|
window.grid = grid;
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const grid = new Grid('#container');
|
||||||
|
grid.columns = ['a', 'b'].map(i => ({
|
||||||
|
key: i,
|
||||||
|
caption: `column ${i}`,
|
||||||
|
width: 200,
|
||||||
|
allowFilter: true
|
||||||
|
}));
|
||||||
|
grid.multiSelect = true;
|
||||||
|
grid.init();
|
||||||
|
|
||||||
|
const items = [];
|
||||||
|
for (let i = 0; i < 10; ++i) {
|
||||||
|
items.push({ a: i + 1, b: `row ${i + 1}` });
|
||||||
|
}
|
||||||
|
grid.source = items;
|
||||||
|
|
||||||
|
window.grid = grid;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#setItem').addEventListener('click', () => {
|
||||||
|
if (window.grid.selectedIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.grid.setItem(window.grid.selectedIndex, {
|
||||||
|
a: 'new',
|
||||||
|
b: 'new item'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#addItem').addEventListener('click', () => {
|
||||||
|
window.grid.addItem({
|
||||||
|
a: 'add',
|
||||||
|
b: 'add item'
|
||||||
|
}, window.grid.selectedIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#addItems').addEventListener('click', () => {
|
||||||
|
window.grid.addItems([
|
||||||
|
{
|
||||||
|
a: 'add1',
|
||||||
|
b: 'add item 1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: 'add2',
|
||||||
|
b: 'add item 2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: 'add3',
|
||||||
|
b: 'add item 3'
|
||||||
|
}
|
||||||
|
], window.grid.selectedIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#removeItem').addEventListener('click', () => {
|
||||||
|
if (window.grid.selectedIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.grid.removeItem(window.grid.selectedIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#removeItems').addEventListener('click', () => {
|
||||||
|
window.grid.removeItems(window.grid.selectedIndexes);
|
||||||
|
});
|
||||||
|
//*/
|
||||||
|
|
||||||
|
// const schedule = new ScheduleItem();
|
||||||
|
// document.querySelector('#container').replaceChildren(
|
||||||
|
// schedule.create(),
|
||||||
|
// createElement('button', button => {
|
||||||
|
// button.innerText = 'Get';
|
||||||
|
// button.addEventListener('click', () => console.log(schedule.getParameters()));
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
|
||||||
|
// document.querySelector('#container').replaceChildren(
|
||||||
|
// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/1740581frZuuFhz5WWCysxs9oGB.jpg'),
|
||||||
|
// createAudio('audio/amr', 'http://vite.tsanie.org/1055003tb0DisaMu1615PeSXKG.amr'),
|
||||||
|
// createPdf('AG-PRO COMPANIES', 'https://fleet.foresightintelligence.com/doc/mmspart/1333321JLrYhkGYqsw6QSVMx3d.pdf'),
|
||||||
|
// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/138390UGZUMWRmqBsEgPnWuW16.gif'),
|
||||||
|
// // createVideo('https://fleet.foresightintelligence.com/doc/mmspart/17359338sR5qsG7TvS7eaUdP9PL.mp4'),
|
||||||
|
// );
|
||||||
|
|
||||||
|
/*
|
||||||
|
init(null, {
|
||||||
|
template: '/res.json',
|
||||||
|
callback: result => console.log(result)
|
||||||
|
}).then(() => {
|
||||||
|
// document.querySelector('#create-icon').appendChild(createIcon('fa-solid', 'user-edit'))
|
||||||
|
resolveIcon(document.querySelector('#create-icon'))
|
||||||
|
|
||||||
|
// document.querySelector('#create-checkbox').appendChild(createCheckbox({
|
||||||
|
// label: 'Switch 1'
|
||||||
|
// }))
|
||||||
|
resolveCheckbox(document.querySelector('#create-checkbox'))
|
||||||
|
|
||||||
|
resolveTooltip(document.querySelector('#buttons'))
|
||||||
|
|
||||||
|
document.querySelector('#button-fetch').addEventListener('click', () => {
|
||||||
|
get('javascript.svg', {
|
||||||
|
// contentType: '',
|
||||||
|
customHeaders: {
|
||||||
|
'X-Auth': 'test/authentication'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(r => r.blob())
|
||||||
|
.then(blob => document.querySelector('#js-logo').src = URL.createObjectURL(blob));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
*/
|
13
sample/web.config
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<system.webServer>
|
||||||
|
<staticContent>
|
||||||
|
<mimeMap fileExtension=".amr" mimeType="audio/amr" />
|
||||||
|
</staticContent>
|
||||||
|
<httpProtocol>
|
||||||
|
<customHeaders>
|
||||||
|
<add name="Access-Control-Allow-Origin" value="*" />
|
||||||
|
</customHeaders>
|
||||||
|
</httpProtocol>
|
||||||
|
</system.webServer>
|
||||||
|
</configuration>
|
@ -14,8 +14,8 @@ const libraries = [
|
|||||||
lib: {
|
lib: {
|
||||||
entry: './lib/ui.js',
|
entry: './lib/ui.js',
|
||||||
name: 'lib-ui',
|
name: 'lib-ui',
|
||||||
formats: ['umd'],
|
formats: ['cjs', 'umd'],
|
||||||
fileName: (_format, name) => `${name}.min.js`
|
fileName: (format, name) => format === 'cjs' ? `${name}-cjs.min.js` : `${name}.min.js`
|
||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
|