-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
/
GridListTile.js
149 lines (134 loc) · 3.9 KB
/
GridListTile.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
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
import withStyles from '../styles/withStyles';
import { isMuiElement } from '../utils/reactHelpers';
export const styles = {
/* Styles applied to the root element. */
root: {
boxSizing: 'border-box',
flexShrink: 0,
},
/* Styles applied to the `div` element that wraps the children. */
tile: {
position: 'relative',
display: 'block', // In case it's not rendered with a div.
height: '100%',
overflow: 'hidden',
},
/* Styles applied to an `img` element child, if needed to ensure it covers the tile. */
imgFullHeight: {
height: '100%',
transform: 'translateX(-50%)',
position: 'relative',
left: '50%',
},
/* Styles applied to an `img` element child, if needed to ensure it covers the tile. */
imgFullWidth: {
width: '100%',
position: 'relative',
transform: 'translateY(-50%)',
top: '50%',
},
};
const fit = (imgEl, classes) => {
if (!imgEl || !imgEl.complete) {
return;
}
if (imgEl.width / imgEl.height > imgEl.parentNode.offsetWidth / imgEl.parentNode.offsetHeight) {
imgEl.classList.remove(...classes.imgFullWidth.split(' '));
imgEl.classList.add(...classes.imgFullHeight.split(' '));
} else {
imgEl.classList.remove(...classes.imgFullHeight.split(' '));
imgEl.classList.add(...classes.imgFullWidth.split(' '));
}
};
function ensureImageCover(imgEl, classes) {
if (!imgEl) {
return;
}
if (imgEl.complete) {
fit(imgEl, classes);
} else {
imgEl.addEventListener('load', () => {
fit(imgEl, classes);
});
}
}
const GridListTile = React.forwardRef(function GridListTile(props, ref) {
// cols rows default values are for docs only
const {
children,
classes,
className,
// eslint-disable-next-line no-unused-vars
cols = 1,
component: Component = 'li',
// eslint-disable-next-line no-unused-vars
rows = 1,
...other
} = props;
const imgRef = React.useRef(null);
React.useEffect(() => {
ensureImageCover(imgRef.current, classes);
});
React.useEffect(() => {
const handleResize = debounce(() => {
fit(imgRef.current, classes);
}, 166); // Corresponds to 10 frames at 60 Hz.
window.addEventListener('resize', handleResize);
return () => {
handleResize.clear();
window.removeEventListener('resize', handleResize);
};
}, [classes]);
return (
<Component className={clsx(classes.root, className)} ref={ref} {...other}>
<div className={classes.tile}>
{React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return null;
}
if (child.type === 'img' || isMuiElement(child, ['Image'])) {
return React.cloneElement(child, {
ref: imgRef,
});
}
return child;
})}
</div>
</Component>
);
});
GridListTile.propTypes = {
/**
* Theoretically you can pass any node as children, but the main use case is to pass an img,
* in which case GridListTile takes care of making the image "cover" available space
* (similar to `background-size: cover` or to `object-fit: cover`).
*/
children: PropTypes.node,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css) below for more details.
*/
classes: PropTypes.object.isRequired,
/**
* @ignore
*/
className: PropTypes.string,
/**
* Width of the tile in number of grid cells.
*/
cols: PropTypes.number,
/**
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
component: PropTypes.elementType,
/**
* Height of the tile in number of grid cells.
*/
rows: PropTypes.number,
};
export default withStyles(styles, { name: 'MuiGridListTile' })(GridListTile);