communications app, popup lib
This commit is contained in:
parent
449196b491
commit
f85d4c9903
@ -13,14 +13,14 @@ $searchInputHeight: 26px;
|
|||||||
$searchIconSize: 13px;
|
$searchIconSize: 13px;
|
||||||
$listMaxHeight: 210px;
|
$listMaxHeight: 210px;
|
||||||
|
|
||||||
.dropdown-wrapper {
|
.drop-wrapper {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: unset;
|
border-radius: unset;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
>.dropdown-header {
|
>.drop-header {
|
||||||
border: 1px solid $borderColor;
|
border: 1px solid $borderColor;
|
||||||
border-radius: $borderRadius;
|
border-radius: $borderRadius;
|
||||||
background-color: $bgColor;
|
background-color: $bgColor;
|
||||||
@ -39,7 +39,7 @@ $listMaxHeight: 210px;
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*>.dropdown-select-container {
|
/*>.drop-select-container {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -104,7 +104,7 @@ $listMaxHeight: 210px;
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
>.dropdown-text {
|
>.drop-text {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: $mediumSize;
|
font-size: $mediumSize;
|
||||||
@ -117,7 +117,7 @@ $listMaxHeight: 210px;
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
>input.dropdown-text {
|
>input.drop-text {
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
@ -126,7 +126,7 @@ $listMaxHeight: 210px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.dropdown-caret {
|
>.drop-caret {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: $caretWidth;
|
width: $caretWidth;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -154,14 +154,14 @@ $listMaxHeight: 210px;
|
|||||||
// box-shadow: none;
|
// box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.dropdown-text,
|
>.drop-text,
|
||||||
>.dropdown-caret {
|
>.drop-caret {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.dropdown-box {
|
>.drop-box {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@ -192,7 +192,7 @@ $listMaxHeight: 210px;
|
|||||||
transform: scaleY(1);
|
transform: scaleY(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
>.dropdown-search {
|
>.drop-search {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: $searchBarHeight;
|
height: $searchBarHeight;
|
||||||
line-height: $searchBarHeight;
|
line-height: $searchBarHeight;
|
||||||
@ -235,7 +235,7 @@ $listMaxHeight: 210px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.dropdown-list {
|
>.drop-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
@ -59,23 +59,4 @@ $boxDisabledColor: #d9d9d9;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
&.disabled {
|
|
||||||
.check-box-inner {
|
|
||||||
border-color: $boxDisabledColor;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
>input:checked+.check-box-inner {
|
|
||||||
border-color: $boxDisabledColor;
|
|
||||||
background-color: $boxDisabledColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
>span {
|
|
||||||
color: $boxDisabledColor;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
@ -63,8 +63,8 @@
|
|||||||
|
|
||||||
&,
|
&,
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
.dropdown-wrapper>.dropdown-header>.dropdown-text,
|
.drop-wrapper>.drop-header>.drop-text,
|
||||||
.dropdown-wrapper>.dropdown-box>.dropdown-list {
|
.drop-wrapper>.drop-box>.drop-list {
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,22 +268,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-wrapper {
|
.drop-wrapper {
|
||||||
height: calc(var(--line-height) + 2px);
|
height: calc(var(--line-height) + 2px);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
>.dropdown-header {
|
>.drop-header {
|
||||||
border: none;
|
border: none;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
>.dropdown-text {
|
>.drop-text {
|
||||||
padding: var(--spacing-cell);
|
padding: var(--spacing-cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>.dropdown-box {
|
>.drop-box {
|
||||||
top: calc(var(--line-height) + 2px);
|
top: calc(var(--line-height) + 2px);
|
||||||
|
|
||||||
&.slide-up {
|
&.slide-up {
|
||||||
|
94
css/popup.scss
Normal file
94
css/popup.scss
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
@import './functions/func.scss';
|
||||||
|
@import './variables/definition.scss';
|
||||||
|
|
||||||
|
$headerLineHeight: 24px;
|
||||||
|
$buttonHeight: 36px;
|
||||||
|
|
||||||
|
.popup-mask {
|
||||||
|
position: fixed;
|
||||||
|
@include inset(0, 0, 0, 0);
|
||||||
|
background-color: rgba(0 0 0 /20%);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 200;
|
||||||
|
transition: opacity .12s ease;
|
||||||
|
|
||||||
|
&.active .popup-container {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-container {
|
||||||
|
min-width: 400px;
|
||||||
|
max-width: 800px;
|
||||||
|
background-color: $bgColor;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0 0 0 /11%);
|
||||||
|
transition: all .12s ease;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: 10px 12px 6px;
|
||||||
|
line-height: $headerLineHeight;
|
||||||
|
user-select: none;
|
||||||
|
background-color: var(--title-bg-color);
|
||||||
|
color: var(--title-color);
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
>div {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: $headerLineHeight;
|
||||||
|
height: $headerLineHeight;
|
||||||
|
padding: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-body {
|
||||||
|
margin: 6px 10px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
line-height: $headerLineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-footer {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 4px 10px 16px 2px;
|
||||||
|
|
||||||
|
.popup-button {
|
||||||
|
margin-left: 8px;
|
||||||
|
border: none;
|
||||||
|
height: $buttonHeight;
|
||||||
|
padding: 0 8px;
|
||||||
|
min-width: 60px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--title-bg-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
import CustomerCommunication from "./app/communications/customer";
|
import CustomerCommunication from "./app/communications/customer";
|
||||||
|
import InternalComment from "./app/communications/internal";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CustomerCommunication
|
CustomerCommunication,
|
||||||
|
InternalComment
|
||||||
}
|
}
|
@ -1,11 +1,13 @@
|
|||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import { createElement, createElementInit } from "../../functions";
|
import { createElement } from "../../functions";
|
||||||
import { r } from "../../utility/lgres";
|
import { r } from "../../utility/lgres";
|
||||||
|
import { nullOrEmpty } from "../../utility/strings";
|
||||||
import { formatUrl, isEmail, isPhone } from "../../utility";
|
import { formatUrl, isEmail, isPhone } from "../../utility";
|
||||||
import { setTooltip } from "../../ui/tooltip";
|
import { setTooltip } from "../../ui/tooltip";
|
||||||
import { createIcon } from "../../ui/icon";
|
import { createIcon } from "../../ui/icon";
|
||||||
import { createCheckbox } from "../../ui/checkbox";
|
import { createCheckbox } from "../../ui/checkbox";
|
||||||
import { createBox } from "./lib";
|
import { createBox } from "./lib";
|
||||||
|
import { createPopup } from "../../ui/popup";
|
||||||
|
|
||||||
class CustomerCommunication {
|
class CustomerCommunication {
|
||||||
#container;
|
#container;
|
||||||
@ -23,123 +25,223 @@ class CustomerCommunication {
|
|||||||
get autoUpdatesEnabled() { return this.#autoUpdates?.disabled !== true }
|
get autoUpdatesEnabled() { return this.#autoUpdates?.disabled !== true }
|
||||||
set autoUpdatesEnabled(flag) {
|
set autoUpdatesEnabled(flag) {
|
||||||
const element = this.#autoUpdates;
|
const element = this.#autoUpdates;
|
||||||
element != null && (element.disabled = flag === false);
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (flag === false) {
|
||||||
|
element.disabled = true;
|
||||||
|
element.parentElement?.classList?.add('disabled');
|
||||||
|
} else {
|
||||||
|
element.disabled = false;
|
||||||
|
element.parentElement?.classList?.remove('disabled');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
get autoUpdates() { return this.#autoUpdates?.checked }
|
get autoUpdates() { return this.#autoUpdates?.checked }
|
||||||
set autoUpdates(flag) {
|
set autoUpdates(flag) {
|
||||||
const element = this.#autoUpdates;
|
const element = this.#autoUpdates;
|
||||||
element != null && (element.checked = flag);
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.checked = flag;
|
||||||
|
element.dispatchEvent(new Event('change'));
|
||||||
}
|
}
|
||||||
|
|
||||||
get #statusLink() { return this.#container.querySelector('.check-status-link') }
|
get #statusLink() { return this.#container.querySelector('.check-status-link') }
|
||||||
get statusLinkEnabled() { return this.#statusLink?.disabled !== true }
|
get statusLinkEnabled() { return this.#statusLink?.disabled !== true }
|
||||||
set statusLinkEnabled(flag) {
|
set statusLinkEnabled(flag) {
|
||||||
const element = this.#statusLink;
|
const element = this.#statusLink;
|
||||||
element != null && (element.disabled = flag === false);
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (flag === false) {
|
||||||
|
element.disabled = true;
|
||||||
|
element.parentElement?.classList?.add('disabled');
|
||||||
|
} else {
|
||||||
|
element.disabled = false;
|
||||||
|
element.parentElement?.classList?.remove('disabled');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
get statusLink() { return this.#statusLink?.checked }
|
get statusLink() { return this.#statusLink?.checked }
|
||||||
set statusLink(flag) {
|
set statusLink(flag) {
|
||||||
const element = this.#statusLink;
|
const element = this.#statusLink;
|
||||||
element != null && (element.checked = flag);
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.checked = flag;
|
||||||
|
element.dispatchEvent(new Event('change'));
|
||||||
}
|
}
|
||||||
|
|
||||||
get text() { return this.#enter?.value }
|
get text() { return this.#enter?.value }
|
||||||
set text(s) {
|
set text(s) {
|
||||||
const element = this.#enter;
|
const element = this.#enter;
|
||||||
element != null && (element.value = s);
|
if (element != null) {
|
||||||
|
element.value = s
|
||||||
|
s = String(nullOrEmpty(s) ? 0 : val.length) + '/3000';
|
||||||
|
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#createContactItem(c) {
|
||||||
|
if (c.OptOut || c.OptOut_BC) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const mp = String(c.MobilePhone).trim();
|
||||||
|
const email = String(c.Email).trim();
|
||||||
|
if (c.ContactPreference === '0' && !isPhone(mp) ||
|
||||||
|
c.ContactPreference === '1' && !isEmail(email)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const to = c.ContactPreference === '0' ? mp : email;
|
||||||
|
return createElement('div', 'contact-item',
|
||||||
|
createIcon('fa-light', c.ContactPreference === '0' ? 'comment-lines' : 'envelope'),
|
||||||
|
setTooltip(createElement('span', span => {
|
||||||
|
span.dataset.to = to;
|
||||||
|
span.dataset.name = c.Name;
|
||||||
|
span.innerText = to;
|
||||||
|
}), to, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get contacts() {
|
get contacts() {
|
||||||
return [...this.#contacts.children].map(el => {
|
return [...this.#contacts.children].map(el => {
|
||||||
const span = el.querySelector('span');
|
const span = el.querySelector('span');
|
||||||
return { 'Key': span.innerText, 'Value': span.dataset.name };
|
return { 'Key': span.dataset.to, 'Value': span.dataset.name };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
set contacts(contacts) {
|
set contacts(contacts) {
|
||||||
this.#contacts.replaceChildren();
|
this.#contacts.replaceChildren();
|
||||||
if (contacts?.length > 0) {
|
if (contacts?.length > 0) {
|
||||||
for (let c of contacts) {
|
for (let c of contacts) {
|
||||||
if (c.OptOut || c.OptOut_BC) {
|
const item = this.#createContactItem(c);
|
||||||
continue;
|
if (item != null) {
|
||||||
|
this.#contacts.appendChild(item);
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
this.#message.scrollTop = this.#message.scrollHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} flag
|
||||||
|
*/
|
||||||
|
set readonly(flag) {
|
||||||
|
this.#option.readonly = flag;
|
||||||
|
if (this.#container == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : '';
|
||||||
|
this.#container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : '';
|
||||||
|
this.#enter.disabled = flag === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get followers() {
|
||||||
|
return [...this.#followers.children].map(el => {
|
||||||
|
const span = el.querySelector('span');
|
||||||
|
return { 'Key': span.dataset.to, 'Value': span.dataset.name };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
set followers(followers) {
|
||||||
|
this.#followers.replaceChildren();
|
||||||
|
if (followers?.length > 0) {
|
||||||
|
this.#container.querySelector('.follower-bar').style.display = '';
|
||||||
|
for (let f of followers) {
|
||||||
|
const item = this.#createContactItem(f);
|
||||||
|
if (item != null) {
|
||||||
|
this.#followers.appendChild(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.#container.querySelector('.follower-bar').style.display = 'none';
|
||||||
|
}
|
||||||
|
this.#message.scrollTop = this.#message.scrollHeight
|
||||||
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
// functions
|
// functions
|
||||||
const checkAutoUpdate = createCheckbox({
|
const checkAutoUpdate = createCheckbox({
|
||||||
className: 'check-auto-update',
|
className: 'check-auto-update',
|
||||||
checked: this.#option.autoUpdates?.checked,
|
checked: this.#option.autoUpdates,
|
||||||
enabled: this.#option.autoUpdates?.enabled,
|
|
||||||
checkedNode: createIcon('fa-regular', 'redo-alt'),
|
checkedNode: createIcon('fa-regular', 'redo-alt'),
|
||||||
uncheckedNode: createIcon('fa-regular', 'ban'),
|
uncheckedNode: createIcon('fa-regular', 'ban'),
|
||||||
onchange: () => {
|
onchange: function () {
|
||||||
setTooltip(checkAutoUpdate, this.checked ?
|
setTooltip(checkAutoUpdate, this.checked ?
|
||||||
r('autoUpdateEnabled', 'Auto Updates Enabled') :
|
r('autoUpdateEnabled', 'Auto Updates Enabled') :
|
||||||
r('autoUpdateDisabled', 'Auto Updates Disabled'));
|
r('autoUpdateDisabled', 'Auto Updates Disabled'));
|
||||||
if (typeof this.#option.autoUpdates?.onchanged === 'function') {
|
|
||||||
this.#option.autoUpdates.onchanged(this.checked);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const checkLink = createCheckbox({
|
const checkLink = createCheckbox({
|
||||||
className: 'check-status-link',
|
className: 'check-status-link',
|
||||||
checked: this.#option.statusLink?.checked,
|
checked: this.#option.statusLink,
|
||||||
enabled: this.#option.statusLink?.enabled,
|
|
||||||
checkedNode: createIcon('fa-regular', 'link'),
|
checkedNode: createIcon('fa-regular', 'link'),
|
||||||
uncheckedNode: createIcon('fa-regular', 'unlink'),
|
uncheckedNode: createIcon('fa-regular', 'unlink'),
|
||||||
onchange: () => {
|
onchange: function () {
|
||||||
setTooltip(checkLink, this.checked ?
|
setTooltip(checkLink, this.checked ?
|
||||||
r('statusLinkIncluded', 'Status Link Included') :
|
r('statusLinkIncluded', 'Status Link Included') :
|
||||||
r('statusLinkExcluded', 'Status Link Excluded'));
|
r('statusLinkExcluded', 'Status Link Excluded'));
|
||||||
if (typeof this.#option.statusLink?.onchanged === 'function') {
|
|
||||||
this.#option.statusLink.onchanged(this.checked);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const container = createBox(
|
const container = createBox(
|
||||||
createElement('div', null,
|
createElement('div', null,
|
||||||
createElementInit('div', div => div.innerText = r('messages', 'Customer Communication')),
|
createElement('div', div => div.innerText = r('messages', 'Customer Communication')),
|
||||||
createElementInit('div', div => div.innerText = consts.user?.companyName)),
|
createElement('div', div => {
|
||||||
|
div.className = 'title-company';
|
||||||
|
if (nullOrEmpty(this.#option.companyName)) {
|
||||||
|
div.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
div.innerText = this.#option.companyName;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
[
|
[
|
||||||
setTooltip(checkAutoUpdate, r('autoUpdateDisabled', 'Auto Updates Disabled')),
|
setTooltip(checkAutoUpdate, r('autoUpdateEnabled', 'Auto Updates Enabled')),
|
||||||
setTooltip(checkLink, r('statusLinkExcluded', 'Status Link Excluded'))
|
setTooltip(checkLink, r('statusLinkExcluded', 'Status Link Excluded'))
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
// contacts
|
// contacts
|
||||||
|
const readonly = this.#option.readonly;
|
||||||
const contacts = createElement('div');
|
const contacts = createElement('div');
|
||||||
container.append(
|
container.append(
|
||||||
createElement('div', 'contact-bar',
|
createElement('div', 'contact-bar',
|
||||||
createIcon('fa-solid', 'user-circle', {
|
createElement('div', 'bar-icon',
|
||||||
'fill': 'lightgray',
|
createIcon('fa-solid', 'user-circle', {
|
||||||
'flex': '0 0 auto'
|
'fill': 'lightgray'
|
||||||
}),
|
})
|
||||||
createElementInit('div', div => div.style.flex = '1 1 auto',
|
),
|
||||||
|
createElement('div', 'bar-list',
|
||||||
contacts,
|
contacts,
|
||||||
createElementInit('button', button => {
|
createElement('button', button => {
|
||||||
button.className = 'roundbtn';
|
button.className = 'roundbtn button-edit-contacts';
|
||||||
button.style.backgroundColor = 'rgb(1, 199, 172)';
|
button.style.backgroundColor = 'rgb(1, 199, 172)';
|
||||||
|
if (readonly === true) {
|
||||||
|
button.style.display = 'none';
|
||||||
|
}
|
||||||
button.appendChild(createIcon('fa-solid', 'user-edit'));
|
button.appendChild(createIcon('fa-solid', 'user-edit'));
|
||||||
setTooltip(button, r('editContacts', 'Edit Contacts'));
|
setTooltip(button, r('editContacts', 'Edit Contacts'));
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
// TODO:
|
// TODO:
|
||||||
|
const pop = createPopup(
|
||||||
|
createElement('div', div => {
|
||||||
|
div.style.display = 'flex';
|
||||||
|
div.append(
|
||||||
|
createElement('span', span => {
|
||||||
|
span.style.flex = '1 1 auto';
|
||||||
|
span.innerText = r('editContacts', 'Edit Contacts');
|
||||||
|
}),
|
||||||
|
createElement('button', button => {
|
||||||
|
button.style.flex = '0 0 auto';
|
||||||
|
button.className = 'roundbtn button-add-contact';
|
||||||
|
button.backgroundColor = 'rgb(1, 199, 172)';
|
||||||
|
button.appendChild(createIcon('fa-regular', 'user'));
|
||||||
|
setTooltip(button, r('addContact', 'Add Contact'))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
createElement('div', div => {
|
||||||
|
div.style.height = '300px';
|
||||||
|
div.innerText = 'Contacts from Customer Record';
|
||||||
|
}));
|
||||||
|
container.append(pop);
|
||||||
|
setTimeout(() => pop.style.opacity = 1, 0);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -149,20 +251,27 @@ class CustomerCommunication {
|
|||||||
// followers
|
// followers
|
||||||
const followers = createElement('div');
|
const followers = createElement('div');
|
||||||
container.append(
|
container.append(
|
||||||
createElement('div', 'contact-bar follower-bar',
|
createElement('div', div => {
|
||||||
createIcon('fa-solid', 'user-tag', {
|
div.className = 'contact-bar follower-bar';
|
||||||
'fill': '#fff',
|
div.style.display = 'none';
|
||||||
'background-color': 'lightgray',
|
},
|
||||||
'box-sizing': 'border-box',
|
setTooltip(createElement('div', 'bar-icon',
|
||||||
'border-radius': '15px',
|
createIcon('fa-solid', 'user-tag', {
|
||||||
'padding': '4px',
|
'fill': '#fff',
|
||||||
'flex': '0 0 auto'
|
'background-color': 'lightgray',
|
||||||
}),
|
'box-sizing': 'border-box',
|
||||||
createElementInit('div', div => div.style.flex = '1 1 auto',
|
'border-radius': '15px',
|
||||||
|
'padding': '4px'
|
||||||
|
})
|
||||||
|
), r('copied', 'Copied')),
|
||||||
|
createElement('div', 'bar-list',
|
||||||
followers,
|
followers,
|
||||||
createElementInit('button', button => {
|
createElement('button', button => {
|
||||||
button.className = 'roundbtn';
|
button.className = 'roundbtn button-edit-followers';
|
||||||
button.style.backgroundColor = 'rgb(48, 107, 255)';
|
button.style.backgroundColor = 'rgb(48, 107, 255)';
|
||||||
|
if (readonly === true) {
|
||||||
|
button.style.display = 'none';
|
||||||
|
}
|
||||||
button.appendChild(createIcon('fa-solid', 'pen'));
|
button.appendChild(createIcon('fa-solid', 'pen'));
|
||||||
setTooltip(button, r('editFollower', 'Edit Followers'));
|
setTooltip(button, r('editFollower', 'Edit Followers'));
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
@ -175,20 +284,31 @@ class CustomerCommunication {
|
|||||||
this.#followers = followers;
|
this.#followers = followers;
|
||||||
// enter box
|
// enter box
|
||||||
const enter = createElement('textarea');
|
const enter = createElement('textarea');
|
||||||
enter.placeholder = r('typeComment', 'Enter Message Here');
|
enter.placeholder = r('typeMessage', 'Enter Message Here');
|
||||||
enter.maxLength = 3000;
|
enter.maxLength = 3000;
|
||||||
|
if (readonly === true) {
|
||||||
|
enter.disabled = true;
|
||||||
|
}
|
||||||
|
enter.addEventListener('input', () => {
|
||||||
|
const val = this.#enter.value;
|
||||||
|
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/3000';
|
||||||
|
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
|
||||||
|
});
|
||||||
this.#enter = enter;
|
this.#enter = enter;
|
||||||
container.appendChild(
|
container.appendChild(
|
||||||
createElement('div', 'message-bar',
|
createElement('div', 'message-bar',
|
||||||
enter,
|
enter,
|
||||||
createElementInit('div', div => div.style.textAlign = 'right',
|
createElement('div', div => div.style.textAlign = 'right',
|
||||||
createElementInit('button', button => {
|
createElement('div', 'prompt-count'),
|
||||||
button.className = 'roundbtn';
|
createElement('button', button => {
|
||||||
|
button.className = 'roundbtn button-send-message';
|
||||||
button.style.backgroundColor = 'rgb(19, 150, 204)';
|
button.style.backgroundColor = 'rgb(19, 150, 204)';
|
||||||
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
||||||
setTooltip(button, r('sendMessage', 'Send Message'));
|
setTooltip(button, r('sendMessage', 'Send Message'));
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
// TODO: Add text
|
if (typeof this.#option.onAddMessage === 'function') {
|
||||||
|
this.#option.onAddMessage(this.#enter.value);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -240,7 +360,7 @@ class CustomerCommunication {
|
|||||||
if (sendto !== '') {
|
if (sendto !== '') {
|
||||||
sendto = r('sendToColon', 'Send To :') + `\n${sendto}`;
|
sendto = r('sendToColon', 'Send To :') + `\n${sendto}`;
|
||||||
}
|
}
|
||||||
div.appendChild(createElementInit('div', div => {
|
div.appendChild(createElement('div', div => {
|
||||||
div.className = 'item-poster';
|
div.className = 'item-poster';
|
||||||
div.innerText = name;
|
div.innerText = name;
|
||||||
if (!comm.IsReply && sendto?.length > 0) {
|
if (!comm.IsReply && sendto?.length > 0) {
|
||||||
@ -248,14 +368,17 @@ class CustomerCommunication {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const content = createElement('div', 'item-content');
|
const content = createElement('div', 'item-content');
|
||||||
if (/https?:\/\//i.test(comm.Message)) {
|
content.appendChild(createElement('span', span => {
|
||||||
content.innerHTML = formatUrl(comm.Message);
|
if (/https?:\/\//i.test(comm.Message)) {
|
||||||
} else {
|
span.innerHTML = formatUrl(comm.Message);
|
||||||
content.innerText = comm.Message;
|
} else {
|
||||||
}
|
span.innerText = comm.Message;
|
||||||
|
}
|
||||||
|
}));
|
||||||
if (comm.IsReply) {
|
if (comm.IsReply) {
|
||||||
div.classList.add('item-other');
|
div.classList.add('item-other');
|
||||||
} else {
|
} else {
|
||||||
|
div.classList.add('item-self');
|
||||||
const [status, statusmsg] = this.#getMessageStatus(comm);
|
const [status, statusmsg] = this.#getMessageStatus(comm);
|
||||||
if (status !== -100) {
|
if (status !== -100) {
|
||||||
let statustext;
|
let statustext;
|
||||||
@ -284,7 +407,7 @@ class CustomerCommunication {
|
|||||||
content.style.backgroundColor = '#ffc107';
|
content.style.backgroundColor = '#ffc107';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const divstatus = createElementInit('div', div => {
|
const divstatus = createElement('div', div => {
|
||||||
div.className = 'item-status';
|
div.className = 'item-status';
|
||||||
div.innerText = statustext;
|
div.innerText = statustext;
|
||||||
if (status == -10) {
|
if (status == -10) {
|
||||||
@ -296,7 +419,7 @@ class CustomerCommunication {
|
|||||||
}
|
}
|
||||||
div.append(
|
div.append(
|
||||||
content,
|
content,
|
||||||
createElementInit('div', div => {
|
createElement('div', div => {
|
||||||
div.className = 'item-time';
|
div.className = 'item-time';
|
||||||
div.innerText = comm.TimeStr;
|
div.innerText = comm.TimeStr;
|
||||||
})
|
})
|
||||||
@ -304,10 +427,10 @@ class CustomerCommunication {
|
|||||||
children.push(div);
|
children.push(div);
|
||||||
}
|
}
|
||||||
children[0].style.marginTop = '0';
|
children[0].style.marginTop = '0';
|
||||||
this.#message.append(...children);
|
|
||||||
this.#message.scrollTop = this.#message.scrollHeight
|
|
||||||
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
|
|
||||||
}
|
}
|
||||||
|
this.#message.replaceChildren(...children);
|
||||||
|
this.#message.scrollTop = this.#message.scrollHeight
|
||||||
|
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getMessageStatus(comm) {
|
#getMessageStatus(comm) {
|
||||||
|
124
lib/app/communications/internal.js
Normal file
124
lib/app/communications/internal.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import "./style.scss";
|
||||||
|
import { createElement } from "../../functions";
|
||||||
|
import { r } from "../../utility/lgres";
|
||||||
|
import { nullOrEmpty } from "../../utility/strings";
|
||||||
|
import { escapeHtml } from "../../utility";
|
||||||
|
import { setTooltip } from "../../ui/tooltip";
|
||||||
|
import { createIcon } from "../../ui/icon";
|
||||||
|
import { createBox } from "./lib";
|
||||||
|
|
||||||
|
class InternalComment {
|
||||||
|
#container;
|
||||||
|
#option;
|
||||||
|
#enter;
|
||||||
|
#message;
|
||||||
|
|
||||||
|
constructor(opt) {
|
||||||
|
this.#option = opt ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get text() { return this.#enter?.value }
|
||||||
|
set text(s) {
|
||||||
|
const element = this.#enter;
|
||||||
|
if (element != null) {
|
||||||
|
element.value = s
|
||||||
|
s = String(nullOrEmpty(s) ? 0 : val.length) + '/3000';
|
||||||
|
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
const container = createBox(
|
||||||
|
createElement('div', null,
|
||||||
|
createElement('div', div => div.innerText = r('internalComments', 'Internal Comments'))
|
||||||
|
), []
|
||||||
|
);
|
||||||
|
// enter box
|
||||||
|
const enter = createElement('textarea');
|
||||||
|
enter.placeholder = r('typeComment', 'Enter Comment Here');
|
||||||
|
enter.maxLength = 3000;
|
||||||
|
enter.addEventListener('input', () => {
|
||||||
|
const val = this.#enter.value;
|
||||||
|
const s = String(nullOrEmpty(val) ? 0 : val.length) + '/3000';
|
||||||
|
this.#container.querySelector('.message-bar .prompt-count').innerText = s;
|
||||||
|
});
|
||||||
|
this.#enter = enter;
|
||||||
|
container.appendChild(
|
||||||
|
createElement('div', 'message-bar',
|
||||||
|
enter,
|
||||||
|
createElement('div', div => div.style.textAlign = 'right',
|
||||||
|
createElement('div', 'prompt-count'),
|
||||||
|
createElement('button', button => {
|
||||||
|
button.className = 'roundbtn button-send-message';
|
||||||
|
button.style.backgroundColor = 'rgb(19, 150, 204)';
|
||||||
|
button.appendChild(createIcon('fa-solid', 'paper-plane'));
|
||||||
|
setTooltip(button, r('sendMessage', 'Send Message'));
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
if (typeof this.#option.onAddMessage === 'function') {
|
||||||
|
this.#option.onAddMessage(this.#enter.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
createElement('button', button => {
|
||||||
|
button.className = 'roundbtn button-post-note';
|
||||||
|
button.style.border = '1px solid rgb(19, 150, 204)';
|
||||||
|
button.style.fill = 'rgb(19, 150, 204)';
|
||||||
|
button.appendChild(createIcon('fa-solid', 'comment-alt-lines'));
|
||||||
|
setTooltip(button, r('postNote', 'Post Note'));
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
if (typeof this.#option.onAddComment === 'function') {
|
||||||
|
this.#option.onAddComment(this.#enter.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const message = createElement('div', 'list-bar');
|
||||||
|
this.#message = message;
|
||||||
|
container.appendChild(message);
|
||||||
|
return this.#container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
load(data) {
|
||||||
|
const children = [];
|
||||||
|
if (data?.length > 0) {
|
||||||
|
for (let comment of data) {
|
||||||
|
const div = createElement('div', 'item-div');
|
||||||
|
// if (sendto !== '') {
|
||||||
|
// sendto = r('sendToColon', 'Send To :') + `\n${sendto}`;
|
||||||
|
// }
|
||||||
|
div.appendChild(createElement('div', div => {
|
||||||
|
div.className = 'item-poster';
|
||||||
|
div.innerText = comment.UserName;
|
||||||
|
}));
|
||||||
|
const content = createElement('div', 'item-content');
|
||||||
|
content.appendChild(createElement('span', span => span.innerText = escapeHtml(comment.Comment)));
|
||||||
|
if (comment.FollowUp?.length > 0) {
|
||||||
|
div.classList.add('item-sent');
|
||||||
|
const sendto = r('sendToColon', 'Send To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n');
|
||||||
|
content.appendChild(createElement('div', div => {
|
||||||
|
div.className = 'item-status';
|
||||||
|
div.innerText = r('sent', 'Sent');
|
||||||
|
setTooltip(div, sendto);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
div.append(
|
||||||
|
content,
|
||||||
|
createElement('div', div => {
|
||||||
|
div.className = 'item-time';
|
||||||
|
div.innerText = comment.SubmitDateStr;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
children.push(div);
|
||||||
|
}
|
||||||
|
children[0].style.marginTop = '0';
|
||||||
|
}
|
||||||
|
this.#message.replaceChildren(...children);
|
||||||
|
this.#message.scrollTop = this.#message.scrollHeight
|
||||||
|
// setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InternalComment;
|
@ -1,4 +1,4 @@
|
|||||||
import { createElement, createElementInit } from "../../functions";
|
import { createElement } from "../../functions";
|
||||||
|
|
||||||
function createBox(title, functions) {
|
function createBox(title, functions) {
|
||||||
const container = createElement('div', 'comm');
|
const container = createElement('div', 'comm');
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
:root {
|
:root {
|
||||||
--dark-fore-color: #fff;
|
|
||||||
--dark-fore-opacity-color: rgba(255, 255, 255, .6);
|
|
||||||
--title-color: #fff;
|
--title-color: #fff;
|
||||||
--title-bg-color: rgb(68, 114, 196);
|
--title-bg-color: rgb(68, 114, 196);
|
||||||
--strong-color: #333;
|
|
||||||
--medium-font-size: .875rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.comm {
|
.comm {
|
||||||
@ -13,6 +9,15 @@
|
|||||||
width: 320px;
|
width: 320px;
|
||||||
background-color: var(--dark-fore-color);
|
background-color: var(--dark-fore-color);
|
||||||
border: 1px solid var(--title-bg-color);
|
border: 1px solid var(--title-bg-color);
|
||||||
|
margin-left: 12px;
|
||||||
|
|
||||||
|
& {
|
||||||
|
--dark-fore-color: #fff;
|
||||||
|
--dark-fore-opacity-color: rgba(255, 255, 255, .6);
|
||||||
|
--strong-color: #333;
|
||||||
|
--light-color: #ccc;
|
||||||
|
--medium-font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
.roundbtn {
|
.roundbtn {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
@ -86,6 +91,7 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
font-size: 1.2em;
|
||||||
|
|
||||||
>div {
|
>div {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
@ -94,6 +100,7 @@
|
|||||||
>.title-functions {
|
>.title-functions {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
padding: 4px;
|
||||||
|
|
||||||
>label {
|
>label {
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
@ -122,6 +129,19 @@
|
|||||||
opacity: .6;
|
opacity: .6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: .6;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--dark-fore-color);
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
opacity: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,29 +152,41 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
border-bottom: 1px solid var(--title-bg-color);
|
border-bottom: 1px solid var(--title-bg-color);
|
||||||
|
|
||||||
>svg {
|
>.bar-icon {
|
||||||
width: 30px;
|
flex: 0 0 auto;
|
||||||
height: 30px;
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 22px;
|
|
||||||
|
|
||||||
>svg {
|
>svg {
|
||||||
flex: 0 0 auto;
|
width: 30px;
|
||||||
width: 16px;
|
height: 30px;
|
||||||
height: 16px;
|
margin: 0 8px;
|
||||||
margin-right: 6px;
|
|
||||||
fill: var(--strong-color);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
>span {
|
>.bar-list {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
color: var(--strong-color);
|
width: calc(100% - 46px);
|
||||||
font-size: var(--medium-font-size);
|
|
||||||
|
.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);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,6 +214,11 @@
|
|||||||
|
|
||||||
>div {
|
>div {
|
||||||
padding: 0 10px 10px;
|
padding: 0 10px 10px;
|
||||||
|
|
||||||
|
>.prompt-count {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--light-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,64 +243,78 @@
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.item-poster {
|
.item-poster {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
align-self: flex-end;
|
align-self: flex-start;
|
||||||
|
|
||||||
.tooltip-wrapper>.tooltip-content {
|
.tooltip-wrapper>.tooltip-content {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
line-height: 1.2rem;
|
|
||||||
padding: 8px 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-word;
|
|
||||||
max-width: 240px;
|
|
||||||
margin-right: 10px;
|
|
||||||
background-color: #9eea6a;
|
|
||||||
align-self: flex-end;
|
|
||||||
|
|
||||||
a>svg {
|
|
||||||
width: 13px;
|
|
||||||
height: 13px;
|
|
||||||
fill: #2140fb;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-status {
|
|
||||||
text-align: right;
|
|
||||||
margin-top: 3px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: -12px;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-time {
|
|
||||||
align-self: flex-end;
|
|
||||||
color: #aaa;
|
|
||||||
font-size: .7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.item-other {
|
|
||||||
|
|
||||||
.item-poster,
|
|
||||||
.item-content {
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-content {
|
.item-content {
|
||||||
|
line-height: 1.2rem;
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
max-width: 240px;
|
||||||
background-color: rgb(244, 244, 244);
|
background-color: rgb(244, 244, 244);
|
||||||
margin-right: unset;
|
|
||||||
margin-left: 10px;
|
a>svg {
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
fill: #2140fb;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>span::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 3px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: -12px;
|
||||||
|
font-size: 10px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-time {
|
||||||
|
align-self: flex-end;
|
||||||
|
color: #aaa;
|
||||||
|
font-size: .7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.item-other {
|
||||||
|
.item-content {
|
||||||
|
margin-left: 10px;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.item-self {
|
||||||
|
|
||||||
|
.item-poster,
|
||||||
|
.item-content {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
margin-right: 10px;
|
||||||
|
background-color: #9eea6a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.item-sent .item-content {
|
||||||
|
background-color: rgb(164, 226, 251);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
lib/functions.d.ts
vendored
3
lib/functions.d.ts
vendored
@ -1,2 +1 @@
|
|||||||
export function createElement<K extends keyof HTMLElementTagNameMap>(tagName: K, className?: string, ...children?: (Node | string)[]): HTMLElementTagNameMap[K];
|
export function createElement<K extends keyof HTMLElementTagNameMap>(tagName: K, init?: string | ((element: HTMLElementTagNameMap[K]) => void), ...children?: (Node | string)[]): HTMLElementTagNameMap[K];
|
||||||
export function createElementInit<K extends keyof HTMLElementTagNameMap>(tagName: K, init?: (element: HTMLElementTagNameMap[K]) => void, ...children?: (Node | string)[]): HTMLElementTagNameMap[K];
|
|
@ -1,19 +1,12 @@
|
|||||||
function createElement(tagName, className, ...children) {
|
export function createElement(tagName, init, ...children) {
|
||||||
return createElementInit(tagName, className && (element => element.className = className), ...children);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createElementInit(tagName, init, ...children) {
|
|
||||||
const element = document.createElement(tagName);
|
const element = document.createElement(tagName);
|
||||||
if (typeof init === 'function') {
|
if (typeof init === 'function') {
|
||||||
init(element);
|
init(element);
|
||||||
|
} else if (init != null) {
|
||||||
|
element.className = init;
|
||||||
}
|
}
|
||||||
if (children.length > 0) {
|
if (children.length > 0) {
|
||||||
element.append(...children);
|
element.append(...children);
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
|
||||||
createElement,
|
|
||||||
createElementInit
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import { createElement, createElementInit } from "../functions";
|
import { createElement } from "../functions";
|
||||||
import { createIcon } from "./icon";
|
import { createIcon } from "./icon";
|
||||||
|
|
||||||
function fillCheckbox(container, type, label) {
|
function fillCheckbox(container, type, label) {
|
||||||
@ -9,14 +9,14 @@ function fillCheckbox(container, type, label) {
|
|||||||
container.appendChild(label);
|
container.appendChild(label);
|
||||||
} else if (label?.length > 0) {
|
} else if (label?.length > 0) {
|
||||||
container.appendChild(
|
container.appendChild(
|
||||||
createElementInit('span', span => span.innerText = label)
|
createElement('span', span => span.innerText = label)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCheckbox(opts = {}) {
|
function createCheckbox(opts = {}) {
|
||||||
const container = createElement('label', 'checkbox-wrapper',
|
const container = createElement('label', 'checkbox-wrapper',
|
||||||
createElementInit('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;
|
||||||
|
@ -279,7 +279,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
#dropdown-sample>.dropdown-wrapper {
|
#dropdown-sample>.drop-wrapper {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ if (dropdownGlobal == null) {
|
|||||||
configurable: false,
|
configurable: false,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
value: function () {
|
value: function () {
|
||||||
const panel = document.querySelector('.dropdown-wrapper .dropdown-box.active');
|
const panel = document.querySelector('.drop-wrapper .drop-box.active');
|
||||||
if (panel == null) {
|
if (panel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ if (dropdownGlobal == null) {
|
|||||||
document.addEventListener('mousedown', e => {
|
document.addEventListener('mousedown', e => {
|
||||||
let parent = e.target;
|
let parent = e.target;
|
||||||
while (parent != null) {
|
while (parent != null) {
|
||||||
if (parent.classList.contains('dropdown-box')) {
|
if (parent.classList.contains('drop-box')) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -111,14 +111,14 @@ class Dropdown {
|
|||||||
const options = this.#options;
|
const options = this.#options;
|
||||||
|
|
||||||
// wrapper
|
// wrapper
|
||||||
const wrapper = createElement('div', 'dropdown-wrapper');
|
const wrapper = createElement('div', 'drop-wrapper');
|
||||||
const dropId = String(Math.random()).substring(2);
|
const dropId = String(Math.random()).substring(2);
|
||||||
wrapper.dataset.dropId = dropId;
|
wrapper.dataset.dropId = dropId;
|
||||||
dropdownGlobal[dropId] = this;
|
dropdownGlobal[dropId] = this;
|
||||||
this.#wrapper = wrapper;
|
this.#wrapper = wrapper;
|
||||||
|
|
||||||
// header
|
// header
|
||||||
const header = createElement('div', 'dropdown-header');
|
const header = createElement('div', 'drop-header');
|
||||||
header.addEventListener('click', () => {
|
header.addEventListener('click', () => {
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
return;
|
return;
|
||||||
@ -136,7 +136,7 @@ class Dropdown {
|
|||||||
// label or input
|
// label or input
|
||||||
let label;
|
let label;
|
||||||
if (options.input) {
|
if (options.input) {
|
||||||
label = createElement('input', 'dropdown-text');
|
label = createElement('input', 'drop-text');
|
||||||
label.setAttribute('type', 'text');
|
label.setAttribute('type', 'text');
|
||||||
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);
|
||||||
@ -151,7 +151,7 @@ class Dropdown {
|
|||||||
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
|
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
|
||||||
} else {
|
} else {
|
||||||
isPositive(options.tabindex) && header.setAttribute('tabindex', options.tabindex);
|
isPositive(options.tabindex) && header.setAttribute('tabindex', options.tabindex);
|
||||||
label = createElement('label', 'dropdown-text');
|
label = createElement('label', 'drop-text');
|
||||||
}
|
}
|
||||||
this.#label = label;
|
this.#label = label;
|
||||||
if (options.multiselect) {
|
if (options.multiselect) {
|
||||||
@ -164,7 +164,7 @@ class Dropdown {
|
|||||||
} else if (options.selected != null) {
|
} else if (options.selected != null) {
|
||||||
this.select(options.selected, true);
|
this.select(options.selected, true);
|
||||||
}
|
}
|
||||||
header.append(label, createElement('label', 'dropdown-caret'));
|
header.append(label, createElement('label', 'drop-caret'));
|
||||||
wrapper.appendChild(header);
|
wrapper.appendChild(header);
|
||||||
|
|
||||||
this.disabled = options.disabled || false;
|
this.disabled = options.disabled || false;
|
||||||
@ -173,16 +173,16 @@ class Dropdown {
|
|||||||
|
|
||||||
get multiselect() { return this.#options.multiselect }
|
get multiselect() { return this.#options.multiselect }
|
||||||
|
|
||||||
get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.dropdown-header.disabled') != null }
|
get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.drop-header.disabled') != null }
|
||||||
|
|
||||||
set disabled(flag) {
|
set disabled(flag) {
|
||||||
if (this.#wrapper == null) {
|
if (this.#wrapper == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (flag) {
|
if (flag) {
|
||||||
this.#wrapper.querySelector('.dropdown-header').classList.add('disabled');
|
this.#wrapper.querySelector('.drop-header').classList.add('disabled');
|
||||||
} else {
|
} else {
|
||||||
this.#wrapper.querySelector('.dropdown-header').classList.remove('disabled');
|
this.#wrapper.querySelector('.drop-header').classList.remove('disabled');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,16 +275,16 @@ class Dropdown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get #expanded() { return this.#container?.style.visibility === 'visible' }
|
get #expanded() { return this.#container?.style?.visibility === 'visible' }
|
||||||
|
|
||||||
#dropdown(flag = true) {
|
#dropdown(flag = true) {
|
||||||
const options = this.#options;
|
const options = this.#options;
|
||||||
let panel = this.#container;
|
let panel = this.#container;
|
||||||
if (panel == null) {
|
if (panel == null) {
|
||||||
panel = createElement('div', 'dropdown-box');
|
panel = createElement('div', 'drop-box');
|
||||||
// search box
|
// search box
|
||||||
if (!options.input && options.search) {
|
if (!options.input && options.search) {
|
||||||
const search = createElement('div', 'dropdown-search');
|
const search = createElement('div', 'drop-search');
|
||||||
const input = createElement('input');
|
const input = createElement('input');
|
||||||
input.setAttribute('type', 'text');
|
input.setAttribute('type', 'text');
|
||||||
isPositive(options.tabindex) && input.setAttribute('tabindex', options.tabindex);
|
isPositive(options.tabindex) && input.setAttribute('tabindex', options.tabindex);
|
||||||
@ -298,7 +298,7 @@ class Dropdown {
|
|||||||
panel.appendChild(search);
|
panel.appendChild(search);
|
||||||
}
|
}
|
||||||
// list
|
// list
|
||||||
const list = createElement('ul', 'dropdown-list');
|
const list = createElement('ul', 'drop-list');
|
||||||
if (!this.multiselect) {
|
if (!this.multiselect) {
|
||||||
list.addEventListener('click', e => {
|
list.addEventListener('click', e => {
|
||||||
let li = e.target;
|
let li = e.target;
|
||||||
@ -321,7 +321,7 @@ class Dropdown {
|
|||||||
if (flag) {
|
if (flag) {
|
||||||
let source = this.source;
|
let source = this.source;
|
||||||
if (!options.input && options.search) {
|
if (!options.input && options.search) {
|
||||||
const search = panel.querySelector('.dropdown-search > input');
|
const search = panel.querySelector('.drop-search > input');
|
||||||
if (!nullOrEmpty(search?.value)) {
|
if (!nullOrEmpty(search?.value)) {
|
||||||
source = filterSource(options.searchkeys, options.textkey, search.value, source);
|
source = filterSource(options.searchkeys, options.textkey, search.value, source);
|
||||||
}
|
}
|
||||||
@ -348,7 +348,7 @@ class Dropdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#filllist(source) {
|
#filllist(source) {
|
||||||
const list = this.#container.querySelector('.dropdown-list');
|
const list = this.#container.querySelector('.drop-list');
|
||||||
list.replaceChildren();
|
list.replaceChildren();
|
||||||
const multiselect = this.multiselect;
|
const multiselect = this.multiselect;
|
||||||
const allchecked = this.#allChecked;
|
const allchecked = this.#allChecked;
|
||||||
|
7
lib/ui/grid.d.ts
vendored
7
lib/ui/grid.d.ts
vendored
@ -5,6 +5,11 @@ interface GridItem {
|
|||||||
displayValue: string;
|
displayValue: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GridSourceItem {
|
||||||
|
value: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
declare var GridColumn: {
|
declare var GridColumn: {
|
||||||
create(): HTMLElement;
|
create(): HTMLElement;
|
||||||
createEdit(trigger: (e: any) => void, col: GridColumnDefinition, body: HTMLElement): HTMLElement;
|
createEdit(trigger: (e: any) => void, col: GridColumnDefinition, body: HTMLElement): HTMLElement;
|
||||||
@ -44,7 +49,7 @@ interface GridColumnDefinition {
|
|||||||
sortFilter?: (a: GridItem | any, b: GridItem | any) => -1 | 0 | 1;
|
sortFilter?: (a: GridItem | any, b: GridItem | any) => -1 | 0 | 1;
|
||||||
bgFilter?: (item: GridItem | any) => string;
|
bgFilter?: (item: GridItem | any) => string;
|
||||||
dropOptions?: DropdownOptions;
|
dropOptions?: DropdownOptions;
|
||||||
source?: Array<any> | ((item: GridItem | any) => Array<any>);
|
source?: Array<any> | ((item: GridItem | any) => Array<any> | Promise<Array<GridSourceItem>>);
|
||||||
iconType?: string;
|
iconType?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
sortFilter?: (a: GridItem | any, b: GridItem | any) => -1 | 0 | 1;
|
sortFilter?: (a: GridItem | any, b: GridItem | any) => -1 | 0 | 1;
|
||||||
bgFilter?: (item: GridItem | any) => string;
|
bgFilter?: (item: GridItem | any) => string;
|
||||||
dropOptions?: DropdownOptions;
|
dropOptions?: DropdownOptions;
|
||||||
source?: Array<any> | ((item: GridItem | any) => Array<any>);
|
source?: Array<any> | ((item: GridItem | any) => Array<any> | Promise<Array<GridSourceItem>>);
|
||||||
iconType?: string;
|
iconType?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
@ -136,8 +136,8 @@
|
|||||||
<samp>val: any</samp>
|
<samp>val: any</samp>
|
||||||
<p>单元格的值</p>
|
<p>单元格的值</p>
|
||||||
<samp>item: GridItem | any</samp>
|
<samp>item: GridItem | any</samp>
|
||||||
<p>单元格所在行的数据项/p>
|
<p>单元格所在行的数据项</p>
|
||||||
<samp>col: GridColumnDefinition</samp>
|
<samp>col: GridColumnDefinition</samp>
|
||||||
<p>单元格所在列的定义对象</p>
|
<p>单元格所在列的定义对象</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h2>示例</h2>
|
<h2>示例</h2>
|
||||||
@ -198,12 +198,22 @@
|
|||||||
key: 'c2a',
|
key: 'c2a',
|
||||||
caption: '下拉',
|
caption: '下拉',
|
||||||
type: Grid.ColumnTypes.Dropdown,
|
type: Grid.ColumnTypes.Dropdown,
|
||||||
source: [
|
source: item => {
|
||||||
{ value: 'off', text: 'Off' },
|
if (item.source == null) {
|
||||||
{ value: 'pending', text: 'Pending' },
|
return new Promise((resolve, reject) => {
|
||||||
{ value: 'broken', text: 'Broken' },
|
setTimeout(() => {
|
||||||
{ value: 'running', text: 'Running' }
|
item.source = [
|
||||||
],
|
{ value: 'off', text: 'Off' },
|
||||||
|
{ value: 'pending', text: 'Pending' },
|
||||||
|
{ value: 'broken', text: 'Broken' },
|
||||||
|
{ value: 'running', text: 'Running' }
|
||||||
|
];
|
||||||
|
resolve(item.source);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return item.source;
|
||||||
|
},
|
||||||
enabled: 'enabled',
|
enabled: 'enabled',
|
||||||
onchanged: (item, value) => console.log('dropdown changed', item, value)
|
onchanged: (item, value) => console.log('dropdown changed', item, value)
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { global, isPositive, isMobile, throttle, truncate } from "../utility";
|
import { global, isPositive, isMobile, throttle, truncate } from "../utility";
|
||||||
import { nullOrEmpty } from "../utility/strings";
|
import { nullOrEmpty } from "../utility/strings";
|
||||||
import { r } from "../utility/lgres";
|
import { r } from "../utility/lgres";
|
||||||
import { createElement, createElementInit } from "../functions";
|
import { createElement } from "../functions";
|
||||||
import { createIcon } from "./icon";
|
import { createIcon } from "./icon";
|
||||||
import { createCheckbox } from "./checkbox";
|
import { createCheckbox } from "./checkbox";
|
||||||
import { setTooltip } from "./tooltip";
|
import { setTooltip } from "./tooltip";
|
||||||
@ -85,7 +85,7 @@ class GridDropdownColumn extends GridColumn {
|
|||||||
return drop.create();
|
return drop.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDrop(element) {
|
static #getDrop(element) {
|
||||||
const dropGlobal = global[SymbolDropdown];
|
const dropGlobal = global[SymbolDropdown];
|
||||||
if (dropGlobal == null) {
|
if (dropGlobal == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -98,7 +98,7 @@ class GridDropdownColumn extends GridColumn {
|
|||||||
return drop;
|
return drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSource(item, col) {
|
static #getSource(item, col) {
|
||||||
let source = col.source;
|
let source = col.source;
|
||||||
if (typeof source === 'function') {
|
if (typeof source === 'function') {
|
||||||
source = source(item);
|
source = source(item);
|
||||||
@ -106,23 +106,37 @@ class GridDropdownColumn extends GridColumn {
|
|||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static #setValue(source, element, val) {
|
||||||
|
const data = source?.find(v => v.value === val);
|
||||||
|
if (data != null) {
|
||||||
|
val = data.text;
|
||||||
|
}
|
||||||
|
super.setValue(element, val);
|
||||||
|
}
|
||||||
|
|
||||||
static setValue(element, val, item, col) {
|
static setValue(element, val, item, col) {
|
||||||
if (element.tagName !== 'DIV') {
|
if (element.tagName !== 'DIV') {
|
||||||
let source = this.getSource(item, col);
|
let source = this.#getSource(item, col);
|
||||||
const data = source?.find(v => v.value === val);
|
if (source instanceof Promise) {
|
||||||
if (data != null) {
|
source.then(s => this.#setValue(s, element, val));
|
||||||
val = data.text;
|
} else {
|
||||||
|
this.#setValue(source, element, val);
|
||||||
}
|
}
|
||||||
super.setValue(element, val);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const drop = this.getDrop(element);
|
const drop = this.#getDrop(element);
|
||||||
if (drop == null) {
|
if (drop == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (drop.source == null || drop.source.length === 0) {
|
if (drop.source == null || drop.source.length === 0) {
|
||||||
let source = this.getSource(item, col);
|
let source = this.#getSource(item, col);
|
||||||
if (source != null) {
|
if (source instanceof Promise) {
|
||||||
|
source.then(s => {
|
||||||
|
drop.source = s;
|
||||||
|
drop.select(val, true);
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
} else if (source != null) {
|
||||||
drop.source = source;
|
drop.source = source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +148,7 @@ class GridDropdownColumn extends GridColumn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static setEnabled(element, enabled) {
|
static setEnabled(element, enabled) {
|
||||||
const drop = this.getDrop(element);
|
const drop = this.#getDrop(element);
|
||||||
if (drop == null) {
|
if (drop == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -316,7 +330,7 @@ class Grid {
|
|||||||
|
|
||||||
get selectedIndex() { return (this.#selectedIndexes && this.#selectedIndexes[0]) ?? -1 }
|
get selectedIndex() { return (this.#selectedIndexes && this.#selectedIndexes[0]) ?? -1 }
|
||||||
|
|
||||||
get loading() { return this.#refs.loading?.style.visibility === 'visible' }
|
get loading() { return this.#refs.loading?.style?.visibility === 'visible' }
|
||||||
set loading(flag) {
|
set loading(flag) {
|
||||||
if (this.#refs.loading == null) {
|
if (this.#refs.loading == null) {
|
||||||
return;
|
return;
|
||||||
|
45
lib/ui/popup.js
Normal file
45
lib/ui/popup.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import "../../css/popup.scss";
|
||||||
|
import { createElement } from "../functions";
|
||||||
|
import { createIcon } from "./icon";
|
||||||
|
|
||||||
|
function createPopup(title, content, ...buttons) {
|
||||||
|
const mask = createElement('div', 'popup-mask');
|
||||||
|
const container = createElement('div', 'popup-container');
|
||||||
|
const close = () => {
|
||||||
|
mask.classList.add('popup-active');
|
||||||
|
mask.style.opacity = 0;
|
||||||
|
setTimeout(() => mask.remove(), 120);
|
||||||
|
};
|
||||||
|
container.append(
|
||||||
|
createElement('div', header => {
|
||||||
|
header.className = 'popup-header';
|
||||||
|
if (title instanceof HTMLElement) {
|
||||||
|
header.appendChild(title);
|
||||||
|
} else {
|
||||||
|
header.appendChild(createElement('div', t => t.innerText = title));
|
||||||
|
}
|
||||||
|
const cancel = createIcon('fa-regular', 'times');
|
||||||
|
cancel.addEventListener('click', () => close());
|
||||||
|
header.appendChild(cancel);
|
||||||
|
}),
|
||||||
|
createElement('div', 'popup-body', content),
|
||||||
|
createElement('div', 'popup-footer', ...buttons.map(b => {
|
||||||
|
const button = createElement('div', 'popup-button');
|
||||||
|
button.innerText = b.text;
|
||||||
|
if (typeof b.trigger === 'function') {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
if (b.trigger(container) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
mask.appendChild(container);
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createPopup
|
||||||
|
}
|
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): HTMLElement
|
export function setTooltip(container: HTMLElement, content: string | HTMLElement, flag?: boolean): HTMLElement
|
||||||
export function resolveTooltip(container?: HTMLElement): HTMLElement
|
export function resolveTooltip(container?: HTMLElement): HTMLElement
|
@ -5,7 +5,7 @@
|
|||||||
给某个元素或者页面上含有 title 属性的元素设置一个统一样式的 tooltip。
|
给某个元素或者页面上含有 title 属性的元素设置一个统一样式的 tooltip。
|
||||||
</p>
|
</p>
|
||||||
<h2>setTooltip</h2>
|
<h2>setTooltip</h2>
|
||||||
<code>function setTooltip(container: HTMLElement, content: string | HTMLElement): void</code>
|
<code>function setTooltip(container: HTMLElement, content: string | HTMLElement, flag?: boolean): void</code>
|
||||||
<h3>container: HTMLElement</h3>
|
<h3>container: HTMLElement</h3>
|
||||||
<p>
|
<p>
|
||||||
要设置 tooltip 的元素
|
要设置 tooltip 的元素
|
||||||
@ -14,6 +14,10 @@
|
|||||||
<p>
|
<p>
|
||||||
要设置的 tooltip 内容,允许为字符串或者 HTML 元素
|
要设置的 tooltip 内容,允许为字符串或者 HTML 元素
|
||||||
</p>
|
</p>
|
||||||
|
<h3>flag?: boolean</h3>
|
||||||
|
<p>
|
||||||
|
是否启用严格模式,只有显示不完整时才显示 tooltip
|
||||||
|
</p>
|
||||||
<h2>resolveTooltip</h2>
|
<h2>resolveTooltip</h2>
|
||||||
<code>function resolveTooltip(container?: HTMLElement): HTMLElement</code>
|
<code>function resolveTooltip(container?: HTMLElement): HTMLElement</code>
|
||||||
<h3>container?: HTMLElement</h3>
|
<h3>container?: HTMLElement</h3>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { createElement, createElementInit } from "../functions";
|
import { createElement } from "../functions";
|
||||||
|
|
||||||
function setTooltip(container, content) {
|
function setTooltip(container, content, flag = false) {
|
||||||
const tip = container.querySelector('.tooltip-wrapper');
|
const tip = container.querySelector('.tooltip-wrapper');
|
||||||
if (tip != null) {
|
if (tip != null) {
|
||||||
tip.remove();
|
tip.remove();
|
||||||
}
|
}
|
||||||
const wrapper = createElementInit('div', wrapper => {
|
const wrapper = createElement('div', wrapper => {
|
||||||
wrapper.className = 'tooltip-wrapper tooltip-color';
|
wrapper.className = 'tooltip-wrapper tooltip-color';
|
||||||
wrapper.style.visibility = 'hidden';
|
wrapper.style.visibility = 'hidden';
|
||||||
wrapper.style.opacity = 0;
|
wrapper.style.opacity = 0;
|
||||||
},
|
},
|
||||||
createElement('div', 'tooltip-pointer tooltip-color'),
|
createElement('div', 'tooltip-pointer tooltip-color'),
|
||||||
createElement('div', 'tooltip-curtain tooltip-color'),
|
createElement('div', 'tooltip-curtain tooltip-color'),
|
||||||
createElementInit('div', cnt => {
|
createElement('div', cnt => {
|
||||||
cnt.className = 'tooltip-content';
|
cnt.className = 'tooltip-content';
|
||||||
if (content instanceof HTMLElement) {
|
if (content instanceof HTMLElement) {
|
||||||
cnt.appendChild(content);
|
cnt.appendChild(content);
|
||||||
@ -27,32 +27,35 @@ function setTooltip(container, content) {
|
|||||||
let tid;
|
let tid;
|
||||||
container.addEventListener('mouseenter', () => {
|
container.addEventListener('mouseenter', () => {
|
||||||
tid && clearTimeout(tid);
|
tid && clearTimeout(tid);
|
||||||
tid = setTimeout(() => {
|
let c = container;
|
||||||
while (container?.offsetWidth == null) {
|
while (c?.offsetWidth == null) {
|
||||||
container = container.parentElement;
|
c = c.parentElement;
|
||||||
}
|
}
|
||||||
if (container == null) {
|
if (c == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let parent = container;
|
if (!flag || c.scrollWidth > c.offsetWidth) {
|
||||||
let left = container.offsetLeft;
|
tid = setTimeout(() => {
|
||||||
let top = container.offsetTop;
|
let parent = c;
|
||||||
while ((parent = parent.offsetParent) != null) {
|
let left = c.offsetLeft;
|
||||||
left += parent.offsetLeft;
|
let top = c.offsetTop;
|
||||||
top += parent.offsetTop;
|
while ((parent = parent.offsetParent) != null) {
|
||||||
}
|
left += parent.offsetLeft;
|
||||||
parent = container;
|
top += parent.offsetTop;
|
||||||
while ((parent = parent.parentElement) != null) {
|
}
|
||||||
left -= parent.scrollLeft;
|
parent = c;
|
||||||
top -= parent.scrollTop;
|
while ((parent = parent.parentElement) != null) {
|
||||||
}
|
left -= parent.scrollLeft;
|
||||||
left -= wrapper.offsetWidth / 2 - container.offsetWidth / 2;
|
top -= parent.scrollTop;
|
||||||
top -= wrapper.offsetHeight + 14;
|
}
|
||||||
wrapper.style.left = `${left}px`;
|
left -= wrapper.offsetWidth / 2 - c.offsetWidth / 2;
|
||||||
wrapper.style.top = `${top}px`;
|
top -= wrapper.offsetHeight + 14;
|
||||||
wrapper.style.visibility = 'visible';
|
wrapper.style.left = `${left}px`;
|
||||||
wrapper.style.opacity = 1;
|
wrapper.style.top = `${top}px`;
|
||||||
}, 100);
|
wrapper.style.visibility = 'visible';
|
||||||
|
wrapper.style.opacity = 1;
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
container.addEventListener('mouseleave', () => {
|
container.addEventListener('mouseleave', () => {
|
||||||
tid && clearTimeout(tid);
|
tid && clearTimeout(tid);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { getCookie, setCookie, deleteCookie } from "./utility/cookie";
|
import { getCookie, setCookie, deleteCookie } from "./utility/cookie";
|
||||||
import { init, r, lang } from "./utility/lgres";
|
import { 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 } from "./utility/strings";
|
import { nullOrEmpty, contains, endsWith, padStart, formatUrl, escapeHtml } from "./utility/strings";
|
||||||
|
|
||||||
let g = typeof globalThis !== 'undefined' ? globalThis : self;
|
let g = typeof globalThis !== 'undefined' ? globalThis : self;
|
||||||
|
|
||||||
@ -58,6 +58,7 @@ export {
|
|||||||
endsWith,
|
endsWith,
|
||||||
padStart,
|
padStart,
|
||||||
formatUrl,
|
formatUrl,
|
||||||
|
escapeHtml,
|
||||||
// variables
|
// variables
|
||||||
g as global,
|
g as global,
|
||||||
isPositive,
|
isPositive,
|
||||||
|
1
lib/utility/strings.d.ts
vendored
1
lib/utility/strings.d.ts
vendored
@ -3,3 +3,4 @@ export function contains(s: string, key: string | any, ignoreCase?: boolean): bo
|
|||||||
export function endsWith(s: string, suffix: string): boolean
|
export function endsWith(s: string, suffix: string): boolean
|
||||||
export function padStart(s: string, num: Number, char: string): boolean
|
export function padStart(s: string, num: Number, char: string): boolean
|
||||||
export function formatUrl(msg: string): string
|
export function formatUrl(msg: string): string
|
||||||
|
export function escapeHtml(text: string): string
|
@ -54,6 +54,12 @@
|
|||||||
<p>
|
<p>
|
||||||
把超链接解析替换为图标
|
把超链接解析替换为图标
|
||||||
</p>
|
</p>
|
||||||
|
<h2>escapeHtml</h2>
|
||||||
|
<code>function escapeHtml(text: string): string</code>
|
||||||
|
<h3>text: string</h3>
|
||||||
|
<p>
|
||||||
|
解析转换 html 代码为显示内容
|
||||||
|
</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h2>用法</h2>
|
<h2>用法</h2>
|
||||||
<pre>const util = window["lib-utility"];
|
<pre>const util = window["lib-utility"];
|
||||||
|
@ -34,7 +34,7 @@ function formatUrl(msg) {
|
|||||||
//const urlArrray = str.match(urlReg);
|
//const urlArrray = str.match(urlReg);
|
||||||
const p = /(http|ftp|https):\/\/.+?(\s|\r\n|\r|\n|\"|\'|\*|$)/g;
|
const p = /(http|ftp|https):\/\/.+?(\s|\r\n|\r|\n|\"|\'|\*|$)/g;
|
||||||
const r = msg.match(p);
|
const r = msg.match(p);
|
||||||
msg = htmlencode(msg);
|
msg = escapeHtml(msg);
|
||||||
|
|
||||||
if (r?.length > 0) {
|
if (r?.length > 0) {
|
||||||
const rs = [];
|
const rs = [];
|
||||||
@ -50,7 +50,17 @@ function formatUrl(msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
if (text == null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return String(text)
|
||||||
|
.replaceAll('&', '&')
|
||||||
|
.replaceAll('<', '<')
|
||||||
|
.replaceAll('>', '>')
|
||||||
.replaceAll('\r\n', '<br/>')
|
.replaceAll('\r\n', '<br/>')
|
||||||
.replaceAll('\n', '<br/>')
|
.replaceAll('\n', '<br/>')
|
||||||
.replaceAll(' ', ' ');
|
.replaceAll(' ', ' ');
|
||||||
@ -61,5 +71,6 @@ export {
|
|||||||
contains,
|
contains,
|
||||||
endsWith,
|
endsWith,
|
||||||
padStart,
|
padStart,
|
||||||
formatUrl
|
formatUrl,
|
||||||
|
escapeHtml
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user