Source: layout-content-page-editor-web/src/main/resources/META-INF/resources/js/actions/updateRowColumnsNumber.es.js

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

import {updatePageEditorLayoutData} from '../utils/FragmentsEditorFetchUtils.es';
import {
	getRowFragmentEntryLinkIds,
	getRowIndex
} from '../utils/FragmentsEditorGetUtils.es';
import {setIn, updateIn} from '../utils/FragmentsEditorUpdateUtils.es';
import {MAX_COLUMNS} from '../utils/rowConstants';
import {UPDATE_ROW_COLUMNS_NUMBER_SUCCESS} from './actions.es';
import {removeFragmentEntryLinksAction} from './removeFragmentEntryLinks.es';
import {
	disableSavingChangesStatusAction,
	enableSavingChangesStatusAction,
	updateLastSaveDateAction
} from './saveChanges.es';

/**
 * @param {number} numberOfColumns
 * @param {string} rowId
 * @return {function}
 * @review
 */
function updateRowColumnsNumberAction(numberOfColumns, rowId) {
	return function(dispatch, getState) {
		const state = getState();

		const columnsSize = Math.floor(MAX_COLUMNS / numberOfColumns);
		const rowIndex = getRowIndex(state.layoutData.structure, rowId);

		const columns = state.layoutData.structure[rowIndex].columns;

		let fragmentEntryLinkIdsToRemove = [];
		let nextData;

		if (numberOfColumns > columns.length) {
			nextData = _addColumns(
				state.layoutData,
				rowIndex,
				numberOfColumns,
				columnsSize
			);
		} else {
			nextData = _removeColumns(
				state.layoutData,
				rowIndex,
				numberOfColumns,
				columnsSize
			);
		}

		if (columns.length > numberOfColumns) {
			fragmentEntryLinkIdsToRemove = getRowFragmentEntryLinkIds({
				columns: columns.slice(numberOfColumns - columns.length)
			});
		}

		dispatch(enableSavingChangesStatusAction());

		updatePageEditorLayoutData(nextData, state.segmentsExperienceId)
			.then(() =>
				dispatch(
					removeFragmentEntryLinksAction(fragmentEntryLinkIdsToRemove)
				)
			)
			.then(() => {
				dispatch(
					updateRowColumnsNumberSuccessAction(
						fragmentEntryLinkIdsToRemove,
						nextData
					)
				);

				dispatch(updateLastSaveDateAction());
				dispatch(disableSavingChangesStatusAction());
			})
			.catch(() => {
				dispatch(disableSavingChangesStatusAction());
			});
	};
}

/**
 * @param {Array} fragmentEntryLinkIdsToRemove
 * @param {object} layoutData
 * @return {object}
 * @review
 */
function updateRowColumnsNumberSuccessAction(
	fragmentEntryLinkIdsToRemove,
	layoutData
) {
	return {
		fragmentEntryLinkIdsToRemove,
		layoutData,
		type: UPDATE_ROW_COLUMNS_NUMBER_SUCCESS
	};
}

/**
 * Returns a new layoutData with the given columns inserted in the specified
 * row with the specified size and resizes the rest of columns to the
 * same size.
 *
 * @param {object} layoutData
 * @param {number} rowIndex
 * @param {number} numberOfColumns
 * @param {number} columnsSize
 * @return {object}
 */
function _addColumns(layoutData, rowIndex, numberOfColumns, columnsSize) {
	let nextColumnId = layoutData.nextColumnId || 0;

	let nextData = updateIn(
		layoutData,
		['structure', rowIndex, 'columns'],
		columns => {
			const newColumns = columns.map((column, index) =>
				setIn(
					column,
					['size'],
					_getColumnSize(numberOfColumns, columnsSize, index)
				)
			);

			const numberOfNewColumns = numberOfColumns - columns.length;
			const numberOfOldColumns = columns.length;

			for (let i = 0; i < numberOfNewColumns; i++) {
				newColumns.push({
					columnId: `${nextColumnId}`,
					fragmentEntryLinkIds: [],
					size: _getColumnSize(
						numberOfColumns,
						columnsSize,
						i + numberOfOldColumns
					)
				});

				nextColumnId += 1;
			}

			return newColumns;
		}
	);

	nextData = setIn(nextData, ['nextColumnId'], nextColumnId);

	return nextData;
}

/**
 * Returns the new size of a column based on the total number of columns of a
 * grid, the general size and its position in the grid.
 *
 * @param {number} numberOfColumns
 * @param {number} columnsSize
 * @param {number} columnIndex
 * @return {string}
 */
function _getColumnSize(numberOfColumns, columnsSize, columnIndex) {
	let newColumnSize = columnsSize;

	const middleColumnPosition = Math.ceil(numberOfColumns / 2) - 1;

	if (middleColumnPosition === columnIndex) {
		newColumnSize = MAX_COLUMNS - (numberOfColumns - 1) * columnsSize;
	}

	return newColumnSize.toString();
}

/**
 * Returns a new layoutData without the columns out of range in the specified
 * row and resizes the rest of columns to the specified size.
 *
 * @param {object} layoutData
 * @param {number} rowIndex
 * @param {number} numberOfColumns
 * @param {number} columnsSize
 * @return {object}
 */
function _removeColumns(layoutData, rowIndex, numberOfColumns, columnsSize) {
	const nextData = updateIn(
		layoutData,
		['structure', rowIndex, 'columns'],
		columns => {
			let newColumns = columns.slice(0, numberOfColumns);

			newColumns = newColumns.map((column, index) =>
				setIn(
					column,
					['size'],
					_getColumnSize(numberOfColumns, columnsSize, index)
				)
			);

			return newColumns;
		}
	);

	return nextData;
}

export {updateRowColumnsNumberAction};