/**
* 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 {FieldSupport} from 'dynamic-data-mapping-form-builder';
import {PagesVisitor} from 'dynamic-data-mapping-form-renderer';
import {createContext} from 'react';
import {
ADD_CUSTOM_OBJECT_FIELD,
ADD_DATA_LAYOUT_RULE,
DELETE_DATA_DEFINITION_FIELD,
DELETE_DATA_LAYOUT_FIELD,
DELETE_DATA_LAYOUT_RULE,
EDIT_CUSTOM_OBJECT_FIELD,
SET_FORM_RENDERER_CUSTOM_FIELDS,
SWITCH_SIDEBAR_PANEL,
UPDATE_APP_PROPS,
UPDATE_CONFIG,
UPDATE_DATA_DEFINITION,
UPDATE_DATA_DEFINITION_AVAILABLE_LANGUAGE,
UPDATE_DATA_DEFINITION_FIELDS,
UPDATE_DATA_LAYOUT,
UPDATE_DATA_LAYOUT_FIELDS,
UPDATE_DATA_LAYOUT_NAME,
UPDATE_DATA_LAYOUT_RULE,
UPDATE_EDITING_DATA_DEFINITION_ID,
UPDATE_EDITING_LANGUAGE_ID,
UPDATE_FIELDSETS,
UPDATE_FIELD_TYPES,
UPDATE_FOCUSED_CUSTOM_OBJECT_FIELD,
UPDATE_FOCUSED_FIELD,
UPDATE_HOVERED_FIELD,
UPDATE_IDS,
UPDATE_PAGES,
} from './actions.es';
import {
getDDMFormFieldSettingsContext,
getDataDefinitionAndDataLayout,
getDataDefinitionField as convertFieldToDataDefinition,
} from './utils/dataConverter.es';
import {getDataDefinitionField} from './utils/dataDefinition.es';
import * as DataLayoutVisitor from './utils/dataLayoutVisitor.es';
import {normalizeRule} from './utils/normalizers.es';
const AppContext = createContext();
const initialState = {
appProps: {},
config: {
allowFieldSets: false,
allowNestedFields: true,
allowRules: false,
disabledProperties: [],
disabledTabs: [],
multiPage: true,
ruleSettings: {},
unimplementedProperties: [],
},
dataDefinition: {
availableLanguageIds: [],
dataDefinitionFields: [],
defaultLanguageId: themeDisplay.getDefaultLanguageId(),
name: {},
},
dataDefinitionId: 0,
dataLayout: {
dataLayoutFields: {},
dataLayoutPages: [],
dataRules: [],
name: {},
paginationMode: 'wizard',
},
dataLayoutId: 0,
editingDataDefinitionId: 0,
editingLanguageId: themeDisplay.getDefaultLanguageId(),
fieldSets: [],
fieldTypes: [],
focusedCustomObjectField: {},
focusedField: {},
hoveredField: {},
initialAvailableLanguageIds: [],
sidebarOpen: true,
sidebarPanelId: 'fields',
spritemap: `${Liferay.ThemeDisplay.getPathThemeImages()}/clay/icons.svg`,
};
const addCustomObjectField = ({fieldTypeName, fieldTypes}) => {
const fieldType = fieldTypes.find(({name}) => name === fieldTypeName);
const dataDefinitionField = convertFieldToDataDefinition(fieldType);
return {
...dataDefinitionField,
label: {
[themeDisplay.getLanguageId()]: fieldType.label,
},
name: FieldSupport.getDefaultFieldName(),
};
};
const deleteDataDefinitionField = (dataDefinition, fieldName) => {
return {
...dataDefinition,
dataDefinitionFields: dataDefinition.dataDefinitionFields.filter(
(field) => field.name !== fieldName
),
};
};
const deleteDataLayoutField = (dataLayout, fieldName) => {
return {
...dataLayout,
dataLayoutFields: {
...dataLayout.dataLayoutFields,
[fieldName]: {
...dataLayout.dataLayoutFields[fieldName],
required: false,
},
},
dataLayoutPages: DataLayoutVisitor.deleteField(
dataLayout.dataLayoutPages,
fieldName
),
};
};
const editFocusedCustomObjectField = (
{propertyName, propertyValue},
{
dataDefinition: {defaultLanguageId},
editingLanguageId,
focusedCustomObjectField,
}
) => {
let localizedValue;
const {
nestedFields,
settingsContext: oldSettingsContext,
} = focusedCustomObjectField;
const visitor = new PagesVisitor(oldSettingsContext.pages);
const settingsContext = {
...oldSettingsContext,
pages: visitor.mapFields((field) => {
const {fieldName} = field;
if (fieldName === propertyName) {
localizedValue = {
...field.localizedValue,
[editingLanguageId || defaultLanguageId]: propertyValue,
};
return {
...field,
localizedValue,
value: propertyValue,
};
}
return field;
}),
};
const dataDefinition = convertFieldToDataDefinition({
nestedFields,
settingsContext,
});
return {
...dataDefinition,
settingsContext,
};
};
/**
* Get unformatted definition field
* @param {object} dataDefinition
* @param {object} field
*/
const getDefinitionField = (dataDefinition, {fieldName}) => {
return getDataDefinitionField(dataDefinition, fieldName);
};
const setDataDefinitionFields = (
dataLayoutBuilder,
dataDefinition,
dataLayout
) => {
const {dataDefinitionFields} = dataDefinition;
const {dataLayoutPages} = dataLayout;
const {
pages,
} = dataLayoutBuilder.formBuilderWithLayoutProvider.refs.layoutProvider.state;
const visitor = new PagesVisitor(pages);
const newFields = [];
visitor.mapFields((field) => {
const convertedField = convertFieldToDataDefinition(field);
if (dataLayoutBuilder.props.contentType === 'app-builder') {
const definitionField = getDefinitionField(dataDefinition, field);
newFields.push({
...convertedField,
customProperties: {
...convertedField.customProperties,
labelAtStructureLevel:
definitionField?.customProperties
?.labelAtStructureLevel ??
convertedField?.customProperties?.labelAtStructureLevel,
},
label: definitionField?.label ?? convertedField?.label,
required: !!definitionField?.required,
});
}
else {
newFields.push(convertedField);
}
});
return newFields.concat(
dataDefinitionFields.filter(
(field) =>
!DataLayoutVisitor.containsField(dataLayoutPages, field.name) &&
!newFields.some(({name}) => name === field.name)
)
);
};
const setDataLayout = (dataLayout, dataLayoutBuilder) => {
const {dataLayoutFields, dataRules: rules} = dataLayout;
const layoutProvider =
dataLayoutBuilder.formBuilderWithLayoutProvider.refs.layoutProvider;
const {
state: {pages},
} = layoutProvider;
const {
availableLanguageIds,
contentType,
defaultLanguageId,
} = dataLayoutBuilder.props;
const {
availableLanguageIds: availableLanguageIdsState,
} = dataLayoutBuilder.state;
const {layout} = getDataDefinitionAndDataLayout({
availableLanguageIds: availableLanguageIdsState ?? availableLanguageIds,
defaultLanguageId,
pages,
paginationMode: layoutProvider.getPaginationMode(),
rules,
});
if (contentType === 'app-builder') {
const visitor = new PagesVisitor(pages);
const fields = [];
visitor.mapFields((field) => {
const formattedDefinitionField = convertFieldToDataDefinition(
field
);
fields.push(formattedDefinitionField);
});
return {
...layout,
dataLayoutFields: fields.reduce((allFields, field) => {
return {
...allFields,
[field.name]: {
...dataLayoutFields[field.name],
required: !!field?.required,
},
};
}, {}),
};
}
return layout;
};
const createReducer = (dataLayoutBuilder) => {
return (state = initialState, action) => {
switch (action.type) {
case ADD_CUSTOM_OBJECT_FIELD: {
const {fieldTypeName} = action.payload;
const {dataDefinition, fieldSets, fieldTypes} = state;
const newCustomObjectField = addCustomObjectField({
dataDefinition,
fieldSets,
fieldTypeName,
fieldTypes,
});
const {
appContext: [{editingLanguageId}],
} = dataLayoutBuilder.props;
const settingsContext = getDDMFormFieldSettingsContext({
dataDefinitionField: newCustomObjectField,
editingLanguageId,
fieldTypes,
});
return {
...state,
dataDefinition: {
...dataDefinition,
dataDefinitionFields: [
...dataDefinition.dataDefinitionFields,
newCustomObjectField,
],
},
focusedCustomObjectField: {
...newCustomObjectField,
settingsContext,
},
};
}
case ADD_DATA_LAYOUT_RULE: {
let {dataRule} = action.payload;
const {
dataLayout: {dataRules},
} = state;
dataRule = normalizeRule(dataRule);
return {
...state,
dataLayout: {
...state.dataLayout,
dataRules: dataRules.concat(dataRule),
},
};
}
case DELETE_DATA_DEFINITION_FIELD: {
const {fieldName} = action.payload;
const {dataDefinition} = state;
return {
...state,
dataDefinition: deleteDataDefinitionField(
dataDefinition,
fieldName
),
};
}
case DELETE_DATA_LAYOUT_FIELD: {
const {fieldName} = action.payload;
const {dataLayout} = state;
return {
...state,
dataLayout: deleteDataLayoutField(dataLayout, fieldName),
};
}
case DELETE_DATA_LAYOUT_RULE: {
const {ruleEditedIndex} = action.payload;
const {
dataLayout: {dataRules},
} = state;
dataLayoutBuilder.formBuilderWithLayoutProvider.refs.layoutProvider?.dispatch?.(
'ruleDeleted',
{
ruleId: ruleEditedIndex,
}
);
return {
...state,
dataLayout: {
...state.dataLayout,
dataRules: dataRules.filter(
(_rule, index) => index !== ruleEditedIndex
),
},
};
}
case EDIT_CUSTOM_OBJECT_FIELD: {
const {dataDefinition, focusedCustomObjectField} = state;
const focusedDataDefinitionField = editFocusedCustomObjectField(
action.payload,
state
);
return {
...state,
dataDefinition: {
...dataDefinition,
dataDefinitionFields: dataDefinition.dataDefinitionFields.map(
(dataDefinitionField) => {
if (
dataDefinitionField.name ===
focusedCustomObjectField.name
) {
return focusedDataDefinitionField;
}
return dataDefinitionField;
}
),
},
focusedCustomObjectField: focusedDataDefinitionField,
};
}
case SET_FORM_RENDERER_CUSTOM_FIELDS: {
return {
...state,
customFields: action.payload,
};
}
case SWITCH_SIDEBAR_PANEL: {
const {sidebarOpen, sidebarPanelId} = action.payload;
return {
...state,
sidebarOpen,
sidebarPanelId,
};
}
case UPDATE_APP_PROPS: {
return {
...state,
appProps: action.payload,
};
}
case UPDATE_DATA_DEFINITION: {
const {dataDefinition} = action.payload;
return {
...state,
dataDefinition: {
...state.dataDefinition,
...dataDefinition,
},
initialAvailableLanguageIds:
dataDefinition.availableLanguageIds,
};
}
case UPDATE_DATA_DEFINITION_AVAILABLE_LANGUAGE: {
const {dataDefinition} = state;
return {
...state,
dataDefinition: {
...dataDefinition,
availableLanguageIds: [
...new Set([
...dataDefinition.availableLanguageIds,
action.payload,
]),
],
},
};
}
case UPDATE_DATA_DEFINITION_FIELDS: {
const {dataDefinitionFields} = action.payload;
return {
...state,
dataDefinition: {
...state.dataDefinition,
dataDefinitionFields,
},
};
}
case UPDATE_DATA_LAYOUT: {
const {dataLayout} = action.payload;
return {
...state,
dataLayout: {
...state.dataLayout,
...dataLayout,
dataRules: dataLayoutBuilder.formBuilderWithLayoutProvider.refs.layoutProvider.getRules(),
},
};
}
case UPDATE_DATA_LAYOUT_FIELDS: {
const {dataLayoutFields} = action.payload;
return {
...state,
dataLayout: {
...state.dataLayout,
dataLayoutFields,
},
};
}
case UPDATE_DATA_LAYOUT_NAME: {
const {name} = action.payload;
return {
...state,
dataLayout: {
...state.dataLayout,
name,
},
};
}
case UPDATE_DATA_LAYOUT_RULE: {
const {dataRule, loc} = action.payload;
const {
dataLayout: {dataRules},
} = state;
return {
...state,
dataLayout: {
...state.dataLayout,
dataRules: dataRules.map((rule, index) => {
if (index === loc) {
return normalizeRule(dataRule);
}
return rule;
}),
},
};
}
case UPDATE_EDITING_DATA_DEFINITION_ID: {
const {editingDataDefinitionId} = action.payload;
return {
...state,
editingDataDefinitionId,
};
}
case UPDATE_EDITING_LANGUAGE_ID: {
return {
...state,
editingLanguageId: action.payload,
};
}
case UPDATE_FIELD_TYPES: {
const {fieldTypes} = action.payload;
return {
...state,
fieldTypes,
};
}
case UPDATE_FIELDSETS: {
const {dataDefinitionId, editingDataDefinitionId} = state;
let {fieldSets} = action.payload;
if (dataDefinitionId) {
fieldSets = fieldSets.filter(
(item) => item.id !== dataDefinitionId
);
}
if (editingDataDefinitionId) {
fieldSets = fieldSets.filter(
(item) => item.id !== editingDataDefinitionId
);
}
return {
...state,
fieldSets,
};
}
case UPDATE_FOCUSED_CUSTOM_OBJECT_FIELD: {
const {dataDefinitionField} = action.payload;
let focusedCustomObjectField = {};
if (Object.keys(dataDefinitionField).length > 0) {
const {
appContext: [{editingLanguageId}],
fieldTypes,
} = dataLayoutBuilder.props;
const settingsContext = getDDMFormFieldSettingsContext({
dataDefinitionField,
editingLanguageId,
fieldTypes,
});
focusedCustomObjectField = {
...dataDefinitionField,
settingsContext,
};
return {
...state,
focusedCustomObjectField,
focusedField: {},
};
}
return {
...state,
focusedCustomObjectField: {},
};
}
case UPDATE_FOCUSED_FIELD: {
const {focusedField} = action.payload;
if (Object.keys(focusedField).length > 0) {
return {
...state,
focusedCustomObjectField: {},
focusedField: {
...focusedField,
settingsContext: {
...focusedField.settingsContext,
editingLanguageId: state.editingLanguageId,
},
},
sidebarOpen: true,
sidebarPanelId: 'fields',
};
}
return {
...state,
focusedCustomObjectField: {},
focusedField: {},
};
}
case UPDATE_HOVERED_FIELD: {
const {hoveredField = {}} = action.payload;
const newHoveredField = getDataDefinitionField(
state.dataDefinition,
hoveredField.fieldName
);
return {
...state,
hoveredField: newHoveredField || state.hoveredField,
};
}
case UPDATE_CONFIG: {
const {config} = action.payload;
return {
...state,
config: config || state.config,
};
}
case UPDATE_IDS: {
const {dataDefinitionId, dataLayoutId} = action.payload;
return {
...state,
dataDefinitionId,
dataLayoutId,
};
}
case UPDATE_PAGES: {
const {dataDefinition, dataLayout} = state;
return {
...state,
dataDefinition: {
...dataDefinition,
dataDefinitionFields: setDataDefinitionFields(
dataLayoutBuilder,
dataDefinition,
dataLayout
),
},
dataLayout: {
...dataLayout,
...setDataLayout(dataLayout, dataLayoutBuilder),
},
};
}
default:
return state;
}
};
};
export default AppContext;
export {initialState, createReducer};