Source: layout-content-page-editor-web/src/main/resources/META-INF/resources/js/components/floating_toolbar/mapping/FloatingToolbarMappingPanel.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 {PortletBase} from 'frontend-js-web';
import Soy, {Config} from 'metal-soy';

import '../../common/AssetSelector.es';

import './FloatingToolbarMappingPanelDelegateTemplate.soy';
import {ADD_MAPPED_ASSET_ENTRY} from '../../../actions/actions.es';
import {
	updateEditableValueFieldIdAction,
	updateEditableValueMappedFieldAction
} from '../../../actions/updateEditableValue.es';
import getConnectedComponent from '../../../store/ConnectedComponent.es';
import {
	openAssetBrowser,
	openCreateContentDialog
} from '../../../utils/FragmentsEditorDialogUtils';
import {getMappingSourceTypes} from '../../../utils/FragmentsEditorGetUtils.es';
import {encodeAssetId} from '../../../utils/FragmentsEditorIdUtils.es';
import {setIn} from '../../../utils/FragmentsEditorUpdateUtils.es';
import {
	BACKGROUND_IMAGE_FRAGMENT_ENTRY_PROCESSOR,
	COMPATIBLE_TYPES,
	EDITABLE_FRAGMENT_ENTRY_PROCESSOR,
	FRAGMENTS_EDITOR_ITEM_TYPES,
	MAPPING_SOURCE_TYPE_IDS
} from '../../../utils/constants';
import templates from './FloatingToolbarMappingPanel.soy';

/**
 * FloatingToolbarMappingPanel
 */
class FloatingToolbarMappingPanel extends PortletBase {
	/**
	 * @return {boolean} Mapping values are empty
	 * @private
	 * @static
	 * @review
	 */
	static emptyEditableValues(editableValues) {
		return (
			!editableValues.classNameId &&
			!editableValues.classPK &&
			!editableValues.fieldId &&
			!editableValues.mappedField
		);
	}

	/**
	 * @inheritdoc
	 * @param {object} state
	 * @return {object}
	 * @review
	 */
	prepareStateForRender(state) {
		let nextState = state;

		nextState = setIn(
			nextState,
			['mappedAssetEntries'],
			nextState.mappedAssetEntries.map(encodeAssetId)
		);

		nextState = setIn(
			nextState,
			['_sourceTypeIds'],
			MAPPING_SOURCE_TYPE_IDS
		);

		if (
			nextState.mappingFieldsURL &&
			nextState.selectedMappingTypes &&
			nextState.selectedMappingTypes.type
		) {
			nextState = setIn(
				nextState,
				['_sourceTypes'],
				getMappingSourceTypes(
					nextState.selectedMappingTypes.subtype
						? nextState.selectedMappingTypes.subtype.label
						: nextState.selectedMappingTypes.type.label
				)
			);
		}

		if (
			nextState.mappedAssetEntries &&
			nextState._selectedAssetEntry &&
			nextState._selectedAssetEntry.classNameId &&
			nextState._selectedAssetEntry.classPK
		) {
			const mappedAssetEntry = nextState.mappedAssetEntries.find(
				assetEntry =>
					nextState._selectedAssetEntry.classNameId ===
						assetEntry.classNameId &&
					nextState._selectedAssetEntry.classPK === assetEntry.classPK
			);

			if (mappedAssetEntry) {
				nextState = setIn(
					nextState,
					['item', 'editableValues', 'title'],
					mappedAssetEntry.title
				);

				nextState = setIn(
					nextState,
					['item', 'editableValues', 'encodedId'],
					mappedAssetEntry
				);
			}
		}

		return nextState;
	}

	/**
	 * @inheritdoc
	 * @param {boolean} firstRender
	 * @review
	 */
	rendered(firstRender) {
		if (firstRender) {
			this._selectedAssetEntry.classNameId = this.item.editableValues.classNameId;
			this._selectedAssetEntry.classPK = this.item.editableValues.classPK;
			this._selectedSourceTypeId = MAPPING_SOURCE_TYPE_IDS.content;

			if (
				this.item &&
				this.mappingFieldsURL &&
				!this.item.editableValues.classNameId
			) {
				this._selectedSourceTypeId = MAPPING_SOURCE_TYPE_IDS.structure;
			}
		}
	}

	/**
	 * @param {{editableValues: object}} newItem
	 * @param {{editableValues: object}} [oldItem]
	 * @inheritdoc
	 * @review
	 */
	syncItem(newItem, oldItem) {
		if (
			!oldItem ||
			newItem.editableValues.classNameId !==
				oldItem.editableValues.classNameId ||
			newItem.editableValues.mappedField !==
				oldItem.editableValues.mappedField
		) {
			this._loadFields();
		}
	}

	/**
	 * Clears editable values
	 * @private
	 * @review
	 */
	_clearEditableValues() {
		this.store.dispatch(
			updateEditableValueFieldIdAction(
				this.item.fragmentEntryLinkId,
				this._getFragmentEntryProcessor(),
				this.item.editableId,
				{}
			)
		);
	}

	/**
	 * Clears fields
	 * @private
	 * @review
	 */
	_clearFields() {
		this._fields = [];
	}

	/**
	 * Gets right processor depending on itemType
	 * @private
	 * @review
	 */
	_getFragmentEntryProcessor() {
		return this.itemType ===
			FRAGMENTS_EDITOR_ITEM_TYPES.backgroundImageEditable
			? BACKGROUND_IMAGE_FRAGMENT_ENTRY_PROCESSOR
			: EDITABLE_FRAGMENT_ENTRY_PROCESSOR;
	}

	/**
	 * @param {MouseEvent} event
	 * @private
	 * @review
	 */
	_handleAssetBrowserLinkClick(event) {
		const {
			assetBrowserUrl,
			assetBrowserWindowTitle
		} = event.delegateTarget.dataset;

		openAssetBrowser({
			assetBrowserURL: assetBrowserUrl,
			callback: selectedAssetEntry => {
				this._selectAssetEntry(selectedAssetEntry);

				requestAnimationFrame(() => {
					this.refs.panel.focus();
				});
			},
			eventName: `${this.portletNamespace}selectAsset`,
			modalTitle: assetBrowserWindowTitle
		});
	}

	/**
	 * @param {MouseEvent} event
	 * @private
	 * @review
	 */
	_handleAssetEntryLinkClick(event) {
		const data = event.delegateTarget.dataset;

		this._selectAssetEntry({
			classNameId: data.classNameId,
			classPK: data.classPk
		});

		requestAnimationFrame(() => {
			this.refs.panel.focus();
		});
	}

	/**
	 * Opens content creation dialog
	 * @private
	 * @review
	 */
	_handleCreateContentClick() {
		openCreateContentDialog(this.store);
	}

	/**
	 * Handle field option change
	 * @param {Event} event
	 * @private
	 * @review
	 */
	_handleFieldOptionChange(event) {
		const fieldId = event.delegateTarget.value;

		const shouldRemoveValues = fieldId === '';

		if (this._selectedSourceTypeId === MAPPING_SOURCE_TYPE_IDS.content) {
			this.store.dispatch(
				updateEditableValueFieldIdAction(
					this.item.fragmentEntryLinkId,
					this._getFragmentEntryProcessor(),
					this.item.editableId,
					{
						classNameId: shouldRemoveValues
							? ''
							: this._selectedAssetEntry.classNameId,
						classPK: shouldRemoveValues
							? ''
							: this._selectedAssetEntry.classPK,
						fieldId
					}
				)
			);
		} else {
			this.store.dispatch(
				updateEditableValueMappedFieldAction(
					this.item.fragmentEntryLinkId,
					this._getFragmentEntryProcessor(),
					this.item.editableId,
					fieldId
				)
			);
		}
	}

	/**
	 * Handle source option change
	 * @param {Event} event
	 * @private
	 * @review
	 */
	_handleSourceTypeChange(event) {
		this._selectedSourceTypeId = event.delegateTarget.value;

		if (
			FloatingToolbarMappingPanel.emptyEditableValues(
				this.item.editableValues
			)
		) {
			this._loadFields();
		} else {
			this._clearEditableValues();
		}
	}

	/**
	 * Load the list of fields
	 * @private
	 * @review
	 */
	_loadFields() {
		let promise;

		this._clearFields();

		if (this._selectedSourceTypeId === MAPPING_SOURCE_TYPE_IDS.structure) {
			const data = {
				classNameId: this.selectedMappingTypes.type.id
			};

			if (this.selectedMappingTypes.subtype) {
				data.classTypeId = this.selectedMappingTypes.subtype.id;
			}

			promise = this.fetch(this.mappingFieldsURL, data);
		} else if (
			this._selectedSourceTypeId === MAPPING_SOURCE_TYPE_IDS.content &&
			this._selectedAssetEntry.classNameId &&
			this._selectedAssetEntry.classPK
		) {
			promise = this.fetch(this.getAssetMappingFieldsURL, {
				classNameId: this._selectedAssetEntry.classNameId,
				classPK: this._selectedAssetEntry.classPK
			});
		}

		if (promise) {
			promise
				.then(response => response.json())
				.then(response => {
					this._fields = response.filter(
						field =>
							COMPATIBLE_TYPES[this.item.type].indexOf(
								field.type
							) !== -1
					);
				});
		} else if (this._fields.length) {
			this._clearFields();
		}
	}

	/**
	 * @param {object} assetEntry
	 * @param {string} assetEntry.classNameId
	 * @param {string} assetEntry.classPK
	 * @private
	 * @review
	 */
	_selectAssetEntry(assetEntry) {
		this._selectedAssetEntry = assetEntry;

		this.store.dispatch({
			...this._selectedAssetEntry,
			type: ADD_MAPPED_ASSET_ENTRY
		});

		this._loadFields();
	}
}

/**
 * State definition.
 * @review
 * @static
 * @type {object}
 */
FloatingToolbarMappingPanel.STATE = {
	/**
	 * @default []
	 * @memberOf FloatingToolbarMappingPanel
	 * @private
	 * @review
	 * @type {object[]}
	 */
	_fields: Config.array()
		.internal()
		.value([]),

	/**
	 * @default undefined
	 * @memberof FloatingToolbarMappingPanel
	 * @review
	 * @type {string}
	 */
	_selectedAssetEntry: Config.object()
		.internal()
		.value({}),

	/**
	 * @default undefined
	 * @memberof FloatingToolbarMappingPanel
	 * @review
	 * @type {string}
	 */
	_selectedSourceTypeId: Config.oneOf(
		Object.values(MAPPING_SOURCE_TYPE_IDS)
	).internal(),

	/**
	 * @default undefined
	 * @memberof FloatingToolbarMappingPanel
	 * @review
	 * @type {object}
	 */
	item: Config.required(),

	/**
	 * @default undefined
	 * @memberof FloatingToolbarMappingPanel
	 * @review
	 * @type {string}
	 */
	itemId: Config.string().required(),

	/**
	 * @default undefined
	 * @memberof FloatingToolbarMappingPanel
	 * @review
	 * @type {string}
	 */
	itemType: Config.string().required()
};

const ConnectedFloatingToolbarMappingPanel = getConnectedComponent(
	FloatingToolbarMappingPanel,
	[
		'assetBrowserLinks',
		'contentCreationEnabled',
		'defaultSegmentsExperienceId',
		'getAssetMappingFieldsURL',
		'languageId',
		'mappedAssetEntries',
		'mappingFieldsURL',
		'portletNamespace',
		'segmentsExperienceId',
		'selectedItems',
		'selectedMappingTypes'
	]
);

Soy.register(ConnectedFloatingToolbarMappingPanel, templates);

export {
	ConnectedFloatingToolbarMappingPanel,
	FloatingToolbarMappingPanel,
	MAPPING_SOURCE_TYPE_IDS
};
export default ConnectedFloatingToolbarMappingPanel;