Source: layout-content-page-editor-web/src/main/resources/META-INF/resources/js/components/sidebar/fragments/SidebarAvailableElements.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 Component from 'metal-component';
import {DragDrop} from 'metal-drag-drop';
import position from 'metal-position';
import Soy from 'metal-soy';
import {Config} from 'metal-state';

import './FragmentsEditorSidebarCard.es';
import {
	ADD_FRAGMENT_ENTRY_LINK,
	CLEAR_DROP_TARGET,
	UPDATE_DROP_TARGET
} from '../../../actions/actions.es';
import {
	disableSavingChangesStatusAction,
	enableSavingChangesStatusAction,
	updateLastSaveDateAction
} from '../../../actions/saveChanges.es';
import {getConnectedComponent} from '../../../store/ConnectedComponent.es';
import {initializeDragDrop} from '../../../utils/FragmentsEditorDragDrop.es';
import {setDraggingItemPosition} from '../../../utils/FragmentsEditorUpdateUtils.es';
import {
	FRAGMENTS_EDITOR_ITEM_BORDERS,
	FRAGMENTS_EDITOR_ITEM_TYPES
} from '../../../utils/constants';
import templates from './SidebarAvailableElements.soy';

/**
 * SidebarAvailableElements
 */
class SidebarAvailableElements extends Component {
	/**
	 * @inheritDoc
	 * @private
	 * @review
	 */
	attached() {
		this._initializeDragAndDrop();
	}

	/**
	 * @inheritDoc
	 * @private
	 * @review
	 */
	disposed() {
		this._dragDrop.dispose();
	}

	/**
	 * Handle layoutData changed
	 * @inheritDoc
	 * @review
	 */
	syncLayoutData() {
		this._initializeDragAndDrop();
	}

	/**
	 * Callback that is executed when an item is being dragged.
	 * @param {object} eventData
	 * @param {MouseEvent} eventData.originalEvent
	 * @private
	 * @review
	 */
	_handleDrag(eventData) {
		const targetItem = eventData.target;

		const data = targetItem ? targetItem.dataset : null;
		const targetIsColumn = targetItem && 'columnId' in data;
		const targetIsFragment = targetItem && 'fragmentEntryLinkId' in data;
		const targetIsRow = targetItem && 'layoutRowId' in data;

		setDraggingItemPosition(eventData.originalEvent);

		if (targetIsColumn || targetIsFragment || targetIsRow) {
			const mouseY = eventData.originalEvent.clientY;
			const targetItemRegion = position.getRegion(targetItem);

			let nearestBorder = FRAGMENTS_EDITOR_ITEM_BORDERS.bottom;

			if (
				Math.abs(mouseY - targetItemRegion.top) <=
				Math.abs(mouseY - targetItemRegion.bottom)
			) {
				nearestBorder = FRAGMENTS_EDITOR_ITEM_BORDERS.top;
			}

			let dropTargetItemId = null;
			let dropTargetItemType = null;

			if (targetIsColumn) {
				dropTargetItemId = data.columnId;
				dropTargetItemType = FRAGMENTS_EDITOR_ITEM_TYPES.column;
			} else if (targetIsFragment) {
				dropTargetItemId = data.fragmentEntryLinkId;
				dropTargetItemType = FRAGMENTS_EDITOR_ITEM_TYPES.fragment;
			} else if (targetIsRow) {
				dropTargetItemId = data.layoutRowId;
				dropTargetItemType = FRAGMENTS_EDITOR_ITEM_TYPES.row;
			}

			this.store.dispatch({
				dropTargetBorder: nearestBorder,
				dropTargetItemId,
				dropTargetItemType,
				type: UPDATE_DROP_TARGET
			});
		}
	}

	/**
	 * Callback that is executed when we leave a drag target.
	 * @private
	 * @review
	 */
	_handleDragEnd() {
		this.store.dispatch({
			type: CLEAR_DROP_TARGET
		});
	}

	/**
	 * Callback that is executed when an item is dropped.
	 * @param {!object} data
	 * @param {!MouseEvent} event
	 * @private
	 * @review
	 */
	_handleDrop(data, event) {
		event.preventDefault();

		if (data.target) {
			const {itemGroupId, itemId, itemName} = data.source.dataset;

			requestAnimationFrame(() => {
				this._initializeDragAndDrop();
			});

			this.store
				.dispatch(enableSavingChangesStatusAction())
				.dispatch({
					fragmentEntryKey: itemId,
					fragmentName: itemName,
					groupId: itemGroupId,
					type: ADD_FRAGMENT_ENTRY_LINK
				})
				.dispatch(updateLastSaveDateAction())
				.dispatch(disableSavingChangesStatusAction())
				.dispatch({
					type: CLEAR_DROP_TARGET
				});
		}
	}

	/**
	 * Callback that is executed when a fragment entry is clicked.
	 * @param {{
	 *   itemId: !string,
	 *   itemName: !string
	 * }} event
	 * @private
	 */
	_handleEntryClick(event) {
		this.store
			.dispatch(enableSavingChangesStatusAction())
			.dispatch({
				fragmentEntryKey: event.itemId,
				fragmentName: event.itemName,
				groupId: event.itemGroupId,
				type: ADD_FRAGMENT_ENTRY_LINK
			})
			.dispatch(updateLastSaveDateAction())
			.dispatch(disableSavingChangesStatusAction());
	}

	/**
	 * @private
	 * @review
	 */
	_initializeDragAndDrop() {
		if (this._dragDrop) {
			this._dragDrop.dispose();
		}

		this._dragDrop = initializeDragDrop({
			handles: '.fragments-editor__drag-handler',
			sources: '.fragments-editor__drag-source--sidebar-fragment',
			targets: '.fragments-editor__drop-target--sidebar-fragment'
		});

		this._dragDrop.on(DragDrop.Events.DRAG, this._handleDrag.bind(this));

		this._dragDrop.on(DragDrop.Events.END, this._handleDrop.bind(this));

		this._dragDrop.on(
			DragDrop.Events.TARGET_LEAVE,
			this._handleDragEnd.bind(this)
		);
	}
}

/**
 * State definition.
 * @type {!Object}
 * @static
 */
SidebarAvailableElements.STATE = {
	/**
	 * Internal DragDrop instance.
	 * @default null
	 * @instance
	 * @memberOf SidebarAvailableElements
	 * @review
	 * @type {object|null}
	 */
	_dragDrop: Config.internal().value(null)
};

const ConnectedSidebarAvailableElements = getConnectedComponent(
	SidebarAvailableElements,
	['layoutData', 'elements', 'spritemap']
);

Soy.register(ConnectedSidebarAvailableElements, templates);

export {ConnectedSidebarAvailableElements, SidebarAvailableElements};
export default ConnectedSidebarAvailableElements;