fix dropdown issue, add autoprefix for scss

This commit is contained in:
Tsanie Lily 2023-03-31 22:19:51 +08:00
parent 41b1bbd7d6
commit 9677f6a82d
18 changed files with 2738 additions and 133 deletions

View File

@ -1,5 +1,5 @@
@import './variables/definition.scss'; @import './variables/definition.scss';
@import './functions/func.scss'; // @import './functions/func.scss';
@import './functions/checkbox.scss'; @import './functions/checkbox.scss';
$headerHeight: 26px; $headerHeight: 26px;
@ -8,7 +8,7 @@ $dropItemHeight: 30px;
$borderRadius: 2px; $borderRadius: 2px;
$scrollBarSize: 4px; $scrollBarSize: 4px;
$searchBarHeight: 40px; $searchBarHeight: 36px;
$searchInputHeight: 26px; $searchInputHeight: 26px;
$searchIconSize: 13px; $searchIconSize: 13px;
$listMaxHeight: 210px; $listMaxHeight: 210px;
@ -16,21 +16,21 @@ $listMaxHeight: 210px;
.dropdown-wrapper { .dropdown-wrapper {
display: inline-block; display: inline-block;
border: none; border: none;
@include border-radius(unset); border-radius: unset;
@include user-select(none); user-select: none;
position: relative; position: relative;
>.dropdown-header { >.dropdown-header {
border: 1px solid $borderColor; border: 1px solid $borderColor;
@include border-radius($borderRadius); border-radius: $borderRadius;
background-color: $bgColor; background-color: $bgColor;
display: flex; display: flex;
height: $headerHeight; height: $headerHeight;
@include transition(all .3s); transition: all .3s;
&:focus { &:focus {
border-color: $focusColor; border-color: $focusColor;
// @include box-shadow(0 0 3px 1px rgba(0, 0, 0, .2)); // box-shadow: 0 0 3px 1px rgba(0, 0, 0, .2);
} }
&:focus, &:focus,
@ -51,7 +51,7 @@ $listMaxHeight: 210px;
} }
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
@include border-radius($borderRadius); border-radius: $borderRadius;
} }
>span { >span {
@ -63,7 +63,7 @@ $listMaxHeight: 210px;
line-height: 20px; line-height: 20px;
background-color: white; background-color: white;
font-size: .75rem; font-size: .75rem;
@include border-radius($borderRadius); border-radius: $borderRadius;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
@ -113,6 +113,7 @@ $listMaxHeight: 210px;
text-overflow: ellipsis; text-overflow: ellipsis;
border: none; border: none;
outline: none; outline: none;
white-space: nowrap;
} }
>.dropdown-caret { >.dropdown-caret {
@ -140,7 +141,7 @@ $listMaxHeight: 210px;
&:focus { &:focus {
border-color: $disabledBgColor; border-color: $disabledBgColor;
// @include box-shadow(none); // box-shadow: none;
} }
>.dropdown-text, >.dropdown-text,
@ -159,12 +160,12 @@ $listMaxHeight: 210px;
background-color: $bgColor; background-color: $bgColor;
top: calc($headerHeight + 2px); top: calc($headerHeight + 2px);
z-index: 2; z-index: 2;
@include transition3(transform 120ms ease, opacity 120ms ease, visibility 120ms ease); transition: transform 120ms ease, opacity 120ms ease, visibility 120ms ease;
width: calc(100% + 2px); width: calc(100% + 2px);
box-sizing: border-box; box-sizing: border-box;
/*border: 1px solid $borderColor; /*border: 1px solid $borderColor;
border-top-width: 0;*/ border-top-width: 0;*/
@include box-shadow3(0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05)); box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05);
left: -1px; left: -1px;
&.slide-up { &.slide-up {
@ -186,6 +187,8 @@ $listMaxHeight: 210px;
line-height: $searchBarHeight; line-height: $searchBarHeight;
padding: 0 8px; padding: 0 8px;
position: relative; position: relative;
display: flex;
align-items: center;
>input[type="text"] { >input[type="text"] {
box-sizing: border-box; box-sizing: border-box;
@ -193,10 +196,10 @@ $listMaxHeight: 210px;
height: $searchInputHeight; height: $searchInputHeight;
outline: none; outline: none;
border: 1px solid $borderColor; border: 1px solid $borderColor;
@include border-radius($borderRadius); border-radius: $borderRadius;
padding: 0 6px 0 22px; padding: 0 6px 0 22px;
color: $foreColor; color: $foreColor;
@include transition(all .3s); transition: all .3s;
&:hover, &:hover,
&:focus { &:focus {
@ -204,7 +207,7 @@ $listMaxHeight: 210px;
} }
// &:focus { // &:focus {
// @include box-shadow(0 0 3px 1px rgba(0, 0, 0, .2)); // box-shadow: 0 0 3px 1px rgba(0, 0, 0, .2);
// } // }
} }
@ -220,10 +223,10 @@ $listMaxHeight: 210px;
>.dropdown-list { >.dropdown-list {
margin: 0; margin: 0;
padding: 0; padding: 0;
padding-bottom: 6px;
list-style: none; list-style: none;
max-height: $listMaxHeight; max-height: $listMaxHeight;
overflow-y: auto; overflow-y: auto;
font-size: .875rem;
&.filtered>li:first-child { &.filtered>li:first-child {
background-color: $hoverColor; background-color: $hoverColor;
@ -247,6 +250,7 @@ $listMaxHeight: 210px;
>.checkbox-wrapper { >.checkbox-wrapper {
height: $dropItemHeight; height: $dropItemHeight;
display: flex;
} }
} }
} }

View File

@ -1,5 +1,3 @@
@import './func.scss';
$boxBorderColor: #999898; $boxBorderColor: #999898;
$boxCheckedColor: #1890ff; $boxCheckedColor: #1890ff;
$boxDisabledColor: #d9d9d9; $boxDisabledColor: #d9d9d9;
@ -13,9 +11,9 @@ $boxDisabledColor: #d9d9d9;
height: 14px; height: 14px;
background-color: #fff; background-color: #fff;
border: 1px solid $boxBorderColor; border: 1px solid $boxBorderColor;
@include user-select(none); user-select: none;
@include border-radius(2px); border-radius: 2px;
@include transition(all .2s); transition: all .2s;
cursor: pointer; cursor: pointer;
>svg { >svg {
@ -27,7 +25,7 @@ $boxDisabledColor: #d9d9d9;
fill: #fff; fill: #fff;
transform: scale(0); transform: scale(0);
opacity: 0; opacity: 0;
@include transition(all .08s cubic-bezier(.78, .14, .15, .86)); transition: all .08s cubic-bezier(.78, .14, .15, .86);
} }
} }

View File

@ -1,46 +1,3 @@
@mixin user-select($prop) {
-moz-user-select: $prop;
-ms-user-select: $prop;
user-select: $prop;
-webkit-user-select: $prop;
}
@mixin border-radius($property) {
-moz-border-radius: $property;
-webkit-border-radius: $property;
border-radius: $property;
}
@mixin box-shadow($property) {
-webkit-box-shadow: $property;
box-shadow: $property;
}
@mixin box-shadow2($p1, $p2) {
-webkit-box-shadow: $p1, $p2;
box-shadow: $p1, $p2;
}
@mixin box-shadow3($p1, $p2, $p3) {
-webkit-box-shadow: $p1, $p2, $p3;
box-shadow: $p1, $p2, $p3;
}
@mixin transition($property) {
-webkit-transition: $property;
transition: $property;
}
@mixin transition2($p1, $p2) {
-webkit-transition: $p1, $p2;
transition: $p1, $p2;
}
@mixin transition3($p1, $p2, $p3) {
-webkit-transition: $p1, $p2, $p3;
transition: $p1, $p2, $p3;
}
@mixin scrollbar() { @mixin scrollbar() {
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 8px; width: 8px;

View File

@ -17,13 +17,13 @@
min-width: 75px; min-width: 75px;
max-width: 480px; max-width: 480px;
min-height: 32px; min-height: 32px;
@include border-radius(2px); border-radius: 2px;
@include box-shadow2(0 3.2px 7.2px 0 rgba(0, 0, 0, .13), 0 0.6px 1.8px 0 rgba(0, 0, 0, .11)); box-shadow: 0 3.2px 7.2px 0 rgba(0, 0, 0, .13), 0 0.6px 1.8px 0 rgba(0, 0, 0, .11);
@include transition2(visibility 0s linear 120ms, opacity 120ms ease); transition: visibility 0s linear 120ms, opacity 120ms ease;
> .tooltip-pointer { >.tooltip-pointer {
box-sizing: border-box; box-sizing: border-box;
@include box-shadow(0 5px 15px 2px rgba(0, 0, 0, .3)); box-shadow: 0 5px 15px 2px rgba(0, 0, 0, .3);
border: 1px solid #fff; border: 1px solid #fff;
z-index: -1; z-index: -1;
width: 16px; width: 16px;
@ -35,20 +35,20 @@
transform-origin: center; transform-origin: center;
} }
> .tooltip-curtain { >.tooltip-curtain {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: -1; z-index: -1;
} }
> .tooltip-content { >.tooltip-content {
font-size: $smallSize; font-size: $smallSize;
line-height: 1rem; line-height: 1rem;
white-space: normal; white-space: normal;
overflow: hidden; overflow: hidden;
margin: 8px; margin: 8px;
height: calc(100% - 16px); height: calc(100% - 16px);
@include user-select(none); user-select: none;
} }
} }

1
css/ui.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -20,6 +20,7 @@
<li data-page="lib/ui/icon.html">icon</li> <li data-page="lib/ui/icon.html">icon</li>
<li data-page="lib/ui/checkbox.html">checkbox</li> <li data-page="lib/ui/checkbox.html">checkbox</li>
<li data-page="lib/ui/tooltip.html">tooltip</li> <li data-page="lib/ui/tooltip.html">tooltip</li>
<li data-page="lib/ui/dropdown.html">dropdown</li>
</ol> </ol>
</li> </li>
<li class="title">lib-utility</li> <li class="title">lib-utility</li>

View File

@ -1,3 +1,4 @@
import "../css/ui.scss";
import { createIcon, resolveIcon } from "./ui/icon"; import { createIcon, resolveIcon } from "./ui/icon";
import { createCheckbox, resolveCheckbox } from "./ui/checkbox"; import { createCheckbox, resolveCheckbox } from "./ui/checkbox";
import { setTooltip, resolveTooltip } from "./ui/tooltip"; import { setTooltip, resolveTooltip } from "./ui/tooltip";

View File

@ -7,7 +7,7 @@ function fillCheckbox(container, type, label) {
container.appendChild(layer); container.appendChild(layer);
if (label instanceof HTMLElement) { if (label instanceof HTMLElement) {
container.appendChild(label); container.appendChild(label);
} else if (label != null && label.length > 0) { } else if (label?.length > 0) {
const span = document.createElement('span'); const span = document.createElement('span');
span.innerText = label; span.innerText = label;
container.appendChild(span); container.appendChild(span);
@ -75,7 +75,7 @@ function resolveCheckbox(container, legacy) {
if (e != null) { if (e != null) {
if (e.tagName === 'LABEL') { if (e.tagName === 'LABEL') {
label = e; label = e;
} else if (e.tagName === 'SPAN' && e.getAttribute('data-lgid') != null) { } else if (e.tagName === 'SPAN' && e.dataset.lgid != null) {
text = e.innerText; text = e.innerText;
e.style.display = 'none'; e.style.display = 'none';
} }
@ -86,7 +86,7 @@ function resolveCheckbox(container, legacy) {
if (e != null) { if (e != null) {
if (e.tagName === 'LABEL') { if (e.tagName === 'LABEL') {
label = e; 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; text = e.innerText;
e.style.display = 'none'; e.style.display = 'none';
} }
@ -114,18 +114,18 @@ function resolveCheckbox(container, legacy) {
box.classList.add('checkbox-image'); box.classList.add('checkbox-image');
} }
} else { } else {
const type = box.getAttribute('data-type') || 'fa-regular'; const type = box.dataset.type || 'fa-regular';
const label = box.getAttribute('data-label'); const label = box.dataset.label;
fillCheckbox(box, type, label) fillCheckbox(box, type, label)
box.removeAttribute('data-type'); box.removeAttribute('data-type');
box.removeAttribute('data-label'); box.removeAttribute('data-label');
} }
const input = document.createElement('input'); const input = document.createElement('input');
const id = box.getAttribute('data-id'); const id = box.dataset.id;
if (id != null && id.length > 0) { if (id?.length > 0) {
input.id = id; input.id = id;
} }
if (box.getAttribute('data-checked') != null) { if (box.dataset.checked != null) {
input.checked = true; input.checked = true;
} }
input.setAttribute('type', 'checkbox'); input.setAttribute('type', 'checkbox');

32
lib/ui/dropdown.d.ts vendored
View File

@ -1,15 +1,21 @@
interface DropdownItem {
value: string;
text: string;
html?: HTMLElement
}
interface DropdownOptions { interface DropdownOptions {
textkey?: string; textkey?: string;
valuekey?: string; valuekey?: string;
htmlkey?: string; htmlkey?: string;
maxlength?: Number; maxlength?: Number;
multiselect?: boolean; multiselect?: boolean;
selected?: any; selected?: DropdownItem | any;
selectedlist?: any[]; selectedlist?: Array<DropdownItem | any>;
disabled?: boolean; disabled?: boolean;
input?: boolean; input?: boolean;
search?: boolean; search?: boolean;
searchkeys?: string[]; searchkeys?: Array<string>;
searchplaceholder?: string; searchplaceholder?: string;
tabindex?: Number; tabindex?: Number;
slidefixed?: boolean; slidefixed?: boolean;
@ -17,16 +23,28 @@ interface DropdownOptions {
} }
interface Dropdown { interface Dropdown {
sourceFilter: () => Array<DropdownItem | any>;
onselectedlist: (list: Array<DropdownItem | any>) => void;
onselected: (item: DropdownItem | any) => void;
onexpanded: () => void;
create(): HTMLElement; create(): HTMLElement;
readonly multiselect: boolean;
get disabled(): boolean; get disabled(): boolean;
set disabled(flag: boolean); set disabled(flag: boolean);
readonly multiselect: boolean; get source(): Array<DropdownItem | any>;
readonly selected: 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: { declare var Dropdown: {
prototype: Dropdown; prototype: Dropdown;
new(options: DropdownOptions): Dropdown; new(options?: DropdownOptions): Dropdown;
};
resolve(dom?: HTMLElement): HTMLElement;
}
export default Dropdown; export default Dropdown;

147
lib/ui/dropdown.html Normal file
View 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>

View File

@ -29,7 +29,7 @@ if (dropdownGlobal == null) {
return; return;
} }
const dropdown = this[dropId]; const dropdown = this[dropId];
if (dropdown != null && dropdown.multiselect && typeof dropdown.oncollapsed === 'function') { if (dropdown?.multiselect && typeof dropdown.oncollapsed === 'function') {
dropdown.oncollapsed(); 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 { class Dropdown {
#options; #options;
@ -211,6 +224,7 @@ class Dropdown {
this.#lastSelected = selected; this.#lastSelected = selected;
const valuekey = this.#options.valuekey; const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey; const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
let item = this.source.find(it => it[valuekey] === selected); let item = this.source.find(it => it[valuekey] === selected);
if (this.#options.input) { if (this.#options.input) {
if (item == null) { if (item == null) {
@ -224,11 +238,16 @@ class Dropdown {
this.#label.innerText = ' '; this.#label.innerText = ' ';
return false; return false;
} }
let text = item[textkey]; const html = item[htmlkey];
if (nullOrEmpty(text)) { if (html instanceof HTMLElement) {
text = ' '; 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; this.#selected = item;
if (!init && typeof this.onselected === 'function') { if (!init && typeof this.onselected === 'function') {
@ -240,6 +259,7 @@ class Dropdown {
const source = this.source; const source = this.source;
const valuekey = this.#options.valuekey; const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey; const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const itemlist = selectedlist.map(v => { const itemlist = selectedlist.map(v => {
let item = source.find(it => it[valuekey] === v); let item = source.find(it => it[valuekey] === v);
if (item == null) { if (item == null) {
@ -249,24 +269,19 @@ class Dropdown {
} }
return item; return item;
}); });
const none = r('noneItem', '( None )');
if (itemlist.length === 0) { if (itemlist.length === 0) {
this.#selectedList = null; this.#selectedList = null;
this.#label.innerText = none; this.#label.innerText = none;
return false; return false;
} }
const text = itemlist.map(it => it[textkey]).join(', '); selectItems(this.#label, itemlist, htmlkey, textkey);
if (nullOrEmpty(text)) {
text = none;
}
this.#selectedList = itemlist; this.#selectedList = itemlist;
this.#label.innerText = text;
if (!init && typeof this.onselectedlist === 'function') { if (!init && typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist); this.onselectedlist(itemlist);
} }
} }
get #expanded() { return this.#container != null && this.#container.style.visibility === 'visible' } get #expanded() { return this.#container?.style.visibility === 'visible' }
#dropdown(flag) { #dropdown(flag) {
flag ??= true; flag ??= true;
@ -277,7 +292,7 @@ class Dropdown {
panel = document.createElement('div'); panel = document.createElement('div');
panel.className = 'dropdown-panel'; panel.className = 'dropdown-panel';
// search box // search box
if (options.search) { if (!options.input && options.search) {
let searchkeys = options.searchkeys; let searchkeys = options.searchkeys;
if (!Array.isArray(searchkeys) || searchkeys.length === 0) { if (!Array.isArray(searchkeys) || searchkeys.length === 0) {
searchkeys = [textkey]; searchkeys = [textkey];
@ -328,7 +343,6 @@ class Dropdown {
panel.appendChild(list); panel.appendChild(list);
this.#container = panel; this.#container = panel;
this.#wrapper.appendChild(panel); this.#wrapper.appendChild(panel);
this.#filllist(this.source);
} }
if (flag) { if (flag) {
if (!options.slidefixed) { if (!options.slidefixed) {
@ -349,6 +363,18 @@ class Dropdown {
// const event = new InputEvent('type'); // const event = new InputEvent('type');
// inputSearch.dispatchEvent(event); // 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 { } else {
panel.classList.remove('active'); panel.classList.remove('active');
} }
@ -425,6 +451,7 @@ class Dropdown {
let list; let list;
const valuekey = this.#options.valuekey; const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey; const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
if (checkbox.getAttribute('isall') === '1') { if (checkbox.getAttribute('isall') === '1') {
const allchecked = this.#allChecked = checkbox.checked; const allchecked = this.#allChecked = checkbox.checked;
const boxes = this.#container.querySelectorAll('input.dataitem'); const boxes = this.#container.querySelectorAll('input.dataitem');
@ -451,16 +478,36 @@ class Dropdown {
list = this.selectedlist.filter(it => it[valuekey] !== val); list = this.selectedlist.filter(it => it[valuekey] !== val);
} }
} }
let text = this.#allChecked ? r('allItem', '( All )') : list.map(it => it[textkey]).join(', '); if (this.#allChecked) {
if (nullOrEmpty(text)) { this.#label.innerText = r('allItem', '( All )');
text = r('noneItem', '( None )'); } else {
selectItems(this.#label, list, htmlkey, textkey);
} }
this.#selectedList = list; this.#selectedList = list;
this.#label.innerText = text;
if (typeof this.onselectedlist === 'function') { if (typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist); 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; export default Dropdown;

View File

@ -18,8 +18,8 @@ function createIcon(type, id) {
function resolveIcon(container) { function resolveIcon(container) {
const svgs = container.querySelectorAll('svg[data-id]'); const svgs = container.querySelectorAll('svg[data-id]');
for (let icon of svgs) { for (let icon of svgs) {
const type = icon.getAttribute('data-type'); const type = icon.dataset.type;
const id = icon.getAttribute('data-id'); const id = icon.dataset.id;
icon.replaceChildren(createUse(type, id)); icon.replaceChildren(createUse(type, id));
icon.removeAttribute('data-type'); icon.removeAttribute('data-type');
icon.removeAttribute('data-id'); icon.removeAttribute('data-id');

View File

@ -1,4 +1,7 @@
function setCookie(name, value, expireDays) { function setCookie(name, value, expireDays) {
if (name == null) {
return;
}
let extra = `; domain=${location.host}; path=/`; let extra = `; domain=${location.host}; path=/`;
if (expireDays != null) { if (expireDays != null) {
const d = new Date(); const d = new Date();
@ -12,6 +15,9 @@ function setCookie(name, value, expireDays) {
} }
function getCookie(name) { function getCookie(name) {
if (name == null) {
return null;
}
name += '='; name += '=';
const cookies = document.cookie.split(';'); const cookies = document.cookie.split(';');
for (let cookie of cookies) { for (let cookie of cookies) {

View File

@ -7,9 +7,9 @@ let cache;
function getCurrentLgId() { function getCurrentLgId() {
let lgid; let lgid;
if (typeof consts !== 'undefined') { if (typeof consts !== 'undefined') {
lgid = consts.cookie != null && getCookie(consts.cookie.lang); lgid = getCookie(consts.cookie?.lang);
if (nullOrEmpty(lgid)) { if (nullOrEmpty(lgid)) {
lgid = consts.user && consts.user.language; lgid = consts.user?.language;
} }
} }
if (nullOrEmpty(lgid)) { if (nullOrEmpty(lgid)) {
@ -83,7 +83,7 @@ function getLanguage(lgres, key, defaultValue) {
function applyLanguage(dom, result) { function applyLanguage(dom, result) {
dom ??= document.body; dom ??= document.body;
for (let text of dom.querySelectorAll('[data-lgid]')) { for (let text of dom.querySelectorAll('[data-lgid]')) {
const key = text.getAttribute('data-lgid'); const key = text.dataset.lgid;
if (text.tagName === 'INPUT') { if (text.tagName === 'INPUT') {
text.value = getLanguage(result, key, text.value); text.value = getLanguage(result, key, text.value);
} else { } else {
@ -91,11 +91,11 @@ function applyLanguage(dom, result) {
} }
} }
for (let title of dom.querySelectorAll('[data-title-lgid]')) { for (let title of dom.querySelectorAll('[data-title-lgid]')) {
const key = title.getAttribute('data-title-lgid'); const key = title.dataset.titleLgid;
title.setAttribute('title', getLanguage(result, key, title.getAttribute('title'))); title.setAttribute('title', getLanguage(result, key, title.getAttribute('title')));
} }
for (let holder of dom.querySelectorAll('[data-placeholder-lgid]')) { for (let holder of dom.querySelectorAll('[data-placeholder-lgid]')) {
const key = holder.getAttribute('data-placeholder-lgid'); const key = holder.dataset.placeholderLgid;
holder.setAttribute('placeholder', getLanguage(result, key, holder.getAttribute('placeholder'))); holder.setAttribute('placeholder', getLanguage(result, key, holder.getAttribute('placeholder')));
} }
} }

View File

@ -1,4 +1,4 @@
import './css/ui.min.css' import './css/ui.scss'
import './style.css' import './style.css'
// import javascriptLogo from './javascript.svg' // import javascriptLogo from './javascript.svg'
import { get } from './lib/utility' import { get } from './lib/utility'
@ -26,7 +26,7 @@ function navigate(page) {
} }
document.querySelector('#directory').addEventListener('click', ev => { document.querySelector('#directory').addEventListener('click', ev => {
const page = ev.target.getAttribute('data-page'); const page = ev.target.dataset.page;
if (typeof page === 'string') { if (typeof page === 'string') {
location.hash = page; location.hash = page;
navigate(page); navigate(page);

2408
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{ {
"name": "ui-lib", "name": "ui-lib",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.1",
"type": "module", "type": "module",
"files": [ "files": [
"dist" "dist"
@ -16,11 +16,17 @@
"require": "./dist/utility.min.js" "require": "./dist/utility.min.js"
} }
}, },
"browserslist": [
"last 1 versions",
"> 1%"
],
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "node ./vite.build.js" "build": "node ./vite.build.js"
}, },
"devDependencies": { "devDependencies": {
"postcss-preset-env": "^8.2.0",
"sass": "^1.60.0",
"vite": "^4.0.4" "vite": "^4.0.4"
} }
} }

View File

@ -1,25 +1,38 @@
import { build } from "vite"; import { build } from "vite";
import postcssPresetEnv from "postcss-preset-env";
const libraries = [ const libraries = [
{ {
entry: './lib/ui.js', clearScreen: false,
name: 'lib-ui' css: {
}, postcss: {
{ plugins: [postcssPresetEnv()]
entry: './lib/utility.js', }
name: 'lib-utility' },
}
]
libraries.forEach(async lib => {
await build({
build: { build: {
lib: { lib: {
...lib, entry: './lib/ui.js',
name: 'lib-ui',
formats: ['umd'], formats: ['umd'],
fileName: (_format, name) => `${name}.min.js` fileName: (_format, name) => `${name}.min.js`
}, },
sourcemap: true sourcemap: true,
emptyOutDir: false
} }
}) },
}) {
clearScreen: false,
build: {
lib: {
entry: './lib/utility.js',
name: 'lib-utility',
formats: ['umd'],
fileName: (_format, name) => `${name}.min.js`
},
sourcemap: true,
emptyOutDir: false
}
}
]
libraries.forEach(async b => await build(b))