Source: layout-admin-web/src/main/resources/META-INF/resources/js/miller_columns/utils/LayoutDropUtils.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 {DROP_TARGET_BORDERS, DROP_TARGET_ITEM_TYPES} from './LayoutDragDrop.es';
import {
	columnIsItemChild,
	getColumnActiveItem,
	getItemColumn,
	getItemColumnIndex
} from './LayoutGetUtils.es';
import {
	appendItemToColumn,
	moveItemInside,
	removeItem
} from './LayoutUpdateUtils.es';

/**
 * @param {object} layoutColumns
 * @param {object} sourceItem
 * @param {string} sourceItemColumnIndex
 * @param {string} targetId
 * @param {string} targetType
 * @return {boolean} Returns whether a drop is valid or not
 * @review
 */
function dropIsValid(
	layoutColumns,
	sourceItem,
	sourceItemColumnIndex,
	targetId,
	targetType
) {
	let targetColumnHasLayoutAssociated = true;
	let targetColumnIsChild = false;
	let targetEqualsSource = false;

	if (targetType === DROP_TARGET_ITEM_TYPES.column) {
		targetColumnIsChild = columnIsItemChild(
			targetId,
			sourceItem,
			sourceItemColumnIndex
		);

		const sourceColumnHasActiveItem =
			getColumnActiveItem(layoutColumns, sourceItemColumnIndex) !== null;

		const targetColumnHasItems = layoutColumns[targetId].length > 0;

		targetColumnHasLayoutAssociated =
			sourceColumnHasActiveItem || targetColumnHasItems;
	} else if (targetType === DROP_TARGET_ITEM_TYPES.item) {
		targetEqualsSource = sourceItem.plid === targetId;
	}

	const targetExists = targetId !== null;

	return (
		targetColumnHasLayoutAssociated &&
		targetExists &&
		!targetEqualsSource &&
		!targetColumnIsChild
	);
}

/**
 * Append an item to a column and calculates newParentPlid and priority
 * @param {object} layoutColumns
 * @param {object} sourceItem
 * @param {number} targetColumnIndex
 * @return {object}
 * @review
 */
function dropItemInsideColumn(layoutColumns, sourceItem, targetColumnIndex) {
	const nextLayoutColumns = appendItemToColumn(
		sourceItem,
		layoutColumns,
		targetColumnIndex
	);

	const newParentPlid = getColumnActiveItem(
		nextLayoutColumns,
		targetColumnIndex - 1
	).plid;

	const priority = layoutColumns[targetColumnIndex].length;

	return {
		layoutColumns: nextLayoutColumns,
		newParentPlid,
		priority
	};
}

/**
 * Inserts an item inside another item's children and
 * calculates new parent plid and priority
 * @param {object} layoutColumns
 * @param {object} sourceItem
 * @param {number} sourceItemColumnIndex
 * @param {bollean} pathUpdated
 * @param {object} targetItem
 * @return {object}
 * @review
 */
function dropItemInsideItem(
	layoutColumns,
	sourceItem,
	sourceItemColumnIndex,
	pathUpdated,
	targetItem
) {
	let nextLayoutColumns = layoutColumns;
	let priority = null;

	nextLayoutColumns = moveItemInside(
		layoutColumns,
		pathUpdated,
		sourceItem,
		sourceItemColumnIndex,
		targetItem
	);

	if (pathUpdated) {
		const targetColumnIndex = getItemColumnIndex(
			nextLayoutColumns,
			targetItem.plid
		);

		const nextColumn = nextLayoutColumns[targetColumnIndex + 1];

		priority = nextColumn.indexOf(sourceItem);
	}

	return {
		layoutColumns: nextLayoutColumns,
		newParentPlid: targetItem.plid,
		priority
	};
}

/**
 * Insert an item next to another item and
 * calculates new parent plid and priority
 * @param {object} layoutColumns
 * @param {object} sourceItem
 * @param {string} dropPosition
 * @param {object} targetItem
 * @return {object}
 * @review
 */
function dropItemNextToItem(
	layoutColumns,
	sourceItem,
	dropPosition,
	targetItem
) {
	const nextLayoutColumns = removeItem(sourceItem.plid, layoutColumns);

	const targetColumn = getItemColumn(nextLayoutColumns, targetItem.plid);
	const targetColumnIndex = nextLayoutColumns.indexOf(targetColumn);

	const parentPlid = getColumnActiveItem(
		nextLayoutColumns,
		targetColumnIndex - 1
	).plid;

	const targetItemIndex = targetColumn.findIndex(
		targetColumnItem => targetColumnItem.plid === targetItem.plid
	);

	const priority =
		dropPosition === DROP_TARGET_BORDERS.bottom
			? targetItemIndex + 1
			: targetItemIndex;

	const nextTargetColumn = [...targetColumn];

	nextTargetColumn.splice(priority, 0, sourceItem);

	nextLayoutColumns[targetColumnIndex] = nextTargetColumn;

	return {
		layoutColumns: nextLayoutColumns,
		newParentPlid: parentPlid,
		priority
	};
}

export {
	dropIsValid,
	dropItemInsideColumn,
	dropItemInsideItem,
	dropItemNextToItem
};