All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.js.components.Layout.objectLayoutContext.tsx Maven / Gradle / Ivy

The newest version!
/**
 * SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
 * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
 */

import React, {createContext, useContext, useReducer} from 'react';

import {
	findObjectFieldIndexById,
	findObjectFieldIndexByName,
	findObjectLayoutRowIndex,
} from '../../utils/layout';
import {BoxesVisitor, RowsVisitor} from '../../utils/visitor';
import {
	BoxType,
	TObjectField,
	TObjectLayout,
	TObjectRelationship,
} from './types';

type TState = {
	creationLanguageId: Liferay.Language.Locale;
	enableCategorization: boolean;
	isViewOnly: boolean;
	objectFieldBusinessTypes: ObjectFieldBusinessType[];
	objectFields: TObjectField[];
	objectLayout: TObjectLayout;
	objectLayoutId: string;
	objectRelationships: TObjectRelationship[];
};

type TAction =
	| {
			payload: {
				creationLanguageId: Liferay.Language.Locale;
				enableCategorization: boolean;
				objectLayout: TObjectLayout;
				objectRelationships: TObjectRelationship[];
			};
			type: TYPES.ADD_OBJECT_LAYOUT;
	  }
	| {
			payload: {
				name: LocalizedValue;
				objectRelationshipId: number;
			};
			type: TYPES.ADD_OBJECT_LAYOUT_TAB;
	  }
	| {
			payload: {
				name: LocalizedValue;
				tabIndex?: number;
				type: BoxType;
			};
			type: TYPES.ADD_OBJECT_LAYOUT_BOX;
	  }
	| {
			payload: {objectFields: TObjectField[]};
			type: TYPES.ADD_OBJECT_FIELDS;
	  }
	| {
			payload: {
				boxIndex: number;
				objectFieldName: string;
				objectFieldSize: number;
				tabIndex: number;
			};
			type: TYPES.ADD_OBJECT_LAYOUT_FIELD;
	  }
	| {
			payload: {name: LocalizedValue};
			type: TYPES.CHANGE_OBJECT_LAYOUT_NAME;
	  }
	| {
			payload: {checked: boolean};
			type: TYPES.SET_OBJECT_LAYOUT_AS_DEFAULT;
	  }
	| {
			payload: {
				attribute: {
					key: 'collapsable';
					value: boolean;
				};
				boxIndex: number;
				tabIndex: number;
			};
			type: TYPES.CHANGE_OBJECT_LAYOUT_BOX_ATTRIBUTE;
	  }
	| {
			payload: {
				boxIndex: number;
				tabIndex: number;
			};
			type: TYPES.DELETE_OBJECT_LAYOUT_BOX;
	  }
	| {
			payload: {
				boxIndex: number;
				tabIndex: number;
			};
			type: TYPES.DELETE_OBJECT_LAYOUT_BOX_CATEGORIZATION;
	  }
	| {
			payload: {
				boxIndex: number;
				columnIndex: number;
				objectFieldName: string;
				rowIndex: number;
				tabIndex: number;
			};
			type: TYPES.DELETE_OBJECT_LAYOUT_FIELD;
	  }
	| {
			payload: {
				tabIndex: number;
			};
			type: TYPES.DELETE_OBJECT_LAYOUT_TAB;
	  };

interface ILayoutContextProps extends Array {
	0: typeof initialState;
	1: React.Dispatch>>;
}

const LayoutContext = createContext({} as ILayoutContextProps);

export enum TYPES {
	ADD_OBJECT_FIELDS = 'ADD_OBJECT_FIELDS',
	ADD_OBJECT_LAYOUT = 'ADD_OBJECT_LAYOUT',
	ADD_OBJECT_LAYOUT_BOX = 'ADD_OBJECT_LAYOUT_BOX',
	ADD_OBJECT_LAYOUT_FIELD = 'ADD_OBJECT_LAYOUT_FIELD',
	ADD_OBJECT_LAYOUT_TAB = 'ADD_OBJECT_LAYOUT_TAB',
	CHANGE_OBJECT_LAYOUT_BOX_ATTRIBUTE = 'CHANGE_OBJECT_LAYOUT_BOX_ATTRIBUTE',
	CHANGE_OBJECT_LAYOUT_NAME = 'CHANGE_OBJECT_LAYOUT_NAME',
	DELETE_OBJECT_LAYOUT_BOX = 'DELETE_OBJECT_LAYOUT_BOX',
	DELETE_OBJECT_LAYOUT_BOX_CATEGORIZATION = 'DELETE_OBJECT_LAYOUT_BOX_CATEGORIZATION',
	DELETE_OBJECT_LAYOUT_FIELD = 'DELETE_OBJECT_LAYOUT_FIELD',
	DELETE_OBJECT_LAYOUT_TAB = 'DELETE_OBJECT_LAYOUT_TAB',
	SET_OBJECT_LAYOUT_AS_DEFAULT = 'SET_OBJECT_LAYOUT_AS_DEFAULT',
}

const initialState = {
	objectFields: [] as TObjectField[],
	objectLayout: {} as TObjectLayout,
	objectRelationships: [] as TObjectRelationship[],
} as TState;

const layoutReducer = (state: TState, action: TAction) => {
	switch (action.type) {
		case TYPES.ADD_OBJECT_LAYOUT: {
			const {
				creationLanguageId,
				enableCategorization,
				objectLayout,
				objectRelationships,
			} = action.payload;

			return {
				...state,
				creationLanguageId,
				enableCategorization,
				objectLayout,
				objectRelationships,
			};
		}
		case TYPES.ADD_OBJECT_LAYOUT_TAB: {
			const {name, objectRelationshipId} = action.payload;

			const newState = {...state};

			const newObjectLayoutTab = {
				name,
				objectLayoutBoxes: [],
				objectRelationshipId,
				priority: 0,
			};

			if (objectRelationshipId) {
				newState.objectRelationships[
					findObjectFieldIndexById(
						newState.objectRelationships,
						objectRelationshipId
					)
				].inLayout = true;
			}

			if (state.objectLayout.objectLayoutTabs.length) {
				return {
					...newState,
					objectLayout: {
						...newState.objectLayout,
						objectLayoutTabs: [
							...newState.objectLayout.objectLayoutTabs,
							newObjectLayoutTab,
						],
					},
				};
			}

			return {
				...newState,
				objectLayout: {
					...newState.objectLayout,
					objectLayoutTabs: [newObjectLayoutTab],
				},
			};
		}
		case TYPES.ADD_OBJECT_LAYOUT_BOX: {
			const {name, tabIndex, type} = action.payload;

			const newState = {...state};

			const objectLayoutBoxes =
				newState.objectLayout.objectLayoutTabs[tabIndex as number]
					.objectLayoutBoxes;

			const newBox = {
				collapsable: false,
				name,
				objectLayoutRows: [],
				priority: 0,
				type,
			};

			const frameworkIndex = objectLayoutBoxes.findIndex(
				(box) => box.type !== 'regular'
			);

			const hasCategorizationBox = !!objectLayoutBoxes.find(
				(layoutBox) => layoutBox.type === 'categorization'
			);

			if (
				(type === 'regular' && hasCategorizationBox) ||
				(type === 'categorization' && frameworkIndex >= 0)
			) {
				objectLayoutBoxes.splice(frameworkIndex, 0, newBox);
			}
			else {
				objectLayoutBoxes.push(newBox);
			}

			return newState;
		}
		case TYPES.ADD_OBJECT_FIELDS: {
			const {objectFields} = action.payload;

			return {
				...state,
				objectFields,
			};
		}
		case TYPES.ADD_OBJECT_LAYOUT_FIELD: {
			const {boxIndex, objectFieldName, objectFieldSize, tabIndex} =
				action.payload;

			const newState = {...state};

			const newField = {
				objectFieldName,
				priority: 0,
				size: objectFieldSize,
			};

			const objectLayoutBox =
				newState.objectLayout.objectLayoutTabs[tabIndex]
					.objectLayoutBoxes[boxIndex];

			const objectLayoutRowIndex = findObjectLayoutRowIndex(
				objectLayoutBox.objectLayoutRows,
				objectFieldSize
			);

			if (objectLayoutRowIndex > -1) {
				objectLayoutBox.objectLayoutRows[
					objectLayoutRowIndex
				].objectLayoutColumns.push(newField);
			}
			else {
				objectLayoutBox.objectLayoutRows.push({
					objectLayoutColumns: [newField],
					priority: 0,
				});
			}

			newState.objectFields[
				findObjectFieldIndexByName(
					newState.objectFields,
					objectFieldName
				)
			].inLayout = true;

			return newState;
		}
		case TYPES.CHANGE_OBJECT_LAYOUT_NAME: {
			const {name} = action.payload;

			return {
				...state,
				objectLayout: {
					...state.objectLayout,
					name,
				},
			};
		}
		case TYPES.SET_OBJECT_LAYOUT_AS_DEFAULT: {
			const {checked} = action.payload;

			return {
				...state,
				objectLayout: {
					...state.objectLayout,
					defaultObjectLayout: checked,
				},
			};
		}
		case TYPES.CHANGE_OBJECT_LAYOUT_BOX_ATTRIBUTE: {
			type TObjectLayoutBoxAttribute = {
				key: keyof {collapsable: boolean};
				value: boolean;
			};

			const {attribute, boxIndex, tabIndex} = action.payload;
			const {key, value}: TObjectLayoutBoxAttribute = attribute;

			const newState = {...state};

			const objectLayoutBox =
				newState.objectLayout.objectLayoutTabs[tabIndex]
					.objectLayoutBoxes[boxIndex];

			objectLayoutBox[key] = value;

			return newState;
		}
		case TYPES.DELETE_OBJECT_LAYOUT_BOX: {
			const {boxIndex, tabIndex} = action.payload;

			const newState = {...state};

			// Change object field inLayout attribute to false to be visible when add field again.

			const objectFieldNames = newState.objectFields.map(
				({name}) => name
			);
			const visitor = new RowsVisitor(
				newState.objectLayout.objectLayoutTabs[
					tabIndex
				].objectLayoutBoxes[boxIndex]
			);

			visitor.mapFields((field) => {
				const objectIndex = objectFieldNames.indexOf(
					field.objectFieldName
				);
				newState.objectFields[objectIndex].inLayout = false;
			});

			// Delete object layout box

			newState.objectLayout.objectLayoutTabs[
				tabIndex
			].objectLayoutBoxes.splice(boxIndex, 1);

			return newState;
		}
		case TYPES.DELETE_OBJECT_LAYOUT_BOX_CATEGORIZATION: {
			const {boxIndex, tabIndex} = action.payload;

			const newState = {...state};

			newState.objectLayout.objectLayoutTabs[
				tabIndex
			].objectLayoutBoxes.splice(boxIndex, 1);

			return newState;
		}
		case TYPES.DELETE_OBJECT_LAYOUT_FIELD: {
			const {boxIndex, columnIndex, objectFieldName, rowIndex, tabIndex} =
				action.payload;

			const newState = {...state};

			const objectLayoutBox =
				newState.objectLayout.objectLayoutTabs[tabIndex]
					.objectLayoutBoxes[boxIndex];

			const objectLayoutRow = objectLayoutBox.objectLayoutRows[rowIndex];

			objectLayoutRow.objectLayoutColumns.splice(columnIndex, 1);

			if (!objectLayoutRow.objectLayoutColumns.length) {
				objectLayoutBox.objectLayoutRows.splice(rowIndex, 1);
			}

			const objectFieldIndex = findObjectFieldIndexByName(
				newState.objectFields,
				objectFieldName
			);

			newState.objectFields[objectFieldIndex].inLayout = false;

			return newState;
		}
		case TYPES.DELETE_OBJECT_LAYOUT_TAB: {
			const {tabIndex} = action.payload;

			const newState = {...state};

			const objectRelationshipId =
				newState.objectLayout.objectLayoutTabs[tabIndex]
					.objectRelationshipId;

			if (objectRelationshipId) {
				const objectRelationshipIds = newState.objectRelationships.map(
					({id}) => id
				);
				const objectRelationshipIndex =
					objectRelationshipIds.indexOf(objectRelationshipId);

				newState.objectRelationships[objectRelationshipIndex].inLayout =
					false;
			}

			// Change object field inLayout attribute to false to be visible when add field again.

			const objectFieldNames = newState.objectFields.map(
				({name}) => name
			);
			const visitor = new BoxesVisitor(
				newState.objectLayout.objectLayoutTabs[tabIndex]
			);

			visitor.mapFields((field) => {
				if (field.objectFieldName) {
					const objectFieldIndex = objectFieldNames.indexOf(
						field.objectFieldName
					);
					newState.objectFields[objectFieldIndex].inLayout = false;
				}
			});

			// Delete object layout tab

			newState.objectLayout.objectLayoutTabs.splice(tabIndex, 1);

			return newState;
		}
		default:
			return state;
	}
};

interface ILayoutContextProviderProps
	extends React.HTMLAttributes {
	value: {
		isViewOnly: boolean;
		objectFieldBusinessTypes: ObjectFieldBusinessType[];
		objectLayoutId: string;
	};
}

export function LayoutContextProvider({
	children,
	value,
}: ILayoutContextProviderProps) {
	const [state, dispatch] = useReducer>(
		layoutReducer,
		{
			...initialState,
			...value,
		}
	);

	return (
		
			{children}
		
	);
}

export function useLayoutContext() {
	return useContext(LayoutContext);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy