Skip to content

Commit

Permalink
Feature/img modal (#283)
Browse files Browse the repository at this point in the history
* inital working modal

* full screen image modal, images with expand icon

* add img processing test

* modify server svg loading, move client svg to import

* move imgModal adding to server

* retain list formatting

* add tests for image modal processing

* remove semicolon

* readable includes syntax

* entirely client side image modal impl

* remove unnecessary comment

* refactor imgModal code into a single partial
  • Loading branch information
xvandish authored Aug 16, 2021
1 parent b6357d0 commit bc0ccdc
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 1 deletion.
4 changes: 4 additions & 0 deletions custom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ The site name, logo, and most of the text on the website can be modified from th
placing a value for the same key in `custom/strings.yaml`, with a custom string,
Javascript function, or image path.

## Image Modal
Images can opened/expanded in a modal/dialog. On hover of an image, the cursor will turn into the pointer style, and an expand button will show. Once clicked, the image will center in the page, and a minimize icon will show. Styles for the modal can be found in
`/styles/partials/core/_furniture.scss` as well as one line in `/styles/partials/core/_base.scss` that adds a pointer cursor on hover of images. All HTML/JS for the Image Modal is included in the [`/layouts/partials/imgModal.ejs`](../layouts/partials/imgModal.ejs) file. The Image Modal is then imported in the default layout for content, code for which can be found [here](../layouts/categories/default.ejs#L29).

## Middleware
Middleware can be added to the beginning or end of the request cycle by placing
files into `custom/middleware`. These files can export `preload` and `postload`
Expand Down
2 changes: 1 addition & 1 deletion layouts/categories/default.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<%- include('partials/childrenList', {children, kicker: template('folder.childrenList.kicker', title)}) %>
<% } %>
</div>

<%- include('partials/imgModal') %>
<%- include('partials/footer', { pageType: 'document', topLevelFolder: url.split('/')[1] }) %>
</div>
</body>
Expand Down
84 changes: 84 additions & 0 deletions layouts/partials/imgModal.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<div id="image-modal" class="image-modal">
<button type="button" class="close" title="Minimize this image" aria-label="Minimize this image">
<svg width="60" height="60" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M35.97 25.37L38.6 28H32v-6.6l2.63 2.63L38.67 20 40 21.33l-4.03 4.04zM24.03 34.63L21.4 32H28v6.6l-2.63-2.63L21.33 40 20 38.67l4.03-4.04z" fill="#000"/></svg>
</button>
<div class="img-wrapper">
<img class="modal-image" id="img1" />
</div>
<!-- expandIconSvg svg inlined, will be used in image overlay and removed from here on document load. JS in footer.ejs does this-->
<svg id="expandIconSvg" width="60" height="60" fill="#fff"><circle cx="30" cy="30" r="30" fill="#000" fill-opacity=".5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M23.97 37.37L26.6 40H20v-6.6l2.63 2.63L26.67 32 28 33.33l-4.03 4.04zM36.03 22.63L33.4 20H40v6.6l-2.63-2.63L33.33 28 32 26.67l4.03-4.04z"/></svg>
</div>

<script>
var imgWrapper
function createImageOverlayWrapper() {
if (imgWrapper) { // cache hit
return imgWrapper
}
// read expandIcon from image-modal where its inlined so that we
// can read it here. then remove it and strip the id since it'll be used multiple times
const expandIconSvg = document.getElementById('expandIconSvg')
expandIconSvg.remove()
expandIconSvg.id = ""
let wrapperDiv = document.createElement('div')
wrapperDiv.className = 'image-wrapper'
let expandBtn = document.createElement('button')
expandBtn.setAttribute('aria-label', 'expand this image')
expandBtn.title = 'expand this image'
expandBtn.className='expand-image-btn'
expandBtn.appendChild(expandIconSvg)
wrapperDiv.appendChild(expandBtn)
imgWrapper = wrapperDiv // add to cache
return wrapperDiv
}
function wrapImageInOverlay(imgElement) {
// clone the wrapper so the original isn't mutated and we can reuse it
const overlayWrapper = createImageOverlayWrapper().cloneNode(true)
imgElement.parentNode.insertBefore(overlayWrapper, imgElement)
overlayWrapper.appendChild(imgElement)
}
window.onload = () => {
const imgs = document.querySelectorAll('.g-main-content img'); // get all images
// for each image - add overlay allowing for expand btn and click handler for expansion
imgs.forEach((img, idx) => {
wrapImageInOverlay(img);
img.addEventListener('click', () => expandImage(img.src));
});
}
var imgModal = document.querySelector('.image-modal')
var imgModalImg = document.querySelector('.image-modal .modal-image');
function expandImage(imgSrc) {
imgModal.style.display = 'block';
imgModalImg.src = imgSrc;
window.addEventListener('scroll', function onScroll() {
window.removeEventListener('scroll', onScroll);
imgModal.style.display='none';
});
};
var modalCloseBtn = document.querySelector('.image-modal .close');
modalCloseBtn.addEventListener('click', () => {
imgModal.style.display = 'none';
});
/* keydown listener so img modal can be closed with Esc key */
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && imgModal.style.display === 'block') {
imgModal.style.display = 'none';
}
});
</script>
1 change: 1 addition & 0 deletions styles/partials/core/_categories.scss
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@
padding: 10px;
display: block;
margin: 20px auto;
cursor: pointer; // images can be opened in a modal

@include tablet{
max-width: 580px;
Expand Down
73 changes: 73 additions & 0 deletions styles/partials/core/_furniture.scss
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,76 @@
text-align: center;
}
}

// the image-wrapper class is added for the benefit of the
// Image Modal, so co-locating it here
.image-wrapper {
position: relative;

.expand-image-btn {
height: 60px;
width: 60px;
position: absolute;
bottom: 25px;
right: 25px;
opacity: 0;
transition: opacity 0.3s ease 0s;
background-color: transparent;
pointer-events: none;
border: none;
}

&:hover {
.expand-image-btn {
opacity: 1;
}
}
}

.image-modal {
display: none;
position: fixed;
z-index: 1000000090; /* search bar icon has z-index of 1*10 */
inset: 0px; /* shorthand for top,right,bottom,left at the same time */
overflow: hidden;
background-color: rgb(255, 255, 255); /* full white allows images to pop */
transition: display 0.2s ease 0s;
}

.image-modal .img-wrapper {
display: flex;
align-items: center;
align-content: center;
height: 100%;
padding: 30px;

.modal-image {
margin: auto;
display: block;
max-width: 100%;
cursor: default;
}
}

.image-modal .close {
display: flex;
align-items: center;
position: absolute;
top: 10px;
right: 10px;
background-color: transparent;
cursor: pointer;
border: 0.5px solid white;
border-radius: 50%;
width: 60px;
height: 60px;
transition: all 0.1s ease-in;
padding: 0px;

&:hover, &:focus {
background-color: #f9f9f9;
border-color: lightgray;
}
}


0 comments on commit bc0ccdc

Please sign in to comment.