208 lines
4.5 KiB
JavaScript
208 lines
4.5 KiB
JavaScript
|
const S = Symbol();
|
||
|
|
||
|
customElements.define('local-services', class extends HTMLElement {
|
||
|
static get observedAttributes() {
|
||
|
return ['filter']
|
||
|
}
|
||
|
constructor() {
|
||
|
super();
|
||
|
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||
|
shadow.innerHTML = `
|
||
|
<style>
|
||
|
:host { display: block; }
|
||
|
* { font-family: 'Noto Sans', sans-serif; }
|
||
|
::slotted(local-service[local-services-visible="invisible"]) {
|
||
|
display: none;
|
||
|
}
|
||
|
</style>
|
||
|
<section>
|
||
|
<input type="text" id="filter" />
|
||
|
</section>
|
||
|
<section id="items">
|
||
|
<slot name="services"></slot>
|
||
|
</section>
|
||
|
`;
|
||
|
{
|
||
|
const filter = shadow.querySelector('#filter');
|
||
|
let t = null;
|
||
|
filter.addEventListener('change', ev => {
|
||
|
ev.stopPropagation();
|
||
|
this.filter = ev.target.value;
|
||
|
});
|
||
|
filter.addEventListener('keyup', ev => {
|
||
|
ev.stopPropagation();
|
||
|
if (t) clearTimeout(t);
|
||
|
t = setTimeout(() => {
|
||
|
this.filter = ev.target.value;
|
||
|
t = null;
|
||
|
}, 1000 / 3);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
connectedCallback() {
|
||
|
this.filter = this.getAttribute('filter');
|
||
|
}
|
||
|
|
||
|
attributeChangedCallback(name, oldV, newV) {
|
||
|
if (oldV == newV) return;
|
||
|
switch (name) {
|
||
|
case 'filter': return this.filter = newV;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get filter() {
|
||
|
return this.getAttribute('filter');
|
||
|
}
|
||
|
|
||
|
set filter(value) {
|
||
|
this.setAttribute('filter', value);
|
||
|
for (const el of this.querySelectorAll('local-service')) {
|
||
|
if (!el.name) continue;
|
||
|
if (el.name.includes(value)) {
|
||
|
el.setAttribute('local-services-visible', 'visible');
|
||
|
} else {
|
||
|
el.setAttribute('local-services-visible', 'invisible');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
customElements.define('local-service', class extends HTMLElement {
|
||
|
static get observedAttributes() {
|
||
|
return ['name', 'service-id', 'state']
|
||
|
}
|
||
|
constructor() {
|
||
|
super();
|
||
|
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||
|
shadow.innerHTML = `
|
||
|
<style>
|
||
|
:host { display: block; }
|
||
|
* { font-family: 'Noto Sans', sans-serif; }
|
||
|
</style>
|
||
|
<h2 id="name"></h2>
|
||
|
<slot name="description"></slot>
|
||
|
<section id="items">
|
||
|
<slot name="item"></slot>
|
||
|
</section>
|
||
|
`;
|
||
|
}
|
||
|
|
||
|
connectedCallback() {
|
||
|
this[S].querySelector('#name').textContent = this.getAttribute('name');
|
||
|
}
|
||
|
|
||
|
attributeChangedCallback(name, oldV, newV) {
|
||
|
switch (name) {
|
||
|
case 'name': return this[S].querySelector('#name').textContent = newV;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get name() {
|
||
|
return this.getAttribute('name') || ''
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
customElements.define('local-service-item', class extends HTMLElement {
|
||
|
static get observedAttributes() {
|
||
|
return ['name', 'price']
|
||
|
}
|
||
|
constructor() {
|
||
|
super();
|
||
|
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||
|
shadow.innerHTML = `
|
||
|
<style>
|
||
|
:host { display: block; }
|
||
|
* { font-family: 'Noto Sans', sans-serif; }
|
||
|
#item {
|
||
|
display: flex;
|
||
|
justify-content: space-between;
|
||
|
}
|
||
|
h3 {
|
||
|
font-weight: normal;
|
||
|
}
|
||
|
#price {
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
</style>
|
||
|
<section id="item">
|
||
|
<h3 id="name"></h3>
|
||
|
<ow-price id="price" />
|
||
|
</section>
|
||
|
`;
|
||
|
}
|
||
|
|
||
|
connectedCallback() {
|
||
|
this[S].querySelector('#name').textContent = this.getAttribute('name');
|
||
|
this[S].querySelector('#price').value = this.price();
|
||
|
}
|
||
|
|
||
|
attributeChangedCallback(name, oldV, newV) {
|
||
|
switch (name) {
|
||
|
case 'name': return this[S].querySelector('#name').textContent = newV;
|
||
|
case 'price': return this[S].querySelector('#price').value = newV;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
price(s) {
|
||
|
const n = parseInt(s || this.getAttribute('price'));
|
||
|
return isNaN(n) ? 0 : n;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
customElements.define('ow-price', class extends HTMLElement {
|
||
|
static get observedAttributes() {
|
||
|
return ['value', 'currency']
|
||
|
}
|
||
|
constructor() {
|
||
|
super();
|
||
|
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||
|
shadow.innerHTML = `
|
||
|
<style>
|
||
|
:host { display: block; }
|
||
|
* { font-family: 'Noto Sans', sans-serif; }
|
||
|
#price {
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
</style>
|
||
|
<span id="price"></span>
|
||
|
`;
|
||
|
}
|
||
|
|
||
|
connectedCallback() {
|
||
|
this[S].querySelector('#price').textContent = this.formatted;
|
||
|
}
|
||
|
|
||
|
attributeChangedCallback(name, oldV, newV) {
|
||
|
switch (name) {
|
||
|
case 'price': {
|
||
|
this.value = newV;
|
||
|
this[S].querySelector('#price').textContent = this.formatted;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get formatted() {
|
||
|
let v = this.value;
|
||
|
let major = Math.ceil(v / 100);
|
||
|
let minor = v % 100;
|
||
|
let formatted = `${major},${ minor < 10 ? `0${minor}` : minor }`;
|
||
|
return `${formatted}${this.currency}`
|
||
|
}
|
||
|
|
||
|
get value() {
|
||
|
const n = parseInt(this.getAttribute('value'));
|
||
|
return isNaN(n) ? 0 : n;
|
||
|
}
|
||
|
|
||
|
set value(v) {
|
||
|
this.setAttribute('value', v)
|
||
|
}
|
||
|
|
||
|
get currency() {
|
||
|
return this.getAttribute('currency') || 'PLN'
|
||
|
}
|
||
|
});
|