133 lines
3.2 KiB
JavaScript
133 lines
3.2 KiB
JavaScript
const endpoint = 'https://leanwebclub.com/course-apis/photos.json';
|
|
|
|
class GridOfPhotos extends HTMLElement {
|
|
|
|
/**
|
|
* Instantiate the component
|
|
*/
|
|
constructor () {
|
|
// Inherit parent class properties
|
|
super();
|
|
|
|
// Track the current focused image button on the grid
|
|
this.currentImageBtn = null;
|
|
|
|
// Listen for events
|
|
this.addEventListener('click', this);
|
|
}
|
|
|
|
/**
|
|
* Runs each time the element is appended to or moved in the DOM
|
|
*/
|
|
async connectedCallback () {
|
|
// Fetch the photos from the API
|
|
this.photos = await this.fetchPhotos();
|
|
|
|
// If there are no photos
|
|
if (!this.photos || !this.photos.length) {
|
|
this.innerHTML = '<p>There are no available photos at this time. Please try again later. Sorry!</p>';
|
|
}
|
|
|
|
// Render the element
|
|
this.innerHTML = `
|
|
<div>
|
|
${this.photos.map( (image) => {
|
|
return `<button id="open-photo-button" class="outline contrast"><img id="${image.id}" src="${image.url}" alt="${image.description}"></img></button>`
|
|
} ).join('')}
|
|
</div>
|
|
`
|
|
}
|
|
|
|
/**
|
|
* Fetch photos from the API
|
|
* @return {Promise} response - The response from the API
|
|
*/
|
|
async fetchPhotos () {
|
|
try {
|
|
let response = await fetch(endpoint);
|
|
if (!response.ok) throw response.status;
|
|
|
|
let data = await response.json();
|
|
if (!data) throw 'No data!';
|
|
|
|
return data;
|
|
} catch (error) {
|
|
console.warn("ERR:", error);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Handle events
|
|
* @param {Event} event - The event object
|
|
*/
|
|
handleEvent (event) {
|
|
this[`on${event.type}`](event);
|
|
}
|
|
|
|
/**
|
|
* Open the modal window that shows the photo.
|
|
*/
|
|
handleOpenPhoto (event) {
|
|
let btn = event.target.closest('#open-photo-button');
|
|
if (!btn) return;
|
|
|
|
this.currentImageBtn = btn;
|
|
|
|
let id = btn.querySelector('img').getAttribute('id');
|
|
let image = this.photos.find( (image) => { return image.id === id } );
|
|
|
|
// Create the dialog element
|
|
let dialog = document.createElement('dialog');
|
|
|
|
dialog.setAttribute('open', '');
|
|
dialog.setAttribute('aria-live', 'polite');
|
|
|
|
dialog.innerHTML = `
|
|
<article>
|
|
<h2>${image.name}</h2>
|
|
<p>
|
|
${image.description}
|
|
</p>
|
|
<img id="${image.id}" src="${image.url}" alt="${image.description}"></img>
|
|
<footer>
|
|
<button id="add-photo-button" class="primary">Add to cart</button>
|
|
<button id="close-photo-button" class="secondary">Cancel</button>
|
|
</footer>
|
|
</article>
|
|
`
|
|
this.append(dialog);
|
|
dialog.focus();
|
|
}
|
|
|
|
/**
|
|
* Close the modal window that shows the photo.
|
|
*/
|
|
handleClosePhoto (event) {
|
|
let btn = event.target.closest('#close-photo-button');
|
|
if (!btn) return;
|
|
|
|
let dialog = event.target.closest('dialog');
|
|
if (!dialog) return;
|
|
|
|
dialog.close();
|
|
dialog.remove();
|
|
|
|
this.currentImageBtn.focus();
|
|
}
|
|
|
|
/**
|
|
* Handle the click event
|
|
* @param {Event} event - The event object
|
|
*/
|
|
onclick (event) {
|
|
this.handleOpenPhoto(event);
|
|
this.handleClosePhoto(event);
|
|
}
|
|
|
|
}
|
|
|
|
// Define the new web component
|
|
if ('customElements' in window) {
|
|
customElements.define('grid-of-photos', GridOfPhotos);
|
|
}
|