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

import './FloatingToolbarLayoutBackgroundImagePanelDelegateTemplate.soy';
import {ADD_MAPPED_ASSET_ENTRY} from '../../../actions/actions.es';
import {updateRowConfigAction} from '../../../actions/updateRowConfig.es';
import {getConnectedComponent} from '../../../store/ConnectedComponent.es';
import {
	openAssetBrowser,
	openImageSelector
} from '../../../utils/FragmentsEditorDialogUtils';
import {
	getAssetMappingFields,
	getStructureMappingFields
} from '../../../utils/FragmentsEditorFetchUtils.es';
import {getMappingSourceTypes} from '../../../utils/FragmentsEditorGetUtils.es';
import {encodeAssetId} from '../../../utils/FragmentsEditorIdUtils.es';
import {setIn} from '../../../utils/FragmentsEditorUpdateUtils.es';
import {
	MAPPING_SOURCE_TYPE_IDS,
	COMPATIBLE_TYPES
} from '../../../utils/constants';
import templates from './FloatingToolbarLayoutBackgroundImagePanel.soy';

const IMAGE_SOURCE_TYPE_IDS = {
	content: 'content_mapping',
	selection: 'manual_selection'
};

/**
 * FloatingToolbarLayoutBackgroundImagePanel
 */
class FloatingToolbarLayoutBackgroundImagePanel extends Component {
	/**
	 * @return {boolean} Mapping values are empty
	 * @private
	 * @static
	 * @review
	 */
	static emptyMappingValues(config) {
		if (config.backgroundImage) {
			return (
				!config.backgroundImage.classNameId &&
				!config.backgroundImage.classPK &&
				!config.backgroundImage.fieldId &&
				!config.backgroundImage.mappedField
			);
		}

		return true;
	}

	/**
	 * @return {Array<{id: string, label: string}>} Image source types
	 * @private
	 * @static
	 * @review
	 */
	static getImageSourceTypes() {
		return [
			{
				id: IMAGE_SOURCE_TYPE_IDS.selection,
				label: Liferay.Language.get('manual-selection')
			},
			{
				id: IMAGE_SOURCE_TYPE_IDS.content,
				label: Liferay.Language.get('content-mapping')
			}
		];
	}

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

		nextState = setIn(
			nextState,
			['_backgroundImage'],
			this._getBackgroundImage()
		);

		nextState = setIn(
			nextState,
			['_imageSourceTypeIds'],
			IMAGE_SOURCE_TYPE_IDS
		);

		nextState = setIn(
			nextState,
			['_imageSourceTypes'],
			FloatingToolbarLayoutBackgroundImagePanel.getImageSourceTypes()
		);

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

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

		if (
			nextState.mappingFieldsURL &&
			nextState.selectedMappingTypes &&
			nextState.selectedMappingTypes.type
		) {
			nextState = setIn(
				nextState,
				['_mappingSourceTypes'],
				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', 'config', 'title'],
					mappedAssetEntry.title
				);
			}
		}

		return nextState;
	}

	/**
	 * @inheritdoc
	 * @param {boolean} firstRender
	 * @review
	 */
	rendered(firstRender) {
		if (firstRender) {
			if (this.item.config.backgroundImage) {
				const {backgroundImage} = this.item.config;

				this._selectedAssetEntry.classNameId = this.item.config.backgroundImage.classNameId;
				this._selectedAssetEntry.classPK = this.item.config.backgroundImage.classPK;

				this._selectedImageSourceTypeId =
					backgroundImage.classNameId || backgroundImage.mappedField
						? IMAGE_SOURCE_TYPE_IDS.content
						: IMAGE_SOURCE_TYPE_IDS.selection;
				this._selectedMappingSourceTypeId = backgroundImage.mappedField
					? MAPPING_SOURCE_TYPE_IDS.structure
					: MAPPING_SOURCE_TYPE_IDS.content;
			} else {
				this._selectedImageSourceTypeId =
					IMAGE_SOURCE_TYPE_IDS.selection;
				this._selectedMappingSourceTypeId =
					MAPPING_SOURCE_TYPE_IDS.content;
			}
		}
	}

	/**
	 * @param {{config: object}} newItem
	 * @param {{config: object}} [oldItem]
	 * @inheritdoc
	 * @review
	 */
	syncItem(newItem, oldItem) {
		const changedBackgroundImage =
			newItem.config &&
			oldItem &&
			oldItem.config &&
			(newItem.config.backgroundImage && !oldItem.config.backgroundImage);

		const changedMappedAsset =
			newItem.config &&
			newItem.config.backgroundImage &&
			oldItem &&
			oldItem.config &&
			oldItem.config.backgroundImage &&
			newItem.config.backgroundImage.classNameId !==
				oldItem.config.backgroundImage.classNameId;

		if (!oldItem || changedBackgroundImage || changedMappedAsset) {
			this._loadFields();
		}
	}

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

	/**
	 * Clears mapping values
	 * @private
	 * @review
	 */
	_clearMappingValues() {
		this.store.dispatch(
			updateRowConfigAction(this.itemId, {
				backgroundImage: ''
			})
		);
	}

	/**
	 * Takes the string of the background image
	 * @private
	 */
	_getBackgroundImage() {
		const {config} = this.item;

		if (!config) {
			return '';
		}

		if (typeof config.backgroundImage === 'string') {
			return config.backgroundImage;
		}

		if (typeof config.backgroundImage === 'object') {
			return config.backgroundImage.title || '';
		}

		return '';
	}

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

		openAssetBrowser({
			assetBrowserURL: assetBrowserUrl,
			callback: selectedAssetEntry => {
				this._selectAssetEntry(selectedAssetEntry);
			},
			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
		});
	}

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

		this._selectField(fieldId);
	}

	/**
	 * Show image selector
	 * @private
	 * @review
	 */
	_handleSelectButtonClick() {
		openImageSelector({
			callback: image => this._updateRowBackgroundImage(image),
			imageSelectorURL: this.imageSelectorURL,
			portletNamespace: this.portletNamespace
		});
	}

	/**
	 * Remove existing image if any
	 * @private
	 * @review
	 */
	_handleClearButtonClick() {
		this._updateRowBackgroundImage({});
	}

	/**
	 * @private
	 * @review
	 */
	_handleImageSourceTypeSelect(event) {
		this._selectedImageSourceTypeId = event.delegateTarget.value;

		if (
			FloatingToolbarLayoutBackgroundImagePanel.emptyMappingValues(
				this.item.config
			)
		) {
			this._loadFields();
		} else {
			this._clearMappingValues();
		}
	}

	/**
	 * @private
	 * @review
	 */
	_handleMappingSourceTypeSelect(event) {
		this._selectedMappingSourceTypeId = event.delegateTarget.value;

		if (
			FloatingToolbarLayoutBackgroundImagePanel.emptyMappingValues(
				this.item.config
			)
		) {
			this._loadFields();
		} else {
			this._clearMappingValues();
		}
	}

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

		this._clearFields();

		if (
			this._selectedMappingSourceTypeId ===
			MAPPING_SOURCE_TYPE_IDS.structure
		) {
			promise = getStructureMappingFields(
				this.selectedMappingTypes.type.id,
				this.selectedMappingTypes.subtype.id
			);
		} else if (
			this._selectedMappingSourceTypeId ===
				MAPPING_SOURCE_TYPE_IDS.content &&
			this._selectedAssetEntry.classNameId &&
			this._selectedAssetEntry.classPK
		) {
			promise = getAssetMappingFields(
				this._selectedAssetEntry.classNameId,
				this._selectedAssetEntry.classPK
			);
		}

		if (promise) {
			promise.then(response => {
				this._fields = response.filter(
					field =>
						COMPATIBLE_TYPES['image'].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();
	}

	/**
	 * @param {string} fieldId
	 * @private
	 * @review
	 */
	_selectField(fieldId) {
		let fieldData =
			this._selectedMappingSourceTypeId ===
			MAPPING_SOURCE_TYPE_IDS.content
				? {
						classNameId: this._selectedAssetEntry.classNameId,
						classPK: this._selectedAssetEntry.classPK,
						fieldId
				  }
				: {mappedField: fieldId};

		if (fieldId === '') {
			fieldData = {
				classNameId: '',
				classPK: '',
				fieldId: '',
				mappedField: ''
			};
		}

		this.store.dispatch(
			updateRowConfigAction(this.itemId, {
				backgroundImage: {
					...fieldData
				}
			})
		);
	}

	/**
	 * Updates row image
	 * @param {object} backgroundImage Row image
	 * @private
	 * @review
	 */
	_updateRowBackgroundImage(backgroundImage) {
		this.store.dispatch(
			updateRowConfigAction(this.itemId, {
				backgroundImage
			})
		);
	}
}

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

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

	/**
	 * @default undefined
	 * @memberof FloatingToolbarLayoutBackgroundImagePanel
	 * @review
	 * @type {string}
	 */
	_selectedImageSourceTypeId: Config.oneOf(
		Object.values(IMAGE_SOURCE_TYPE_IDS)
	).internal(),

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

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

	/**
	 * @default undefined
	 * @memberof FloatingToolbarLayoutBackgroundImagePanel
	 * @review
	 * @type {!string}
	 */
	itemId: Config.string().required()
};

const ConnectedFloatingToolbarLayoutBackgroundImagePanel = getConnectedComponent(
	FloatingToolbarLayoutBackgroundImagePanel,
	[
		'assetBrowserLinks',
		'imageSelectorURL',
		'mappedAssetEntries',
		'mappingFieldsURL',
		'portletNamespace',
		'selectedMappingTypes'
	]
);

Soy.register(ConnectedFloatingToolbarLayoutBackgroundImagePanel, templates);

export {
	ConnectedFloatingToolbarLayoutBackgroundImagePanel,
	FloatingToolbarLayoutBackgroundImagePanel
};
export default ConnectedFloatingToolbarLayoutBackgroundImagePanel;