oswilno/assets/js/app.js

706 lines
21 KiB
JavaScript
Raw Normal View History

2022-07-04 08:31:12 +02:00
const S = Symbol();
2022-07-04 16:00:17 +02:00
customElements.define('ow-nav', class extends HTMLElement {
constructor() {
super();
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
shadow.innerHTML = `
<style>
:host { display: block; }
* { font-family: 'Noto Sans', sans-serif; }
section {
display: flex;
align-items: stretch;
align-content: stretch;
margin-bottom: 12px;
color: #2b2727;
font-family: Raleway, sans-serif;
font-size: 14px;
line-height: 32px;
}
section::after {
display: block;
content: ' ';
border: none;
padding: 10px 18px;
text-decoration: none;
color: #2b2727;
text-transform: uppercase;
align-self: stretch;
flex-shrink: 20;
flex-grow: 20;
}
</style>
<section>
<slot></slot>
</section>
`;
}
});
customElements.define('ow-path', class extends HTMLElement {
static get observedAttributes() {
return ['selected', 'path'];
}
constructor() {
super();
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
shadow.innerHTML = `
<style>
:host { display: block; }
* { font-family: 'Noto Sans', sans-serif; }
a {
display: block;
padding: 10px 18px;
text-decoration: none;
color: #495057;
text-transform: uppercase;
border: none;
border-bottom: 1px solid var(--border-slim-color);
}
a:hover {
color: var(--hover-color);
}
:host(:not([selected])) a {
border: none;
}
</style>
<a><slot></slot></a>
`;
}
connectedCallback() {
this.selected = this.getAttribute('selected');
}
attributeChangedCallback(name, oldV, newV) {
if (oldV === newV) return;
switch (name) {
case 'selected':
return this.selected = newV;
case 'path':
return this.path = newV;
}
}
get selected() {
return this.getAttribute('selected') === 'selected';
}
set selected(value) {
if (value === 'selected') this.setAttribute('selected', 'selected');
else this.removeAttribute('selected');
}
get path() {
return this.getAttribute('path') || ''
}
set path(value) {
if (!value || value === '') {
this.removeAttribute('path');
return;
}
this.setAttribute('path', value);
this[S].querySelector('a').setAttribute('href', value);
}
});
2022-07-04 08:31:12 +02:00
customElements.define('local-services', class extends HTMLElement {
2022-07-04 16:00:17 +02:00
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;
}
input {
font-size: 20pt;
line-height: 2.6em;
height: 2.6em;
margin: 0;
padding: 0;
width: 100%;
border:none;
outline:none;
display: block;
background: transparent;
border-bottom: 1px solid #ccc;
text-indent: 20px;
}
</style>
<section>
<input type="text" id="filter" placeholder="Filtruj" />
</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();
const value = ev.target.value;
if (t) clearTimeout(t);
t = setTimeout(() => {
this.filter = 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) {
if (!value || value === '') {
this.removeAttribute('filter');
for (const el of this.querySelectorAll('local-service')) {
el.removeAttribute('local-services-visible');
}
} else {
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');
}
}
}
}
2022-07-04 08:31:12 +02:00
});
customElements.define('local-service', class extends HTMLElement {
2022-07-04 16:00:17 +02:00
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; }
#items {
margin-top: 16px;
}
</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) {
if (oldV === newV) return;
switch (name) {
case 'name':
return this[S].querySelector('#name').textContent = newV;
}
}
get name() {
return this.getAttribute('name') || ''
}
2022-07-04 08:31:12 +02:00
});
customElements.define('local-service-item', class extends HTMLElement {
2022-07-04 16:00:17 +02:00
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) {
if (oldV === newV) return;
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;
}
2022-07-04 08:31:12 +02:00
});
customElements.define('ow-price', class extends HTMLElement {
2022-07-04 16:00:17 +02:00
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) {
if (oldV === newV) return;
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'
}
});
customElements.define('ow-account', class extends HTMLElement {
static get observedAttributes() {
return ['mode']
}
constructor() {
super();
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
shadow.innerHTML = `
<style>
:host { display: block; }
* { font-family: 'Noto Sans', sans-serif; }
article > * {
display: none;
}
:host([mode="login"]) login-form, :host([mode="login"]) #switch-register {
display: block;
}
:host([mode="register"]) register-form, :host([mode="register"]) #switch-login {
display: block;
}
a{
display: block;
cursor: pointer;
}
a:hover {
color: var(--hover-color);
border-bottom-color: var(--hover-color);
text-decoration: none;
}
</style>
<article>
<login-form></login-form>
<register-form></register-form>
<section id="switch-login">
<a>Nie masz konta? Utwórz nowe</a>
</section>
<section id="switch-register">
<a>Masz konta? Zaloguj się</a>
</section>
</article>
`;
shadow.querySelector('#switch-login > a').addEventListener('click', ev => {
ev.stopPropagation();
ev.preventDefault();
this.mode = 'login';
});
shadow.querySelector('#switch-register > a').addEventListener('click', ev => {
ev.stopPropagation();
ev.preventDefault();
this.mode = 'register';
});
}
connectedCallback() {
this.mode = 'login';
}
attributeChangedCallback(name, oldV, newV) {
if (oldV === newV) return;
switch (name) {
}
}
get mode() {
return this.getAttribute('mode');
}
set mode(value) {
value = value === 'login' || value === 'register' ? value : 'login';
this.setAttribute('mode', value);
}
});
const FORM_STYLE = `
form {
display: block;
}
form > div {
display: block;
margin-bottom: 1rem;
}
form > div > input, form > div > textarea {
font-size: 16px;
border: none;
border-bottom-style: none;
border-bottom-width: medium;
border-bottom: 1px solid rgba(0,0,0,.1);
border-radius: 2px;
padding: 0;
height: 36px;
background: #fff;
color: rgba(0,0,0,.8);
font-size: 14px;
box-shadow: none !important;
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: .375rem .75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-clip: padding-box;
transition: border-color .15s ease-in-out , -webkit-box-shadow .15s ease-in-out;
transition: border-color .15s ease-in-out , box-shadow .15s ease-in-out;
transition: border-color .15s ease-in-out , box-shadow .15s ease-in-out , -webkit-box-shadow .15s ease-in-out;
}
form > div > input[type="text"],
form > div > input[type="number"],
form > div > input[type="email"],
form > div > input[type="password"],
form > div > textarea {
width: calc(100% - 1.5rem - 2px);
}
form > div > label {
color: #000;
text-transform: uppercase;
font-size: 12px;
font-weight: 600;
display: inline-block;
margin-bottom: .5rem;
}
form > div > input[type="button"], form > div > input[type="submit"] {
padding: 12px 16px;
cursor: pointer;
border: none;
border-width: 1px;
border-radius: 5px;
font-size: 14px;
font-weight: 400;
box-shadow: 0 10px 20px -6px rgba(0,0,0,.12);
position: relative;
margin-bottom: 20px;
transition: .3s;
background: #46b5d1;
color: #fff;
display: inline-block;
font-weight: 400;
text-align: center;
vertical-align: middle;
user-select: none;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
transition: color .15s ease-in-out,
background-color .15s ease-in-out,
border-color .15s ease-in-out,
box-shadow .15s ease-in-out,
-webkit-box-shadow .15s ease-in-out;
width: auto;
height: calc(1.5em + 0.75rem + 2px);
padding: .375rem .75rem;
}
`;
customElements.define('register-form', class extends HTMLElement {
static get observedAttributes() {
return ['step']
}
constructor() {
super();
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
shadow.innerHTML = `
<style>
:host { display: block; }
* { font-family: 'Noto Sans', sans-serif; }
article > form { display: none; }
:host([step="1"]) #step-1 { display: block; }
:host([step="2"]) #step-2 { display: block; }
:host([step="3"]) #step-3 { display: block; }
${ FORM_STYLE }
.actions {
display: flex;
justify-content: space-between;
}
.actions > input:first-child {
margin-right: 8px;
}
.actions > input:last-child {
margin-left: 8px;
}
</style>
<article>
<form id="step-1">
<div>
<label>Login</label>
<input id="login" name="login" placeholder="Login" type="text" required />
</div>
<div>
<label>E-Mail</label>
<input name="email" placeholder="Email" type="email" required />
</div>
<div>
<label>Hasło</label>
<input name="pass" placeholder="Hasło" type="password" required />
</div>
<div>
<input type="submit" value="Następny" />
</div>
</form>
<form id="step-2">
<div>
<input name="name" placeholder="Nazwa usługi" type="text" required />
</div>
<div>
<label>description</label>
<textarea name="description" required></textarea>
</div>
<div class="actions">
<input type="button" value="Wróć" />
<input type="submit" value="Następny" />
</div>
</form>
<form id="step-3">
<input id="hidden-login" name="login" type="hidden" />
<input id="hidden-email" name="email" type="hidden" />
<input id="hidden-pass" name="pass" type="hidden" />
<input id="hidden-name" name="name" type="hidden" />
<div class="actions">
<input type="button" value="Wróć" />
<input type="submit" value="Dodaj usługi/produkty" />
</div>
</form>
</article>
`;
const copyForm = (form) => {
for (const el of form.elements) {
if (el.name === '') continue;
finalForm.querySelector(`[id="hidden-${el.name}"]`).value = el.value;
}
};
const finalForm = shadow.querySelector('#step-3');
{
const el = shadow.querySelector('#step-1');
el.addEventListener('submit', ev => {
ev.preventDefault();
ev.stopPropagation();
copyForm(el);
this.step = 2;
});
}
{
const el = shadow.querySelector('#step-2');
el.addEventListener('submit', ev => {
ev.preventDefault();
ev.stopPropagation();
copyForm(el);
this.step = 3;
});
el.querySelector('.actions > input[type="button"]').addEventListener('click', ev => {
ev.preventDefault();
ev.stopPropagation();
this.step = 1;
});
}
{
const el = finalForm;
el.addEventListener('submit', ev => {
ev.preventDefault();
ev.stopPropagation();
copyForm(el);
let content = [
...shadow.querySelector('#step-1').elements,
...shadow.querySelector('#step-2').elements,
...shadow.querySelector('#step-3').elements,
]
.filter(el => el && el.name !== '')
.reduce((mem, el) => ({ ...mem, [el.name]: el.value }), {});
console.info(content);
});
el.querySelector('.actions > input[type="button"]').addEventListener('click', ev => {
ev.preventDefault();
ev.stopPropagation();
this.step = 2;
});
}
}
connectedCallback() {
this.step = 1;
}
attributeChangedCallback(name, oldV, newV) {
if (oldV === newV) return;
switch (name) {
}
}
get step() {
const step = parseInt(this.getAttribute('step'));
return isNaN(step) ? 1 : step;
}
set step(n) {
this.setAttribute('step', n);
}
});
customElements.define('login-form', class extends HTMLElement {
static get observedAttributes() {
return []
}
constructor() {
super();
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
shadow.innerHTML = `
<style>
:host { display: block; }
* { font-family: 'Noto Sans', sans-serif; }
${ FORM_STYLE }
</style>
<form>
<div>
<label>Login</label>
<input name="login" placeholder="Login" type="text" required />
</div>
<div>
<label>Hasło</label>
<input name="pass" placeholder="Hasło" type="password" required />
</div>
<div>
<input type="button" value="Zaloguj" />
</div>
</form>
`;
}
connectedCallback() {
}
attributeChangedCallback(name, oldV, newV) {
if (oldV === newV) return;
switch (name) {
}
}
2022-07-04 08:31:12 +02:00
});