business logic, customer communication.
This commit is contained in:
parent
7c5a9214a1
commit
6157f994a9
@ -161,7 +161,7 @@ $listMaxHeight: 210px;
|
||||
}
|
||||
}
|
||||
|
||||
>.dropdown-panel {
|
||||
>.dropdown-box {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
|
@ -64,7 +64,7 @@
|
||||
&,
|
||||
input[type="text"],
|
||||
.dropdown-wrapper>.dropdown-header>.dropdown-text,
|
||||
.dropdown-wrapper>.dropdown-panel>.dropdown-list {
|
||||
.dropdown-wrapper>.dropdown-box>.dropdown-list {
|
||||
font-size: var(--font-size);
|
||||
}
|
||||
|
||||
@ -283,7 +283,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
>.dropdown-panel {
|
||||
>.dropdown-box {
|
||||
top: calc(var(--line-height) + 2px);
|
||||
|
||||
&.slide-up {
|
||||
|
5
lib/app.js
Normal file
5
lib/app.js
Normal file
@ -0,0 +1,5 @@
|
||||
import CustomerCommunication from "./app/communications/customer";
|
||||
|
||||
export {
|
||||
CustomerCommunication
|
||||
}
|
28
lib/app/communications/customer.d.ts
vendored
Normal file
28
lib/app/communications/customer.d.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
interface CheckboxConfig {
|
||||
checked: boolean
|
||||
onchanged: (flag: boolean) => void
|
||||
}
|
||||
|
||||
interface InitConfig {
|
||||
autoUpdates?: CheckboxConfig;
|
||||
statusLink?: CheckboxConfig;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
export class CustomerCommunication {
|
||||
get autoUpdatesEnabled(): boolean;
|
||||
set autoUpdatesEnabled(enabled: boolean);
|
||||
get autoUpdates(): boolean;
|
||||
set autoUpdates(checked: boolean);
|
||||
|
||||
get statusLinkEnabled(): boolean;
|
||||
set statusLinkEnabled(enabled: boolean);
|
||||
get statusLink(): boolean;
|
||||
set statusLink(checked: boolean);
|
||||
|
||||
create(): HTMLElement;
|
||||
}
|
||||
|
||||
declare var CustomerCommunication: {
|
||||
new(opt: InitConfig): CustomerCommunication
|
||||
}
|
226
lib/app/communications/customer.js
Normal file
226
lib/app/communications/customer.js
Normal file
@ -0,0 +1,226 @@
|
||||
import "./style.scss";
|
||||
import { createElement, createElementInit } from "../../functions";
|
||||
import { r } from "../../utility/lgres";
|
||||
import { isEmail, isPhone } from "../../utility";
|
||||
import { setTooltip } from "../../ui/tooltip";
|
||||
import { createIcon } from "../../ui/icon";
|
||||
import { createCheckbox } from "../../ui/checkbox";
|
||||
import { createBox } from "./lib";
|
||||
|
||||
class CustomerCommunication {
|
||||
#container;
|
||||
#option;
|
||||
#contacts;
|
||||
#followers;
|
||||
#enter;
|
||||
#message;
|
||||
|
||||
constructor(opt) {
|
||||
this.#option = opt ?? {};
|
||||
}
|
||||
|
||||
get #autoUpdates() { return this.#container.querySelector('.check-auto-update>input') }
|
||||
get autoUpdatesEnabled() { return this.#autoUpdates?.disabled !== true }
|
||||
set autoUpdatesEnabled(flag) {
|
||||
const element = this.#autoUpdates;
|
||||
element != null && (element.disabled = flag === false);
|
||||
}
|
||||
get autoUpdates() { return this.#autoUpdates?.checked }
|
||||
set autoUpdates(flag) {
|
||||
const element = this.#autoUpdates;
|
||||
element != null && (element.checked = flag);
|
||||
}
|
||||
|
||||
get #statusLink() { return this.#container.querySelector('.check-status-link') }
|
||||
get statusLinkEnabled() { return this.#statusLink?.disabled !== true }
|
||||
set statusLinkEnabled(flag) {
|
||||
const element = this.#statusLink;
|
||||
element != null && (element.disabled = flag === false);
|
||||
}
|
||||
get statusLink() { return this.#statusLink?.checked }
|
||||
set statusLink(flag) {
|
||||
const element = this.#statusLink;
|
||||
element != null && (element.checked = flag);
|
||||
}
|
||||
|
||||
get text() { return this.#enter?.value }
|
||||
set text(s) {
|
||||
const element = this.#enter;
|
||||
element != null && (element.value = s);
|
||||
}
|
||||
|
||||
get contacts() {
|
||||
return [...this.#contacts.children].map(el => {
|
||||
const span = el.querySelector('span');
|
||||
return { 'Key': span.innerText, 'Value': span.dataset.name };
|
||||
});
|
||||
}
|
||||
set contacts(contacts) {
|
||||
this.#contacts.replaceChildren();
|
||||
if (contacts?.length > 0) {
|
||||
for (let c of contacts) {
|
||||
if (c.OptOut || c.OptOut_BC) {
|
||||
continue;
|
||||
}
|
||||
const mp = String(c.MobilePhone).trim();
|
||||
const email = String(c.Email).trim();
|
||||
if (c.ContactPreference === '0' && !isPhone(mp) ||
|
||||
c.ContactPreference === '1' && !isEmail(email)) {
|
||||
continue;
|
||||
}
|
||||
const to = c.ContactPreference === '0' ? mp : email;
|
||||
this.#contacts.appendChild(
|
||||
createElement('div', 'contact-item',
|
||||
createIcon('fa-light', c.ContactPreference === '0' ? 'comment-lines' : 'envelope'),
|
||||
createElementInit('span', span => {
|
||||
span.dataset.name = c.Name;
|
||||
span.innerText = to;
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create() {
|
||||
// functions
|
||||
const checkAutoUpdate = createCheckbox({
|
||||
className: 'check-auto-update',
|
||||
checked: this.#option.autoUpdates?.checked,
|
||||
enabled: this.#option.autoUpdates?.enabled,
|
||||
checkedNode: createIcon('fa-regular', 'redo-alt'),
|
||||
uncheckedNode: createIcon('fa-regular', 'ban'),
|
||||
onchange: () => {
|
||||
setTooltip(checkAutoUpdate, this.checked ?
|
||||
r('autoUpdateEnabled', 'Auto Updates Enabled') :
|
||||
r('autoUpdateDisabled', 'Auto Updates Disabled'));
|
||||
if (typeof this.#option.autoUpdates?.onchanged === 'function') {
|
||||
this.#option.autoUpdates.onchanged(this.checked);
|
||||
}
|
||||
}
|
||||
});
|
||||
const checkLink = createCheckbox({
|
||||
className: 'check-status-link',
|
||||
checked: this.#option.statusLink?.checked,
|
||||
enabled: this.#option.statusLink?.enabled,
|
||||
checkedNode: createIcon('fa-regular', 'link'),
|
||||
uncheckedNode: createIcon('fa-regular', 'unlink'),
|
||||
onchange: () => {
|
||||
setTooltip(checkLink, this.checked ?
|
||||
r('statusLinkIncluded', 'Status Link Included') :
|
||||
r('statusLinkExcluded', 'Status Link Excluded'));
|
||||
if (typeof this.#option.statusLink?.onchanged === 'function') {
|
||||
this.#option.statusLink.onchanged(this.checked);
|
||||
}
|
||||
}
|
||||
});
|
||||
const container = createBox(
|
||||
createElement('div', null,
|
||||
createElementInit('div', div => div.innerText = r('messages', 'Customer Communication')),
|
||||
createElementInit('div', div => div.innerText = consts.user?.companyName)),
|
||||
[
|
||||
setTooltip(checkAutoUpdate, r('autoUpdateDisabled', 'Auto Updates Disabled')),
|
||||
setTooltip(checkLink, r('statusLinkExcluded', 'Status Link Excluded'))
|
||||
]
|
||||
);
|
||||
// contacts
|
||||
const contacts = createElement('div');
|
||||
container.append(
|
||||
createElement('div', 'contact-bar',
|
||||
createIcon('fa-solid', 'user-circle', {
|
||||
'fill': 'lightgray',
|
||||
'flex': '0 0 auto'
|
||||
}),
|
||||
createElementInit('div', div => div.style.flex = '1 1 auto',
|
||||
contacts,
|
||||
createElementInit('button', button => {
|
||||
button.className = 'roundbtn';
|
||||
button.style.backgroundColor = 'rgb(1, 199, 172)';
|
||||
button.appendChild(createIcon('fa-solid', 'user-edit'));
|
||||
setTooltip(button, r('editContacts', 'Edit Contacts'));
|
||||
button.addEventListener('click', () => {
|
||||
// TODO:
|
||||
});
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
this.#contacts = contacts;
|
||||
// followers
|
||||
const followers = createElement('div');
|
||||
container.append(
|
||||
createElement('div', 'contact-bar follower-bar',
|
||||
createIcon('fa-solid', 'user-tag', {
|
||||
'fill': '#fff',
|
||||
'background-color': 'lightgray',
|
||||
'box-sizing': 'border-box',
|
||||
'border-radius': '15px',
|
||||
'padding': '4px',
|
||||
'flex': '0 0 auto'
|
||||
}),
|
||||
createElementInit('div', div => div.style.flex = '1 1 auto',
|
||||
followers,
|
||||
createElementInit('button', button => {
|
||||
button.className = 'roundbtn';
|
||||
button.style.backgroundColor = 'rgb(48, 107, 255)';
|
||||
button.appendChild(createIcon('fa-solid', 'pen'));
|
||||
setTooltip(button, r('editFollower', 'Edit Followers'));
|
||||
button.addEventListener('click', () => {
|
||||
// TODO:
|
||||
});
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
this.#followers = followers;
|
||||
// enter box
|
||||
const enter = createElement('textarea');
|
||||
enter.placeholder = r('typeComment', 'Enter Message Here');
|
||||
enter.maxLength = 3000;
|
||||
this.#enter = enter;
|
||||
container.appendChild(
|
||||
createElement('div', 'message-bar',
|
||||
enter,
|
||||
createElementInit('div', div => div.style.textAlign = 'right',
|
||||
createElementInit('button', button => {
|
||||
button.className = 'roundbtn';
|
||||
button.style.backgroundColor = 'rgb(19, 150, 204)';
|
||||
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
||||
setTooltip(button, r('sendMessage', 'Send Message'));
|
||||
button.addEventListener('click', () => {
|
||||
// TODO: Add text
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const message = createElement('div');
|
||||
this.#message = message;
|
||||
container.appendChild(message);
|
||||
return this.#container = container;
|
||||
}
|
||||
|
||||
load(data, contacts) {
|
||||
const children = [];
|
||||
if (data?.length > 0) {
|
||||
for (let comm of data) {
|
||||
const div = document.createElement('div', 'txtdiv');
|
||||
let name;
|
||||
if (comm.IsReply) {
|
||||
const email = isEmail(comm.Sender);
|
||||
const c = contacts.find(c => email ?
|
||||
c.Email === comm.Sender :
|
||||
c.MobilePhone === comm.Sender);
|
||||
if (c != null) {
|
||||
name = c.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
children[0].style.marginTop = '0';
|
||||
this.#message.append(...children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomerCommunication;
|
1
lib/app/communications/lib.d.ts
vendored
Normal file
1
lib/app/communications/lib.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export function createBox(title: HTMLElement, functions: HTMLElement[]): HTMLElement
|
15
lib/app/communications/lib.js
Normal file
15
lib/app/communications/lib.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { createElement, createElementInit } from "../../functions";
|
||||
|
||||
function createBox(title, functions) {
|
||||
const container = createElement('div', 'comm');
|
||||
const header = createElement('div', 'title-bar',
|
||||
title,
|
||||
createElement('div', 'title-functions', ...functions)
|
||||
);
|
||||
container.appendChild(header);
|
||||
return container;
|
||||
}
|
||||
|
||||
export {
|
||||
createBox
|
||||
}
|
242
lib/app/communications/style.scss
Normal file
242
lib/app/communications/style.scss
Normal file
@ -0,0 +1,242 @@
|
||||
:root {
|
||||
--dark-fore-color: #fff;
|
||||
--dark-fore-opacity-color: rgba(255, 255, 255, .6);
|
||||
--title-color: #fff;
|
||||
--title-bg-color: rgb(68, 114, 196);
|
||||
--strong-color: #333;
|
||||
--medium-font-size: .875rem;
|
||||
}
|
||||
|
||||
.comm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 320px;
|
||||
background-color: var(--dark-fore-color);
|
||||
border: 1px solid var(--title-bg-color);
|
||||
|
||||
.roundbtn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
padding: 7px;
|
||||
margin-left: 10px;
|
||||
fill: var(--dark-fore-color);
|
||||
border-radius: 15px;
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: background-color .2s;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--dark-fore-opacity-color);
|
||||
|
||||
>svg {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
fill: lightgray;
|
||||
background-color: transparent !important;
|
||||
|
||||
&:hover {
|
||||
opacity: unset;
|
||||
}
|
||||
}
|
||||
|
||||
>svg {
|
||||
width: 13px;
|
||||
height: 14px;
|
||||
display: block;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
}
|
||||
|
||||
.image-check-box {
|
||||
user-select: none;
|
||||
|
||||
>input[type="checkbox"] {
|
||||
display: none;
|
||||
|
||||
&:checked~.unchecked {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:checked~.checked {
|
||||
display: unset;
|
||||
}
|
||||
}
|
||||
|
||||
>.unchecked {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
>.checked {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
flex: 0 0 auto;
|
||||
padding: 5px 0 5px 10px;
|
||||
color: var(--title-color);
|
||||
background-color: var(--title-bg-color);
|
||||
line-height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
>div {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
>.title-functions {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
|
||||
>label {
|
||||
margin: 0 4px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-color: var(--dark-fore-color);
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color .2s;
|
||||
|
||||
>svg {
|
||||
fill: var(--strong-color);
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--dark-fore-opacity-color);
|
||||
|
||||
>svg {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contact-bar {
|
||||
flex: 0 0 auto;
|
||||
padding: 4px 0;
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--title-bg-color);
|
||||
|
||||
>svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 22px;
|
||||
|
||||
>svg {
|
||||
flex: 0 0 auto;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 6px;
|
||||
fill: var(--strong-color);
|
||||
}
|
||||
|
||||
>span {
|
||||
flex: 1 1 auto;
|
||||
color: var(--strong-color);
|
||||
font-size: var(--medium-font-size);
|
||||
}
|
||||
}
|
||||
|
||||
.roundbtn {
|
||||
float: right;
|
||||
margin: 4px 10px 10px;
|
||||
|
||||
>svg {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-bar {
|
||||
flex: 0 0 auto;
|
||||
border-bottom: 1px solid var(--title-bg-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
>textarea {
|
||||
padding: 10px 10px 0;
|
||||
border: none;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
>div {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
&+div {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
margin-top: 8px;
|
||||
|
||||
.msgdiv {
|
||||
margin-top: 5px;
|
||||
border-bottom: solid 1px lightgray;
|
||||
padding: 3px 10px 5px;
|
||||
line-height: 1.5rem;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
font-size: .8125rem;
|
||||
color: #333;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.msgposter {
|
||||
font-weight: bold;
|
||||
|
||||
&+div {
|
||||
line-height: 1.2rem;
|
||||
padding: 8px 20px;
|
||||
background-color: rgb(244, 244, 244);
|
||||
border-radius: 5px;
|
||||
white-space: pre-wrap;
|
||||
text-align: left;
|
||||
word-break: break-word;
|
||||
|
||||
&.txtself {
|
||||
max-width: 240px;
|
||||
margin-right: 10px;
|
||||
background-color: #9eea6a;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&.txtother {
|
||||
max-width: 240px;
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.msgtime {
|
||||
text-align: right;
|
||||
color: #aaa;
|
||||
font-size: .7rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
lib/functions.d.ts
vendored
Normal file
2
lib/functions.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export function createElement<K extends keyof HTMLElementTagNameMap>(tagName: K, className?: string, ...children?: (Node | string)[]): HTMLElementTagNameMap[K];
|
||||
export function createElementInit<K extends keyof HTMLElementTagNameMap>(tagName: K, init?: (element: HTMLElementTagNameMap[K]) => void, ...children?: (Node | string)[]): HTMLElementTagNameMap[K];
|
19
lib/functions.js
Normal file
19
lib/functions.js
Normal file
@ -0,0 +1,19 @@
|
||||
function createElement(tagName, className, ...children) {
|
||||
return createElementInit(tagName, className && (element => element.className = className), ...children);
|
||||
}
|
||||
|
||||
function createElementInit(tagName, init, ...children) {
|
||||
const element = document.createElement(tagName);
|
||||
if (typeof init === 'function') {
|
||||
init(element);
|
||||
}
|
||||
if (children.length > 0) {
|
||||
element.append(...children);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export {
|
||||
createElement,
|
||||
createElementInit
|
||||
}
|
3
lib/ui/checkbox.d.ts
vendored
3
lib/ui/checkbox.d.ts
vendored
@ -1,8 +1,9 @@
|
||||
interface CheckboxOptions {
|
||||
className?: string;
|
||||
enabled?: boolean;
|
||||
type?: string;
|
||||
label?: string | HTMLElement;
|
||||
checked?: boolean;
|
||||
isImage?: boolean;
|
||||
imageHeight?: Number;
|
||||
checkedNode?: HTMLElement;
|
||||
uncheckedNode?: HTMLElement;
|
||||
|
@ -14,7 +14,6 @@
|
||||
type?: string;
|
||||
label?: string;
|
||||
checked?: boolean;
|
||||
isImage?: boolean;
|
||||
imageHeight?: Number;
|
||||
checkedNode?: HTMLElement;
|
||||
uncheckedNode?: HTMLElement;
|
||||
@ -22,6 +21,14 @@
|
||||
onchange?: (this: HTMLInputElement, ev: Event) => any;
|
||||
}</pre>
|
||||
</p>
|
||||
<h4>className?: string</h4>
|
||||
<p>
|
||||
复选框的自定义 className
|
||||
</p>
|
||||
<h4>enabled?: boolean</h4>
|
||||
<p>
|
||||
复选框默认是否可用
|
||||
</p>
|
||||
<h4>type?: string</h4>
|
||||
<p>
|
||||
复选框图标的样式,可选值目前有 <code>fa-regular</code>、<code>fa-light</code>、<code>fa-solid</code>
|
||||
@ -34,10 +41,6 @@
|
||||
<p>
|
||||
初始是否选中
|
||||
</p>
|
||||
<h4>isImage?: boolean</h4>
|
||||
<p>
|
||||
是否为图片复选框
|
||||
</p>
|
||||
<h4>imageHeight?: Number</h4>
|
||||
<p>
|
||||
为图片复选框时的图片限制高度
|
||||
|
@ -1,54 +1,51 @@
|
||||
import { createElement, createElementInit } from "../functions";
|
||||
import { createIcon } from "./icon";
|
||||
|
||||
function fillCheckbox(container, type, label) {
|
||||
const layer = document.createElement('layer');
|
||||
layer.className = 'check-box-inner';
|
||||
layer.appendChild(createIcon(type, 'check'));
|
||||
container.appendChild(layer);
|
||||
container.appendChild(
|
||||
createElement('layer', 'check-box-inner', createIcon(type, 'check'))
|
||||
);
|
||||
if (label instanceof HTMLElement) {
|
||||
container.appendChild(label);
|
||||
} else if (label?.length > 0) {
|
||||
const span = document.createElement('span');
|
||||
span.innerText = label;
|
||||
container.appendChild(span);
|
||||
container.appendChild(
|
||||
createElementInit('span', span => span.innerText = label)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function createCheckbox(opts = {}) {
|
||||
const container = document.createElement('label');
|
||||
container.className = 'checkbox-wrapper';
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'checkbox');
|
||||
if (opts.checked === true) {
|
||||
input.checked = true;
|
||||
const container = createElement('label', 'checkbox-wrapper',
|
||||
createElementInit('input', input => {
|
||||
input.setAttribute('type', 'checkbox');
|
||||
if (opts.checked === true) {
|
||||
input.checked = true;
|
||||
}
|
||||
if (opts.enabled === false) {
|
||||
input.disabled = true;
|
||||
}
|
||||
if (opts.customerAttributes != null) {
|
||||
for (let entry of Object.entries(opts.customerAttributes)) {
|
||||
input.setAttribute(entry[0], entry[1]);
|
||||
}
|
||||
}
|
||||
if (typeof opts.onchange === 'function') {
|
||||
input.addEventListener('change', opts.onchange);
|
||||
}
|
||||
}));
|
||||
if (opts.className) {
|
||||
container.classList.add(opts.className);
|
||||
}
|
||||
if (opts.customerAttributes != null) {
|
||||
for (let entry of Object.entries(opts.customerAttributes)) {
|
||||
input.setAttribute(entry[0], entry[1]);
|
||||
}
|
||||
}
|
||||
if (typeof opts.onchange === 'function') {
|
||||
input.addEventListener('change', opts.onchange);
|
||||
}
|
||||
container.appendChild(input);
|
||||
if (opts.isImage) {
|
||||
if (opts.checkedNode != null && opts.uncheckedNode != null) {
|
||||
container.classList.add('checkbox-image');
|
||||
let height = opts.imageHeight;
|
||||
if (isNaN(height) || height <= 0) {
|
||||
height = 14;
|
||||
}
|
||||
if (opts.checkedNode != null) {
|
||||
if (!opts.checkedNode.classList.contains('checked')) {
|
||||
opts.checkedNode.classList.add('checked');
|
||||
}
|
||||
container.appendChild(opts.checkedNode);
|
||||
}
|
||||
if (opts.uncheckedNode != null) {
|
||||
if (!opts.uncheckedNode.classList.contains('unchecked')) {
|
||||
opts.uncheckedNode.classList.add('unchecked');
|
||||
}
|
||||
container.appendChild(opts.uncheckedNode);
|
||||
}
|
||||
opts.checkedNode.classList.add('checked');
|
||||
container.appendChild(opts.checkedNode);
|
||||
opts.uncheckedNode.classList.add('unchecked');
|
||||
container.appendChild(opts.uncheckedNode);
|
||||
} else {
|
||||
fillCheckbox(container, opts.type || 'fa-regular', opts.label);
|
||||
}
|
||||
@ -91,7 +88,7 @@ function resolveCheckbox(container = document.body, legacy) {
|
||||
}
|
||||
}
|
||||
if (label == null) {
|
||||
label = document.createElement('label');
|
||||
label = createElement('label');
|
||||
chk.parentElement.insertBefore(label, chk);
|
||||
} else {
|
||||
text = label.innerText;
|
||||
@ -118,7 +115,7 @@ function resolveCheckbox(container = document.body, legacy) {
|
||||
box.removeAttribute('data-type');
|
||||
box.removeAttribute('data-label');
|
||||
}
|
||||
const input = document.createElement('input');
|
||||
const input = createElement('input');
|
||||
const id = box.dataset.id;
|
||||
if (id?.length > 0) {
|
||||
input.id = id;
|
||||
|
@ -2,6 +2,7 @@
|
||||
import { r } from "../utility/lgres";
|
||||
import { contains, nullOrEmpty } from "../utility/strings";
|
||||
import { global, isPositive } from "../utility";
|
||||
import { createElement } from "../functions";
|
||||
import { createCheckbox } from "./checkbox";
|
||||
import { createIcon } from "./icon"
|
||||
|
||||
@ -19,7 +20,7 @@ if (dropdownGlobal == null) {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: function () {
|
||||
const panel = document.querySelector('.dropdown-wrapper .dropdown-panel.active');
|
||||
const panel = document.querySelector('.dropdown-wrapper .dropdown-box.active');
|
||||
if (panel == null) {
|
||||
return;
|
||||
}
|
||||
@ -39,7 +40,7 @@ if (dropdownGlobal == null) {
|
||||
document.addEventListener('mousedown', e => {
|
||||
let parent = e.target;
|
||||
while (parent != null) {
|
||||
if (parent.classList.contains('dropdown-panel')) {
|
||||
if (parent.classList.contains('dropdown-box')) {
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
@ -110,16 +111,14 @@ class Dropdown {
|
||||
const options = this.#options;
|
||||
|
||||
// wrapper
|
||||
const wrapper = document.createElement('div');
|
||||
const wrapper = createElement('div', 'dropdown-wrapper');
|
||||
const dropId = String(Math.random()).substring(2);
|
||||
wrapper.dataset.dropId = dropId;
|
||||
wrapper.className = 'dropdown-wrapper';
|
||||
dropdownGlobal[dropId] = this;
|
||||
this.#wrapper = wrapper;
|
||||
|
||||
// header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'dropdown-header';
|
||||
const header = createElement('div', 'dropdown-header');
|
||||
header.addEventListener('click', () => {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
@ -137,8 +136,7 @@ class Dropdown {
|
||||
// label or input
|
||||
let label;
|
||||
if (options.input) {
|
||||
label = document.createElement('input');
|
||||
label.className = 'dropdown-text';
|
||||
label = createElement('input', 'dropdown-text');
|
||||
label.setAttribute('type', 'text');
|
||||
options.placeholder && label.setAttribute('placeholder', options.placeholder);
|
||||
isPositive(options.maxlength) && label.setAttribute('maxlength', options.maxlength);
|
||||
@ -153,8 +151,7 @@ class Dropdown {
|
||||
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
|
||||
} else {
|
||||
isPositive(options.tabindex) && header.setAttribute('tabindex', options.tabindex);
|
||||
label = document.createElement('label');
|
||||
label.className = 'dropdown-text';
|
||||
label = createElement('label', 'dropdown-text');
|
||||
}
|
||||
this.#label = label;
|
||||
if (options.multiselect) {
|
||||
@ -167,10 +164,7 @@ class Dropdown {
|
||||
} else if (options.selected != null) {
|
||||
this.select(options.selected, true);
|
||||
}
|
||||
header.appendChild(label);
|
||||
const caret = document.createElement('label');
|
||||
caret.className = 'dropdown-caret';
|
||||
header.appendChild(caret);
|
||||
header.append(label, createElement('label', 'dropdown-caret'));
|
||||
wrapper.appendChild(header);
|
||||
|
||||
this.disabled = options.disabled || false;
|
||||
@ -285,16 +279,13 @@ class Dropdown {
|
||||
|
||||
#dropdown(flag = true) {
|
||||
const options = this.#options;
|
||||
const textkey = options.textkey;
|
||||
let panel = this.#container;
|
||||
if (panel == null) {
|
||||
panel = document.createElement('div');
|
||||
panel.className = 'dropdown-panel';
|
||||
panel = createElement('div', 'dropdown-box');
|
||||
// search box
|
||||
if (!options.input && options.search) {
|
||||
const search = document.createElement('div');
|
||||
search.className = 'dropdown-search';
|
||||
const input = document.createElement('input');
|
||||
const search = createElement('div', 'dropdown-search');
|
||||
const input = createElement('input');
|
||||
input.setAttribute('type', 'text');
|
||||
isPositive(options.tabindex) && input.setAttribute('tabindex', options.tabindex);
|
||||
!nullOrEmpty(options.searchplaceholder) && input.setAttribute('placeholder', options.searchplaceholder);
|
||||
@ -303,13 +294,11 @@ class Dropdown {
|
||||
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
|
||||
this.#filllist(source);
|
||||
})
|
||||
search.appendChild(input);
|
||||
search.appendChild(createIcon('fa-light', 'search'));
|
||||
search.append(input, createIcon('fa-light', 'search'));
|
||||
panel.appendChild(search);
|
||||
}
|
||||
// list
|
||||
const list = document.createElement('ul');
|
||||
list.className = 'dropdown-list';
|
||||
const list = createElement('ul', 'dropdown-list');
|
||||
if (!this.multiselect) {
|
||||
list.addEventListener('click', e => {
|
||||
let li = e.target;
|
||||
@ -364,15 +353,16 @@ class Dropdown {
|
||||
const multiselect = this.multiselect;
|
||||
const allchecked = this.#allChecked;
|
||||
if (multiselect) {
|
||||
const liall = document.createElement('li');
|
||||
const boxall = createCheckbox({
|
||||
label: r('allItem', '( All )'),
|
||||
checked: allchecked,
|
||||
customerAttributes: { 'isall': '1' },
|
||||
onchange: e => this.#triggerselect(e.target)
|
||||
});
|
||||
liall.appendChild(boxall);
|
||||
list.appendChild(liall);
|
||||
list.appendChild(
|
||||
createElement('li', null,
|
||||
createCheckbox({
|
||||
label: r('allItem', '( All )'),
|
||||
checked: allchecked,
|
||||
customerAttributes: { 'isall': '1' },
|
||||
onchange: e => this.#triggerselect(e.target)
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
// TODO: virtual mode
|
||||
const valuekey = this.#options.valuekey;
|
||||
@ -383,7 +373,7 @@ class Dropdown {
|
||||
let scrolled;
|
||||
source.slice(0, 200).forEach((item, i) => {
|
||||
const val = item[valuekey];
|
||||
const li = document.createElement('li');
|
||||
const li = createElement('li');
|
||||
li.dataset.value = val;
|
||||
li.setAttribute('title', item[textkey]);
|
||||
let label;
|
||||
@ -394,7 +384,7 @@ class Dropdown {
|
||||
if (multiselect) {
|
||||
const selected = selectedlist.some(s => s[valuekey] === val);
|
||||
if (label == null) {
|
||||
label = document.createElement('span');
|
||||
label = createElement('span');
|
||||
label.innerText = item[textkey];
|
||||
}
|
||||
const box = createCheckbox({
|
||||
|
@ -170,8 +170,6 @@
|
||||
};
|
||||
|
||||
const iconCol = {
|
||||
...Grid.GridColumn,
|
||||
|
||||
create() {
|
||||
const a = document.createElement('a');
|
||||
a.target = '_blank';
|
||||
@ -186,7 +184,8 @@
|
||||
},
|
||||
setEnabled(element, enabled) {
|
||||
element.style.display = enabled === false ? 'none' : '';
|
||||
}
|
||||
},
|
||||
setStyle: Grid.GridColumn.setStyle
|
||||
};
|
||||
|
||||
// grid.readonly = true;
|
||||
@ -205,7 +204,8 @@
|
||||
{ value: 'broken', text: 'Broken' },
|
||||
{ value: 'running', text: 'Running' }
|
||||
],
|
||||
enabled: 'enabled'
|
||||
enabled: 'enabled',
|
||||
onchanged: (item, value) => console.log('dropdown changed', item, value)
|
||||
},
|
||||
{
|
||||
key: 'c2b',
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { global, isPositive, isMobile, throttle, truncate } from "../utility";
|
||||
import { nullOrEmpty } from "../utility/strings";
|
||||
import { r } from "../utility/lgres";
|
||||
import { createElement, createElementInit } from "../functions";
|
||||
import { createIcon } from "./icon";
|
||||
import { createCheckbox } from "./checkbox";
|
||||
import { setTooltip } from "./tooltip";
|
||||
@ -41,7 +42,7 @@ function indexOfParent(target) {
|
||||
}
|
||||
|
||||
class GridColumn {
|
||||
static create() { return document.createElement('span') }
|
||||
static create() { return createElement('span') }
|
||||
|
||||
static setValue(element, val) { element.innerText = val }
|
||||
|
||||
@ -54,7 +55,7 @@ class GridColumn {
|
||||
|
||||
class GridInputColumn extends GridColumn {
|
||||
static createEdit(trigger) {
|
||||
const input = document.createElement('input');
|
||||
const input = createElement('input');
|
||||
input.setAttribute('type', 'text');
|
||||
if (typeof trigger === 'function') {
|
||||
input.addEventListener('change', trigger);
|
||||
@ -157,12 +158,7 @@ class GridCheckboxColumn extends GridColumn {
|
||||
}
|
||||
|
||||
class GridIconColumn extends GridColumn {
|
||||
static create() {
|
||||
const element = document.createElement('span');
|
||||
element.className = 'icon';
|
||||
// element.appendChild(document.createElement('layer'));
|
||||
return element;
|
||||
}
|
||||
static create() { return createElement('span', 'icon') }
|
||||
|
||||
static setValue(element, val, item, col) {
|
||||
let type = col.iconType;
|
||||
@ -351,8 +347,7 @@ class Grid {
|
||||
throw new Error('no specified parent.');
|
||||
}
|
||||
this.#parent = container;
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'grid';
|
||||
const grid = createElement('div', 'grid');
|
||||
grid.setAttribute('tabindex', 0);
|
||||
grid.addEventListener('keydown', e => {
|
||||
let index = this.selectedIndex;
|
||||
@ -382,8 +377,7 @@ class Grid {
|
||||
}
|
||||
});
|
||||
container.replaceChildren(grid);
|
||||
const sizer = document.createElement('span');
|
||||
sizer.className = 'grid-sizer';
|
||||
const sizer = createElement('span', 'grid-sizer');
|
||||
grid.appendChild(sizer);
|
||||
this.#refs.sizer = sizer;
|
||||
|
||||
@ -394,11 +388,9 @@ class Grid {
|
||||
grid.appendChild(body);
|
||||
|
||||
// loading
|
||||
const loading = document.createElement('div');
|
||||
loading.className = 'grid-loading';
|
||||
const loadingHolder = document.createElement('div');
|
||||
loadingHolder.appendChild(createIcon('fa-regular', 'spinner-third'));
|
||||
loading.appendChild(loadingHolder);
|
||||
const loading = createElement('div', 'grid-loading',
|
||||
createElement('div', null, createIcon('fa-regular', 'spinner-third'))
|
||||
);
|
||||
this.#refs.loading = loading;
|
||||
grid.appendChild(loading);
|
||||
this.#el = grid;
|
||||
@ -548,14 +540,13 @@ class Grid {
|
||||
}
|
||||
|
||||
#createHeader() {
|
||||
const thead = document.createElement('table');
|
||||
thead.className = 'grid-header';
|
||||
const header = document.createElement('tr');
|
||||
const thead = createElement('table', 'grid-header');
|
||||
const header = createElement('tr');
|
||||
thead.appendChild(header);
|
||||
const sizer = this.#refs.sizer;
|
||||
for (let col of this.columns) {
|
||||
if (col.visible === false) {
|
||||
const hidden = document.createElement('th');
|
||||
const hidden = createElement('th');
|
||||
hidden.style.display = 'none';
|
||||
if (col.sortable !== false) {
|
||||
hidden.dataset.key = col.key;
|
||||
@ -594,8 +585,7 @@ class Grid {
|
||||
};
|
||||
this.#set(col.key, 'style', style);
|
||||
// element
|
||||
const th = document.createElement('th');
|
||||
th.className = 'column';
|
||||
const th = createElement('th', 'column');
|
||||
th.dataset.key = col.key;
|
||||
for (let css of Object.entries(style)) {
|
||||
th.style.setProperty(css[0], css[1]);
|
||||
@ -608,7 +598,7 @@ class Grid {
|
||||
col.orderable = true;
|
||||
th.addEventListener('mousedown', e => this.#onDragStart(e, col));
|
||||
}
|
||||
const wrapper = document.createElement('div');
|
||||
const wrapper = createElement('div');
|
||||
th.appendChild(wrapper);
|
||||
if (!this.readonly && col.enabled !== false && col.allcheck && isCheckbox) {
|
||||
const check = createCheckbox({
|
||||
@ -616,7 +606,7 @@ class Grid {
|
||||
});
|
||||
wrapper.appendChild(check);
|
||||
}
|
||||
const caption = document.createElement('span');
|
||||
const caption = createElement('span');
|
||||
if (col.textStyle != null) {
|
||||
for (let css of Object.entries(col.textStyle)) {
|
||||
caption.style.setProperty(css[0], css[1]);
|
||||
@ -626,9 +616,7 @@ class Grid {
|
||||
wrapper.appendChild(caption);
|
||||
// order arrow
|
||||
if (col.sortable) {
|
||||
const arrow = document.createElement('layer');
|
||||
arrow.className = 'arrow';
|
||||
th.appendChild(arrow);
|
||||
th.appendChild(createElement('layer', 'arrow'));
|
||||
}
|
||||
// filter
|
||||
if (col.allowFilter) {
|
||||
@ -636,8 +624,7 @@ class Grid {
|
||||
}
|
||||
// resize spliter
|
||||
if (col.resizable !== false) {
|
||||
const spliter = document.createElement('layer');
|
||||
spliter.className = 'spliter';
|
||||
const spliter = createElement('layer', 'spliter');
|
||||
spliter.addEventListener('mousedown', e => this.#onResizeStart(e, col));
|
||||
spliter.addEventListener('dblclick', e => this.#onAutoResize(e, col));
|
||||
th.appendChild(spliter);
|
||||
@ -646,13 +633,9 @@ class Grid {
|
||||
// !nullOrEmpty(col.tooltip) && setTooltip(th, col.tooltip);
|
||||
header.appendChild(th);
|
||||
}
|
||||
const placeholder = document.createElement('th');
|
||||
const dragger = document.createElement('div');
|
||||
dragger.className = 'dragger';
|
||||
const draggerCursor = document.createElement('layer');
|
||||
draggerCursor.className = 'dragger-cursor';
|
||||
placeholder.append(dragger, draggerCursor);
|
||||
header.appendChild(placeholder);
|
||||
const dragger = createElement('div', 'dragger');
|
||||
const draggerCursor = createElement('layer', 'dragger-cursor');
|
||||
header.appendChild(createElement('th', null, dragger, draggerCursor));
|
||||
|
||||
sizer.replaceChildren();
|
||||
this.#refs.header = header;
|
||||
@ -662,8 +645,7 @@ class Grid {
|
||||
}
|
||||
|
||||
#createBody() {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'grid-body';
|
||||
const body = createElement('div', 'grid-body');
|
||||
body.addEventListener('scroll', e => throttle(this.#onScroll, RefreshInterval, this, e), { passive: true });
|
||||
const cols = this.columns;
|
||||
let width = 1;
|
||||
@ -673,7 +655,7 @@ class Grid {
|
||||
}
|
||||
}
|
||||
// body container
|
||||
const bodyContainer = document.createElement('div');
|
||||
const bodyContainer = createElement('div');
|
||||
bodyContainer.style.position = 'relative';
|
||||
bodyContainer.style.minWidth = '100%';
|
||||
bodyContainer.style.minHeight = '1px';
|
||||
@ -682,8 +664,7 @@ class Grid {
|
||||
}
|
||||
body.appendChild(bodyContainer);
|
||||
// body content
|
||||
const bodyContent = document.createElement('table');
|
||||
bodyContent.className = 'grid-body-content';
|
||||
const bodyContent = createElement('table', 'grid-body-content');
|
||||
bodyContent.addEventListener('mousedown', e => {
|
||||
let [parent, target] = this.#getRowTarget(e.target);
|
||||
const rowIndex = indexOfParent(parent);
|
||||
@ -698,8 +679,7 @@ class Grid {
|
||||
// this.#adjustRows();
|
||||
// events
|
||||
if (!this.holderDisabled) {
|
||||
const holder = document.createElement('div');
|
||||
holder.className = 'grid-hover-holder';
|
||||
const holder = createElement('div', 'grid-hover-holder');
|
||||
holder.addEventListener('mousedown', e => {
|
||||
const keyid = e.currentTarget.keyid;
|
||||
if (keyid == null) {
|
||||
@ -730,12 +710,11 @@ class Grid {
|
||||
count -= exists;
|
||||
if (count > 0) {
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
const row = document.createElement('tr');
|
||||
row.className = 'grid-row';
|
||||
const row = createElement('tr', 'grid-row');
|
||||
// row.addEventListener('mousedown', e => this.#onRowClicked(e, exists + i));
|
||||
// row.addEventListener('dblclick', e => this.#onRowDblClicked(e));
|
||||
cols.forEach((col, j) => {
|
||||
const cell = document.createElement('td');
|
||||
const cell = createElement('td');
|
||||
if (col.visible !== false) {
|
||||
cell.keyid = ((exists + i) << MaxColumnBit) | j;
|
||||
const style = this.#get(col.key, 'style');
|
||||
@ -771,7 +750,7 @@ class Grid {
|
||||
}
|
||||
row.appendChild(cell);
|
||||
});
|
||||
row.appendChild(document.createElement('td'));
|
||||
row.appendChild(createElement('td'));
|
||||
content.appendChild(row);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
|
2
lib/ui/icon.d.ts
vendored
2
lib/ui/icon.d.ts
vendored
@ -1,2 +1,2 @@
|
||||
export function createIcon(type: string, id: string): SVGSVGElement
|
||||
export function createIcon(type: string, id: string, style?: { [key: string]: string }): SVGSVGElement
|
||||
export function resolveIcon(container: HTMLElement): HTMLElement
|
@ -6,7 +6,7 @@
|
||||
标签到指定的图标元素。
|
||||
</p>
|
||||
<h2>createIcon</h2>
|
||||
<code>function createIcon(type: string, id: string): SVGElement</code>
|
||||
<code>function createIcon(type: string, id: string, style?: { [key: string]: string }): SVGElement</code>
|
||||
<h3>type: string</h3>
|
||||
<p>
|
||||
图标类型,可选值目前有 <code>fa-regular</code>、<code>fa-light</code>、<code>fa-solid</code>
|
||||
@ -16,6 +16,10 @@
|
||||
图形 id,例如
|
||||
<code>user-edit</code>、<code>address-card</code>、<code>frog</code>……
|
||||
</p>
|
||||
<h3>style?: { [key: string]: string }</h3>
|
||||
<p>
|
||||
自定义样式的对象
|
||||
</p>
|
||||
<h2>resolveIcon</h2>
|
||||
<code>function resolveIcon(container: HTMLElement): HTMLElement</code>
|
||||
<h3>container: HTMLElement</h3>
|
||||
|
@ -9,9 +9,14 @@ function createUse(type, id) {
|
||||
return use;
|
||||
}
|
||||
|
||||
function createIcon(type, id) {
|
||||
function createIcon(type, id, style) {
|
||||
const svg = document.createElementNS(svgns, 'svg');
|
||||
svg.appendChild(createUse(type, id));
|
||||
if (style != null) {
|
||||
for (let css of Object.entries(style)) {
|
||||
svg.style.setProperty(css[0], css[1]);
|
||||
}
|
||||
}
|
||||
return svg;
|
||||
}
|
||||
|
||||
|
2
lib/ui/tooltip.d.ts
vendored
2
lib/ui/tooltip.d.ts
vendored
@ -1,2 +1,2 @@
|
||||
export function setTooltip(container: HTMLElement, content: string | HTMLElement): void
|
||||
export function setTooltip(container: HTMLElement, content: string | HTMLElement): HTMLElement
|
||||
export function resolveTooltip(container?: HTMLElement): HTMLElement
|
@ -1,23 +1,28 @@
|
||||
function setTooltip(container, content) {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'tooltip-wrapper tooltip-color';
|
||||
wrapper.style.visibility = 'hidden';
|
||||
wrapper.style.opacity = 0;
|
||||
const pointer = document.createElement('div');
|
||||
pointer.className = 'tooltip-pointer tooltip-color';
|
||||
const curtain = document.createElement('div');
|
||||
curtain.className = 'tooltip-curtain tooltip-color';
|
||||
wrapper.append(pointer, curtain);
|
||||
import { createElement, createElementInit } from "../functions";
|
||||
|
||||
const cnt = document.createElement('div');
|
||||
cnt.className = 'tooltip-content';
|
||||
if (content instanceof HTMLElement) {
|
||||
cnt.appendChild(content);
|
||||
} else {
|
||||
cnt.innerText = content;
|
||||
function setTooltip(container, content) {
|
||||
const tip = container.querySelector('.tooltip-wrapper');
|
||||
if (tip != null) {
|
||||
tip.remove();
|
||||
}
|
||||
wrapper.appendChild(cnt);
|
||||
container.insertAdjacentElement('afterend', wrapper);
|
||||
const wrapper = createElementInit('div', wrapper => {
|
||||
wrapper.className = 'tooltip-wrapper tooltip-color';
|
||||
wrapper.style.visibility = 'hidden';
|
||||
wrapper.style.opacity = 0;
|
||||
},
|
||||
createElement('div', 'tooltip-pointer tooltip-color'),
|
||||
createElement('div', 'tooltip-curtain tooltip-color'),
|
||||
createElementInit('div', cnt => {
|
||||
cnt.className = 'tooltip-content';
|
||||
if (content instanceof HTMLElement) {
|
||||
cnt.appendChild(content);
|
||||
} else {
|
||||
cnt.innerText = content;
|
||||
}
|
||||
})
|
||||
);
|
||||
// container.insertAdjacentElement('afterend', wrapper);
|
||||
container.appendChild(wrapper);
|
||||
|
||||
let tid;
|
||||
container.addEventListener('mouseenter', () => {
|
||||
@ -56,6 +61,7 @@ function setTooltip(container, content) {
|
||||
wrapper.style.opacity = 0;
|
||||
}, 300);
|
||||
});
|
||||
return container;
|
||||
}
|
||||
|
||||
function resolveTooltip(container = document.body) {
|
||||
|
@ -31,6 +31,14 @@ function truncate(v) {
|
||||
return (v > 0 ? Math.floor : Math.ceil)(v);
|
||||
}
|
||||
|
||||
function isEmail(text) {
|
||||
return /^\w[-\w.+]*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(text);
|
||||
}
|
||||
|
||||
function isPhone(text) {
|
||||
return /^[1-9]\d{9,}$/.test(text);
|
||||
}
|
||||
|
||||
export {
|
||||
// cookie
|
||||
getCookie,
|
||||
@ -55,5 +63,7 @@ export {
|
||||
isMobile,
|
||||
// functions
|
||||
throttle,
|
||||
truncate
|
||||
truncate,
|
||||
isEmail,
|
||||
isPhone
|
||||
}
|
@ -16,6 +16,34 @@ const libraries = [
|
||||
formats: ['umd'],
|
||||
fileName: (_format, name) => `${name}.min.js`
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
assetFileNames: info => info.name === 'style.css' ? 'ui.min.css' : info.name
|
||||
}
|
||||
},
|
||||
sourcemap: true,
|
||||
emptyOutDir: false
|
||||
}
|
||||
},
|
||||
{
|
||||
clearScreen: false,
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [postcssPresetEnv()]
|
||||
}
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry: './lib/app.js',
|
||||
name: 'lib-app',
|
||||
formats: ['umd'],
|
||||
fileName: (_format, name) => `${name}.min.js`
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
assetFileNames: info => info.name === 'style.css' ? 'app.min.css' : info.name
|
||||
}
|
||||
},
|
||||
sourcemap: true,
|
||||
emptyOutDir: false
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user