This commit is contained in:
2024-01-17 17:31:41 +08:00
parent 84190ed9f1
commit fb9e920c15
35 changed files with 3003 additions and 1304 deletions

View File

@ -6,6 +6,15 @@
font-family: var(--font-family);
@include outborder();
&.validation-error {
border-color: var(--red-color);
&:focus,
&:hover {
border-color: var(--red-color);
}
}
}
.ui-input {

1
lib/ui/css/grid.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,10 +3,7 @@
.ui-grid {
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: visible;
overflow-x: hidden;
overflow: auto;
& {
--cell-hover-bg-color: lightyellow;
@ -52,6 +49,7 @@
}
@include outline();
@include scrollbar();
&,
input[type="text"],
@ -67,321 +65,338 @@
visibility: hidden;
}
>.ui-grid-header {
width: 100%;
min-width: 100%;
margin: 0;
border-bottom: 1px solid var(--header-border-color);
background-color: var(--header-bg-color);
color: var(--header-fore-color);
user-select: none;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
>.ui-grid-wrapper {
position: relative;
tr {
position: relative;
>.ui-grid-table {
position: absolute;
width: 100%;
min-width: 100%;
margin: 0;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
>th {
padding: 0;
margin: 0;
word-wrap: break-word;
white-space: normal;
position: relative;
>thead {
tr {
color: var(--header-fore-color);
position: sticky;
top: 0;
z-index: 2;
>div {
line-height: var(--header-line-height);
min-height: var(--row-height);
display: flex;
align-items: center;
padding: var(--header-padding);
>th {
background-color: var(--header-bg-color);
user-select: none;
padding: 0;
margin: 0;
word-wrap: break-word;
white-space: normal;
position: relative;
&.sticky {
position: sticky;
z-index: 2;
}
>div {
border-bottom: 1px solid var(--header-border-color);
line-height: var(--header-line-height);
min-height: var(--row-height);
display: flex;
align-items: center;
padding: var(--header-padding);
box-sizing: border-box;
// overflow-x: hidden;
>span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
>.arrow {
width: 0;
height: 0;
top: 50%;
margin-top: calc(0px - var(--arrow-size) / 2);
right: calc(var(--arrow-size) / 2);
position: absolute;
&.asc {
border-bottom: var(--arrow-size) solid var(--dark-border-color);
}
&.desc {
border-top: var(--arrow-size) solid var(--dark-border-color);
}
&.asc,
&.desc {
border-left: var(--arrow-size) solid transparent;
border-right: var(--arrow-size) solid transparent;
}
}
>.filter {
width: var(--filter-size);
height: var(--filter-size);
top: 50%;
margin-top: calc(0px - var(--filter-size) / 2);
right: calc(var(--arrow-size) * 2 + 4px);
position: absolute;
display: flex;
>svg {
width: 100%;
height: 100%;
fill: var(--color);
opacity: .2;
transition: opacity .12s ease;
&:hover {
opacity: .8;
}
}
&.hover>svg {
opacity: .8;
}
&.active>svg {
opacity: 1;
}
}
>.spliter {
position: absolute;
height: 100%;
top: 0;
right: calc(0px - var(--split-width) /2);
width: var(--split-width);
cursor: ew-resize;
z-index: 1;
&::after {
content: '';
height: 100%;
width: 1px;
display: block;
margin: 0 auto;
transition: background-color .12s ease;
}
&:hover::after {
background-color: var(--split-border-color);
}
}
>.dragger {
position: absolute;
left: 0;
top: 0;
min-width: var(--dragger-size);
height: 100%;
background-color: var(--dragger-bg-color);
opacity: var(--dragger-opacity);
display: none;
}
>.dragger-cursor {
position: absolute;
top: 0;
height: 100%;
border: 1px solid var(--dragger-cursor-color);
box-sizing: border-box;
margin-left: 0;
opacity: var(--dragger-cursor-opacity);
display: none;
transition: left .12s ease;
&::before {
top: -1px;
border-top: var(--dragger-cursor-size) solid;
}
&::after {
bottom: -1px;
border-bottom: var(--dragger-cursor-size) solid;
}
&::before,
&::after {
content: '';
position: absolute;
left: var(--dragger-cursor-pos);
border-left: var(--dragger-cursor-size) solid transparent;
border-right: var(--dragger-cursor-size) solid transparent;
}
}
&.header-filter>div {
padding: var(--header-filter-padding);
}
}
}
}
>tbody {
color: var(--cell-fore-color);
>.ui-grid-row {
line-height: var(--line-height);
white-space: nowrap;
background-color: var(--row-bg-color);
border-bottom: 1px solid var(--cell-border-color);
box-sizing: border-box;
// overflow-x: hidden;
>span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&:hover {
background-color: var(--row-active-bg-color);
>.arrow {
width: 0;
height: 0;
top: 50%;
margin-top: calc(0px - var(--arrow-size) / 2);
right: calc(var(--arrow-size) / 2);
position: absolute;
&.asc {
border-bottom: var(--arrow-size) solid var(--dark-border-color);
}
&.desc {
border-top: var(--arrow-size) solid var(--dark-border-color);
}
&.asc,
&.desc {
border-left: var(--arrow-size) solid transparent;
border-right: var(--arrow-size) solid transparent;
}
}
>.filter {
width: var(--filter-size);
height: var(--filter-size);
top: 50%;
margin-top: calc(0px - var(--filter-size) / 2);
right: calc(var(--arrow-size) * 2 + 4px);
position: absolute;
display: flex;
>svg {
width: 100%;
height: 100%;
fill: var(--color);
opacity: .2;
transition: opacity .12s ease;
&:hover {
opacity: .8;
>td.sticky {
background-color: var(--row-active-bg-color);
}
}
&.hover>svg {
opacity: .8;
&.selected {
background-color: var(--row-selected-bg-color);
>td.sticky {
background-color: var(--row-selected-bg-color);
}
}
&.active>svg {
>td {
padding: 0;
&.sticky {
position: sticky;
z-index: 1;
background-color: var(--row-bg-color);
}
>span {
padding: var(--spacing-cell);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
>input[type="text"],
>textarea {
border: none;
box-sizing: border-box;
width: 100%;
padding: 0;
@include outline();
&:disabled {
color: var(--text-disabled-color);
}
}
>input[type="text"] {
height: var(--row-height);
text-indent: var(--text-indent);
}
>textarea {
resize: none;
line-height: var(--line-height);
display: block;
padding: var(--spacing-cell);
white-space: nowrap;
@include scrollbar();
}
.ui-check-wrapper {
display: flex;
justify-content: center;
.ui-check-inner {
&,
>svg {
transition: none;
}
}
}
.ui-drop-wrapper {
height: var(--row-height);
width: 100%;
display: flex;
flex-direction: column;
>.ui-drop-header {
border: none;
height: 100%;
>.ui-drop-text {
padding: var(--spacing-cell);
}
}
>.ui-drop-box {
top: calc(var(--row-height) + 2px);
&.slide-up {
top: unset;
bottom: calc(var(--row-height) + 2px);
}
}
}
.col-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
position: relative;
>svg {
width: 16px;
height: 16px;
fill: var(--primary-color);
transition: opacity .12s ease;
}
&:hover>svg {
opacity: .4;
}
&.disabled {
cursor: unset;
>svg {
fill: var(--header-border-color);
opacity: unset;
}
}
}
}
}
.ui-grid-hover-holder {
box-sizing: border-box;
position: absolute;
line-height: var(--line-height);
padding: var(--spacing-cell);
background-color: var(--cell-hover-bg-color);
white-space: pre;
display: flex;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear .12s, opacity .12s ease;
&.active {
visibility: visible;
opacity: 1;
}
}
>.spliter {
position: absolute;
height: 100%;
top: 0;
right: calc(0px - var(--split-width) /2);
width: var(--split-width);
cursor: ew-resize;
z-index: 1;
&::after {
content: '';
height: 100%;
width: 1px;
display: block;
margin: 0 auto;
transition: background-color .12s ease;
}
&:hover::after {
background-color: var(--split-border-color);
}
}
>.dragger {
position: absolute;
left: 0;
top: 0;
min-width: var(--dragger-size);
height: 100%;
background-color: var(--dragger-bg-color);
opacity: var(--dragger-opacity);
display: none;
}
>.dragger-cursor {
position: absolute;
top: 0;
height: 100%;
border: 1px solid var(--dragger-cursor-color);
box-sizing: border-box;
margin-left: 0;
opacity: var(--dragger-cursor-opacity);
display: none;
transition: left .12s ease;
&::before {
top: -1px;
border-top: var(--dragger-cursor-size) solid;
}
&::after {
bottom: -1px;
border-bottom: var(--dragger-cursor-size) solid;
}
&::before,
&::after {
content: '';
position: absolute;
left: var(--dragger-cursor-pos);
border-left: var(--dragger-cursor-size) solid transparent;
border-right: var(--dragger-cursor-size) solid transparent;
}
}
&.header-filter>div {
padding: var(--header-filter-padding);
}
}
}
}
>.ui-grid-body {
flex: 1 1 auto;
overflow: auto;
color: var(--cell-fore-color);
@include scrollbar();
.ui-grid-body-content {
position: absolute;
min-width: 100%;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
>.ui-grid-row {
line-height: var(--line-height);
white-space: nowrap;
background-color: var(--row-bg-color);
border-bottom: 1px solid var(--cell-border-color);
box-sizing: border-box;
&:hover {
background-color: var(--row-active-bg-color);
}
&.selected {
background-color: var(--row-selected-bg-color);
}
>td {
padding: 0;
>span {
padding: var(--spacing-cell);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
>input[type="text"],
>textarea {
border: none;
box-sizing: border-box;
width: 100%;
padding: 0;
@include outline();
&:disabled {
color: var(--text-disabled-color);
}
}
>input[type="text"] {
height: var(--row-height);
text-indent: var(--text-indent);
}
>textarea {
resize: none;
line-height: var(--line-height);
display: block;
padding: var(--spacing-cell);
white-space: nowrap;
@include scrollbar();
}
.ui-check-wrapper {
display: flex;
justify-content: center;
.ui-check-inner {
&,
>svg {
transition: none;
}
}
}
.ui-drop-wrapper {
height: var(--row-height);
width: 100%;
display: flex;
flex-direction: column;
>.ui-drop-header {
border: none;
height: 100%;
>.ui-drop-text {
padding: var(--spacing-cell);
}
}
>.ui-drop-box {
top: calc(var(--row-height) + 2px);
&.slide-up {
top: unset;
bottom: calc(var(--row-height) + 2px);
}
}
}
.col-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
position: relative;
>svg {
width: 16px;
height: 16px;
fill: var(--primary-color);
transition: opacity .12s ease;
}
&:hover>svg {
opacity: .4;
}
&.disabled {
cursor: unset;
>svg {
fill: var(--header-border-color);
opacity: unset;
}
}
}
}
}
}
.ui-grid-hover-holder {
box-sizing: border-box;
position: absolute;
line-height: var(--line-height);
padding: var(--spacing-cell);
background-color: var(--cell-hover-bg-color);
white-space: pre;
display: flex;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear .12s, opacity .12s ease;
&.active {
visibility: visible;
opacity: 1;
}
}
}
@ -423,6 +438,7 @@
opacity: 0;
display: flex;
flex-direction: column;
z-index: 3;
&.active {
transform: scaleY(1);

View File

@ -48,7 +48,7 @@ $buttonHeight: 28px;
border-radius: var(--corner-radius) var(--corner-radius) 0 0;
line-height: $headerLineHeight;
user-select: none;
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
color: var(--title-color);
display: flex;
align-items: center;
@ -214,7 +214,7 @@ $buttonHeight: 28px;
text-align: center;
cursor: pointer;
user-select: none;
background-color: var(--title-bg-color);
background-color: var(--title-ctrlbg-color);
transition: opacity .12s ease;
&:focus,

View File

@ -82,17 +82,18 @@ function filterSource(searchkeys, textkey, key, source) {
}
export class Dropdown {
#options;
_var = {};
// _var.options;
#wrapper;
#container;
#label;
// _var.wrapper;
// _var.container;
// _var.label;
#allChecked;
#source;
#lastSelected;
#selected;
#selectedList;
// _var.allChecked;
// _var.source;
// _var.lastSelected;
// _var.selected;
// _var.selectedList;
sourceFilter;
onselectedlist;
@ -105,18 +106,18 @@ export class Dropdown {
options.valuekey ??= 'value';
options.htmlkey ??= 'html';
options.maxlength ??= 500;
this.#options = options;
this._var.options = options;
}
create() {
const options = this.#options;
const options = this._var.options;
// wrapper
const wrapper = createElement('div', 'ui-drop-wrapper');
const dropId = String(Math.random()).substring(2);
wrapper.dataset.dropId = dropId;
dropdownGlobal[dropId] = this;
this.#wrapper = wrapper;
this._var.wrapper = wrapper;
// header
const header = createElement('div', 'ui-drop-header');
@ -131,8 +132,8 @@ export class Dropdown {
if (up || down) {
const source = this.source;
const count = source.length;
const valuekey = this.#options.valuekey;
let index = source?.indexOf(this.#selected);
const valuekey = this._var.options.valuekey;
let index = source?.indexOf(this._var.selected);
if (isNaN(index) || index < -1) {
index = -1;
} else if (index >= count) {
@ -158,19 +159,19 @@ export class Dropdown {
this.select(target);
}
} else if (e.key === 'Tab') {
this.#dropdown(false);
this._dropdown(false);
}
});
header.addEventListener('click', () => {
if (this.disabled) {
return;
}
const active = this.#expanded;
const label = this.#label;
const active = this._expanded;
const label = this._var.label;
if (active && label.ownerDocument.activeElement === label) {
return;
}
this.#dropdown(!active);
this._dropdown(!active);
if (!active && typeof this.onexpanded === 'function') {
setTimeout(() => this.onexpanded(), 120);
}
@ -187,21 +188,21 @@ export class Dropdown {
label.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this.#container.classList.add('active');
this._filllist(source);
this._var.container.classList.add('active');
});
label.addEventListener('blur', e => this.select(e.target.value));
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
label.addEventListener('mousedown', e => this._expanded && e.stopPropagation());
} else {
isPositive(options.tabIndex) && header.setAttribute('tabindex', options.tabIndex);
label = createElement('label', 'ui-drop-text');
}
this.#label = label;
this._var.label = label;
if (options.multiselect) {
if (Array.isArray(options.selectedlist)) {
this.selectlist(options.selectedlist, true);
} else {
this.#allChecked = true;
this._var.allChecked = true;
label.innerText = r('allItem', '( All )');
}
} else if (options.selected != null) {
@ -214,23 +215,23 @@ export class Dropdown {
return wrapper;
}
get multiselect() { return this.#options.multiselect }
get multiselect() { return this._var.options.multiselect }
get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.ui-drop-header.disabled') != null }
get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }
set disabled(flag) {
if (this.#wrapper == null) {
if (this._var.wrapper == null) {
return;
}
if (flag) {
this.#wrapper.querySelector('.ui-drop-header').classList.add('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.add('disabled');
} else {
this.#wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
}
}
get source() {
let source = this.#source;
let source = this._var.source;
if (source == null || !Array.isArray(source)) {
if (typeof this.sourceFilter === 'function') {
source = this.sourceFilter();
@ -238,7 +239,7 @@ export class Dropdown {
if (!Array.isArray(source)) {
source = [];
}
this.#source = source;
this._var.source = source;
}
return source;
}
@ -247,59 +248,59 @@ export class Dropdown {
if (!Array.isArray(list)) {
return;
}
this.#source = list;
if (this.#expanded) {
setTimeout(() => this.#dropdown(), 120);
this._var.source = list;
if (this._expanded) {
setTimeout(() => this._dropdown(), 120);
}
}
get selected() { return this.#selected }
get selected() { return this._var.selected }
get selectedlist() { return this.#selectedList || [] }
get selectedlist() { return this._var.selectedList || [] }
select(selected, silence) {
if (this.#lastSelected === selected) {
if (this._var.lastSelected === selected) {
return false;
}
this.#lastSelected = selected;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
this._var.lastSelected = selected;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
let item = this.source.find(it => it[valuekey] === selected);
if (this.#options.input) {
if (this._var.options.input) {
if (item == null) {
item = { [valuekey]: selected };
}
this.#label.value = selected;
this._var.label.value = selected;
} else {
const expanded = this.#expanded;
const expanded = this._expanded;
if (expanded) {
this.#container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
this._var.container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
}
if (item == null) {
this.#selected = null;
this.#label.innerText = ' ';
this._var.selected = null;
this._var.label.innerText = ' ';
return false;
}
const html = item[htmlkey];
if (html instanceof HTMLElement) {
this.#label.replaceChildren(html.cloneNode(true));
this._var.label.replaceChildren(html.cloneNode(true));
} else {
let text = item[textkey];
if (nullOrEmpty(text)) {
text = ' ';
}
this.#label.innerText = text;
this._var.label.innerText = text;
}
if (expanded) {
const val = selected.replace(/"/g, '\\"');
const li = this.#container.querySelector(`li[data-value="${val}"]`);
const li = this._var.container.querySelector(`li[data-value="${val}"]`);
if (li != null) {
li.classList.add('selected');
}
}
}
this.#selected = item;
this._var.selected = item;
if (!silence && typeof this.onselected === 'function') {
this.onselected(item);
}
@ -307,9 +308,9 @@ export class Dropdown {
selectlist(selectedlist, silence) {
const source = this.source;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const itemlist = selectedlist.map(v => {
let item = source.find(it => it[valuekey] === v);
if (item == null) {
@ -318,22 +319,22 @@ export class Dropdown {
return item;
});
if (itemlist.length === 0) {
this.#selectedList = null;
this.#label.innerText = none;
this._var.selectedList = null;
this._var.label.innerText = none;
return false;
}
selectItems(this.#label, itemlist, htmlkey, textkey);
this.#selectedList = itemlist;
selectItems(this._var.label, itemlist, htmlkey, textkey);
this._var.selectedList = itemlist;
if (!silence && typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}
}
get #expanded() { return this.#container?.classList?.contains('active') }
get _expanded() { return this._var.container?.classList?.contains('active') }
#dropdown(flag = true) {
const options = this.#options;
let panel = this.#container;
_dropdown(flag = true) {
const options = this._var.options;
let panel = this._var.container;
if (panel == null) {
panel = createElement('div', 'ui-drop-box');
// search box
@ -346,7 +347,7 @@ export class Dropdown {
input.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this._filllist(source);
})
search.append(input, createIcon('fa-light', 'search'));
panel.appendChild(search);
@ -369,8 +370,12 @@ export class Dropdown {
});
}
panel.appendChild(list);
this.#container = panel;
this.#wrapper.appendChild(panel);
this._var.container = panel;
if (options.holder instanceof HTMLElement) {
options.holder.appendChild(panel);
} else {
this._var.wrapper.appendChild(panel);
}
}
if (flag) {
let source = this.source;
@ -380,11 +385,11 @@ export class Dropdown {
source = filterSource(options.searchkeys, options.textkey, search.value, source);
}
}
this.#filllist(source);
this._filllist(source);
// slide direction
if (!options.slidefixed) {
let parent = options.parent ?? document.body;
let p = this.#wrapper;
let p = this._var.wrapper;
let top = p.offsetTop;
while ((p = p.parentElement) != null && p !== parent) {
top -= p.scrollTop;
@ -401,11 +406,11 @@ export class Dropdown {
}
}
#filllist(source) {
const list = this.#container.querySelector('.ui-drop-list');
_filllist(source) {
const list = this._var.container.querySelector('.ui-drop-list');
list.replaceChildren();
const multiselect = this.multiselect;
const allchecked = this.#allChecked;
const allchecked = this._var.allChecked;
if (multiselect) {
list.appendChild(
createElement('li', null,
@ -413,15 +418,15 @@ export class Dropdown {
label: r('allItem', '( All )'),
checked: allchecked,
customAttributes: { 'isall': '1' },
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
})
)
);
}
// TODO: virtual mode
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const selected = this.selected;
const selectedlist = this.selectedlist;
let scrolled;
@ -448,7 +453,7 @@ export class Dropdown {
'class': 'dataitem',
'data-value': val
},
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
});
li.appendChild(box);
} else {
@ -469,43 +474,43 @@ export class Dropdown {
}
}
#triggerselect(checkbox) {
_triggerselect(checkbox) {
let list;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
if (checkbox.getAttribute('isall') === '1') {
const allchecked = this.#allChecked = checkbox.checked;
const boxes = this.#container.querySelectorAll('input.dataitem');
const allchecked = this._var.allChecked = checkbox.checked;
const boxes = this._var.container.querySelectorAll('input.dataitem');
boxes.forEach(box => box.checked = allchecked);
list = [];
} else if (checkbox.checked) {
if (this.#container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this.#allChecked = true;
this.#container.querySelector('input[isall="1"]').checked = true;
if (this._var.container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this._var.allChecked = true;
this._var.container.querySelector('input[isall="1"]').checked = true;
list = [];
} else {
const source = this.source;
list = [...this.#container.querySelectorAll('input.dataitem:checked')]
list = [...this._var.container.querySelectorAll('input.dataitem:checked')]
.map(c => source.find(it => it[valuekey] === c.dataset.value))
.filter(it => it != null);
}
} else {
const val = checkbox.dataset.value;
if (this.#allChecked) {
this.#allChecked = false;
this.#container.querySelector('input[isall="1"]').checked = false;
if (this._var.allChecked) {
this._var.allChecked = false;
this._var.container.querySelector('input[isall="1"]').checked = false;
list = this.source.filter(it => it[valuekey] !== val);
} else {
list = this.selectedlist.filter(it => it[valuekey] !== val);
}
}
if (this.#allChecked) {
this.#label.innerText = r('allItem', '( All )');
if (this._var.allChecked) {
this._var.label.innerText = r('allItem', '( All )');
} else {
selectItems(this.#label, list, htmlkey, textkey);
selectItems(this._var.label, list, htmlkey, textkey);
}
this.#selectedList = list;
this._var.selectedList = list;
if (typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}

1
lib/ui/extension.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export function validation(element: HTMLElement, regex: RegExp) : HTMLElement

12
lib/ui/extension.js Normal file
View File

@ -0,0 +1,12 @@
export function validation(element, regex) {
if (element instanceof HTMLElement && regex instanceof RegExp) {
element.addEventListener('change', e => {
if (regex.test(e.target.value)) {
e.target.classList.remove('validation-error');
} else {
e.target.classList.add('validation-error');
}
})
}
return element;
}

View File

@ -102,12 +102,16 @@ const SymbolDropdown = Symbol.for('ui-dropdown');
export class GridDropdownColumn extends GridColumn {
static createEdit(trigger, col, parent) {
const drop = new Dropdown({ ...col.dropOptions, parent });
const drop = new Dropdown({
...col.dropOptions,
parent,
holder: parent
});
drop.onselected = trigger;
return drop.create();
}
static #getDrop(element) {
static _getDrop(element) {
const dropGlobal = global[SymbolDropdown];
if (dropGlobal == null) {
return null;
@ -120,7 +124,7 @@ export class GridDropdownColumn extends GridColumn {
return drop;
}
static #getSource(item, col) {
static _getSource(item, col) {
let source = col.source;
if (typeof source === 'function') {
source = source(item);
@ -128,7 +132,7 @@ export class GridDropdownColumn extends GridColumn {
return source;
}
static #setValue(source, element, val) {
static _setValue(source, element, val) {
const data = source?.find(v => v.value === val);
if (data != null) {
val = data.text;
@ -138,20 +142,20 @@ export class GridDropdownColumn extends GridColumn {
static setValue(element, val, item, col) {
if (element.tagName !== 'DIV') {
let source = this.#getSource(item, col);
let source = this._getSource(item, col);
if (source instanceof Promise) {
source.then(s => this.#setValue(s, element, val));
source.then(s => this._setValue(s, element, val));
} else {
this.#setValue(source, element, val);
this._setValue(source, element, val);
}
return;
}
const drop = this.#getDrop(element);
const drop = this._getDrop(element);
if (drop == null) {
return;
}
if (drop.source == null || drop.source.length === 0) {
let source = this.#getSource(item, col);
let source = this._getSource(item, col);
if (source instanceof Promise) {
source.then(s => {
drop.source = s;
@ -171,7 +175,7 @@ export class GridDropdownColumn extends GridColumn {
static setEnabled(element, enabled) {
super.setEnabled(element , enabled);
const drop = this.#getDrop(element);
const drop = this._getDrop(element);
if (drop == null) {
return;
}

File diff suppressed because it is too large Load Diff

View File

@ -67,7 +67,7 @@ function getTimeLabel(time) {
}
export function createAudio(mime, url) {
if (mime === 'audio/amr' && typeof AMR !== 'undefined') {
if ((mime === 'audio/amr' || mime === '.amr') && typeof AMR !== 'undefined') {
const timestamp = createElement('span', 'ui-media-timestamp');
timestamp.textContent = '00:00 / 00:00';
let context;

View File

@ -39,16 +39,17 @@ function trimPx(px) {
}
export class Popup {
#mask;
#option;
#bounds;
// #cursor;
_var = {};
// _var.mask;
// _var.option;
// _var.bounds;
// _var.cursor;
constructor(opts = {}) {
this.#option = opts;
this._var.option = opts;
}
get container() { return this.#mask.querySelector('.ui-popup-container') }
get container() { return this._var.mask.querySelector('.ui-popup-container') }
get rect() {
const container = this.container;
@ -57,7 +58,7 @@ export class Popup {
}
const style = global.getComputedStyle(container);
const collapsed = container.classList.contains('ui-popup-collapse');
const bounds = this.#bounds;
const bounds = this._var.bounds;
return {
collapsed,
left: trimPx(style.left),
@ -81,7 +82,7 @@ export class Popup {
const collapse = container.querySelector('.ui-popup-header-icons>.icon-expand');
if (r.collapsed === true) {
css.push('width: 160px', 'height: 40px');
this.#bounds = r;
this._var.bounds = r;
container.classList.add('ui-popup-collapse');
if (collapse != null) {
changeIcon(collapse, 'fa-regular', 'expand-alt');
@ -94,7 +95,7 @@ export class Popup {
css.push(`height: ${r.height}px`);
}
container.classList.remove('ui-popup-collapse');
this.#bounds = null;
this._var.bounds = null;
if (collapse != null) {
changeIcon(collapse, 'fa-regular', 'compress-alt');
}
@ -105,7 +106,7 @@ export class Popup {
}
close(animation = true) {
const mask = this.#mask;
const mask = this._var.mask;
if (animation) {
mask.classList.add('ui-popup-active');
mask.style.opacity = 0;
@ -113,17 +114,17 @@ export class Popup {
} else {
mask.remove();
}
if (typeof this.#option.onMasking === 'function') {
this.#option.onMasking.call(this, false);
if (typeof this._var.option.onMasking === 'function') {
this._var.option.onMasking.call(this, false);
}
if (typeof this.#option.resolve === 'function') {
this.#option.resolve();
if (typeof this._var.option.resolve === 'function') {
this._var.option.resolve();
}
}
create() {
const mask = createElement('div', 'ui-popup-mask');
const option = this.#option;
const option = this._var.option;
if (option.mask === false) {
mask.classList.add('ui-popup-transparent');
} else if (typeof option.onMasking === 'function') {
@ -135,7 +136,7 @@ export class Popup {
const container = createElement('div', 'ui-popup-container');
if (option.changeZIndex === true) {
container.addEventListener('mousedown', () => {
const masks = [...this.#mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask'));
const masks = [...this._var.mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask'));
let max = 200;
masks.forEach(m => {
let index;
@ -223,16 +224,16 @@ export class Popup {
});
collapse.addEventListener('click', () => {
if (container.classList.contains('ui-popup-collapse')) {
const bounds = this.#bounds;
const bounds = this._var.bounds;
if (bounds != null) {
container.style.cssText += `width: ${bounds.width}px; height: ${bounds.height}px`;
this.#bounds = null;
this._var.bounds = null;
}
container.classList.remove('ui-popup-collapse');
changeIcon(collapse, 'fa-regular', 'compress-alt');
} else {
const rect = this.rect;
this.#bounds = rect;
this._var.bounds = rect;
container.style.cssText += `width: 160px; height: 40px`;
container.classList.add('ui-popup-collapse');
changeIcon(collapse, 'fa-regular', 'expand-alt');
@ -310,40 +311,40 @@ export class Popup {
container.append(
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.right, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.right, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottom, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottom, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.left, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.left, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.top, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.top, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomRight, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomRight, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-bottom-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomLeft, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomLeft, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top-left';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topLeft, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.topLeft, e));
}),
createElement('layer', layer => {
layer.className = 'ui-popup-border ui-popup-border-top-right';
layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topRight, e));
layer.addEventListener('mousedown', e => this._resize(ResizeMods.topRight, e));
})
)
}
mask.appendChild(container);
this.#mask = mask;
this._var.mask = mask;
return mask;
}
@ -351,7 +352,7 @@ export class Popup {
if (parent == null) {
return;
}
let mask = this.#mask ?? this.create();
let mask = this._var.mask ?? this.create();
// const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
const exists = parent.querySelectorAll('.ui-popup-mask');
let zindex = 0;
@ -365,7 +366,7 @@ export class Popup {
mask.style.zIndex = String(zindex + 1);
}
parent.appendChild(mask);
if (this.#option.mask === false) {
if (this._var.option.mask === false) {
// calculator position
const container = this.container;
container.style.left = String((parent.offsetWidth - container.offsetWidth) / 2) + 'px';
@ -380,9 +381,9 @@ export class Popup {
});
}
get loading() { return this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
set loading(flag) {
let loading = this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading');
let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
if (loading == null) {
return;
}
@ -395,17 +396,17 @@ export class Popup {
}
}
#resize(mod, e) {
_resize(mod, e) {
if (e.buttons !== 1) {
return;
}
const container = this.container;
const option = this.#option;
const option = this._var.option;
if (typeof option.onResizeStarted === 'function') {
option.onResizeStarted.call(this);
}
const mask = this.#mask;
// this.#cursor = mask.style.cursor;
const mask = this._var.mask;
// this._var.cursor = mask.style.cursor;
// mask.style.cursor = Cursors[mod];
const originalX = e.clientX;
const originalY = e.clientY;
@ -471,7 +472,7 @@ export class Popup {
const up = () => {
parent.removeEventListener('mousemove', move, { passive: false });
parent.removeEventListener('mouseup', up);
// mask.style.cursor = this.#cursor;
// mask.style.cursor = this._var.cursor;
if (resized === true && typeof option.onResizeEnded === 'function') {
option.onResizeEnded.call(this);
}

4
lib/ui/tooltip.d.ts vendored
View File

@ -2,8 +2,8 @@
* 为元素设置一个 tooltip
* @param container 要设置 tooltip 的容器元素
* @param content 提示内容,可以是字符串也可以是 html 元素
* @param flag 是否仅在元素内容没有呈现完全时显示 tooltip
* @param parent 呈现在哪个父元素上,默认添加在 `container` 末端
* @param [flag=true] 是否仅在元素内容没有呈现完全时显示 tooltip
* @param [parent=null] 呈现在哪个父元素上,默认添加在 `container` 末端
*/
export function setTooltip(container: HTMLElement, content: string | HTMLElement, flag?: boolean, parent?: HTMLElement): HTMLElement

View File

@ -91,6 +91,9 @@ export function setTooltip(container, content, flag = false, parent = null) {
let t = c.offsetTop;
let l = c.offsetLeft;
p = c.offsetParent;
if (p == null) {
return;
}
let lastWidth = p.clientWidth;
let lastHeight = p.clientHeight;
while (p != null) {