fix dropdown issue, add autoprefix for scss
This commit is contained in:
parent
41b1bbd7d6
commit
9677f6a82d
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
1
css/ui.min.css
vendored
File diff suppressed because one or more lines are too long
@ -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>
|
||||||
|
@ -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";
|
||||||
|
@ -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
32
lib/ui/dropdown.d.ts
vendored
@ -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
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;
|
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,12 +238,17 @@ class Dropdown {
|
|||||||
this.#label.innerText = ' ';
|
this.#label.innerText = ' ';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const html = item[htmlkey];
|
||||||
|
if (html instanceof HTMLElement) {
|
||||||
|
this.#label.replaceChildren(html.cloneNode(true));
|
||||||
|
} else {
|
||||||
let text = item[textkey];
|
let text = item[textkey];
|
||||||
if (nullOrEmpty(text)) {
|
if (nullOrEmpty(text)) {
|
||||||
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') {
|
||||||
this.onselected(item);
|
this.onselected(item);
|
||||||
@ -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;
|
@ -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');
|
||||||
|
@ -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) {
|
||||||
|
@ -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')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
main.js
4
main.js
@ -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
2408
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user