This repository has been archived by the owner on Jun 12, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
image.js
170 lines (154 loc) · 5.5 KB
/
image.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
const breakpoints = require('../helpers/breakpoints');
const buildImageServiceUrl = require('../helpers/build-image-service-url');
const createImageSizes = require('../helpers/create-image-sizes');
/**
* @param {string} src - Actual src to use. If set, assume it's non-responsive, i.e. ignore url, widths, sizes
* @param {string} srcSet - URL of the image to use in srcset
* @param {string} url - DEPRECATED URL of the image
* @param {number[]} widths - Widths of the image, in pixels
* @param {Object} [sizes = {}] - OPTIONAL Keys are breakpoints, values the length. Used instead of colspan and position e.g.
* { default: '33.3vw', L: 'calc(.333 * (100vw - 12em)'}
* @param {string} [colspan = "{}"] - OPTIONAL Keys are breakpoints, values the grid columns required. Used together with position to calculate sizes attribute e.g.
* '{"default": 12, "M": 5}',
* @param {string} [position = "{}"] - OPTIONAL Keys are breakpoints, values the position (top, bottom, left, right, stream-list). Used together with colspan to calculate sizes attribute e.g.
* '{"default": "left", "M": "top"}'
* @param {string[]|string} [classes = []] - Additional classes to add to the img element
* @param {string[]|string} [wrapperClasses = []] - Additional classes to add to the wrapper element
* @param {string} [width] - Width of the image
* @param {string} [height] - Height of the image
* @param {string} [alt = ''] - Alt text for the image
* @param {boolean} [lazyLoad = false] - Lazy load the image
* @param {string} [sourceParam = 'next'] - The `source` parameter used in the image service request
* @param {string|number} [placeholder] - Set to add a placeholder. Value should be the ratio of the image as a number
* (width divided by height, e.g.`16/9`), or string (`square` or `landscape`)
*/
class ImagePresenter {
constructor (data) {
this.data = data;
}
get ratio () {
let ratio = this.placeholder;
if (typeof ratio === 'number') {
if (ratio === 1) {
ratio = 'square';
} else if (ratio === 16 / 9) {
ratio = 'landscape';
}
}
return ratio;
}
get lazyLoad () {
return this.data.lazyLoad;
}
get placeholder () {
if (parseFloat(this.data.placeholder)) {
return (parseFloat(this.data.placeholder));
}
return this.data.placeholder;
}
get wrapperAttrs () {
let wrapperClassName = 'n-image-wrapper';
let style;
let styleString;
const ratio = this.ratio;
if (this.data.wrapperClasses) {
wrapperClassName += ` ${this.optionalClasses(this.data.wrapperClasses)}`;
}
if (this.lazyLoad) {
wrapperClassName += ' n-image-wrapper--lazy-loading';
}
if (this.placeholder) {
wrapperClassName += ' n-image-wrapper--placeholder';
}
if (['square'].indexOf(ratio) > -1) {
wrapperClassName += ` n-image-wrapper--${ratio}-placeholder`;
} else if (typeof ratio === 'number') {
style = { 'paddingBottom': `${100 * (1 / ratio)}%` };
styleString = `padding-bottom:${style.paddingBottom}`;
}
return {
className: wrapperClassName,
styleString,
style
};
}
get imgAttrs () {
const className = `n-image ${this.optionalClasses(this.data.classes)}`;
const attrs = {
alt: this.data.alt || '',
className
};
if (!attrs.alt) {
attrs.role = 'presentation';
}
Object.assign(attrs, this.imageSource());
if (this.lazyLoad) {
Object.assign(attrs, this.hideImage(attrs));
attrs.className += ' n-image--lazy-loading';
}
return attrs;
}
// handlebars is limited to strings for attributes, conversion for array and objects
convertToJson (datum) {
return (typeof datum === 'string') ? JSON.parse(datum) : datum;
}
optionalClasses (classes) {
if (!classes) {
return '';
} else if (Array.isArray(classes)) {
return classes.join(' ');
} else {
return classes;
}
}
// convert the `src` or `srcset` attributes to a data attribute
hideImage (obj) {
return Object.keys(obj)
.reduce((dataObj, name) => {
if (name === 'src' || name === 'srcSet') {
dataObj[`data-${name.toLowerCase()}`] = obj[name];
obj[name] = null;
}
return dataObj;
}, {});
}
imageSource () {
const sourceAttrs = {};
const sizes = this.convertToJson(this.data.sizes) || this.imageSizes() || {};
const widths = this.convertToJson(this.data.widths) || [];
const srcSet = this.data.srcSet || this.data.url;
const sourceParam = this.data.sourceParam || 'next';
if (!this.data.src && !srcSet) {
// Avoiding n-logger due to errors during build (plus client-side bloat)
// eslint-disable-next-line no-console
console.warn('No source for image provided');
} else if (this.data.src) {
return { src: this.data.src, width: this.data.width, height: this.data.height };
} else {
if (widths.length === 0) {
// eslint-disable-next-line no-console
console.warn('Widths must be provided if setting srcSet');
}
sourceAttrs.srcSet = widths
.sort((widthOne, widthTwo) => widthTwo - widthOne)
.map(width => `${buildImageServiceUrl(srcSet, { width, source: sourceParam })} ${width}w`)
.join(', ');
sourceAttrs.sizes = breakpoints
.map(breakpoint => {
const size = sizes[breakpoint.name];
return size ?
breakpoint.name === 'default' ? size : `(min-width: ${breakpoint.px}px) ${size}` :
null;
})
.filter(size => size)
.join(', ');
return sourceAttrs;
}
}
imageSizes () {
const colspan = this.convertToJson(this.data.colspan) || {'default': 12};
const position = this.convertToJson(this.data.position) || {'default': 'top'};
return createImageSizes(colspan, position);
}
}
module.exports = ImagePresenter;