kurpachsv/react-gallery

View on GitHub
src/Engine.js

Summary

Maintainability
B
4 hrs
Test Coverage
export const COLUMNS_MAX_COUNT = 5;
export const COLUMN_MAX_WIDTH = 200;
export const COLUMN_MAX_HEIGHT = 200;
export const GUTTER_IN_PERCENT = 0.5;
export const FIXED_BOTTOM_GUTTER_IN_PERCENT = 2.5;
export const FIXED_BOTTOM = 50;
export const PLACEHOLDER_COLOR = '#f0f0f0';
export const FIXED_IMAGE_PLACEHOLDER_COLOR = '#ffffff';
export const VIEWPORT_WIDTH = 0;

class Engine {

    static getMinHeight(items) {
        return Math.min.apply(null,
            (items.map(item => item.height)));
    }

    static getRowWidth(items) {
        return items.map(item => item.width)
            .reduce((a, b) => a + b, 0);
    }

    static resizeByHeight(item, newHeight) {
        const aspectRatio = item.width / item.height;
        return {
            ...item,
            width: aspectRatio * newHeight,
            height: newHeight,
        };
    }

    static resizeByWidth(item, newWidth) {
        const aspectRatio = item.width / item.height;
        return {
            ...item,
            width: newWidth,
            height: newWidth / aspectRatio,
        };
    }

    setImages(images) {
        if (!images) {
            images = [];
        }
        this.images = images;
        return this;
    }

    setGutterInPercent(gutterInPercent) {
        if (!gutterInPercent || gutterInPercent < 0) {
            gutterInPercent = 0;
        }
        this.gutterInPercent = gutterInPercent;
        return this;
    }

    getGutterInPercent() {
        return this.gutterInPercent;
    }

    setMaxColumnsCount(maxColumnsCount) {
        if (!maxColumnsCount || maxColumnsCount < 0) {
            maxColumnsCount = COLUMNS_MAX_COUNT;
        }
        this.maxColumnsCount = maxColumnsCount;
        return this;
    }

    getMaxColumnsCount() {
        return this.maxColumnsCount;
    }

    setColumnMaxWidth(columnMaxWidth) {
        if (!columnMaxWidth || columnMaxWidth < 0) {
            columnMaxWidth = COLUMN_MAX_WIDTH;
        }
        this.columnMaxWidth = columnMaxWidth;
        return this;
    }

    getColumnsMaxWidth() {
        return this.columnMaxWidth;
    }

    setColumnMaxHeight(columnMaxHeight) {
        if (!columnMaxHeight || columnMaxHeight < 0) {
            columnMaxHeight = COLUMN_MAX_HEIGHT;
        }
        this.columnMaxHeight = columnMaxHeight;
        return this;
    }

    setViewportWidth(viewportWidth) {
        this.viewportWidth = viewportWidth;
        return this;
    }

    normalizeByHeight(items) {
        const minHeight = this.columnMaxHeight;
        let result = [];
        items.forEach(el => {
            result.push(Engine.resizeByHeight(el, minHeight));
        });
        return result;
    }

    normalizeByWidth() {
        const calculateHeight = (item) => {
            const itemAfterResize = Engine.resizeByWidth(
                item, calculateWidth(item)
            );
            return itemAfterResize.height;
        };
        const calculateWidth = (item) => {
            return item.width > this.columnMaxWidth ? this.columnMaxWidth : item.width;
        };

        let result = [];
        this.images.forEach(el => {
            result.push({...el, height: calculateHeight(el), width: calculateWidth(el)});
        });
        return result;

    }

    buildRow(items) {
        let row = [];
        let columnsCount = 0;
        let totalRowWidth = 0;
        const isIncompleteRow = () => {
            if (!this.viewportWidth) {
                return columnsCount < this.maxColumnsCount;
            }
            return totalRowWidth < this.viewportWidth;
        };
        while (
            items.length > 0 && isIncompleteRow()
        ) {
            const column = items.shift();
            row.push(column);
            columnsCount++;
            totalRowWidth += column.width;
        }
        return {
            row,
            isIncomplete: isIncompleteRow(),
        };
    }

    getNormalizedItems(items) {
        items = items.map(item => {
            return {
                ...item,
                height: item.height,
                width: item.width,
                src: item.src,
            };
        });
        return this.normalizeByHeight(items);
    }

    calculateHeight(item, row, isLastRow, disableLastRowDetecting) {
        const rowWidth = Engine.getRowWidth(row);
        const ratio = this.maxColumnsCount * this.columnMaxWidth / rowWidth;
        const newHeight = Engine.getMinHeight(row) * ratio * (100 - (row.length - 1) * this.gutterInPercent) / 100;
        if (isLastRow && !disableLastRowDetecting) {
            return newHeight > this.columnMaxHeight ? this.columnMaxHeight : newHeight;
        }
        return newHeight;
    }

    calculateWidth(item, row, isLastRow, disableLastRowDetecting) {
        const itemAfterResize = Engine.resizeByHeight(
            item, this.calculateHeight(item, row, isLastRow, disableLastRowDetecting)
        );
        return itemAfterResize.width;
    }

    calculateFixedWidthInPercent(item, row) {
        return (100 + this.gutterInPercent) / row.length - this.gutterInPercent;
    }

    buildFixedRows() {
        let rows = [];
        const items = this.images.slice(0);
        while (items.length > 0) {
            const row = this.buildRow(items);
            rows.push(row);
        }
        return rows;
    }

    buildRows() {
        let rows = [];
        const items = this.getNormalizedItems(this.images);
        while (items.length > 0) {
            const row = this.buildRow(items);
            rows.push(row);
        }
        return rows;
    }

    buildColumns() {
        const columns = [];
        let order;
        const items = this.normalizeByWidth();
        for (let i = 0; i < this.maxColumnsCount; i++) {
            columns.push({images: [], order: i});
        }
        for (let i = 0; i < items.length; i++) {
            order = (i + 1) % this.maxColumnsCount === 0
                ? this.maxColumnsCount
                : (i + 1) % this.maxColumnsCount;
            columns[order - 1].images.push(items[i]);
            items[i].order = order;
        }
        return columns;
    }
}

export default Engine;