mirror of
https://github.com/FJurmanovic/wallet-web.git
synced 2026-02-06 06:08:10 +00:00
custom dropdown element
This commit is contained in:
@@ -48,10 +48,10 @@ class BaseElement extends HTMLElement {
|
|||||||
return this.appMain?.isAuth();
|
return this.appMain?.isAuth();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bindEvents = (): void => {
|
public bindEvents = (attrName): void => {
|
||||||
const _elems = this.querySelectorAll("[data-action]");
|
const _elems = this.querySelectorAll(`[${attrName}]`);
|
||||||
_elems?.forEach((el) => {
|
_elems?.forEach((el) => {
|
||||||
for (const action of (el.getAttribute("data-action") || "")
|
for (const action of (el.getAttribute(attrName) || "")
|
||||||
.trim()
|
.trim()
|
||||||
.split(/\s+/)) {
|
.split(/\s+/)) {
|
||||||
const eventSep = action.lastIndexOf(":");
|
const eventSep = action.lastIndexOf(":");
|
||||||
@@ -88,7 +88,8 @@ class BaseElement extends HTMLElement {
|
|||||||
|
|
||||||
update = (): void => {
|
update = (): void => {
|
||||||
render(this.render(), this);
|
render(this.render(), this);
|
||||||
this.bindEvents();
|
this.bindEvents("data-action");
|
||||||
|
this.bindEvents("app-action");
|
||||||
this.updateCallback();
|
this.updateCallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { attr, controller, target } from "@github/catalyst";
|
import { attr, controller, target } from "@github/catalyst";
|
||||||
import { findMethod, firstUpper } from "core/utils";
|
import { findMethod, firstUpper } from "core/utils";
|
||||||
import { html, TemplateResult } from "@github/jtml";
|
import { html } from "@github/jtml";
|
||||||
import { RouterService } from "core/services";
|
|
||||||
import randomId from "core/utils/random-id";
|
import randomId from "core/utils/random-id";
|
||||||
import validator from "validator";
|
import validator from "validator";
|
||||||
import { validatorErrors } from "core/constants";
|
import { validatorErrors } from "core/constants";
|
||||||
@@ -14,12 +13,20 @@ class AppDropdownElement extends BaseComponentElement {
|
|||||||
@attr rules: string;
|
@attr rules: string;
|
||||||
@target main: HTMLElement;
|
@target main: HTMLElement;
|
||||||
@target inp: HTMLElement;
|
@target inp: HTMLElement;
|
||||||
@attr displaykey: string;
|
@attr displaykey: string = "name";
|
||||||
@attr valuekey: string;
|
@attr valuekey: string = "id";
|
||||||
@attr fetch: string;
|
@attr fetch: string;
|
||||||
fetchFunc: any;
|
fetchFunc: any;
|
||||||
error: string;
|
|
||||||
|
error: boolean;
|
||||||
|
errorMessage: string;
|
||||||
|
|
||||||
|
searchPhrase: string;
|
||||||
|
|
||||||
randId: string;
|
randId: string;
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
@attr isOpen: boolean = false;
|
||||||
|
|
||||||
items: Array<any>;
|
items: Array<any>;
|
||||||
totalItems: number;
|
totalItems: number;
|
||||||
@@ -51,32 +58,20 @@ class AppDropdownElement extends BaseComponentElement {
|
|||||||
items = [];
|
items = [];
|
||||||
}
|
}
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.renderOptions(items);
|
this.update();
|
||||||
this.renderOptions(items);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderOptions = (items) => {
|
get selectedItem() {
|
||||||
const displayKey = this.displaykey || "name";
|
const { value, valuekey, items } = this;
|
||||||
const valueKey = this.valuekey || "id";
|
const item = items?.find((item) => {
|
||||||
|
return value == item[valuekey];
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
console.log(item, value, valuekey);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
get optionValues() {
|
get optionValues() {
|
||||||
let values = [];
|
let values = [];
|
||||||
@@ -88,10 +83,6 @@ class AppDropdownElement extends BaseComponentElement {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange = () => {
|
|
||||||
this.renderOptions(this.items);
|
|
||||||
};
|
|
||||||
|
|
||||||
public elementConnected = (): void => {
|
public elementConnected = (): void => {
|
||||||
this.randId = `${name}${randomId()}`;
|
this.randId = `${name}${randomId()}`;
|
||||||
this.fetchFunc = findMethod(this.fetch, this.appMain);
|
this.fetchFunc = findMethod(this.fetch, this.appMain);
|
||||||
@@ -104,6 +95,10 @@ class AppDropdownElement extends BaseComponentElement {
|
|||||||
this.getItems(options);
|
this.getItems(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
attributeChangedCallback(): void {
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
get valid(): boolean {
|
get valid(): boolean {
|
||||||
return !!this.error;
|
return !!this.error;
|
||||||
}
|
}
|
||||||
@@ -144,13 +139,102 @@ class AppDropdownElement extends BaseComponentElement {
|
|||||||
return _return;
|
return _return;
|
||||||
}
|
}
|
||||||
|
|
||||||
render = (): TemplateResult => {
|
openDropdown = () => {
|
||||||
return html`<div class="input-main" data-target="app-dropdown.main">
|
this.isOpen = true;
|
||||||
<select
|
};
|
||||||
data-target="app-dropdown.inp"
|
|
||||||
data-action="change:app-dropdown#onChange"
|
stopPropagation = (e) => {
|
||||||
></select>
|
e.stopPropagation();
|
||||||
</div>`;
|
};
|
||||||
|
|
||||||
|
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>
|
||||||
|
`;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,13 +65,11 @@ class AppFormElement extends BaseComponentElement {
|
|||||||
get values(): any {
|
get values(): any {
|
||||||
const formObject: any = {};
|
const formObject: any = {};
|
||||||
this.inputField.forEach((input: InputFieldElement) => {
|
this.inputField.forEach((input: InputFieldElement) => {
|
||||||
const inputType = input.inp;
|
formObject[input.name] = input._value;
|
||||||
formObject[input.name] = (inputType as HTMLInputElement).value;
|
|
||||||
});
|
});
|
||||||
this.appDropdown?.forEach((input: AppDropdownElement) => {
|
this.appDropdown?.forEach((input: AppDropdownElement) => {
|
||||||
if (input.required && (input.inp as HTMLSelectElement).value) {
|
if (input.required && input.value) {
|
||||||
const inputType = input.inp;
|
formObject[input.name] = input._value;
|
||||||
formObject[input.name] = (inputType as HTMLSelectElement).value;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return formObject;
|
return formObject;
|
||||||
@@ -114,7 +112,7 @@ class AppFormElement extends BaseComponentElement {
|
|||||||
if (hasCancel) {
|
if (hasCancel) {
|
||||||
return html`<button
|
return html`<button
|
||||||
type="button"
|
type="button"
|
||||||
data-action="click:app-form#goBack"
|
app-action="click:app-form#goBack"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>`;
|
</button>`;
|
||||||
@@ -123,7 +121,7 @@ class AppFormElement extends BaseComponentElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return html`<form
|
return html`<form
|
||||||
data-action="submit:app-form#onSubmit"
|
app-action="submit:app-form#onSubmit"
|
||||||
data-target="app-form.formElement"
|
data-target="app-form.formElement"
|
||||||
>
|
>
|
||||||
<slot data-target="app-form.innerSlot"></slot>
|
<slot data-target="app-form.innerSlot"></slot>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class AppLinkElement extends BaseComponentElement {
|
|||||||
: html`<a
|
: html`<a
|
||||||
class="btn btn-link"
|
class="btn btn-link"
|
||||||
data-target="app-link.main"
|
data-target="app-link.main"
|
||||||
data-action="click:app-link#goTo"
|
app-action="click:app-link#goTo"
|
||||||
href="${this.to}"
|
href="${this.to}"
|
||||||
style="text-decoration: underline; cursor: pointer;"
|
style="text-decoration: underline; cursor: pointer;"
|
||||||
>${this.title}</a
|
>${this.title}</a
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class AppPaginationElement extends BaseComponentElement {
|
|||||||
class="btn btn-primary btn-squared ${page <= 1
|
class="btn btn-primary btn-squared ${page <= 1
|
||||||
? "disabled"
|
? "disabled"
|
||||||
: ""}"
|
: ""}"
|
||||||
data-action="click:app-pagination#pageBack"
|
app-action="click:app-pagination#pageBack"
|
||||||
>
|
>
|
||||||
Prev
|
Prev
|
||||||
</button>
|
</button>
|
||||||
@@ -132,7 +132,7 @@ class AppPaginationElement extends BaseComponentElement {
|
|||||||
pageRange
|
pageRange
|
||||||
? "disabled"
|
? "disabled"
|
||||||
: ""}"
|
: ""}"
|
||||||
data-action="click:app-pagination#pageNext"
|
app-action="click:app-pagination#pageNext"
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ class InputFieldElement extends BaseComponentElement {
|
|||||||
return this.rules.includes("required");
|
return this.rules.includes("required");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get _value() {
|
||||||
|
return (this.inp as HTMLInputElement)?.value;
|
||||||
|
}
|
||||||
|
|
||||||
validate = (): boolean => {
|
validate = (): boolean => {
|
||||||
let _return = true;
|
let _return = true;
|
||||||
const rules = this.rules?.split("|").filter((a) => a);
|
const rules = this.rules?.split("|").filter((a) => a);
|
||||||
@@ -104,7 +108,7 @@ class InputFieldElement extends BaseComponentElement {
|
|||||||
return html` <input
|
return html` <input
|
||||||
type="${this.type}"
|
type="${this.type}"
|
||||||
data-target="input-field.inp"
|
data-target="input-field.inp"
|
||||||
data-action="
|
app-action="
|
||||||
input:input-field#inputChange
|
input:input-field#inputChange
|
||||||
blur:input-field#validateDisplay
|
blur:input-field#validateDisplay
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class MenuItemElement extends BaseComponentElement {
|
|||||||
${this.customaction
|
${this.customaction
|
||||||
? html`<div
|
? html`<div
|
||||||
data-target="menu-item.customButton"
|
data-target="menu-item.customButton"
|
||||||
data-action="${this.customaction}"
|
app-action="${this.customaction}"
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</div>`
|
</div>`
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class HomePageElement extends BasePageElement {
|
|||||||
|
|
||||||
render = (): TemplateResult => {
|
render = (): TemplateResult => {
|
||||||
return html`
|
return html`
|
||||||
<button data-action="click:home-page#openModal">New Wallet</button>
|
<button app-action="click:home-page#openModal">New Wallet</button>
|
||||||
<wallet-header
|
<wallet-header
|
||||||
data-target="home-page.walletHeader"
|
data-target="home-page.walletHeader"
|
||||||
data-current-balance="0"
|
data-current-balance="0"
|
||||||
|
|||||||
107
src/styles/app-dropdown/app-dropdown.scss
Normal file
107
src/styles/app-dropdown/app-dropdown.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/styles/app-dropdown/index.scss
Normal file
1
src/styles/app-dropdown/index.scss
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@import "./app-dropdown.scss";
|
||||||
@@ -8,3 +8,4 @@
|
|||||||
@import "./page/index.scss";
|
@import "./page/index.scss";
|
||||||
@import "./app-form/index.scss";
|
@import "./app-form/index.scss";
|
||||||
@import "./layout/index.scss";
|
@import "./layout/index.scss";
|
||||||
|
@import "./app-dropdown/index.scss";
|
||||||
|
|||||||
Reference in New Issue
Block a user