@kdsoft/lit-mvvm-components
Version:
Webcomponents based on @kdsoft-lit-mvvm
254 lines (228 loc) • 7.14 kB
JavaScript
import { html, css, nothing } from '@kdsoft/lit-mvvm';
import KdsNavLayout from './kds-nav-layout.js';
export default class KdsCarousel extends KdsNavLayout {
_scrollToActiveItem(itemsControl, activeIndex) {
if (this.vertical) {
const scrollPoint = itemsControl.clientHeight * activeIndex;
itemsControl.scroll({ top: scrollPoint, behavior: 'smooth' });
} else {
const scrollPoint = itemsControl.clientWidth * activeIndex;
itemsControl.scroll({ left: scrollPoint, behavior: 'smooth' });
}
}
_itemsKeyDown(e) {
switch (e.key) {
case 'ArrowDown':
case 'ArrowRight': {
this.model.incrementActiveIndex();
break;
}
case 'ArrowUp':
case 'ArrowLeft': {
this.model.decrementActiveIndex();
break;
}
default:
// ignore, let bubble up
return;
}
e.preventDefault();
}
carouselClickDown(e) {
e.preventDefault();
e.stopImmediatePropagation();
this.model.decrementActiveIndex();
}
carouselClickUp(e) {
e.preventDefault();
e.stopImmediatePropagation();
this.model.incrementActiveIndex();
}
shouldRender() {
return !!this.model;
}
static get styles() {
return [
...super.styles,
css`
:host {
height: var(--height);
width: var(--width);
}
#container > div {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 3rem;
padding: 0;
/* opacity:0.3; */
}
#container.vertical > div {
flex-direction: column;
width: 100%;
height: 3rem;
}
#container .angle {
position: relative;
display: flex;
height: 100%;
width: 100%;
padding: 0.3rem;
}
#container .angle > svg {
display: none;
fill: gray;
fill-opacity: 0.3;
stroke-width: 2;
stroke: white;
width: 100%;
height: 50%;
margin: auto;
}
#container.vertical .angle > svg {
width: 50%;
height: 100%;
}
#container > div:hover svg {
display: unset;
}
#container div.end-item {
display:none;
}
`,
];
}
/* eslint-disable indent */
get header() {
const cm = this.model;
const indx = cm.activeIndex;
const firstAngleClass = indx <= 0 ? 'end-item' : '';
return this.vertical
? html`
<div class="angle ${firstAngleClass}">
<svg @click=${this.carouselClickDown}>
<use href="#angle-top"></use>
</svg>
</div>`
: nothing;
}
get left() {
const cm = this.model;
const indx = cm.activeIndex;
const firstAngleClass = indx <= 0 ? 'end-item' : '';
return this.vertical
? nothing
: html`
<div class="angle ${firstAngleClass}">
<svg @click=${this.carouselClickDown}>
<use href="#angle-left"></use>
</svg>
</div>`;
}
get footer() {
const cm = this.model;
const len = cm.items.length || 0;
const indx = cm.activeIndex;
const lastAngleClass = indx >= (len - 1) ? 'end-item' : '';
return this.vertical
? html`
<div class="angle ${lastAngleClass}">
<svg @click=${this.carouselClickUp}>
<use href="#angle-bottom"></use>
</svg>
</div>`
: nothing;
}
get right() {
const cm = this.model;
const len = cm.items.length || 0;
const indx = cm.activeIndex;
const lastAngleClass = indx >= (len - 1) ? 'end-item' : '';
return this.vertical
? nothing
: html`
<div class="angle ${lastAngleClass}">
<svg @click=${this.carouselClickUp}>
<use href="#angle-right"></use>
</svg>
</div>`;
}
render() {
return html`
<style>
:host {
--height: ${this.vertical ? 'var(--itemWidth, 600px)' : 'var(--itemHeight, 300px)'};
--width: ${this.vertical ? 'var(--itemHeight, 300px)' : 'var(--itemWidth, 600px)'};
}
</style>
<svg style="display:none" version="1.1"
<defs>
<symbol id="angle-left"
viewBox="0 0 69.773 122.88"
preserveAspectRatio="none"
enable-background="new 0 0 69.773 122.88"
xml:space="preserve">
<g>
<polygon points="69.773,0 49.771,0 0,61.44 49.771,122.88 69.773,122.88 20,61.44 69.773,0"/>
</g>
</symbol>
<symbol id="angle-right"
viewBox="0 0 69.773 122.88"
preserveAspectRatio="none"
enable-background="new 0 0 69.773 122.88"
xml:space="preserve">
<g>
<polygon points="0,0 20,0 69.773,61.44 20,122.88 0,122.88 49.772,61.44 0,0"/>
</g>
</symbol>
<symbol id="angle-top"
viewBox="0 0 122.88 69.773"
preserveAspectRatio="none"
enable-background="new 0 0 122.88 69.773"
xml:space="preserve">
<g>
<polygon points="122.88,69.773 122.88,49.772 61.44,0 0,49.772 0,69.773 61.44,20 122.88,69.773"/>
</g>
</symbol>
<symbol id="angle-bottom"
viewBox="0 0 122.88 69.773"
preserveAspectRatio="none"
enable-background="new 0 0 122.88 69.773"
xml:space="preserve">
<g>
<polygon points="122.88,0 122.88,20 61.44,69.773 0,20 0,0 61.44,49.772 122.88,0"/>
</g>
</symbol>
</defs>
</svg>
${super.render()}
`;
}
rendered() {
// reading observable properties here will still register them for the next render()
const activeIndex = this.model.activeIndex;
window.setTimeout(() => {
const itemsControl = this.renderRoot.getElementById('items');
if (itemsControl) {
this._scrollToActiveItem(itemsControl, activeIndex);
}
}, 5);
// this.schedule(() => {
// const itemsControl = this.renderRoot.getElementById('items');
// if (itemsControl) {
// this._scrollToActiveItem(itemsControl, activeIndex);
// }
// });
// requestAnimationFrame(() => {
// const itemsControl = this.renderRoot.getElementById('items');
// if (itemsControl) {
// this._scrollToActiveItem(itemsControl, activeIndex);
// }
// });
// seems that setTimout with at least 5ms is more reliable when the debugger is running;
// looks like one of the reasons is that it does not use the microtask queue
}
}
window.customElements.define('kds-carousel', KdsCarousel);