/**
* 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 '../../common/AssetSelector.es';
import {debounce, PortletBase} from 'frontend-js-web';
import Soy, {Config} from 'metal-soy';
import './FloatingToolbarLinkPanelDelegateTemplate.soy';
import {
ADD_MAPPED_ASSET_ENTRY,
UPDATE_CONFIG_ATTRIBUTES
} from '../../../actions/actions.es';
import {
disableSavingChangesStatusAction,
enableSavingChangesStatusAction,
updateLastSaveDateAction
} from '../../../actions/saveChanges.es';
import getConnectedComponent from '../../../store/ConnectedComponent.es';
import {openAssetBrowser} from '../../../utils/FragmentsEditorDialogUtils';
import {getMappingSourceTypes} from '../../../utils/FragmentsEditorGetUtils.es';
import {encodeAssetId} from '../../../utils/FragmentsEditorIdUtils.es';
import {setIn} from '../../../utils/FragmentsEditorUpdateUtils.es';
import {MAPPING_SOURCE_TYPE_IDS, TARGET_TYPES} from '../../../utils/constants';
import templates from './FloatingToolbarLinkPanel.soy';
/**
* FloatingToolbarLinkPanel
*/
class FloatingToolbarLinkPanel extends PortletBase {
/**
* @inheritdoc
* @review
*/
created() {
this._handleInputHrefKeyUp = debounce(
this._handleInputHrefKeyUp.bind(this),
400
);
}
/**
* @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', 'config', 'title'],
mappedAssetEntry.title
);
nextState = setIn(
nextState,
['item', 'editableValues', 'config', 'encodedId'],
mappedAssetEntry
);
if (
nextState.item.editableValues.config.fieldId &&
!nextState._mappedFieldValue
) {
const fieldId =
nextState.item.editableValues.config.fieldId;
this._getMappedValue(fieldId).then(fieldValue => {
if (typeof fieldValue === 'string') {
this._mappedFieldValue = fieldValue;
}
});
}
}
}
return nextState;
}
/**
* @inheritdoc
* @param {boolean} firstRender
* @review
*/
rendered(firstRender) {
if (firstRender) {
this._selectedAssetEntry.classNameId = this.item.editableValues.config.classNameId;
this._selectedAssetEntry.classPK = this.item.editableValues.config.classPK;
this._selectedSourceTypeId = MAPPING_SOURCE_TYPE_IDS.content;
if (
this.item &&
this.mappingFieldsURL &&
(!this.item.editableValues.config ||
!this.item.editableValues.config.classNameId)
) {
this._selectedSourceTypeId = MAPPING_SOURCE_TYPE_IDS.structure;
}
}
}
/**
* @param {{editableValues: object}} newItem
* @param {{editableValues: object}} [oldItem]
* @inheritdoc
* @review
*/
syncItem(newItem, oldItem) {
if (
newItem &&
oldItem &&
newItem.editableValues.config &&
oldItem.editableValues.config
) {
const newConfig = newItem.editableValues.config;
const oldConfig = newItem.editableValues.config;
if (
newConfig.classNameId !== oldConfig.classNameId ||
newConfig.classPK !== oldConfig.classPK ||
this._fields.length === 0
) {
this._loadFields();
}
} else {
this._loadFields();
}
}
/**
* Clears fields
* @private
* @review
*/
_clearFields() {
this._fields = [];
}
/**
* Clears row config
* @private
* @review
*/
_clearRowConfig() {
const config = {
classNameId: '',
classPK: '',
fieldId: '',
href: '',
mappedField: '',
mapperType: 'link'
};
this._updateRowConfig(config);
}
/**
* @private
* @review
*/
_focusPanel() {
requestAnimationFrame(() => {
if (this.refs.panel) {
this.refs.panel.focus();
}
});
}
_getMappedValue(fieldId) {
if (fieldId) {
return this.fetch(this.getAssetFieldValueURL, {
classNameId: this._selectedAssetEntry.classNameId,
classPK: this._selectedAssetEntry.classPK,
fieldId
})
.then(response => response.json())
.then(response => response.fieldValue);
}
}
/**
* @param {MouseEvent} event
* @private
* @review
*/
_handleAssetBrowserLinkClick(event) {
const {
assetBrowserUrl,
assetBrowserWindowTitle
} = event.delegateTarget.dataset;
openAssetBrowser({
assetBrowserURL: assetBrowserUrl,
callback: selectedAssetEntry => {
this._selectAssetEntry(selectedAssetEntry);
this._focusPanel();
},
eventName: `${this.portletNamespace}selectAsset`,
modalTitle: assetBrowserWindowTitle,
portletNamespace: this.portletNamespace
});
}
/**
* @param {MouseEvent} event
* @private
* @review
*/
_handleAssetEntryLinkClick(event) {
const data = event.delegateTarget.dataset;
this._selectAssetEntry({
classNameId: data.classNameId,
classPK: data.classPk
});
this._focusPanel();
}
/**
* Handle field option change
* @param {Event} event
* @private
* @review
*/
_handleFieldOptionChange(event) {
const fieldId = event.delegateTarget.value;
if (fieldId === '') {
this._clearRowConfig();
} else {
const config = {
classNameId: this._selectedAssetEntry.classNameId,
classPK: this._selectedAssetEntry.classPK,
href: '',
mapperType: 'link'
};
if (
this._selectedSourceTypeId === MAPPING_SOURCE_TYPE_IDS.content
) {
config.fieldId = fieldId;
} else if (
this._selectedSourceTypeId === MAPPING_SOURCE_TYPE_IDS.structure
) {
config.mappedField = fieldId;
}
this._updateRowConfig(config);
if (
!fieldId ||
this._selectedSourceTypeId === MAPPING_SOURCE_TYPE_IDS.structure
) {
this._mappedFieldValue = '';
} else {
this._getMappedValue(fieldId).then(fieldValue => {
if (typeof fieldValue === 'string') {
this._mappedFieldValue = fieldValue;
this._updateRowConfig({href: fieldValue});
}
});
}
}
}
/**
* Callback executed on href keyup
* @param {object} event
* @private
* @review
*/
_handleInputHrefKeyUp(event) {
const hrefElement = event.target;
const config = {
classNameId: '',
classPK: '',
fieldId: '',
href: hrefElement.value,
mappedField: '',
mapperType: 'link'
};
this._updateRowConfig(config);
}
/**
* Handle source option change
* @param {Event} event
* @private
* @review
*/
_handleSourceTypeChange(event) {
this._selectedSourceTypeId = event.delegateTarget.value;
this._clearFields();
this._clearRowConfig();
}
/**
* @param {object} event
* @private
* @review
*/
_handleSubmit(event) {
event.preventDefault();
event.stopPropagation();
}
/**
* Load the list of fields
* @private
* @review
*/
_loadFields() {
let promise;
this._clearFields();
if (
this._selectedSourceTypeId === MAPPING_SOURCE_TYPE_IDS.structure &&
this.selectedMappingTypes.type
) {
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 =>
['text', 'url'].includes(field.type)
);
});
} 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();
}
/**
* Updates row configuration
* @param {object} config Row configuration
* @private
* @review
*/
_updateRowConfig(config) {
this.store
.dispatch(enableSavingChangesStatusAction())
.dispatch({
config,
editableId: this.item.editableId,
fragmentEntryLinkId: this.item.fragmentEntryLinkId,
type: UPDATE_CONFIG_ATTRIBUTES
})
.dispatch(updateLastSaveDateAction())
.dispatch(disableSavingChangesStatusAction());
}
/**
* Handle button type option change
* @param {Event} event
*/
_handleTargetOptionChange(event) {
const targetElement = event.delegateTarget;
const config = {
target: targetElement.options[targetElement.selectedIndex].value
};
this._updateRowConfig(config);
}
/**
* Handle link type option change
* @param {Event} event
*/
_handleTypeOptionChange(event) {
const targetElement = event.delegateTarget;
const type = targetElement.options[targetElement.selectedIndex].value;
const config = {
classNameId: '',
classPK: '',
fieldId: '',
href: '',
mappedField: '',
mapperType: 'link',
type
};
this._clearFields();
this._updateRowConfig(config);
}
}
/**
* State definition.
* @review
* @static
* @type {!Object}
*/
FloatingToolbarLinkPanel.STATE = {
/**
* @default []
* @memberOf FloatingToolbarLinkPanel
* @private
* @review
* @type {object[]}
*/
_fields: Config.array()
.internal()
.value([]),
/**
* Mapped asset field value
* @instance
* @memberOf FloatingToolbarLinkPanel
* @private
* @review
* @type {string}
*/
_mappedFieldValue: Config.internal().string(),
/**
* @default undefined
* @memberof FloatingToolbarLinkPanel
* @review
* @type {string}
*/
_selectedAssetEntry: Config.object()
.internal()
.value({}),
/**
* @default undefined
* @memberof FloatingToolbarLinkPanel
* @review
* @type {string}
*/
_selectedSourceTypeId: Config.oneOf(
Object.values(MAPPING_SOURCE_TYPE_IDS)
).internal(),
/**
* @default TARGET_TYPES
* @memberOf FloatingToolbarLinkPanel
* @private
* @review
* @type {object[]}
*/
_targetTypes: Config.array()
.internal()
.value(TARGET_TYPES),
/**
* @default undefined
* @memberof FloatingToolbarLinkPanel
* @review
* @type {object}
*/
item: Config.object().value(null),
/**
* @default undefined
* @memberof FloatingToolbarLinkPanel
* @review
* @type {!string}
*/
itemId: Config.string().required(),
/**
* @default undefined
* @memberof FloatingToolbarLinkPanel
* @review
* @type {object}
*/
store: Config.object().value(null)
};
const ConnectedFloatingToolbarLinkPanel = getConnectedComponent(
FloatingToolbarLinkPanel,
[
'assetBrowserLinks',
'getAssetFieldValueURL',
'getAssetMappingFieldsURL',
'mappedAssetEntries',
'mappingFieldsURL',
'portletNamespace',
'selectedMappingTypes',
'spritemap'
]
);
Soy.register(ConnectedFloatingToolbarLinkPanel, templates);
export {ConnectedFloatingToolbarLinkPanel, FloatingToolbarLinkPanel};
export default ConnectedFloatingToolbarLinkPanel;