uikit
Version:
UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.
160 lines (131 loc) • 4.58 kB
JavaScript
import Margin from './margin';
import Class from '../mixin/class';
import Scroll from '../mixin/scroll';
import {
addClass,
children,
css,
hasClass,
scrolledOver,
sortBy,
toFloat,
toggleClass,
} from 'uikit-util';
export default {
extends: Margin,
mixins: [Class, Scroll],
name: 'grid',
props: {
masonry: Boolean,
parallax: Number,
},
data: {
margin: 'uk-grid-margin',
clsStack: 'uk-grid-stack',
masonry: false,
parallax: 0,
},
connected() {
this.masonry && addClass(this.$el, 'uk-flex-top uk-flex-wrap-top');
},
update: [
{
write({ columns }) {
toggleClass(this.$el, this.clsStack, columns.length < 2);
},
events: ['resize'],
},
{
read(data) {
let { columns, rows } = data;
// Filter component makes elements positioned absolute
if (
!columns.length ||
(!this.masonry && !this.parallax) ||
positionedAbsolute(this.$el)
) {
data.translates = false;
return false;
}
let translates = false;
const nodes = children(this.$el);
const columnHeights = getColumnHeights(columns);
const margin = getMarginTop(nodes, this.margin) * (rows.length - 1);
const elHeight = Math.max(...columnHeights) + margin;
if (this.masonry) {
columns = columns.map((column) => sortBy(column, 'offsetTop'));
translates = getTranslates(rows, columns);
}
let padding = Math.abs(this.parallax);
if (padding) {
padding = columnHeights.reduce(
(newPadding, hgt, i) =>
Math.max(
newPadding,
hgt + margin + (i % 2 ? padding : padding / 8) - elHeight
),
0
);
}
return { padding, columns, translates, height: translates ? elHeight : '' };
},
write({ height, padding }) {
css(this.$el, 'paddingBottom', padding || '');
height !== false && css(this.$el, 'height', height);
},
events: ['resize'],
},
{
read() {
if (this.parallax && positionedAbsolute(this.$el)) {
return false;
}
return {
scrolled: this.parallax
? scrolledOver(this.$el) * Math.abs(this.parallax)
: false,
};
},
write({ columns, scrolled, translates }) {
if (scrolled === false && !translates) {
return;
}
columns.forEach((column, i) =>
column.forEach((el, j) =>
css(
el,
'transform',
!scrolled && !translates
? ''
: `translateY(${
(translates && -translates[i][j]) +
(scrolled ? (i % 2 ? scrolled : scrolled / 8) : 0)
}px)`
)
)
);
},
events: ['scroll', 'resize'],
},
],
};
function positionedAbsolute(el) {
return children(el).some((el) => css(el, 'position') === 'absolute');
}
function getTranslates(rows, columns) {
const rowHeights = rows.map((row) => Math.max(...row.map((el) => el.offsetHeight)));
return columns.map((elements) => {
let prev = 0;
return elements.map(
(element, row) =>
(prev += row ? rowHeights[row - 1] - elements[row - 1].offsetHeight : 0)
);
});
}
function getMarginTop(nodes, cls) {
const [node] = nodes.filter((el) => hasClass(el, cls));
return toFloat(node ? css(node, 'marginTop') : css(nodes[0], 'paddingLeft'));
}
function getColumnHeights(columns) {
return columns.map((column) => column.reduce((sum, el) => sum + el.offsetHeight, 0));
}