monphind-ui
Version:
A reactive component library built on top of the Web Components API
322 lines (321 loc) • 10 kB
JavaScript
import { useElement } from "./core/element";
import { select } from "./core/select-elememt";
const name = "m-picker";
const style = `:host {
display: inline-flex;
vertical-align: middle;
position: relative;
}
:host .view {
height: 25px;
width: inherit;
min-width: 100px;
padding: 5px 13px;
background-color: var(--m-picker-default-view-backgroundColor,${"white"});
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 15px;
position: relative;
transition: all 0.3s;
outline: 2px solid transparent;
}
:host .view .label {
color: var(--m-picker-default-label-color,${"rgb(178,178,178)"});
position: absolute;
font-size: 12px;
pointer-events: none;
height: -moz-fit-content;
height: fit-content;
width: -moz-fit-content;
width: fit-content;
transition: all 0.3s;
top: 0;
bottom: 0;
margin: auto;
max-width: calc(100% - 26px - 20px);
overflow-x: hidden;
white-space: nowrap;
font-weight: 300;
}
:host .view .text {
color: var(--m-picker-default-text-color,${"black"});
font-size: 13px;
}
:host .view .icon {
width: 20px;
height: 20px;
color: var(--m-picker-default-icon-color,${"black"});
transition: transform 0.2s;
margin-left: 20px;
}
:host .view .icon svg {
stroke: currentColor;
width: inherit;
height: inherit;
fill: currentColor;
}
:host .view[has_text=true] .label {
top: -100%;
bottom: 100%;
margin: auto;
}
:host .options {
position: absolute;
border-radius: 10px;
box-shadow: 0px 4px 20px var(--m-picker-default-options-boxShadow-color,${"rgba(89,89,89,0.122)"});
background-color: var(--m-picker-default-options-backgroundColor,${"white"});
overflow-y: scroll;
opacity: 0;
transform: scale(0.98);
pointer-events: none;
transition: all 0.3s;
max-height: 50vh;
z-index: 1;
transform: scaleY(0%);
transform-origin: top;
}
:host .options::-webkit-scrollbar{
display: none;
}
:host .options:not([direction=north]) {
top: calc(100% + 10px);
transform-origin: top;
}
:host .options[direction=north] {
bottom: calc(100% + 30px);
transform-origin: bottom;
}
:host .options .picker-item,
::slotted(m-picker-item) {
white-space: nowrap;
width: 100%;
padding: 10px 20px;
font-size: 13px;
box-sizing: border-box;
background-color: var(--m-picker-item-backgroundColor,${"white"});
color: var(--m-picker-item-color,${"black"});
transition: all 0.1s;
}
:host .options .picker-item:not([disabled=true]):hover,
::slotted(m-picker-item):hover {
filter: brightness(90%);
}
:host .options .picker-item[selected=true],
::slotted(m-picker-item[selected=true]) {
background-color: var(--m-picker-item-selected-backgroundColor,${"#2EA2F9"});
color: var(--m-picker-item-selected-color,${"white"});
}
:host .options .picker-item[disabled=true],
::slotted(m-picker-item[disabled=true]) {
background-color: var(--m-picker-item-disabled-backgroundColor,${"white"});
color: var(--m-picker-item-disabled-color,${"gray"});
pointer-events: none;
}
:host([disabled=true]) {
pointer-events: none;
}
:host([disabled=true]) .view {
background-color: var(--m-picker-disabled-view-backgroundColor,${"#EFEFEF"});
}
:host([disabled=true]) .view .text {
color: var(--m-picker-disabled-view-color,${"#BABABA"});
}
:host(:not(:host([disabled=true]))[focused=true]) .view {
outline: 2px solid var(--m-picker-focused-outline-color,${"#2ea1f9d6"});
}
:host(:not(:host([disabled=true]))[focused=true]) .view .label {
color: var(--m-picker-focused-label-color,${"#2EA2F9"});
top: -100%;
bottom: 100%;
margin: auto;
}
:host(:not(:host([disabled=true]))[focused=true]) .view .icon {
transform: rotate(-180deg);
}
:host(:not(:host([disabled=true]))[focused=true]) .options {
pointer-events: initial;
transform: scale(1);
opacity: 1;
transform: scaleY(100%);
}`;
const template = `<div class="view">
<div class="label"></div>
<div class="text"></div>
<div class="icon">
<svg viewBox="0 -960 960 960">
<path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z"></path>
</svg>
</div>
</div>
<div class="options">
<slot></slot>
</div>`;
const props = {
disabled: false,
label: "",
value: "",
};
export class Picker extends useElement({
name,
style,
template,
props,
syncProps: ["disabled", "label", "value"],
dispatch: {
propChanged(key, value) {
const view = this.shadowRoot?.querySelector(".view");
const text = this.shadowRoot?.querySelector(".view .text");
switch (key) {
case "value": {
const selectedElement = select(this, "m-picker-item", {
value: value,
});
const allItems = select(this, "m-picker-item", {
selected: true
});
if (selectedElement.length) {
allItems.forEach(i => i.selected = false);
text.textContent = selectedElement[0].textContent;
if (text.textContent != '')
view.setAttribute("has_text", "true");
else
view.removeAttribute('has_text');
selectedElement[0].selected = true;
}
break;
}
}
},
connected() {
const label = this.label;
const view = this.shadowRoot?.querySelector(".view");
const labelArea = view.querySelector(".label");
labelArea.textContent = label;
const items = this?.querySelectorAll("m-picker-item");
items.forEach(i => {
i.addEventListener("click", () => {
if (this.value != i.value) {
const selectedItems = select(this, "m-picker-item", {
selected: true
});
selectedItems.forEach(i => i.selected = false);
this.value = i.value;
this.dispatchEvent(new Event('change'));
}
});
});
setTimeout(() => {
const defaultValue = this.value;
if (defaultValue) {
const defaultSelected = select(this, "m-picker-item", {
value: defaultValue
});
if (defaultSelected)
defaultSelected[0].selected = true;
const text = this.shadowRoot?.querySelector(".view .text");
text.textContent = defaultSelected[0].textContent;
if (text.textContent != '')
view.setAttribute("has_text", "true");
else
view.removeAttribute('has_text');
}
});
}
},
setup(shadowRoot) {
const view = shadowRoot.querySelector(".view");
const options = shadowRoot.querySelector(".options");
const computeDirection = () => {
const direction = {
top: view.offsetTop,
bottom: window.innerHeight - view.getBoundingClientRect().top - view.getBoundingClientRect().height
};
if (options.offsetHeight >= direction.bottom) {
options.setAttribute("direction", "north");
}
else {
options.setAttribute("direction", "sourth");
}
};
computeDirection();
view.addEventListener("click", () => {
const status = this.getAttribute("focused") || "false";
this.setAttribute("focused", status === 'false' ? "true" : "false");
computeDirection();
});
this.parentElement?.addEventListener("scroll", computeDirection);
let is_mouseover = false;
this.addEventListener("mouseover", () => {
is_mouseover = true;
});
this.addEventListener("mouseleave", () => {
is_mouseover = false;
});
window.addEventListener("click", () => {
if (!is_mouseover) {
this.setAttribute("focused", "false");
}
});
return {};
}
}) {
}
Picker.defineElement();
import 'vue';
const itemName = 'm-picker-item';
const itemTemplate = `<slot></slot>`;
const itemStyle = `:host {
white-space: nowrap;
width: 100%;
padding: 10px 20px;
font-size: 13px;
box-sizing: border-box;
background-color: var(--m-picker-item-backgroundColor,${"white"});
color: var(--m-picker-item-color,${"black"});
transition: all 0.1s;
display: block;
}
:host(:hover) {
filter: brightness(90%);
}
:host([selected=true]) {
background-color: var(--m-picker-item-selected-backgroundColor,${"#2EA2F9"});
color: var(--m-picker-item-selected-color,${"white"});
}
:host([disabled=true]) {
background-color: var(--m-picker-item-disabled-backgroundColor,${"white"});
color: var(--m-picker-item-disabled-color,${"gray"});
pointer-events: none;
}`;
const itemProps = {
disabled: false,
selected: false,
value: ""
};
export class PickerItem extends useElement({
name: itemName,
template: itemTemplate,
style: itemStyle,
props: itemProps,
syncProps: ["disabled", "selected"],
dispatch: {
propChanged(key, value) {
switch (key) {
case "selected": {
if (value == 'true' || value == true) {
const picker = this.parentElement;
picker.value = this.value;
}
break;
}
}
}
},
setup() {
return {};
}
}) {
}
PickerItem.defineElement();
import 'vue';