custom dropdown element

This commit is contained in:
Fran Jurmanović
2021-06-10 22:31:12 +02:00
parent 1cafcbedfa
commit f03d407590
11 changed files with 251 additions and 55 deletions

View File

@@ -48,10 +48,10 @@ class BaseElement extends HTMLElement {
return this.appMain?.isAuth();
}
public bindEvents = (): void => {
const _elems = this.querySelectorAll("[data-action]");
public bindEvents = (attrName): void => {
const _elems = this.querySelectorAll(`[${attrName}]`);
_elems?.forEach((el) => {
for (const action of (el.getAttribute("data-action") || "")
for (const action of (el.getAttribute(attrName) || "")
.trim()
.split(/\s+/)) {
const eventSep = action.lastIndexOf(":");
@@ -88,7 +88,8 @@ class BaseElement extends HTMLElement {
update = (): void => {
render(this.render(), this);
this.bindEvents();
this.bindEvents("data-action");
this.bindEvents("app-action");
this.updateCallback();
};

View File

@@ -1,7 +1,6 @@
import { attr, controller, target } from "@github/catalyst";
import { findMethod, firstUpper } from "core/utils";
import { html, TemplateResult } from "@github/jtml";
import { RouterService } from "core/services";
import { html } from "@github/jtml";
import randomId from "core/utils/random-id";
import validator from "validator";
import { validatorErrors } from "core/constants";
@@ -14,12 +13,20 @@ class AppDropdownElement extends BaseComponentElement {
@attr rules: string;
@target main: HTMLElement;
@target inp: HTMLElement;
@attr displaykey: string;
@attr valuekey: string;
@attr displaykey: string = "name";
@attr valuekey: string = "id";
@attr fetch: string;
fetchFunc: any;
error: string;
error: boolean;
errorMessage: string;
searchPhrase: string;
randId: string;
value: string;
@attr isOpen: boolean = false;
items: Array<any>;
totalItems: number;
@@ -51,32 +58,20 @@ class AppDropdownElement extends BaseComponentElement {
items = [];
}
this.items = items;
this.renderOptions(items);
this.renderOptions(items);
this.update();
}
};
private renderOptions = (items) => {
const displayKey = this.displaykey || "name";
const valueKey = this.valuekey || "id";
const options = items?.map((item) => {
const val = { name: item[displayKey], value: item[valueKey] };
if (
this.optionValues.some((value) => {
if (value.name == val.name && value.value == val.value) {
return true;
}
return false;
})
)
return;
const _option = document.createElement("option");
_option.setAttribute("value", val.value);
_option.innerText = val.name;
this.inp?.appendChild(_option);
get selectedItem() {
const { value, valuekey, items } = this;
const item = items?.find((item) => {
return value == item[valuekey];
});
};
console.log(item, value, valuekey);
return item;
}
get optionValues() {
let values = [];
@@ -88,10 +83,6 @@ class AppDropdownElement extends BaseComponentElement {
return values;
}
onChange = () => {
this.renderOptions(this.items);
};
public elementConnected = (): void => {
this.randId = `${name}${randomId()}`;
this.fetchFunc = findMethod(this.fetch, this.appMain);
@@ -104,6 +95,10 @@ class AppDropdownElement extends BaseComponentElement {
this.getItems(options);
};
attributeChangedCallback(): void {
this.update();
}
get valid(): boolean {
return !!this.error;
}
@@ -144,13 +139,102 @@ class AppDropdownElement extends BaseComponentElement {
return _return;
}
render = (): TemplateResult => {
return html`<div class="input-main" data-target="app-dropdown.main">
<select
data-target="app-dropdown.inp"
data-action="change:app-dropdown#onChange"
></select>
</div>`;
openDropdown = () => {
this.isOpen = true;
};
stopPropagation = (e) => {
e.stopPropagation();
};
toggleDropdown = () => {
const isOpen = this.isOpen;
this.isOpen = !isOpen;
};
itemSelected = (e) => {
const value = (e.target as HTMLSpanElement).getAttribute("data-value");
this.value = value;
this.isOpen = false;
};
get _value() {
return this.value;
}
render = () => {
const {
label,
error,
errorMessage,
isOpen,
searchPhrase,
items,
selectedItem,
displaykey,
valuekey,
} = this;
console.log(isOpen);
const renderItem = (item) => {
return html` <li
class="dropdown-custom-listitem ${selectedItem?.[valuekey] ==
item[valuekey]
? "--selected"
: ""}"
app-action="click:app-dropdown#itemSelected"
data-value="${item[valuekey]}"
>
${item[displaykey]}
</li>`;
};
const renderItems = (_items) => {
return _items.map((item) => renderItem(item));
};
return html`
<div>
<label app-action="click:app-dropdown#openDropdown">
${label ? html`<div>${label}</div>` : html``}
<div
class="dropdown-custom"
app-action="click:app-dropdown#stopPropagation"
>
<div
class="dropdown-custom-top"
app-action="click:app-dropdown#toggleDropdown"
>
<span class="dropdown-custom-fieldname"
>${selectedItem
? selectedItem[displaykey]
: "Select"}</span
>
</div>
${isOpen
? html`
<div class="dropdown-custom-open">
<input
class="dropdown-custom-search"
type="text"
value="${searchPhrase}"
app-action="input:app-dropdown#phraseChange"
autofocus
/>
<ul class="dropdown-custom-list">
${renderItems(items)}
</ul>
</div>
`
: html``}
</div>
${error
? html` <div class="h5 text-red">${errorMessage}</div>`
: html``}
</label>
</div>
`;
};
}

View File

@@ -65,13 +65,11 @@ class AppFormElement extends BaseComponentElement {
get values(): any {
const formObject: any = {};
this.inputField.forEach((input: InputFieldElement) => {
const inputType = input.inp;
formObject[input.name] = (inputType as HTMLInputElement).value;
formObject[input.name] = input._value;
});
this.appDropdown?.forEach((input: AppDropdownElement) => {
if (input.required && (input.inp as HTMLSelectElement).value) {
const inputType = input.inp;
formObject[input.name] = (inputType as HTMLSelectElement).value;
if (input.required && input.value) {
formObject[input.name] = input._value;
}
});
return formObject;
@@ -114,7 +112,7 @@ class AppFormElement extends BaseComponentElement {
if (hasCancel) {
return html`<button
type="button"
data-action="click:app-form#goBack"
app-action="click:app-form#goBack"
>
Cancel
</button>`;
@@ -123,7 +121,7 @@ class AppFormElement extends BaseComponentElement {
};
return html`<form
data-action="submit:app-form#onSubmit"
app-action="submit:app-form#onSubmit"
data-target="app-form.formElement"
>
<slot data-target="app-form.innerSlot"></slot>

View File

@@ -61,7 +61,7 @@ class AppLinkElement extends BaseComponentElement {
: html`<a
class="btn btn-link"
data-target="app-link.main"
data-action="click:app-link#goTo"
app-action="click:app-link#goTo"
href="${this.to}"
style="text-decoration: underline; cursor: pointer;"
>${this.title}</a

View File

@@ -123,7 +123,7 @@ class AppPaginationElement extends BaseComponentElement {
class="btn btn-primary btn-squared ${page <= 1
? "disabled"
: ""}"
data-action="click:app-pagination#pageBack"
app-action="click:app-pagination#pageBack"
>
Prev
</button>
@@ -132,7 +132,7 @@ class AppPaginationElement extends BaseComponentElement {
pageRange
? "disabled"
: ""}"
data-action="click:app-pagination#pageNext"
app-action="click:app-pagination#pageNext"
>
Next
</button>

View File

@@ -38,6 +38,10 @@ class InputFieldElement extends BaseComponentElement {
return this.rules.includes("required");
}
get _value() {
return (this.inp as HTMLInputElement)?.value;
}
validate = (): boolean => {
let _return = true;
const rules = this.rules?.split("|").filter((a) => a);
@@ -104,7 +108,7 @@ class InputFieldElement extends BaseComponentElement {
return html` <input
type="${this.type}"
data-target="input-field.inp"
data-action="
app-action="
input:input-field#inputChange
blur:input-field#validateDisplay
"

View File

@@ -42,7 +42,7 @@ class MenuItemElement extends BaseComponentElement {
${this.customaction
? html`<div
data-target="menu-item.customButton"
data-action="${this.customaction}"
app-action="${this.customaction}"
>
+
</div>`

View File

@@ -53,7 +53,7 @@ class HomePageElement extends BasePageElement {
render = (): TemplateResult => {
return html`
<button data-action="click:home-page#openModal">New Wallet</button>
<button app-action="click:home-page#openModal">New Wallet</button>
<wallet-header
data-target="home-page.walletHeader"
data-current-balance="0"

View File

@@ -0,0 +1,107 @@
.dropdown-custom {
text-align: center;
text-transform: capitalize;
div.dropdown-custom-top {
position: relative;
background-color: #ffffff;
border: 1px solid transparent;
color: #09090a;
border-radius: 2px;
cursor: pointer;
padding: 5px 0;
font-weight: 500;
font-size: 16px;
border: 1px solid #767676;
border-radius: 3px;
&::after {
content: "";
position: absolute;
right: 0;
margin-top: 8px;
margin-right: 4px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #2e2e2e;
}
&.--open {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom: transparent;
margin-bottom: 0 !important;
&::after {
content: "";
position: absolute;
right: 0;
margin-top: 8px;
margin-right: 4px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 8px solid #2e2e2e;
border-top: none;
}
}
span.dropdown-custom-fieldname {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
}
div.dropdown-custom-open {
border: 1px solid transparent;
position: absolute;
width: 100%;
border-top: none;
margin-top: 0 !important;
background-color: #fbfafa;
border-bottom-right-radius: 0.2em;
border-bottom-left-radius: 0.2em;
font-size: 16px;
input.dropdown-custom-search {
position: relative;
width: calc(100% - 2 * 2px);
margin: 2px;
background-color: #fbfbfb;
border: 1px solid #9c9c9c;
border-radius: 0.3em;
&:hover {
border: 1px solid #b6b6b6;
}
}
ul.dropdown-custom-list {
padding: 1px 0;
max-height: 100px;
overflow-y: scroll;
&::-webkit-scrollbar {
display: none;
}
-ms-overflow-style: none;
scrollbar-width: none;
li.dropdown-custom-listitem {
margin: 2px;
padding: 1px 0;
list-style-type: none;
color: #0e0d0d;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
&:hover {
background-color: #c6d8ff;
}
&.--selected {
background-color: #d8e4ff;
}
}
}
}
}

View File

@@ -0,0 +1 @@
@import "./app-dropdown.scss";

View File

@@ -8,3 +8,4 @@
@import "./page/index.scss";
@import "./app-form/index.scss";
@import "./layout/index.scss";
@import "./app-dropdown/index.scss";