add: ui-switch
style
add: virtual mode in Dropdown
This commit is contained in:
@ -48,4 +48,62 @@
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-switch {
|
||||
position: relative;
|
||||
line-height: 1rem;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
>span:first-of-type {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
max-width: 30px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
background-color: var(--switch-bg-color);
|
||||
border-radius: 8px;
|
||||
transition: background-color .08s ease;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 1px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: white;
|
||||
border-radius: 7px;
|
||||
box-shadow: 1px 1px 8px rgb(0 0 0 / 20%);
|
||||
transition: left .08s ease;
|
||||
}
|
||||
}
|
||||
|
||||
>input[type="checkbox"] {
|
||||
display: none;
|
||||
|
||||
&:checked+span:first-of-type {
|
||||
|
||||
&::before {
|
||||
background-color: var(--switch-active-bg-color);
|
||||
}
|
||||
|
||||
&::after {
|
||||
left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled+span:first-of-type {
|
||||
|
||||
&::before {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -152,21 +152,25 @@ $listMaxHeight: 210px;
|
||||
}
|
||||
|
||||
>.ui-drop-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
max-height: $listMaxHeight;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
font-size: var(--font-size);
|
||||
@include scrollbar();
|
||||
|
||||
&.filtered>li:first-child {
|
||||
&.filtered>.drop-content>.li:first-child {
|
||||
background-color: var(--hover-bg-color);
|
||||
}
|
||||
|
||||
>li {
|
||||
>.drop-content {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
list-style: none;
|
||||
line-height: $dropItemHeight;
|
||||
height: $dropItemHeight;
|
||||
padding: 0 10px;
|
||||
@ -186,4 +190,4 @@ $listMaxHeight: 210px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,8 @@
|
||||
--disabled-color: #aaa;
|
||||
--disabled-bg-color: #e9e9e9;
|
||||
--disabled-border-color: #d9d9d9;
|
||||
--switch-bg-color: #eae9eb;
|
||||
--switch-active-bg-color: #33c559;
|
||||
|
||||
--red-color: red;
|
||||
--title-color: #fff;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import './css/dropdown.scss';
|
||||
import { r } from "../utility/lgres";
|
||||
import { contains, nullOrEmpty } from "../utility/strings";
|
||||
import { global, isPositive } from "../utility";
|
||||
import { global, isPositive, throttle } from "../utility";
|
||||
import { createElement } from "../functions";
|
||||
import { createCheckbox } from "./checkbox";
|
||||
import { createIcon } from "./icon"
|
||||
@ -137,6 +137,9 @@ export class Dropdown {
|
||||
// wrapper
|
||||
const wrapper = createElement('div', 'ui-drop-wrapper');
|
||||
const dropId = String(Math.random()).substring(2);
|
||||
if (options.wrapper instanceof HTMLElement) {
|
||||
options.wrapper.dataset.dropId = dropId;
|
||||
}
|
||||
wrapper.dataset.dropId = dropId;
|
||||
dropdownGlobal[dropId] = this;
|
||||
this._var.wrapper = wrapper;
|
||||
@ -241,6 +244,8 @@ export class Dropdown {
|
||||
|
||||
get multiSelect() { return this._var.options.multiSelect }
|
||||
|
||||
get ignoreAll() { return this._var.options.ignoreAll }
|
||||
|
||||
get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }
|
||||
|
||||
set disabled(flag) {
|
||||
@ -363,7 +368,7 @@ export class Dropdown {
|
||||
});
|
||||
if (itemlist.length === 0) {
|
||||
this._var.selectedList = null;
|
||||
this._var.label.innerText = none;
|
||||
this._var.label.innerText = r('none', '( None )');
|
||||
return false;
|
||||
}
|
||||
selectItems(this._var.label, itemlist, htmlkey, textkey);
|
||||
@ -396,7 +401,8 @@ export class Dropdown {
|
||||
panel.appendChild(search);
|
||||
}
|
||||
// list
|
||||
const list = createElement('ul', 'ui-drop-list');
|
||||
const list = createElement('div', 'ui-drop-list');
|
||||
list.addEventListener('scroll', e => throttle(this._onlistscroll, 10, this, list, e.target.scrollTop), { passive: true });
|
||||
if (!this.multiSelect) {
|
||||
list.addEventListener('click', e => {
|
||||
let li = e.target;
|
||||
@ -468,36 +474,99 @@ export class Dropdown {
|
||||
}
|
||||
}
|
||||
panel.classList.add('active');
|
||||
this._var.dropTop = 0;
|
||||
panel.querySelector('.ui-drop-list').dispatchEvent(new Event('scroll'));
|
||||
} else {
|
||||
panel.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
_onlistscroll(list, top) {
|
||||
const offset = (this.multiSelect && !this.ignoreAll) ? DropdownItemHeight : 0;
|
||||
top -= (top % (DropdownItemHeight * 2)) + offset;
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
} else {
|
||||
let bottomTop = this._var.dropHeight - (20 * DropdownItemHeight);
|
||||
if (bottomTop < 0) {
|
||||
bottomTop = 0;
|
||||
}
|
||||
if (top > bottomTop) {
|
||||
top = bottomTop;
|
||||
}
|
||||
}
|
||||
if (this._var.dropTop !== top) {
|
||||
this._var.dropTop = top;
|
||||
const startIndex = top / DropdownItemHeight;
|
||||
let array = this._var.currentSource;
|
||||
if (startIndex + 20 < array.length) {
|
||||
array = array.slice(startIndex, startIndex + 20);
|
||||
} else {
|
||||
array = array.slice(-20);
|
||||
}
|
||||
const content = list.querySelector('.drop-content');
|
||||
content.replaceChildren();
|
||||
this._dofilllist(content, array);
|
||||
content.style.top = `${top + offset}px`;
|
||||
}
|
||||
}
|
||||
|
||||
_filllist(source) {
|
||||
const list = this._var.container.querySelector('.ui-drop-list');
|
||||
list.replaceChildren();
|
||||
const multiselect = this.multiSelect;
|
||||
const allchecked = this._var.allChecked;
|
||||
if (multiselect) {
|
||||
const height = source.length * DropdownItemHeight;
|
||||
this._var.dropHeight = height;
|
||||
this._var.currentSource = source;
|
||||
const holder = createElement('div', 'drop-holder');
|
||||
holder.style.height = `${height}px`;
|
||||
const content = createElement('div', 'drop-content');
|
||||
if (this.multiSelect && !this.ignoreAll) {
|
||||
list.appendChild(
|
||||
createElement('li', null,
|
||||
createCheckbox({
|
||||
label: r('allItem', '( All )'),
|
||||
checked: allchecked,
|
||||
checked: this._var.allChecked,
|
||||
customAttributes: { 'isall': '1' },
|
||||
onchange: e => this._triggerselect(e.target)
|
||||
})
|
||||
)
|
||||
);
|
||||
content.style.top = `${DropdownItemHeight}px`;
|
||||
} else {
|
||||
content.style.top = '0px';
|
||||
}
|
||||
// TODO: virtual mode
|
||||
const multiselect = this.multiSelect;
|
||||
const valuekey = this._var.options.valueKey;
|
||||
const allchecked = this._var.allChecked;
|
||||
const selectedlist = this.selectedList;
|
||||
source.forEach((item, i) => {
|
||||
let val = item[valuekey];
|
||||
if (typeof val !== 'string') {
|
||||
val = String(val);
|
||||
}
|
||||
if (multiselect) {
|
||||
const selected = selectedlist.some(s => String(s[valuekey]) === val);
|
||||
item.__checked = allchecked || selected;
|
||||
}
|
||||
});
|
||||
if (source.length > 20) {
|
||||
source = source.slice(0, 20);
|
||||
}
|
||||
const scrolled = this._dofilllist(content, source);
|
||||
list.append(holder, content);
|
||||
if (scrolled != null) {
|
||||
setTimeout(() => list.scrollTop = scrolled, 10);
|
||||
}
|
||||
}
|
||||
|
||||
_dofilllist(content, array) {
|
||||
const multiselect = this.multiSelect;
|
||||
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;
|
||||
source.slice(0, 200).forEach((item, i) => {
|
||||
array.forEach((item, i) => {
|
||||
let val = item[valuekey];
|
||||
if (typeof val !== 'string') {
|
||||
val = String(val);
|
||||
@ -514,19 +583,18 @@ export class Dropdown {
|
||||
label.innerHTML = html;
|
||||
}
|
||||
if (multiselect) {
|
||||
const selected = selectedlist.some(s => String(s[valuekey]) === val);
|
||||
if (label == null) {
|
||||
label = createElement('span');
|
||||
label.innerText = item[textkey];
|
||||
}
|
||||
const box = createCheckbox({
|
||||
label,
|
||||
checked: allchecked || selected,
|
||||
checked: item.__checked,
|
||||
customAttributes: {
|
||||
'class': 'dataitem',
|
||||
'data-value': val
|
||||
},
|
||||
onchange: e => this._triggerselect(e.target)
|
||||
onchange: e => this._triggerselect(e.target, item)
|
||||
});
|
||||
li.appendChild(box);
|
||||
} else {
|
||||
@ -540,14 +608,12 @@ export class Dropdown {
|
||||
li.classList.add('selected');
|
||||
}
|
||||
}
|
||||
list.appendChild(li);
|
||||
content.appendChild(li);
|
||||
});
|
||||
if (scrolled != null) {
|
||||
setTimeout(() => list.scrollTop = scrolled, 10);
|
||||
}
|
||||
return scrolled;
|
||||
}
|
||||
|
||||
_triggerselect(checkbox) {
|
||||
_triggerselect(checkbox, item) {
|
||||
let list;
|
||||
const valuekey = this._var.options.valueKey;
|
||||
const textkey = this._var.options.textKey;
|
||||
@ -557,28 +623,31 @@ export class Dropdown {
|
||||
const boxes = this._var.container.querySelectorAll('input.dataitem');
|
||||
boxes.forEach(box => box.checked = allchecked);
|
||||
list = [];
|
||||
} else if (checkbox.checked) {
|
||||
if (this._var.container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
|
||||
this._var.allChecked = true;
|
||||
this._var.container.querySelector('input[isall="1"]').checked = true;
|
||||
list = [];
|
||||
} else {
|
||||
const source = this.source;
|
||||
list = [...this._var.container.querySelectorAll('input.dataitem:checked')]
|
||||
.map(c => {
|
||||
const v = c.dataset.value;
|
||||
return source.find(it => String(it[valuekey]) === v);
|
||||
})
|
||||
.filter(it => it != null);
|
||||
}
|
||||
} else {
|
||||
const val = checkbox.dataset.value;
|
||||
if (this._var.allChecked) {
|
||||
this._var.allChecked = false;
|
||||
this._var.container.querySelector('input[isall="1"]').checked = false;
|
||||
list = this.source.filter(it => String(it[valuekey]) !== val);
|
||||
item.__checked = checkbox.checked;
|
||||
const all = this._var.container.querySelector('input[isall="1"]');
|
||||
if (checkbox.checked) {
|
||||
const source = this.source;
|
||||
if (source.some(it => it.__checked) == null) {
|
||||
this._var.allChecked = true;
|
||||
if (all != null) {
|
||||
all.checked = true;
|
||||
}
|
||||
list = [];
|
||||
} else {
|
||||
list = source.filter(it => it.__checked);
|
||||
}
|
||||
} else {
|
||||
list = this.selectedList.filter(it => String(it[valuekey]) !== val);
|
||||
const val = checkbox.dataset.value;
|
||||
if (this._var.allChecked) {
|
||||
this._var.allChecked = false;
|
||||
if (all != null) {
|
||||
all.checked = false;
|
||||
}
|
||||
list = this.source.filter(it => String(it[valuekey]) !== val);
|
||||
} else {
|
||||
list = this.selectedList.filter(it => String(it[valuekey]) !== val);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._var.allChecked) {
|
||||
|
Reference in New Issue
Block a user