Skip to content

Commit c5d5d98

Browse files
authored
Add lightbox for figures (#101)
* Add lightbox js and styles for interactive graphic * Renamed styles file * Fix close button and size
1 parent d8551ad commit c5d5d98

File tree

5 files changed

+330
-5
lines changed

5 files changed

+330
-5
lines changed

gcx/gulpfile.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ const mergeJS = () => {
5757
'node_modules/swiper/swiper-bundle.min.js',
5858
'styles/src/js/06-tabs-block.js',
5959
'styles/src/js/07-copy-to-clipboard.js',
60-
'styles/src/js/08-gcx-helios.js'])
60+
'styles/src/js/08-gcx-helios.js',
61+
'styles/src/js/09-lightbox.js'])
6162
.pipe(concat('site.js'))
6263
.pipe(dest('build/js'))
6364
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
.modal {
2+
display: none;
3+
position: fixed;
4+
z-index: 10;
5+
/*padding-top: 5vh;*/
6+
left: 0;
7+
top: 0;
8+
width: 100%;
9+
height: 100%;
10+
overflow: auto;
11+
backdrop-filter: blur(3px);
12+
background-color: rgba(30, 30, 30, 0.8);
13+
}
14+
15+
.imageblock .lightbox {
16+
cursor: pointer;
17+
}
18+
19+
.imageblock .lightbox object {
20+
pointer-events: none;
21+
}
22+
23+
/* Modal Content */
24+
.modal .content {
25+
position: relative;
26+
margin: auto;
27+
padding: 0;
28+
width: 90%;
29+
max-height: 90vh;
30+
cursor: pointer;
31+
}
32+
33+
.modal .content img {
34+
display: block;
35+
margin-right: auto;
36+
margin-left: auto;
37+
}
38+
39+
.modal .content object {
40+
display: block;
41+
margin-right: auto;
42+
margin-left: auto;
43+
/* will prevent clicking on links on objects, therefore avoid! */
44+
/* pointer-events: none; */
45+
}
46+
47+
.modal .content svg {
48+
display: block;
49+
margin-right: auto;
50+
margin-left: auto;
51+
}
52+
53+
.modal .close {
54+
position: absolute;
55+
top: 10px;
56+
right: 25px;
57+
z-index: 2;
58+
border-radius: 50%;
59+
/*background: rgba(0,0,0,0.1);*/
60+
padding: 10px;
61+
width: 48px;
62+
height: 48px;
63+
text-align: center;
64+
}
65+
66+
.modal .close img {
67+
height: 24px;
68+
width: 24px;
69+
filter: invert(100%) drop-shadow(1px 2px 4px rgb(0 0 0 / 0.6));
70+
}

gcx/styles/src/css/slides.css

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,18 @@ swiper-container {
3232
position: relative;
3333
overflow: hidden;
3434
list-style: none;
35-
padding: 0;
35+
padding: 0.75rem;
3636
/* Fix of Webkit flickering */
3737
z-index: 1;
3838
display: block;
39+
border: 1px solid var(--example-border-color);
40+
border-radius: 6px;
3941
}
4042
.swiper-vertical > .swiper-wrapper {
4143
flex-direction: column;
4244
}
4345
.swiper-wrapper,
44-
.swiper > .content {
46+
.doc .swiper > .content {
4547
position: relative;
4648
width: 100%;
4749
height: 100%;
@@ -50,6 +52,7 @@ swiper-container {
5052
transition-property: transform;
5153
transition-timing-function: var(--swiper-wrapper-transition-timing-function, initial);
5254
box-sizing: content-box;
55+
border: none;
5356
}
5457
.swiper-android .slide ,
5558
.swiper-wrapper {
@@ -371,13 +374,20 @@ swiper-container:not(.swiper-watch-progress) .swiper-lazy-preloader,
371374
.swiper-button-lock {
372375
display: none;
373376
}
377+
.left-button .swiper-button-prev.swiper-button-disabled {
378+
display: none;
379+
}
380+
.left-button .swiper-button-wrapper{
381+
justify-content: start;
382+
}
374383
.swiper-button-wrapper{
375384
display: grid;
376385
justify-content: end;
377386
justify-items: end;
378387
align-items: end;
379388
grid-auto-rows: minmax(32px, auto);
380389
grid-template-columns: repeat(2, 100px);
390+
margin-bottom: 1rem;
381391
}
382392
:root {
383393
/*
@@ -665,7 +675,7 @@ swiper-container .swiper-notification {
665675
transition-timing-function: ease-out;
666676
}
667677
.swiper-fade .slide {
668-
pointer-events: none;
678+
/*pointer-events: none;*/
669679
transition-property: opacity;
670680
}
671681
.swiper-fade .slide .slide {

gcx/styles/src/js/08-gcx-helios.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ sliderBlock.forEach( (s,i) => {
182182
// Optional parameters
183183
direction: 'horizontal',
184184
allowTouchMove: false,
185-
touchStartPreventDefault: false,
185+
//touchStartPreventDefault: false,
186+
watchSlidesProgress: true,
186187
loop: false,
187188
effect: 'fade',
188189
slideClass: 'slide',

gcx/styles/src/js/09-lightbox.js

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/*
2+
https://gitlab.com/antora/antora-ui-default/-/merge_requests/126/
3+
Lightbox functionality: a user clicks on an image, and it will open full screen until user closes it again.
4+
5+
Supported functionality:
6+
7+
when lightbox is closed for all images:
8+
* if the image has a link, the standard behavior for hovering and clicking is unchanged.
9+
10+
when lightbox is closed for standard block image:
11+
* when hovering over image AND if original size of image is greater than displayed image,
12+
THEN change mouse pointer to pointer
13+
(works for both bitmap images the same way as for SVG images)
14+
* when clicking on image AND if original size of image is greater than displayed image, THEN open lightbox
15+
(works for both bitmap images the same way as for SVG images)
16+
17+
when lightbox is closed for inlined or interactive SVG image:
18+
* when hovering over image THEN change mouse pointer to pointer
19+
* when clicking on image THEN open lightbox
20+
21+
when lightbox is open for all images:
22+
* when user clicks on close icon, lightbox will close
23+
* when user presses Escape, lightbox will close
24+
* when user presses Tab to select the close button and presses Space or Enter, lightbox will close
25+
26+
when lightbox is opened for standard block image:
27+
* when user clicks enlarged image, lightbox will close
28+
29+
when lightbox is opened for interactive SVG:
30+
* when user clicks enlarged image AND if the SVG doesn't contain any hyperlinks, lightbox will close
31+
* when user clicks enlarged image AND if the SVG contain a hyperlinks AND the user clicks outside of a hyperlink,
32+
nothing happens (the lightbox will stay open)
33+
* when user clicks enlarged image AND if the SVG contains hyperlinks AND the user clicks on a hyperlink,
34+
the hyperlinks will work as before
35+
36+
when lightbox is opened for inlined SVG:
37+
* when user clicks on links included in the SVG, the hyperlinks will work as before
38+
* when user clicks outside of the links withing the SVG, the lightbox will close
39+
40+
*/
41+
42+
;(function () {
43+
'use strict'
44+
var lightbox
45+
var config = (document.getElementById('site-script') || { dataset: {} }).dataset
46+
var content
47+
48+
function init () {
49+
if (!lightbox) {
50+
lightbox = document.createElement('div')
51+
lightbox.setAttribute('aria-modal', 'true')
52+
lightbox.className = 'modal'
53+
var closeLink = document.createElement('a')
54+
// set href to make it selectable with tab / assistive technologies
55+
closeLink.href = '#'
56+
closeLink.className = 'close'
57+
closeLink.setAttribute('title', 'Close lightbox')
58+
if (config.svgAs === 'svg') {
59+
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
60+
svg.setAttribute('class', 'copy-icon')
61+
var use = document.createElementNS('http://www.w3.org/2000/svg', 'use')
62+
use.setAttribute('href', window.uiRootPath + '/img/octicons-16.svg#icon-x')
63+
svg.appendChild(use)
64+
closeLink.appendChild(svg)
65+
} else {
66+
// https://codepen.io/marianab/pen/gOPJOjJ
67+
let svgIcon = `<svg xmlns="http://www.w3.org/2000/svg" height="329pt" viewBox="0 0 329.26933 329" width="329pt">
68+
<path d="m194.800781 164.769531 128.210938-128.214843c8.34375-8.339844 8.34375-21.824219
69+
0-30.164063-8.339844-8.339844-21.824219-8.339844-30.164063 0l-128.214844
70+
128.214844-128.210937-128.214844c-8.34375-8.339844-21.824219-8.339844-30.164063
71+
0-8.34375 8.339844-8.34375 21.824219 0 30.164063l128.210938 128.214843-128.210938
72+
128.214844c-8.34375 8.339844-8.34375 21.824219 0 30.164063 4.15625 4.160156 9.621094
73+
6.25 15.082032 6.25 5.460937 0 10.921875-2.089844 15.082031-6.25l128.210937-128.214844
74+
128.214844 128.214844c4.160156 4.160156 9.621094 6.25 15.082032 6.25 5.460937 0
75+
10.921874-2.089844 15.082031-6.25 8.34375-8.339844 8.34375-21.824219 0-30.164063zm0 0"/></svg>`
76+
var img = document.createElement('img')
77+
img.src= `data:image/svg+xml;utf-8,${svgIcon}`
78+
img.alt = 'close icon'
79+
img.className = 'x-icon'
80+
closeLink.appendChild(img)
81+
}
82+
lightbox.appendChild(closeLink)
83+
content = document.createElement('div')
84+
content.className = 'content'
85+
lightbox.appendChild(content)
86+
var body = document.getElementsByTagName('body')[0]
87+
body.appendChild(lightbox)
88+
body.addEventListener('keydown', function (e) {
89+
if (e.code === 'Escape' && isOpen()) {
90+
close(e)
91+
}
92+
})
93+
94+
content.addEventListener('click', close)
95+
closeLink.addEventListener('click', function (e) {
96+
close(e)
97+
e.preventDefault()
98+
})
99+
closeLink.addEventListener('keydown', function (e) {
100+
if (e.code === 'Space' || e.code === 'Enter') {
101+
close(e)
102+
e.preventDefault()
103+
}
104+
})
105+
}
106+
}
107+
108+
function open () {
109+
lightbox.style.display = 'flex'
110+
document.body.style.overflow = 'hidden'
111+
}
112+
113+
function isOpen () {
114+
return lightbox && lightbox.style.display === 'flex'
115+
}
116+
117+
function close (e) {
118+
lightbox.style.display = 'none'
119+
content.firstChild.remove()
120+
document.body.style.overflow = ''
121+
// don't prevent default here, as that will allow links in SVGs to work
122+
}
123+
124+
// depending on ratio of source vs target element, make the lightbox content 90% of height or width
125+
function setImageSize (img, source, target) {
126+
var ratioSource = source.offsetWidth / source.offsetHeight
127+
var ratioTarget = target.offsetWidth / target.offsetHeight
128+
if (ratioSource < ratioTarget) {
129+
img.style.height = '70vh'
130+
} else {
131+
img.style.width = '70vw'
132+
}
133+
}
134+
/* swiper slider img*/
135+
document.querySelectorAll('.slide img').forEach(function (element) {
136+
if (element.parentNode.nodeName === 'A') {
137+
// if parent node is an anchor, keep the anchor instead of opening lightbox
138+
return
139+
}
140+
element.parentNode.className += ' lightbox'
141+
if (typeof element.parentNode.classList.remove === 'function') {
142+
element.parentNode.addEventListener('mouseover', function (e) {
143+
if (element.naturalWidth <= element.offsetWidth && element.naturalHeight <= element.offsetHeight) {
144+
element.parentNode.classList.remove('lightbox')
145+
} else {
146+
element.parentNode.classList.add('lightbox')
147+
}
148+
})
149+
}
150+
element.style.cursor = 'pointer'
151+
element.addEventListener('click', function (e) {
152+
if (element.naturalWidth <= element.offsetWidth && element.naturalHeight <= element.offsetHeight) {
153+
// don't open lightbox is already shown at 100% or more
154+
return
155+
}
156+
init()
157+
var img = document.createElement('img')
158+
img.src = e.currentTarget.src
159+
img.alt = e.currentTarget.alt
160+
setImageSize(img, element.parentNode, content.parentNode)
161+
162+
/* Render swiper
163+
let str = e.currentTarget.closest('.swiper').className
164+
let index = parseInt(str.match(/slider-(\d+)/i)[1]);
165+
content.appendChild(sliderBlock[index])*/
166+
content.appendChild(img)
167+
open()
168+
})
169+
})
170+
171+
document.querySelectorAll('.imageblock img').forEach(function (element) {
172+
if (element.parentNode.nodeName === 'A') {
173+
// if parent node is an anchor, keep the anchor instead of opening lightbox
174+
return
175+
}
176+
element.parentNode.className += ' lightbox'
177+
if (typeof element.parentNode.classList.remove === 'function') {
178+
element.parentNode.addEventListener('mouseover', function (e) {
179+
if (element.naturalWidth <= element.offsetWidth && element.naturalHeight <= element.offsetHeight) {
180+
element.parentNode.classList.remove('lightbox')
181+
} else {
182+
element.parentNode.classList.add('lightbox')
183+
}
184+
})
185+
}
186+
element.addEventListener('click', function (e) {
187+
if (element.naturalWidth <= element.offsetWidth && element.naturalHeight <= element.offsetHeight) {
188+
// don't open lightbox is already shown at 100% or more
189+
return
190+
}
191+
init()
192+
var img = document.createElement('img')
193+
img.src = e.currentTarget.src
194+
img.alt = e.currentTarget.alt
195+
setImageSize(img, element.parentNode, content.parentNode)
196+
content.appendChild(img)
197+
open()
198+
})
199+
})
200+
201+
document.querySelectorAll('.imageblock object').forEach(function (element) {
202+
if (element.parentNode.nodeName === 'A') {
203+
// if parent node is an anchor, keep the anchor instead of opening lightbox
204+
return
205+
}
206+
element.parentNode.className += ' lightbox'
207+
element.parentNode.addEventListener('click', function (e) {
208+
init()
209+
var img = document.createElement('object')
210+
img.type = element.type
211+
img.data = element.data
212+
open()
213+
setImageSize(img, element, content.parentNode)
214+
if (element.getSVGDocument() && element.getSVGDocument().querySelectorAll('a').length === 0) {
215+
// if the SVG doesn't contain any links, allow user to click on image to close the image
216+
img.style.pointerEvents = 'none'
217+
}
218+
content.appendChild(img)
219+
// prevent links in SVGs to open, as this should only open the lightbox
220+
e.preventDefault()
221+
})
222+
})
223+
document.querySelectorAll('.imageblock svg').forEach(function (element) {
224+
if (element.parentNode.nodeName === 'A') {
225+
// if parent node is an anchor, keep the anchor instead of opening lightbox
226+
return
227+
}
228+
element.parentNode.className += ' lightbox'
229+
element.parentNode.addEventListener('click', function (e) {
230+
init()
231+
var img = element.cloneNode(true)
232+
open()
233+
// override height/width from cloned element
234+
img.style.height = 'auto'
235+
img.style.width = 'auto'
236+
// need to select element's parent node, as offsetWidth/offsetHeight not available on SVG
237+
setImageSize(img, element.parentNode, content.parentNode)
238+
content.appendChild(img)
239+
// prevent links in SVGs to open, as this should only open the lightbox
240+
e.preventDefault()
241+
})
242+
})
243+
})()

0 commit comments

Comments
 (0)