@dkvz/img-lightbox 0.2.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -6
- package/dist/img-lightbox.js +204 -6
- package/dist/img-lightbox.js.map +1 -1
- package/dist/img-lightbox.mjs +193 -0
- package/dist/img-lightbox.mjs.map +1 -0
- package/package.json +10 -7
- package/src/shadow/img-lightbox.js +17 -9
- package/src/shadow/index.pug +7 -0
- package/src/shared/testpage.html +4 -4
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ I quickly made this in regard to a blog article I was writing, it could be impro
|
|
|
9
9
|
|
|
10
10
|
Uncompressed minified size: 8.5Kb including the loading spinner image.
|
|
11
11
|
|
|
12
|
+
**Note**: we no longer minify the build - It seems to be standard practice for libraries nowadays.
|
|
13
|
+
|
|
12
14
|
## Using the component from npm
|
|
13
15
|
To use it in a project with a module bundler, install the dependency first:
|
|
14
16
|
```
|
|
@@ -44,6 +46,22 @@ img-lightbox:active, img-lightbox:focus {
|
|
|
44
46
|
}
|
|
45
47
|
```
|
|
46
48
|
|
|
49
|
+
You also may have strange spacing issues if the a and img elements have their default "inline" display mode. Which could be changed like so:
|
|
50
|
+
```css
|
|
51
|
+
img-lightbox a, img-lightbox img {
|
|
52
|
+
display: block;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This is also where you should set any max-width or max-height. Or you could set it on img-lightbox itself but then you need to set width and height to 100% as in:
|
|
57
|
+
```css
|
|
58
|
+
img-lightbox a, img-lightbox img {
|
|
59
|
+
display: block;
|
|
60
|
+
width: 100%;
|
|
61
|
+
height: 100%;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
47
65
|
## Building the component
|
|
48
66
|
|
|
49
67
|
### Build requirements
|
|
@@ -62,11 +80,6 @@ Build using either:
|
|
|
62
80
|
```
|
|
63
81
|
npm run build
|
|
64
82
|
```
|
|
65
|
-
For the shadow DOM (default) version and:
|
|
66
|
-
```
|
|
67
|
-
npm run build-no-shadow
|
|
68
|
-
```
|
|
69
|
-
For the version that does not use the shadow DOM API at all.
|
|
70
83
|
|
|
71
84
|
You'll find the build script in the `dist` folder.
|
|
72
85
|
|
|
@@ -77,8 +90,20 @@ To manually use it in some page, you could do something like this:
|
|
|
77
90
|
customElements.define('img-lightbox', ImgLightbox.default);
|
|
78
91
|
</script>
|
|
79
92
|
```
|
|
93
|
+
**Update 2025**: There is a .mjs file built as well and it might be a good idea to use that one (with `type="module" in the script tag`).
|
|
94
|
+
|
|
80
95
|
The loading icon SVG file is normally not necessary but since I change my mind all the time about it you might want to copy it along anyway.
|
|
81
96
|
|
|
97
|
+
## Build without shadow DOM
|
|
98
|
+
In the past I had a way to build the component without using the shadow DOM at all, which has a few consequences but I think the main idea was to support some old browsers.
|
|
99
|
+
|
|
100
|
+
Anyway, after upgrading Parcel to v2 I didn't take time to fix the build command for the no-shadow DOM version.
|
|
101
|
+
|
|
102
|
+
It used to be:
|
|
103
|
+
```
|
|
104
|
+
npm run build-no-shadow
|
|
105
|
+
```
|
|
106
|
+
|
|
82
107
|
The no-shadow DOM version also requires extra styles to work. See `src/no-shadow/index.pug`.
|
|
83
108
|
|
|
84
109
|
## Remarks
|
|
@@ -127,7 +152,6 @@ if (event.altKey)
|
|
|
127
152
|
- [ ] Write tests - Probably going to need jsdom
|
|
128
153
|
- [x] Don't forget to register the keyboard events
|
|
129
154
|
- [x] Use template tags, they say it's better (here)[https://github.com/GoogleChromeLabs/howto-components/blob/master/elements/howto-checkbox/howto-checkbox.js]
|
|
130
|
-
- [ ] Document how to check for web component browser support
|
|
131
155
|
- [x] Disable overflow on the fullscreen overlay
|
|
132
156
|
- [x] Make a shadow DOM version
|
|
133
157
|
- [x] To maximize accessibility we need some kind of focus outline
|
package/dist/img-lightbox.js
CHANGED
|
@@ -1,6 +1,204 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
|
|
2
|
+
function $parcel$interopDefault(a) {
|
|
3
|
+
return a && a.__esModule ? a.default : a;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function $parcel$defineInteropFlag(a) {
|
|
7
|
+
Object.defineProperty(a, '__esModule', {value: true, configurable: true});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function $parcel$export(e, n, v, s) {
|
|
11
|
+
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
$parcel$defineInteropFlag(module.exports);
|
|
15
|
+
|
|
16
|
+
$parcel$export(module.exports, "default", () => $a6ba83244539002c$export$2e2bcd8739ae039);
|
|
17
|
+
// const loaderSvgUrl = require('../../assets/hourglass.svg');
|
|
18
|
+
var $735957c3b581425f$exports = {};
|
|
19
|
+
$735957c3b581425f$exports = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" enable-background=\"new 0 0 64 64\" version=\"1.1\" viewBox=\"0 0 64 64\" xml:space=\"preserve\"><metadata><rdf:RDF><cc:Work rdf:about=\"\"><dc:format>image/svg+xml</dc:format><dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\"/><dc:title/></cc:Work></rdf:RDF></metadata>\n<g>\n\t\t<circle class=\"st0\" cx=\"32\" cy=\"32\" r=\"32\"/>\n\t</g><g class=\"st1\" opacity=\".2\">\n\t\t<path class=\"st2\" d=\"m46 50h-2v-4c0-4.5-3-7.4-3-7.4s-3-2.8-3.9-3.7 0-1.8 0-1.8 1.3-1.3 4.2-4c2.8-2.7 2.7-6.7 2.7-6.7v-5.1l-12-1-12 1v5.1s-0.1 3.9 2.7 6.7c2.8 2.7 4.2 4 4.2 4s0.9 0.9 0 1.8-3.9 3.7-3.9 3.7-3 2.8-3 7.4v4h-2c-1.1 0-2 0.9-2 2s0.9 2 2 2h28c1.1 0 2-0.9 2-2s-0.9-2-2-2z\" fill=\"#231f20\"/>\n\t</g>\n\t\t<path class=\"st3\" d=\"m41 36.6s-3-2.8-3.9-3.7 0-1.8 0-1.8 1.3-1.3 4.2-4c2.8-2.7 2.7-6.7 2.7-6.7v-5.1l-12-1-12 1v5.1s-0.1 3.9 2.7 6.7c2.8 2.7 4.2 4 4.2 4s0.9 0.9 0 1.8-3.9 3.7-3.9 3.7-3 2.8-3 7.4v5.1h24v-5.1c0-4.5-3-7.4-3-7.4z\" fill=\"#fff\"/>\n\t\n\t\t<path class=\"st4\" d=\"m31 43v-11c0-2.1-1-3.4-1.3-3.7l-4.2-4c-1.5-1.4-1.5-3.7-1.5-3.7h16s0 2.3-1.5 3.7l-4.2 4c-0.4 0.3-1.3 1.6-1.3 3.7v11z\"/>\n\t<g class=\"st5\" opacity=\".3\">\n\t\t<path class=\"st2\" d=\"m48 16c0 1.1-0.9 2-2 2h-28c-1.1 0-2-0.9-2-2s0.9-2 2-2h28c1.1 0 2 0.9 2 2z\" fill=\"#231f20\"/>\n\t</g><g fill=\"#fff\">\n\t\t<path class=\"st6\" d=\"m48 14c0 1.1-0.9 2-2 2h-28c-1.1 0-2-0.9-2-2s0.9-2 2-2h28c1.1 0 2 0.9 2 2z\" fill=\"#fff\"/>\n\t</g><g fill=\"#fff\">\n\t\t<path class=\"st6\" d=\"m48 50c0 1.1-0.9 2-2 2h-28c-1.1 0-2-0.9-2-2s0.9-2 2-2h28c1.1 0 2 0.9 2 2z\" fill=\"#fff\"/>\n\t</g><g>\n\t\t<polygon class=\"st4\" points=\"23 48 23 48 32 40 41 48\"/>\n\t</g>\n\n</svg>";
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class $a6ba83244539002c$var$ImgLightbox extends HTMLElement {
|
|
23
|
+
constructor(){
|
|
24
|
+
super();
|
|
25
|
+
this.loading = false;
|
|
26
|
+
this.attachShadow({
|
|
27
|
+
mode: 'open'
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
connectedCallback() {
|
|
31
|
+
// Template is added to prototype below the
|
|
32
|
+
// class definition.
|
|
33
|
+
// It's from a template tag because it's
|
|
34
|
+
// supposed to be faster this way.
|
|
35
|
+
this.shadowRoot.appendChild(this.template.content.cloneNode(true));
|
|
36
|
+
// Get some element references from shadow DOM:
|
|
37
|
+
this.overlay = this.shadowRoot.querySelector('#overlay');
|
|
38
|
+
this.loadingOverlay = this.shadowRoot.querySelector('#loader');
|
|
39
|
+
// Add the loading SVG to that element:
|
|
40
|
+
this.loadingOverlay.innerHTML = (0, (/*@__PURE__*/$parcel$interopDefault($735957c3b581425f$exports)));
|
|
41
|
+
// Component needs a tabIndex to be focusable.
|
|
42
|
+
this.tabIndex = 0;
|
|
43
|
+
// The slotted elements are actually not in
|
|
44
|
+
// the shadow DOM so we can get them like so:
|
|
45
|
+
const link = this.querySelector('a');
|
|
46
|
+
const img = this.querySelector('img');
|
|
47
|
+
if (link !== null) {
|
|
48
|
+
// Store the URL as the full image URL:
|
|
49
|
+
this.fullImage = link.getAttribute('href');
|
|
50
|
+
// Register a click event that has to
|
|
51
|
+
// prevent default:
|
|
52
|
+
//this._addListeners(link);
|
|
53
|
+
// Prevent ability to focus the link:
|
|
54
|
+
link.tabIndex = -1;
|
|
55
|
+
} else if (img !== null) this.fullImage = img.getAttribute('src');
|
|
56
|
+
this.fullImage && this._addListeners();
|
|
57
|
+
}
|
|
58
|
+
_addListeners() {
|
|
59
|
+
this.addEventListener('click', this.showLightbox.bind(this), true);
|
|
60
|
+
this.addEventListener('keydown', (e)=>{
|
|
61
|
+
// It's common to ignore anything with alt
|
|
62
|
+
// modifiers. I've copied this from Google
|
|
63
|
+
// people.
|
|
64
|
+
if (e.altKey) return;
|
|
65
|
+
// Catch enter and space:
|
|
66
|
+
switch(e.keyCode){
|
|
67
|
+
case 13:
|
|
68
|
+
case 32:
|
|
69
|
+
this.showLightbox(e);
|
|
70
|
+
default:
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}, true);
|
|
74
|
+
}
|
|
75
|
+
showLightbox(e) {
|
|
76
|
+
// This is required so that the link element does not get
|
|
77
|
+
// followed:
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
// Implement some kind of lock:
|
|
80
|
+
if (!this.loading) {
|
|
81
|
+
this.loading = true;
|
|
82
|
+
// Preload the full image.
|
|
83
|
+
// Show the spinner overlay:
|
|
84
|
+
this._showOverlay(this.loadingOverlay);
|
|
85
|
+
if (!this.img) {
|
|
86
|
+
this.img = document.createElement('img');
|
|
87
|
+
// For the moment we just do nothing on error with
|
|
88
|
+
// the link target.
|
|
89
|
+
this.img.addEventListener('error', ()=>{
|
|
90
|
+
this._hideOverlay(this.loadingOverlay);
|
|
91
|
+
this.loading = false;
|
|
92
|
+
});
|
|
93
|
+
this.img.addEventListener('load', ()=>this.showImage());
|
|
94
|
+
this.img.src = this.fullImage;
|
|
95
|
+
this.img.tabIndex = 1;
|
|
96
|
+
// Prepare the events to close the overlay.
|
|
97
|
+
// I'm doing this here to light up what's
|
|
98
|
+
// happening in connectedCallback.
|
|
99
|
+
// I'm not doing a check for which key was
|
|
100
|
+
// called on purpose, but maybe I should do
|
|
101
|
+
// that alt key check?
|
|
102
|
+
[
|
|
103
|
+
'click',
|
|
104
|
+
'keydown'
|
|
105
|
+
].forEach((type)=>this.overlay.addEventListener(type, (e)=>this._hideOverlay(e.currentTarget)));
|
|
106
|
+
this.overlay.appendChild(this.img);
|
|
107
|
+
} else this.showImage();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
showImage() {
|
|
111
|
+
this.img.style.transform = 'scale(0.1)';
|
|
112
|
+
// Hide the loading overlay:
|
|
113
|
+
this._hideOverlay(this.loadingOverlay);
|
|
114
|
+
this._showOverlay(this.overlay);
|
|
115
|
+
// Focus the overlay (requires it to have a tabIndex):
|
|
116
|
+
this.overlay.focus();
|
|
117
|
+
// Start the CSS transition:
|
|
118
|
+
this.img.style.transform = 'scale(1)';
|
|
119
|
+
// Don't forget to unlock the click event:
|
|
120
|
+
this.loading = false;
|
|
121
|
+
}
|
|
122
|
+
_showOverlay(overlay) {
|
|
123
|
+
overlay.style.display = 'flex';
|
|
124
|
+
}
|
|
125
|
+
_hideOverlay(overlay) {
|
|
126
|
+
overlay.style.display = '';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// I'm using a true template tag because the Chrome
|
|
130
|
+
// team says it's faster. The template tag has
|
|
131
|
+
// better browser support than web components anyway.
|
|
132
|
+
// The strange comment here is needed for syntax
|
|
133
|
+
// highlighting with a VS Code extension I'm using.
|
|
134
|
+
const $a6ba83244539002c$var$tpl = document.createElement('template');
|
|
135
|
+
$a6ba83244539002c$var$tpl.innerHTML = /*template*/ `
|
|
136
|
+
<style>
|
|
137
|
+
:host {
|
|
138
|
+
display: inline-block;
|
|
139
|
+
cursor: pointer;
|
|
140
|
+
position: relative;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
:host([hidden]) {
|
|
144
|
+
display: none;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
:host(:focus), :host(:active) {
|
|
148
|
+
outline: 2px solid #77b;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#loader {
|
|
152
|
+
position: absolute;
|
|
153
|
+
z-index: 1;
|
|
154
|
+
top: 0;
|
|
155
|
+
left: 0;
|
|
156
|
+
width: 100%;
|
|
157
|
+
height: 100%;
|
|
158
|
+
background-color: rgba(0,0,0,.2);
|
|
159
|
+
display: none;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#overlay {
|
|
165
|
+
position: fixed;
|
|
166
|
+
top: 0;
|
|
167
|
+
left: 0;
|
|
168
|
+
bottom: 0;
|
|
169
|
+
right: 0;
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
z-index: 999;
|
|
172
|
+
background-color: rgba(0,0,0,.5);
|
|
173
|
+
display: none;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: center;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
#loader svg {
|
|
179
|
+
width: 50%;
|
|
180
|
+
opacity: 0.6;
|
|
181
|
+
animation: lightbox-loader 2s infinite;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#overlay img {
|
|
185
|
+
max-width: 100%;
|
|
186
|
+
max-height: 100%;
|
|
187
|
+
transition: transform 0.8s;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@keyframes lightbox-loader {
|
|
191
|
+
0% {transform: rotate(0deg);}
|
|
192
|
+
100% {transform: rotate(360deg);}
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
195
|
+
<slot></slot>
|
|
196
|
+
<div id="overlay" tabindex="0"></div>
|
|
197
|
+
<div id="loader">
|
|
198
|
+
</div>
|
|
199
|
+
`;
|
|
200
|
+
$a6ba83244539002c$var$ImgLightbox.prototype.template = $a6ba83244539002c$var$tpl;
|
|
201
|
+
var $a6ba83244539002c$export$2e2bcd8739ae039 = $a6ba83244539002c$var$ImgLightbox;
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
//# sourceMappingURL=img-lightbox.js.map
|
package/dist/img-lightbox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"
|
|
1
|
+
{"mappings":"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","sources":["src/shadow/img-lightbox.js","node_modules/@parcel/runtime-js/lib/bundles/runtime-4f432e39aae9d60a.js"],"sourcesContent":["// const loaderSvgUrl = require('../../assets/hourglass.svg');\nimport loaderSvg from 'bundle-text:../../assets/hourglass.svg';\n\nclass ImgLightbox extends HTMLElement {\n\n constructor() {\n super();\n this.loading = false;\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n // Template is added to prototype below the \n // class definition.\n // It's from a template tag because it's \n // supposed to be faster this way.\n this.shadowRoot.appendChild(\n this.template.content.cloneNode(true)\n );\n // Get some element references from shadow DOM:\n this.overlay = this.shadowRoot.querySelector('#overlay');\n this.loadingOverlay = this.shadowRoot.querySelector('#loader');\n // Add the loading SVG to that element:\n this.loadingOverlay.innerHTML = loaderSvg;\n // Component needs a tabIndex to be focusable.\n this.tabIndex = 0;\n // The slotted elements are actually not in \n // the shadow DOM so we can get them like so:\n const link = this.querySelector('a');\n const img = this.querySelector('img');\n if (link !== null) {\n // Store the URL as the full image URL:\n this.fullImage = link.getAttribute('href');\n // Register a click event that has to \n // prevent default:\n //this._addListeners(link);\n // Prevent ability to focus the link:\n link.tabIndex = -1;\n } else if (img !== null) {\n this.fullImage = img.getAttribute('src');\n // Nothing wrong with giving the same tabIndex\n // to things, they're ordered by position.\n // I think.\n //img.tabIndex = 0;\n //this._addListeners(img);\n }\n this.fullImage && this._addListeners();\n }\n\n _addListeners() {\n this.addEventListener('click', this.showLightbox.bind(this), true);\n this.addEventListener('keydown', (e) => {\n // It's common to ignore anything with alt\n // modifiers. I've copied this from Google\n // people.\n if (e.altKey) return;\n // Catch enter and space:\n switch (e.keyCode) {\n case 13:\n case 32:\n this.showLightbox(e);\n default:\n return;\n }\n }, true);\n }\n\n showLightbox(e) {\n // This is required so that the link element does not get \n // followed:\n e.preventDefault();\n\n // Implement some kind of lock:\n if (!this.loading) {\n this.loading = true;\n // Preload the full image.\n // Show the spinner overlay:\n this._showOverlay(this.loadingOverlay);\n if (!this.img) {\n this.img = document.createElement('img');\n // For the moment we just do nothing on error with\n // the link target.\n this.img.addEventListener('error', () => {\n this._hideOverlay(this.loadingOverlay);\n this.loading = false;\n });\n this.img.addEventListener('load', () => this.showImage());\n this.img.src = this.fullImage;\n this.img.tabIndex = 1;\n // Prepare the events to close the overlay.\n // I'm doing this here to light up what's\n // happening in connectedCallback.\n // I'm not doing a check for which key was\n // called on purpose, but maybe I should do\n // that alt key check?\n ['click', 'keydown'].forEach(\n (type) =>\n this.overlay.addEventListener(\n type,\n (e) => this._hideOverlay(e.currentTarget)\n )\n );\n this.overlay.appendChild(this.img);\n } else {\n this.showImage();\n }\n }\n }\n\n showImage() {\n this.img.style.transform = 'scale(0.1)';\n // Hide the loading overlay:\n this._hideOverlay(this.loadingOverlay);\n this._showOverlay(this.overlay);\n // Focus the overlay (requires it to have a tabIndex):\n this.overlay.focus();\n // Start the CSS transition:\n this.img.style.transform = 'scale(1)';\n // Don't forget to unlock the click event:\n this.loading = false;\n }\n\n _showOverlay(overlay) {\n overlay.style.display = 'flex';\n }\n\n _hideOverlay(overlay) {\n overlay.style.display = '';\n }\n\n}\n\n// I'm using a true template tag because the Chrome\n// team says it's faster. The template tag has \n// better browser support than web components anyway.\n// The strange comment here is needed for syntax\n// highlighting with a VS Code extension I'm using.\nconst tpl = document.createElement('template');\ntpl.innerHTML = /*template*/`\n<style>\n :host {\n display: inline-block;\n cursor: pointer;\n position: relative;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host(:focus), :host(:active) {\n outline: 2px solid #77b;\n }\n\n #loader {\n position: absolute;\n z-index: 1;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0,0,0,.2);\n display: none;\n align-items: center;\n justify-content: center;\n }\n\n #overlay {\n position: fixed;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n overflow: hidden;\n z-index: 999;\n background-color: rgba(0,0,0,.5);\n display: none;\n align-items: center;\n justify-content: center;\n }\n\n #loader svg {\n width: 50%;\n opacity: 0.6;\n animation: lightbox-loader 2s infinite;\n }\n\n #overlay img {\n max-width: 100%;\n max-height: 100%;\n transition: transform 0.8s;\n }\n\n @keyframes lightbox-loader {\n 0% {transform: rotate(0deg);}\n 100% {transform: rotate(360deg);}\n }\n</style>\n<slot></slot>\n<div id=\"overlay\" tabindex=\"0\"></div>\n<div id=\"loader\">\n</div>\n`;\nImgLightbox.prototype.template = tpl;\n\nexport default ImgLightbox;\n","module.exports = \"1f3d06fcd1df419a\";"],"names":[],"version":3,"file":"img-lightbox.js.map"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
|
|
2
|
+
function $parcel$interopDefault(a) {
|
|
3
|
+
return a && a.__esModule ? a.default : a;
|
|
4
|
+
}
|
|
5
|
+
// const loaderSvgUrl = require('../../assets/hourglass.svg');
|
|
6
|
+
var $6ba1613a1599b26d$exports = {};
|
|
7
|
+
$6ba1613a1599b26d$exports = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" enable-background=\"new 0 0 64 64\" version=\"1.1\" viewBox=\"0 0 64 64\" xml:space=\"preserve\"><metadata><rdf:RDF><cc:Work rdf:about=\"\"><dc:format>image/svg+xml</dc:format><dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\"/><dc:title/></cc:Work></rdf:RDF></metadata>\n<g>\n\t\t<circle class=\"st0\" cx=\"32\" cy=\"32\" r=\"32\"/>\n\t</g><g class=\"st1\" opacity=\".2\">\n\t\t<path class=\"st2\" d=\"m46 50h-2v-4c0-4.5-3-7.4-3-7.4s-3-2.8-3.9-3.7 0-1.8 0-1.8 1.3-1.3 4.2-4c2.8-2.7 2.7-6.7 2.7-6.7v-5.1l-12-1-12 1v5.1s-0.1 3.9 2.7 6.7c2.8 2.7 4.2 4 4.2 4s0.9 0.9 0 1.8-3.9 3.7-3.9 3.7-3 2.8-3 7.4v4h-2c-1.1 0-2 0.9-2 2s0.9 2 2 2h28c1.1 0 2-0.9 2-2s-0.9-2-2-2z\" fill=\"#231f20\"/>\n\t</g>\n\t\t<path class=\"st3\" d=\"m41 36.6s-3-2.8-3.9-3.7 0-1.8 0-1.8 1.3-1.3 4.2-4c2.8-2.7 2.7-6.7 2.7-6.7v-5.1l-12-1-12 1v5.1s-0.1 3.9 2.7 6.7c2.8 2.7 4.2 4 4.2 4s0.9 0.9 0 1.8-3.9 3.7-3.9 3.7-3 2.8-3 7.4v5.1h24v-5.1c0-4.5-3-7.4-3-7.4z\" fill=\"#fff\"/>\n\t\n\t\t<path class=\"st4\" d=\"m31 43v-11c0-2.1-1-3.4-1.3-3.7l-4.2-4c-1.5-1.4-1.5-3.7-1.5-3.7h16s0 2.3-1.5 3.7l-4.2 4c-0.4 0.3-1.3 1.6-1.3 3.7v11z\"/>\n\t<g class=\"st5\" opacity=\".3\">\n\t\t<path class=\"st2\" d=\"m48 16c0 1.1-0.9 2-2 2h-28c-1.1 0-2-0.9-2-2s0.9-2 2-2h28c1.1 0 2 0.9 2 2z\" fill=\"#231f20\"/>\n\t</g><g fill=\"#fff\">\n\t\t<path class=\"st6\" d=\"m48 14c0 1.1-0.9 2-2 2h-28c-1.1 0-2-0.9-2-2s0.9-2 2-2h28c1.1 0 2 0.9 2 2z\" fill=\"#fff\"/>\n\t</g><g fill=\"#fff\">\n\t\t<path class=\"st6\" d=\"m48 50c0 1.1-0.9 2-2 2h-28c-1.1 0-2-0.9-2-2s0.9-2 2-2h28c1.1 0 2 0.9 2 2z\" fill=\"#fff\"/>\n\t</g><g>\n\t\t<polygon class=\"st4\" points=\"23 48 23 48 32 40 41 48\"/>\n\t</g>\n\n</svg>";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class $ef17987508a11d23$var$ImgLightbox extends HTMLElement {
|
|
11
|
+
constructor(){
|
|
12
|
+
super();
|
|
13
|
+
this.loading = false;
|
|
14
|
+
this.attachShadow({
|
|
15
|
+
mode: 'open'
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
connectedCallback() {
|
|
19
|
+
// Template is added to prototype below the
|
|
20
|
+
// class definition.
|
|
21
|
+
// It's from a template tag because it's
|
|
22
|
+
// supposed to be faster this way.
|
|
23
|
+
this.shadowRoot.appendChild(this.template.content.cloneNode(true));
|
|
24
|
+
// Get some element references from shadow DOM:
|
|
25
|
+
this.overlay = this.shadowRoot.querySelector('#overlay');
|
|
26
|
+
this.loadingOverlay = this.shadowRoot.querySelector('#loader');
|
|
27
|
+
// Add the loading SVG to that element:
|
|
28
|
+
this.loadingOverlay.innerHTML = (0, (/*@__PURE__*/$parcel$interopDefault($6ba1613a1599b26d$exports)));
|
|
29
|
+
// Component needs a tabIndex to be focusable.
|
|
30
|
+
this.tabIndex = 0;
|
|
31
|
+
// The slotted elements are actually not in
|
|
32
|
+
// the shadow DOM so we can get them like so:
|
|
33
|
+
const link = this.querySelector('a');
|
|
34
|
+
const img = this.querySelector('img');
|
|
35
|
+
if (link !== null) {
|
|
36
|
+
// Store the URL as the full image URL:
|
|
37
|
+
this.fullImage = link.getAttribute('href');
|
|
38
|
+
// Register a click event that has to
|
|
39
|
+
// prevent default:
|
|
40
|
+
//this._addListeners(link);
|
|
41
|
+
// Prevent ability to focus the link:
|
|
42
|
+
link.tabIndex = -1;
|
|
43
|
+
} else if (img !== null) this.fullImage = img.getAttribute('src');
|
|
44
|
+
this.fullImage && this._addListeners();
|
|
45
|
+
}
|
|
46
|
+
_addListeners() {
|
|
47
|
+
this.addEventListener('click', this.showLightbox.bind(this), true);
|
|
48
|
+
this.addEventListener('keydown', (e)=>{
|
|
49
|
+
// It's common to ignore anything with alt
|
|
50
|
+
// modifiers. I've copied this from Google
|
|
51
|
+
// people.
|
|
52
|
+
if (e.altKey) return;
|
|
53
|
+
// Catch enter and space:
|
|
54
|
+
switch(e.keyCode){
|
|
55
|
+
case 13:
|
|
56
|
+
case 32:
|
|
57
|
+
this.showLightbox(e);
|
|
58
|
+
default:
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}, true);
|
|
62
|
+
}
|
|
63
|
+
showLightbox(e) {
|
|
64
|
+
// This is required so that the link element does not get
|
|
65
|
+
// followed:
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
// Implement some kind of lock:
|
|
68
|
+
if (!this.loading) {
|
|
69
|
+
this.loading = true;
|
|
70
|
+
// Preload the full image.
|
|
71
|
+
// Show the spinner overlay:
|
|
72
|
+
this._showOverlay(this.loadingOverlay);
|
|
73
|
+
if (!this.img) {
|
|
74
|
+
this.img = document.createElement('img');
|
|
75
|
+
// For the moment we just do nothing on error with
|
|
76
|
+
// the link target.
|
|
77
|
+
this.img.addEventListener('error', ()=>{
|
|
78
|
+
this._hideOverlay(this.loadingOverlay);
|
|
79
|
+
this.loading = false;
|
|
80
|
+
});
|
|
81
|
+
this.img.addEventListener('load', ()=>this.showImage());
|
|
82
|
+
this.img.src = this.fullImage;
|
|
83
|
+
this.img.tabIndex = 1;
|
|
84
|
+
// Prepare the events to close the overlay.
|
|
85
|
+
// I'm doing this here to light up what's
|
|
86
|
+
// happening in connectedCallback.
|
|
87
|
+
// I'm not doing a check for which key was
|
|
88
|
+
// called on purpose, but maybe I should do
|
|
89
|
+
// that alt key check?
|
|
90
|
+
[
|
|
91
|
+
'click',
|
|
92
|
+
'keydown'
|
|
93
|
+
].forEach((type)=>this.overlay.addEventListener(type, (e)=>this._hideOverlay(e.currentTarget)));
|
|
94
|
+
this.overlay.appendChild(this.img);
|
|
95
|
+
} else this.showImage();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
showImage() {
|
|
99
|
+
this.img.style.transform = 'scale(0.1)';
|
|
100
|
+
// Hide the loading overlay:
|
|
101
|
+
this._hideOverlay(this.loadingOverlay);
|
|
102
|
+
this._showOverlay(this.overlay);
|
|
103
|
+
// Focus the overlay (requires it to have a tabIndex):
|
|
104
|
+
this.overlay.focus();
|
|
105
|
+
// Start the CSS transition:
|
|
106
|
+
this.img.style.transform = 'scale(1)';
|
|
107
|
+
// Don't forget to unlock the click event:
|
|
108
|
+
this.loading = false;
|
|
109
|
+
}
|
|
110
|
+
_showOverlay(overlay) {
|
|
111
|
+
overlay.style.display = 'flex';
|
|
112
|
+
}
|
|
113
|
+
_hideOverlay(overlay) {
|
|
114
|
+
overlay.style.display = '';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// I'm using a true template tag because the Chrome
|
|
118
|
+
// team says it's faster. The template tag has
|
|
119
|
+
// better browser support than web components anyway.
|
|
120
|
+
// The strange comment here is needed for syntax
|
|
121
|
+
// highlighting with a VS Code extension I'm using.
|
|
122
|
+
const $ef17987508a11d23$var$tpl = document.createElement('template');
|
|
123
|
+
$ef17987508a11d23$var$tpl.innerHTML = /*template*/ `
|
|
124
|
+
<style>
|
|
125
|
+
:host {
|
|
126
|
+
display: inline-block;
|
|
127
|
+
cursor: pointer;
|
|
128
|
+
position: relative;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
:host([hidden]) {
|
|
132
|
+
display: none;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
:host(:focus), :host(:active) {
|
|
136
|
+
outline: 2px solid #77b;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#loader {
|
|
140
|
+
position: absolute;
|
|
141
|
+
z-index: 1;
|
|
142
|
+
top: 0;
|
|
143
|
+
left: 0;
|
|
144
|
+
width: 100%;
|
|
145
|
+
height: 100%;
|
|
146
|
+
background-color: rgba(0,0,0,.2);
|
|
147
|
+
display: none;
|
|
148
|
+
align-items: center;
|
|
149
|
+
justify-content: center;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#overlay {
|
|
153
|
+
position: fixed;
|
|
154
|
+
top: 0;
|
|
155
|
+
left: 0;
|
|
156
|
+
bottom: 0;
|
|
157
|
+
right: 0;
|
|
158
|
+
overflow: hidden;
|
|
159
|
+
z-index: 999;
|
|
160
|
+
background-color: rgba(0,0,0,.5);
|
|
161
|
+
display: none;
|
|
162
|
+
align-items: center;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#loader svg {
|
|
167
|
+
width: 50%;
|
|
168
|
+
opacity: 0.6;
|
|
169
|
+
animation: lightbox-loader 2s infinite;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
#overlay img {
|
|
173
|
+
max-width: 100%;
|
|
174
|
+
max-height: 100%;
|
|
175
|
+
transition: transform 0.8s;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@keyframes lightbox-loader {
|
|
179
|
+
0% {transform: rotate(0deg);}
|
|
180
|
+
100% {transform: rotate(360deg);}
|
|
181
|
+
}
|
|
182
|
+
</style>
|
|
183
|
+
<slot></slot>
|
|
184
|
+
<div id="overlay" tabindex="0"></div>
|
|
185
|
+
<div id="loader">
|
|
186
|
+
</div>
|
|
187
|
+
`;
|
|
188
|
+
$ef17987508a11d23$var$ImgLightbox.prototype.template = $ef17987508a11d23$var$tpl;
|
|
189
|
+
var $ef17987508a11d23$export$2e2bcd8739ae039 = $ef17987508a11d23$var$ImgLightbox;
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
export {$ef17987508a11d23$export$2e2bcd8739ae039 as default};
|
|
193
|
+
//# sourceMappingURL=img-lightbox.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"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","sources":["src/shadow/img-lightbox.js","node_modules/@parcel/runtime-js/lib/bundles/runtime-7833d3a46095a7f4.js"],"sourcesContent":["// const loaderSvgUrl = require('../../assets/hourglass.svg');\nimport loaderSvg from 'bundle-text:../../assets/hourglass.svg';\n\nclass ImgLightbox extends HTMLElement {\n\n constructor() {\n super();\n this.loading = false;\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n // Template is added to prototype below the \n // class definition.\n // It's from a template tag because it's \n // supposed to be faster this way.\n this.shadowRoot.appendChild(\n this.template.content.cloneNode(true)\n );\n // Get some element references from shadow DOM:\n this.overlay = this.shadowRoot.querySelector('#overlay');\n this.loadingOverlay = this.shadowRoot.querySelector('#loader');\n // Add the loading SVG to that element:\n this.loadingOverlay.innerHTML = loaderSvg;\n // Component needs a tabIndex to be focusable.\n this.tabIndex = 0;\n // The slotted elements are actually not in \n // the shadow DOM so we can get them like so:\n const link = this.querySelector('a');\n const img = this.querySelector('img');\n if (link !== null) {\n // Store the URL as the full image URL:\n this.fullImage = link.getAttribute('href');\n // Register a click event that has to \n // prevent default:\n //this._addListeners(link);\n // Prevent ability to focus the link:\n link.tabIndex = -1;\n } else if (img !== null) {\n this.fullImage = img.getAttribute('src');\n // Nothing wrong with giving the same tabIndex\n // to things, they're ordered by position.\n // I think.\n //img.tabIndex = 0;\n //this._addListeners(img);\n }\n this.fullImage && this._addListeners();\n }\n\n _addListeners() {\n this.addEventListener('click', this.showLightbox.bind(this), true);\n this.addEventListener('keydown', (e) => {\n // It's common to ignore anything with alt\n // modifiers. I've copied this from Google\n // people.\n if (e.altKey) return;\n // Catch enter and space:\n switch (e.keyCode) {\n case 13:\n case 32:\n this.showLightbox(e);\n default:\n return;\n }\n }, true);\n }\n\n showLightbox(e) {\n // This is required so that the link element does not get \n // followed:\n e.preventDefault();\n\n // Implement some kind of lock:\n if (!this.loading) {\n this.loading = true;\n // Preload the full image.\n // Show the spinner overlay:\n this._showOverlay(this.loadingOverlay);\n if (!this.img) {\n this.img = document.createElement('img');\n // For the moment we just do nothing on error with\n // the link target.\n this.img.addEventListener('error', () => {\n this._hideOverlay(this.loadingOverlay);\n this.loading = false;\n });\n this.img.addEventListener('load', () => this.showImage());\n this.img.src = this.fullImage;\n this.img.tabIndex = 1;\n // Prepare the events to close the overlay.\n // I'm doing this here to light up what's\n // happening in connectedCallback.\n // I'm not doing a check for which key was\n // called on purpose, but maybe I should do\n // that alt key check?\n ['click', 'keydown'].forEach(\n (type) =>\n this.overlay.addEventListener(\n type,\n (e) => this._hideOverlay(e.currentTarget)\n )\n );\n this.overlay.appendChild(this.img);\n } else {\n this.showImage();\n }\n }\n }\n\n showImage() {\n this.img.style.transform = 'scale(0.1)';\n // Hide the loading overlay:\n this._hideOverlay(this.loadingOverlay);\n this._showOverlay(this.overlay);\n // Focus the overlay (requires it to have a tabIndex):\n this.overlay.focus();\n // Start the CSS transition:\n this.img.style.transform = 'scale(1)';\n // Don't forget to unlock the click event:\n this.loading = false;\n }\n\n _showOverlay(overlay) {\n overlay.style.display = 'flex';\n }\n\n _hideOverlay(overlay) {\n overlay.style.display = '';\n }\n\n}\n\n// I'm using a true template tag because the Chrome\n// team says it's faster. The template tag has \n// better browser support than web components anyway.\n// The strange comment here is needed for syntax\n// highlighting with a VS Code extension I'm using.\nconst tpl = document.createElement('template');\ntpl.innerHTML = /*template*/`\n<style>\n :host {\n display: inline-block;\n cursor: pointer;\n position: relative;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host(:focus), :host(:active) {\n outline: 2px solid #77b;\n }\n\n #loader {\n position: absolute;\n z-index: 1;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0,0,0,.2);\n display: none;\n align-items: center;\n justify-content: center;\n }\n\n #overlay {\n position: fixed;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n overflow: hidden;\n z-index: 999;\n background-color: rgba(0,0,0,.5);\n display: none;\n align-items: center;\n justify-content: center;\n }\n\n #loader svg {\n width: 50%;\n opacity: 0.6;\n animation: lightbox-loader 2s infinite;\n }\n\n #overlay img {\n max-width: 100%;\n max-height: 100%;\n transition: transform 0.8s;\n }\n\n @keyframes lightbox-loader {\n 0% {transform: rotate(0deg);}\n 100% {transform: rotate(360deg);}\n }\n</style>\n<slot></slot>\n<div id=\"overlay\" tabindex=\"0\"></div>\n<div id=\"loader\">\n</div>\n`;\nImgLightbox.prototype.template = tpl;\n\nexport default ImgLightbox;\n","module.exports = \"0470ae78683fa4f4\";"],"names":[],"version":3,"file":"img-lightbox.mjs.map"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dkvz/img-lightbox",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Web component to add a lightbox effect to img elements",
|
|
5
5
|
"main": "dist/img-lightbox.js",
|
|
6
|
+
"module": "dist/img-lightbox.mjs",
|
|
7
|
+
"source": "src/shadow/img-lightbox.js",
|
|
6
8
|
"scripts": {
|
|
7
9
|
"start": "parcel src/shadow/index.pug",
|
|
8
|
-
"build": "
|
|
10
|
+
"build": "rimraf dist && parcel build",
|
|
9
11
|
"start-no-shadow": "parcel src/no-shadow/index.pug",
|
|
10
|
-
"build-no-shadow": "parcel build src/no-shadow/img-lightbox.js --public-url ./
|
|
12
|
+
"build-no-shadow": "parcel build src/no-shadow/img-lightbox.js --public-url ./"
|
|
11
13
|
},
|
|
12
14
|
"repository": {
|
|
13
15
|
"type": "git",
|
|
@@ -16,9 +18,10 @@
|
|
|
16
18
|
"author": "dkvz",
|
|
17
19
|
"license": "MIT",
|
|
18
20
|
"devDependencies": {
|
|
19
|
-
"parcel-
|
|
20
|
-
"parcel-
|
|
21
|
-
"parcel
|
|
22
|
-
"pug": "^2.0.4"
|
|
21
|
+
"@parcel/transformer-inline-string": "^2.15.4",
|
|
22
|
+
"@parcel/transformer-pug": "^2.15.4",
|
|
23
|
+
"parcel": "^2.15.4",
|
|
24
|
+
"pug": "^2.0.4",
|
|
25
|
+
"rimraf": "^6.0.1"
|
|
23
26
|
}
|
|
24
27
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
const loaderSvgUrl = require('../../assets/hourglass.svg');
|
|
1
|
+
// const loaderSvgUrl = require('../../assets/hourglass.svg');
|
|
2
|
+
import loaderSvg from 'bundle-text:../../assets/hourglass.svg';
|
|
2
3
|
|
|
3
4
|
class ImgLightbox extends HTMLElement {
|
|
4
5
|
|
|
5
6
|
constructor() {
|
|
6
7
|
super();
|
|
7
8
|
this.loading = false;
|
|
8
|
-
this.attachShadow({mode: 'open'});
|
|
9
|
+
this.attachShadow({ mode: 'open' });
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
connectedCallback() {
|
|
@@ -19,13 +20,15 @@ class ImgLightbox extends HTMLElement {
|
|
|
19
20
|
// Get some element references from shadow DOM:
|
|
20
21
|
this.overlay = this.shadowRoot.querySelector('#overlay');
|
|
21
22
|
this.loadingOverlay = this.shadowRoot.querySelector('#loader');
|
|
23
|
+
// Add the loading SVG to that element:
|
|
24
|
+
this.loadingOverlay.innerHTML = loaderSvg;
|
|
22
25
|
// Component needs a tabIndex to be focusable.
|
|
23
26
|
this.tabIndex = 0;
|
|
24
27
|
// The slotted elements are actually not in
|
|
25
28
|
// the shadow DOM so we can get them like so:
|
|
26
29
|
const link = this.querySelector('a');
|
|
27
30
|
const img = this.querySelector('img');
|
|
28
|
-
if (link) {
|
|
31
|
+
if (link !== null) {
|
|
29
32
|
// Store the URL as the full image URL:
|
|
30
33
|
this.fullImage = link.getAttribute('href');
|
|
31
34
|
// Register a click event that has to
|
|
@@ -33,7 +36,7 @@ class ImgLightbox extends HTMLElement {
|
|
|
33
36
|
//this._addListeners(link);
|
|
34
37
|
// Prevent ability to focus the link:
|
|
35
38
|
link.tabIndex = -1;
|
|
36
|
-
} else if (img) {
|
|
39
|
+
} else if (img !== null) {
|
|
37
40
|
this.fullImage = img.getAttribute('src');
|
|
38
41
|
// Nothing wrong with giving the same tabIndex
|
|
39
42
|
// to things, they're ordered by position.
|
|
@@ -75,6 +78,12 @@ class ImgLightbox extends HTMLElement {
|
|
|
75
78
|
this._showOverlay(this.loadingOverlay);
|
|
76
79
|
if (!this.img) {
|
|
77
80
|
this.img = document.createElement('img');
|
|
81
|
+
// For the moment we just do nothing on error with
|
|
82
|
+
// the link target.
|
|
83
|
+
this.img.addEventListener('error', () => {
|
|
84
|
+
this._hideOverlay(this.loadingOverlay);
|
|
85
|
+
this.loading = false;
|
|
86
|
+
});
|
|
78
87
|
this.img.addEventListener('load', () => this.showImage());
|
|
79
88
|
this.img.src = this.fullImage;
|
|
80
89
|
this.img.tabIndex = 1;
|
|
@@ -85,9 +94,9 @@ class ImgLightbox extends HTMLElement {
|
|
|
85
94
|
// called on purpose, but maybe I should do
|
|
86
95
|
// that alt key check?
|
|
87
96
|
['click', 'keydown'].forEach(
|
|
88
|
-
(type) =>
|
|
97
|
+
(type) =>
|
|
89
98
|
this.overlay.addEventListener(
|
|
90
|
-
type,
|
|
99
|
+
type,
|
|
91
100
|
(e) => this._hideOverlay(e.currentTarget)
|
|
92
101
|
)
|
|
93
102
|
);
|
|
@@ -170,7 +179,7 @@ tpl.innerHTML = /*template*/`
|
|
|
170
179
|
justify-content: center;
|
|
171
180
|
}
|
|
172
181
|
|
|
173
|
-
#loader
|
|
182
|
+
#loader svg {
|
|
174
183
|
width: 50%;
|
|
175
184
|
opacity: 0.6;
|
|
176
185
|
animation: lightbox-loader 2s infinite;
|
|
@@ -190,9 +199,8 @@ tpl.innerHTML = /*template*/`
|
|
|
190
199
|
<slot></slot>
|
|
191
200
|
<div id="overlay" tabindex="0"></div>
|
|
192
201
|
<div id="loader">
|
|
193
|
-
<img src="${loaderSvgUrl}">
|
|
194
202
|
</div>
|
|
195
203
|
`;
|
|
196
204
|
ImgLightbox.prototype.template = tpl;
|
|
197
205
|
|
|
198
|
-
export default ImgLightbox;
|
|
206
|
+
export default ImgLightbox;
|
package/src/shadow/index.pug
CHANGED
|
@@ -18,6 +18,13 @@ html
|
|
|
18
18
|
display: inline-block;
|
|
19
19
|
border: 1px solid #333;
|
|
20
20
|
box-shadow: 0px 0px 4px rgba(0,0,0,0.6);
|
|
21
|
+
background-color: #333;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Both slotted elements should be display: block */
|
|
25
|
+
img-lightbox a, img-lightbox img {
|
|
26
|
+
display: block;
|
|
27
|
+
max-width: 200px;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
img-lightbox:active, img-lightbox:focus {
|
package/src/shared/testpage.html
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<p>The image should still show and link to the full version with JS disabled.</p>
|
|
3
3
|
|
|
4
4
|
<img-lightbox>
|
|
5
|
-
<a href="../../assets/example.
|
|
6
|
-
<img src="../../assets/example_preview.
|
|
7
|
-
alt="
|
|
5
|
+
<a href="../../assets/example.jpg" target="_blank" rel="noopener noreferrer">
|
|
6
|
+
<img src="../../assets/example_preview.jpg"
|
|
7
|
+
alt="Some cute cat">
|
|
8
8
|
</a>
|
|
9
9
|
</img-lightbox>
|
|
10
10
|
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
|
-
<script src="./example.js"></script>
|
|
17
|
+
<script type="module" src="./example.js"></script>
|