globular-mvc
Version:
Generic template to create web-application that made use of globular as backend and materialize as css (wrap in web-component's)
433 lines (355 loc) • 13.2 kB
JavaScript
import { ImageViewer } from './Image'
import { ApplicationView } from '../ApplicationView'
/**
* Image galery component
*/
export class ImageGallery extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor() {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
outline: none;
box-sizing: border-box;
}
.container {
margin: 0 auto;
max-width: 700px;
max-height: 100vh;
background-color: white;
}
/* Useful Classes */
.xy-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.transition {
transition: all 350ms ease-in-out;
}
.r-3-2 {
width: 100%;
padding-bottom: 66.667%;
background-color: #ddd;
/*background-color: radial-gradient(circle, rgb(170 170 170 / 0%) 0%, rgb(66 66 66 / 0%) 44%, var(--palette-background-default) 100%);*/
}
.image-holder {
background-position: center center;
background-repeat: no-repeat;
background-size: auto 100%;
}
/* Main Styles */
.gallery-wrapper {
position: relative;
overflow: hidden;
}
.gallery {
position: relative;
white-space: nowrap;
font-size: 0;
}
.item-wrapper {
cursor: pointer;
width: 23%; /* arbitrary value */
display: inline-block;
background-color: white;
}
.gallery-item { opacity: 0.5; }
.gallery-item.active { opacity: 1; }
.controls {
font-size: 0;
border-top: none;
}
.move-btn {
display: inline-block;
width: 50%;
border: none;
color: #ccc;
background-color: transparent;
padding: 0.2em 1.5em;
}
.move-btn:first-child {border-right: none;}
.move-btn.left { cursor: w-resize; }
.move-btn.right { cursor: e-resize; }
#leftA {
font-size:16px;
background-color: #3e3c3c99;
color:white;
text-align: left;
}
#rightA {
font-size:16px;
background-color: #3e3c3c99;
color:white;
text-align: right;
}
.feature{
position: relative;
}
paper-icon-button {
position: absolute;
top: 0px;
left: 0px;
background-color: black;
height: 25px;
width: 25px;
}
globular-image-viewer {
position: fixed;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
}
#close-btn{
position: absolute;
top: 0px;
left: 0px;
background-color: black;
--paper-icon-button-ink-color: white;
--iron-icon-fill-color: white;
border-bottom: 1px solid var(--palette-divider);
border-right: 1px solid var(--palette-divider);
}
</style>
<div class="container">
<div class="feature">
<figure class="featured-item image-holder r-3-2 transition"></figure>
<paper-icon-button id="close-btn" style="display: none;" icon="icons:close"></paper-icon-button>
</div>
<div class="gallery-wrapper">
<div class="gallery">
</div>
</div>
<div class="controls">
<div id='leftA' class="move-btn left" >❮</div>
<div id='rightA' class="move-btn right" >❯</div>
</div>
</div>
`
// give the focus to the input.
this.gallery = this.shadowRoot.querySelector('.gallery');
this.itemWidth = 23; // percent: as set in css
this.leftBtn = this.shadowRoot.querySelector('.move-btn.left');
this.rightBtn = this.shadowRoot.querySelector('.move-btn.right');
// old the interval...
this.leftInterval;
this.rightInterval;
// scroll speed...
this.scrollRate = 0.2;
this.left;
this.images = []
// connect event listener's
this.leftBtn.ontouchstart = this.leftBtn.onmouseenter = e => this.moveLeft(e);
this.leftBtn.ontouchend = this.leftBtn.onmouseleave = e => this.stopMovement(e);
this.rightBtn.ontouchstart = this.rightBtn.onmouseenter = e => this.moveRight(e);
this.rightBtn.ontouchend = this.rightBtn.onmouseleave = e => this.stopMovement(e);
this.closeBtn = this.shadowRoot.querySelector("#close-btn")
this.closeBtn.onclick = () => {
const url = new URL(this.featured().image.src);
// Here I will ask the user for confirmation before actually delete the contact informations.
let toast = ApplicationView.displayMessage(
`
<style>
#yes-no-picture-delete-box{
display: flex;
flex-direction: column;
}
#yes-no-picture-delete-box globular-picture-card{
padding-bottom: 10px;
}
#yes-no-picture-delete-box div{
display: flex;
padding-bottom: 10px;
}
</style>
<div id="yes-no-picture-delete-box">
<div>Your about to remove image from the gallery</div>
<img style="max-height: 256px; object-fit: contain; width: 100%;" src="${this.featured().image.src}"></img>
<span style="font-size: .75rem;">${decodeURIComponent(url.pathname)}</span>
<div>Is it what you want to do? </div>
<div style="justify-content: flex-end;">
<paper-button raised id="yes-delete-picture">Yes</paper-button>
<paper-button raised id="no-delete-picture">No</paper-button>
</div>
</div>
`,
60 * 1000 // 60 sec...
);
let yesBtn = document.querySelector("#yes-delete-picture")
let noBtn = document.querySelector("#no-delete-picture")
// On yes
yesBtn.onclick = () => {
// so here I will remove the image...
this.images = this.images.filter(e => e !== this.featured().image.src);
toast.dismiss();
ApplicationView.displayMessage(
`<div style="display: flex; flex-direction: column;">
<span style="font-size: .85rem;">${url.pathname}</span>
<span>was remove from the gallery</span>
</div>`,
3000
);
this.setImages(this.images)
if (this.onremoveimage) {
this.onremoveimage(decodeURIComponent(url.pathname))
}
}
noBtn.onclick = () => {
toast.dismiss();
}
}
}
setEditable(editable) {
// so here I will display the delete image button.
if (editable)
this.closeBtn.style.display = "block"
else
this.closeBtn.style.display = "none"
}
getImage(index) {
return this.images[index]
}
setImages(images) {
// keep reference to images.
this.images = images
let controls = this.shadowRoot.querySelector(".controls")
if (this.images.length > 1) {
controls.style.display = "block"
} else {
controls.style.display = "none"
}
//Set Initial Featured Image
// The image viewer to display image to it full size
this.imageViewer = new ImageViewer
this.imageViewer.style.position = "fixed"
this.imageViewer.style.top = "0px"
this.imageViewer.style.left = "0px"
this.imageViewer.style.right = "0px"
this.imageViewer.style.bottom = "0px"
this.imageViewer.onclose = () => {
// remove it from the layout.
this.imageViewer.parentNode.removeChild(this.imageViewer)
}
this.featured().onclick = () => {
this.imageViewer.activeImage(this.featured().index)
this.imageViewer.style.display = "block"
document.body.appendChild(this.imageViewer)
}
// remove existing images.
this.gallery.innerHTML = ""
let range = document.createRange()
//Set Images for Gallery and Add Event Listeners
for (var i = 0; i < images.length; i++) {
let html = `
<div class="item-wrapper">
<figure class="gallery-item image-holder r-3-2 transition"></figure>
</div>
`
this.gallery.appendChild(range.createContextualFragment(html))
let galleryItem = this.gallery.children[this.gallery.children.length - 1]
// create the image to display by the image viewer.
let img = document.createElement("img")
img.src = images[i]
this.imageViewer.addImage(img)
galleryItem.children[0].style.backgroundImage = 'url(' + images[i] + ')';
let index = i;
galleryItem.children[0].onclick = e => {
if (e.target.classList.contains('active')) return;
this.featured().style.backgroundImage = e.target.style.backgroundImage;
this.featured().index = index;
this.featured().image = img
for (var i = 0; i < this.galleryItems().length; i++) {
if (this.galleryItems()[i].classList.contains('active'))
this.galleryItems()[i].classList.remove('active');
}
e.target.classList.add('active');
};
if (i == 0) {
galleryItem.children[0].classList.add('active')
this.featured().index = index;
this.featured().style.backgroundImage = 'url(' + images[0] + ')';
this.featured().image = img
}
}
}
getImages(){
let images = []
this.images.forEach(src=>{
let img = document.createElement("img")
img.src = src
images.push(img)
})
return images;
}
featured() {
return this.shadowRoot.querySelector('.featured-item');
}
// Call search event.
numOfItems() {
return this.gallery.children.length
}
galleryItems() {
return this.shadowRoot.querySelectorAll('.gallery-item');
}
galleryWrapLeft() {
var first = this.gallery.children[0];
this.gallery.removeChild(first);
this.gallery.style.left = -this.itemWidth + '%';
this.gallery.appendChild(first);
this.gallery.style.left = '0%';
}
galleryWrapRight() {
var last = this.gallery.children[this.gallery.children.length - 1];
this.gallery.removeChild(last);
this.gallery.insertBefore(last, this.gallery.children[0]);
this.gallery.style.left = '-23%';
}
moveLeft() {
this.left = this.left || 0;
this.leftInterval = setInterval(() => {
this.gallery.style.left = this.left + '%';
if (this.left > - this.itemWidth) {
this.left -= this.scrollRate;
} else {
this.left = 0;
this.galleryWrapLeft();
}
}, 1);
}
moveRight() {
//Make sure there is element to the leftd
if (this.left > -this.itemWidth && this.left < 0) {
this.left = this.left - this.itemWidth;
var last = this.gallery.children[this.gallery.children.length - 1];
this.gallery.removeChild(last);
this.gallery.style.left = this.left + '%';
this.gallery.insertBefore(last, this.gallery.children[0]);
}
this.left = this.left || 0;
this.leftInterval = setInterval(() => {
this.gallery.style.left = this.left + '%';
if (this.left < 0) {
this.left += this.scrollRate;
} else {
this.left = -this.itemWidth;
this.galleryWrapRight();
}
}, 1);
}
stopMovement() {
clearInterval(this.leftInterval);
clearInterval(this.rightInterval);
}
}
customElements.define('globular-image-gallery', ImageGallery)