fix dropdown issue, add autoprefix for scss
This commit is contained in:
@ -7,7 +7,7 @@ function fillCheckbox(container, type, label) {
|
||||
container.appendChild(layer);
|
||||
if (label instanceof HTMLElement) {
|
||||
container.appendChild(label);
|
||||
} else if (label != null && label.length > 0) {
|
||||
} else if (label?.length > 0) {
|
||||
const span = document.createElement('span');
|
||||
span.innerText = label;
|
||||
container.appendChild(span);
|
||||
@ -75,7 +75,7 @@ function resolveCheckbox(container, legacy) {
|
||||
if (e != null) {
|
||||
if (e.tagName === 'LABEL') {
|
||||
label = e;
|
||||
} else if (e.tagName === 'SPAN' && e.getAttribute('data-lgid') != null) {
|
||||
} else if (e.tagName === 'SPAN' && e.dataset.lgid != null) {
|
||||
text = e.innerText;
|
||||
e.style.display = 'none';
|
||||
}
|
||||
@ -86,7 +86,7 @@ function resolveCheckbox(container, legacy) {
|
||||
if (e != null) {
|
||||
if (e.tagName === 'LABEL') {
|
||||
label = e;
|
||||
} else if (text == null && e.tagName === 'SPAN' && e.getAttribute('data-lgid') != null) {
|
||||
} else if (text == null && e.tagName === 'SPAN' && e.dataset.lgid != null) {
|
||||
text = e.innerText;
|
||||
e.style.display = 'none';
|
||||
}
|
||||
@ -114,18 +114,18 @@ function resolveCheckbox(container, legacy) {
|
||||
box.classList.add('checkbox-image');
|
||||
}
|
||||
} else {
|
||||
const type = box.getAttribute('data-type') || 'fa-regular';
|
||||
const label = box.getAttribute('data-label');
|
||||
const type = box.dataset.type || 'fa-regular';
|
||||
const label = box.dataset.label;
|
||||
fillCheckbox(box, type, label)
|
||||
box.removeAttribute('data-type');
|
||||
box.removeAttribute('data-label');
|
||||
}
|
||||
const input = document.createElement('input');
|
||||
const id = box.getAttribute('data-id');
|
||||
if (id != null && id.length > 0) {
|
||||
const id = box.dataset.id;
|
||||
if (id?.length > 0) {
|
||||
input.id = id;
|
||||
}
|
||||
if (box.getAttribute('data-checked') != null) {
|
||||
if (box.dataset.checked != null) {
|
||||
input.checked = true;
|
||||
}
|
||||
input.setAttribute('type', 'checkbox');
|
||||
|
32
lib/ui/dropdown.d.ts
vendored
32
lib/ui/dropdown.d.ts
vendored
@ -1,15 +1,21 @@
|
||||
interface DropdownItem {
|
||||
value: string;
|
||||
text: string;
|
||||
html?: HTMLElement
|
||||
}
|
||||
|
||||
interface DropdownOptions {
|
||||
textkey?: string;
|
||||
valuekey?: string;
|
||||
htmlkey?: string;
|
||||
maxlength?: Number;
|
||||
multiselect?: boolean;
|
||||
selected?: any;
|
||||
selectedlist?: any[];
|
||||
selected?: DropdownItem | any;
|
||||
selectedlist?: Array<DropdownItem | any>;
|
||||
disabled?: boolean;
|
||||
input?: boolean;
|
||||
search?: boolean;
|
||||
searchkeys?: string[];
|
||||
searchkeys?: Array<string>;
|
||||
searchplaceholder?: string;
|
||||
tabindex?: Number;
|
||||
slidefixed?: boolean;
|
||||
@ -17,16 +23,28 @@ interface DropdownOptions {
|
||||
}
|
||||
|
||||
interface Dropdown {
|
||||
sourceFilter: () => Array<DropdownItem | any>;
|
||||
onselectedlist: (list: Array<DropdownItem | any>) => void;
|
||||
onselected: (item: DropdownItem | any) => void;
|
||||
onexpanded: () => void;
|
||||
|
||||
create(): HTMLElement;
|
||||
readonly multiselect: boolean;
|
||||
get disabled(): boolean;
|
||||
set disabled(flag: boolean);
|
||||
readonly multiselect: boolean;
|
||||
readonly selected: any;
|
||||
get source(): Array<DropdownItem | any>;
|
||||
set source(list: Array<DropdownItem | any>): void;
|
||||
readonly selected: DropdownItem | any;
|
||||
readonly selectedlist: Array<DropdownItem | any>;
|
||||
select(selected: DropdownItem | any, init?: boolean): void;
|
||||
selectlist(selectedlist: Array<DropdownItem | any>, init?: boolean): void;
|
||||
}
|
||||
|
||||
declare var Dropdown: {
|
||||
prototype: Dropdown;
|
||||
new(options: DropdownOptions): Dropdown;
|
||||
};
|
||||
new(options?: DropdownOptions): Dropdown;
|
||||
|
||||
resolve(dom?: HTMLElement): HTMLElement;
|
||||
}
|
||||
|
||||
export default Dropdown;
|
147
lib/ui/dropdown.html
Normal file
147
lib/ui/dropdown.html
Normal file
@ -0,0 +1,147 @@
|
||||
<div>
|
||||
<h1>dropdown</h1>
|
||||
<hr />
|
||||
<p>
|
||||
创建一个统一样式的下拉输入、选择框元素,或者解析转换页面上的 select 标签为该元素。
|
||||
</p>
|
||||
<!-- <h2>createCheckbox</h2>
|
||||
<code>function createCheckbox(opts?: CheckboxOptions): HTMLElement</code>
|
||||
<h3>opts?: CheckboxOptions</h3>
|
||||
<p>
|
||||
复选框初始参数,结构为
|
||||
<pre>interface CheckboxOptions {
|
||||
type?: string;
|
||||
label?: string;
|
||||
checked?: boolean;
|
||||
isImage?: boolean;
|
||||
imageHeight?: Number;
|
||||
checkedNode?: HTMLElement;
|
||||
uncheckedNode?: HTMLElement;
|
||||
customerAttributes?: { [key: string]: string };
|
||||
onchange?: (this: HTMLInputElement, ev: Event) => any;
|
||||
}</pre>
|
||||
</p>
|
||||
<h3>type?: string</h3>
|
||||
<p>
|
||||
复选框图标的样式,可选值目前有 <code>fa-regular</code>、<code>fa-light</code>、<code>fa-solid</code>
|
||||
</p>
|
||||
<h3>label?: string | HTMLElement</h3>
|
||||
<p>
|
||||
复选框的标签文本,或者想要呈现的元素
|
||||
</p>
|
||||
<h3>checked?: boolean</h3>
|
||||
<p>
|
||||
初始是否选中
|
||||
</p>
|
||||
<h3>isImage?: boolean</h3>
|
||||
<p>
|
||||
是否为图片复选框
|
||||
</p>
|
||||
<h3>imageHeight?: Number</h3>
|
||||
<p>
|
||||
为图片复选框时的图片限制高度
|
||||
</p>
|
||||
<h3>checkedNode?: HTMLElement</h3>
|
||||
<p>
|
||||
为图片复选框时的选中时显示的元素
|
||||
</p>
|
||||
<h3>uncheckedNode?: HTMLElement</h3>
|
||||
<p>
|
||||
为图片复选框时的未选中时显示的元素
|
||||
</p>
|
||||
<h3>customerAttributes?: { [key: string]: string }</h3>
|
||||
<p>
|
||||
自定义属性,例如
|
||||
<pre>{
|
||||
'data-id': 'xxxxxx',
|
||||
'disabled': ''
|
||||
}</pre>
|
||||
</p>
|
||||
<h3>onchange?: (this: HTMLInputElement, ev: Event) => any</h3>
|
||||
<p>
|
||||
复选框改变时触发的事件
|
||||
</p>
|
||||
<h2>resolveCheckbox</h2>
|
||||
<code>function resolveCheckbox(container?: HTMLElement, legacy?: boolean): HTMLElement</code>
|
||||
<h3>container?: HTMLElement</h3>
|
||||
<p>
|
||||
将把此 HTML 元素,为 null 则把 document.body 下的所有 <code>label[data-checkbox]</code> 元素解析为复选框,<code>[data-id]</code> 为复选框元素的 id,包含
|
||||
<code>[data-checked]</code> 时复选框默认选中。
|
||||
</p>
|
||||
<p>当该元素无子元素时,<code>[data-type]</code> 同上述参数中的 <code>type?: string</code>,<code>[data-label]</code> 同上述参数中的
|
||||
<code>label?: string</code>。
|
||||
</p>
|
||||
<p>当该元素有子元素时,解析为图片复选框,class 为 <code>checked</code>、<code>unchecked</code> 的子元素将分别在选中与未选中时显示。</p>
|
||||
<h3>legacy?: boolean</h3>
|
||||
<p>
|
||||
是否开启兼容模式,启用兼容模式时将试图匹配 <code>input[type="checkbox"]</code> 标签,与其周围的 label,将其转换为统一样式的复选框。
|
||||
</p>
|
||||
<hr /> -->
|
||||
<h2>示例</h2>
|
||||
<pre></pre>
|
||||
<div id="dropdown-sample">
|
||||
<select>
|
||||
<option value="cs1">Case 1</option>
|
||||
<option value="cs2" selected>Case 2</option>
|
||||
<option value="cs3">Case 3</option>
|
||||
</select>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
!function () {
|
||||
const Dropdown = window["lib-ui"].Dropdown;
|
||||
console.log(Dropdown);
|
||||
|
||||
Dropdown.resolve(document.querySelector('#dropdown-sample'));
|
||||
|
||||
const drop = new Dropdown({
|
||||
selected: '#ff0',
|
||||
search: true,
|
||||
multiselect: true
|
||||
});
|
||||
drop.source = [
|
||||
{ value: '#fff', text: 'White' },
|
||||
{ value: '#f00', text: 'Red' },
|
||||
{ value: '#0f0', text: 'Green' },
|
||||
{ value: '#00f', text: 'Blue' },
|
||||
{ value: '#ff0', text: 'Yellow' },
|
||||
{ value: '#0ff', text: 'Cyan' },
|
||||
{ value: '#f0f', text: 'Magenta' },
|
||||
];
|
||||
drop.source.forEach(it => {
|
||||
const span = document.createElement('span');
|
||||
span.className = 'drop-item';
|
||||
span.style.setProperty('--color', it.value);
|
||||
span.innerText = it.text;
|
||||
it.html = span;
|
||||
});
|
||||
document.querySelector("#dropdown-sample").appendChild(drop.create());
|
||||
}();
|
||||
</script>
|
||||
<style type="text/css">
|
||||
#dropdown-sample {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#dropdown-sample > .dropdown-wrapper {
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.drop-item {
|
||||
font-size: .75rem !important;
|
||||
padding: 0 0 0 22px !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.drop-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 6px;
|
||||
top: calc(50% - 6px);
|
||||
left: 6px;
|
||||
background-color: var(--color);
|
||||
}
|
||||
</style>
|
||||
</div>
|
@ -29,7 +29,7 @@ if (dropdownGlobal == null) {
|
||||
return;
|
||||
}
|
||||
const dropdown = this[dropId];
|
||||
if (dropdown != null && dropdown.multiselect && typeof dropdown.oncollapsed === 'function') {
|
||||
if (dropdown?.multiselect && typeof dropdown.oncollapsed === 'function') {
|
||||
dropdown.oncollapsed();
|
||||
}
|
||||
}
|
||||
@ -49,6 +49,19 @@ if (dropdownGlobal == null) {
|
||||
});
|
||||
}
|
||||
|
||||
function selectItems(label, itemlist, htmlkey, textkey) {
|
||||
const htmls = itemlist.map(it => it[htmlkey]);
|
||||
if (htmls.some(it => it instanceof HTMLElement)) {
|
||||
label.replaceChildren(...htmls.filter(it => it != null).map(it => it.cloneNode(true)));
|
||||
} else {
|
||||
let text = itemlist.map(it => it[textkey]).join(', ');
|
||||
if (nullOrEmpty(text)) {
|
||||
text = r('noneItem', '( None )');
|
||||
}
|
||||
label.innerText = text;
|
||||
}
|
||||
}
|
||||
|
||||
class Dropdown {
|
||||
#options;
|
||||
|
||||
@ -211,6 +224,7 @@ class Dropdown {
|
||||
this.#lastSelected = selected;
|
||||
const valuekey = this.#options.valuekey;
|
||||
const textkey = this.#options.textkey;
|
||||
const htmlkey = this.#options.htmlkey;
|
||||
let item = this.source.find(it => it[valuekey] === selected);
|
||||
if (this.#options.input) {
|
||||
if (item == null) {
|
||||
@ -224,11 +238,16 @@ class Dropdown {
|
||||
this.#label.innerText = ' ';
|
||||
return false;
|
||||
}
|
||||
let text = item[textkey];
|
||||
if (nullOrEmpty(text)) {
|
||||
text = ' ';
|
||||
const html = item[htmlkey];
|
||||
if (html instanceof HTMLElement) {
|
||||
this.#label.replaceChildren(html.cloneNode(true));
|
||||
} else {
|
||||
let text = item[textkey];
|
||||
if (nullOrEmpty(text)) {
|
||||
text = ' ';
|
||||
}
|
||||
this.#label.innerText = text;
|
||||
}
|
||||
this.#label.innerText = text;
|
||||
}
|
||||
this.#selected = item;
|
||||
if (!init && typeof this.onselected === 'function') {
|
||||
@ -240,6 +259,7 @@ class Dropdown {
|
||||
const source = this.source;
|
||||
const valuekey = this.#options.valuekey;
|
||||
const textkey = this.#options.textkey;
|
||||
const htmlkey = this.#options.htmlkey;
|
||||
const itemlist = selectedlist.map(v => {
|
||||
let item = source.find(it => it[valuekey] === v);
|
||||
if (item == null) {
|
||||
@ -249,24 +269,19 @@ class Dropdown {
|
||||
}
|
||||
return item;
|
||||
});
|
||||
const none = r('noneItem', '( None )');
|
||||
if (itemlist.length === 0) {
|
||||
this.#selectedList = null;
|
||||
this.#label.innerText = none;
|
||||
return false;
|
||||
}
|
||||
const text = itemlist.map(it => it[textkey]).join(', ');
|
||||
if (nullOrEmpty(text)) {
|
||||
text = none;
|
||||
}
|
||||
selectItems(this.#label, itemlist, htmlkey, textkey);
|
||||
this.#selectedList = itemlist;
|
||||
this.#label.innerText = text;
|
||||
if (!init && typeof this.onselectedlist === 'function') {
|
||||
this.onselectedlist(itemlist);
|
||||
}
|
||||
}
|
||||
|
||||
get #expanded() { return this.#container != null && this.#container.style.visibility === 'visible' }
|
||||
get #expanded() { return this.#container?.style.visibility === 'visible' }
|
||||
|
||||
#dropdown(flag) {
|
||||
flag ??= true;
|
||||
@ -277,7 +292,7 @@ class Dropdown {
|
||||
panel = document.createElement('div');
|
||||
panel.className = 'dropdown-panel';
|
||||
// search box
|
||||
if (options.search) {
|
||||
if (!options.input && options.search) {
|
||||
let searchkeys = options.searchkeys;
|
||||
if (!Array.isArray(searchkeys) || searchkeys.length === 0) {
|
||||
searchkeys = [textkey];
|
||||
@ -328,7 +343,6 @@ class Dropdown {
|
||||
panel.appendChild(list);
|
||||
this.#container = panel;
|
||||
this.#wrapper.appendChild(panel);
|
||||
this.#filllist(this.source);
|
||||
}
|
||||
if (flag) {
|
||||
if (!options.slidefixed) {
|
||||
@ -349,6 +363,18 @@ class Dropdown {
|
||||
// const event = new InputEvent('type');
|
||||
// inputSearch.dispatchEvent(event);
|
||||
// }
|
||||
if (options.input) {
|
||||
this.#label.dispatchEvent(new InputEvent('type'));
|
||||
} else if (options.search) {
|
||||
const search = panel.querySelector('.dropdown-search > input');
|
||||
if (!nullOrEmpty(search?.value)) {
|
||||
search.dispatchEvent(new InputEvent('type'));
|
||||
} else {
|
||||
this.#filllist(this.source);
|
||||
}
|
||||
} else {
|
||||
this.#filllist(this.source);
|
||||
}
|
||||
} else {
|
||||
panel.classList.remove('active');
|
||||
}
|
||||
@ -425,6 +451,7 @@ class Dropdown {
|
||||
let list;
|
||||
const valuekey = this.#options.valuekey;
|
||||
const textkey = this.#options.textkey;
|
||||
const htmlkey = this.#options.htmlkey;
|
||||
if (checkbox.getAttribute('isall') === '1') {
|
||||
const allchecked = this.#allChecked = checkbox.checked;
|
||||
const boxes = this.#container.querySelectorAll('input.dataitem');
|
||||
@ -451,16 +478,36 @@ class Dropdown {
|
||||
list = this.selectedlist.filter(it => it[valuekey] !== val);
|
||||
}
|
||||
}
|
||||
let text = this.#allChecked ? r('allItem', '( All )') : list.map(it => it[textkey]).join(', ');
|
||||
if (nullOrEmpty(text)) {
|
||||
text = r('noneItem', '( None )');
|
||||
if (this.#allChecked) {
|
||||
this.#label.innerText = r('allItem', '( All )');
|
||||
} else {
|
||||
selectItems(this.#label, list, htmlkey, textkey);
|
||||
}
|
||||
this.#selectedList = list;
|
||||
this.#label.innerText = text;
|
||||
if (typeof this.onselectedlist === 'function') {
|
||||
this.onselectedlist(itemlist);
|
||||
}
|
||||
}
|
||||
|
||||
static resolve(dom) {
|
||||
dom ??= document.body;
|
||||
const selects = dom.querySelectorAll('select');
|
||||
for (let sel of selects) {
|
||||
let selected;
|
||||
const source = [...sel.children].map(it => {
|
||||
if (it.selected) {
|
||||
selected = it.value;
|
||||
}
|
||||
return { value: it.value, text: it.innerText }
|
||||
});
|
||||
const drop = new Dropdown({
|
||||
selected
|
||||
});
|
||||
drop.source = source;
|
||||
sel.parentElement.replaceChild(drop.create(), sel);
|
||||
}
|
||||
return dom;
|
||||
}
|
||||
}
|
||||
|
||||
export default Dropdown;
|
@ -18,8 +18,8 @@ function createIcon(type, id) {
|
||||
function resolveIcon(container) {
|
||||
const svgs = container.querySelectorAll('svg[data-id]');
|
||||
for (let icon of svgs) {
|
||||
const type = icon.getAttribute('data-type');
|
||||
const id = icon.getAttribute('data-id');
|
||||
const type = icon.dataset.type;
|
||||
const id = icon.dataset.id;
|
||||
icon.replaceChildren(createUse(type, id));
|
||||
icon.removeAttribute('data-type');
|
||||
icon.removeAttribute('data-id');
|
||||
|
Reference in New Issue
Block a user