Source: custom/form/FormFieldSettings.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 {ClayIconSpriteContext} from '@clayui/icon';
import React, {useEffect, useRef} from 'react';

import {EVENT_TYPES as CORE_EVENT_TYPES} from '../../core/actions/eventTypes.es';
import {INITIAL_CONFIG_STATE} from '../../core/config/initialConfigState.es';
import {INITIAL_STATE} from '../../core/config/initialState.es';
import {ConfigProvider} from '../../core/hooks/useConfig.es';
import {FormProvider, useForm} from '../../core/hooks/useForm.es';
import {
	activePageReducer,
	fieldReducer,
	languageReducer,
	pageValidationReducer,
	pagesStructureReducer,
} from '../../core/reducers/index.es';
import {getConnectedReactComponentAdapter} from '../../util/ReactComponentAdapter.es';
import {parseProps} from '../../util/parseProps.es';
import {Form} from './FormView.es';
import {EVENT_TYPES} from './eventTypes.es';
import {paginationReducer, rulesReducer} from './reducers/index.es';

/**
 * Updates the state of the FieldSettings when any value coming
 * from layers above changes.
 */
const StateSync = ({
	activePage,
	defaultLanguageId,
	editingLanguageId,
	focusedField,
	pages,
	rules,
}) => {
	const dispatch = useForm();

	useEffect(() => {
		dispatch({
			payload: rules,
			type: EVENT_TYPES.RULES.UPDATE,
		});
	}, [dispatch, rules]);

	useEffect(() => {
		dispatch({
			payload: pages,
			type: CORE_EVENT_TYPES.PAGE.UPDATE,
		});
	}, [dispatch, pages]);

	useEffect(() => {
		dispatch({payload: {activePage}, type: CORE_EVENT_TYPES.PAGE.CHANGE});
	}, [dispatch, activePage]);

	useEffect(() => {
		dispatch({
			payload: {
				activePage: focusedField?.settingsContext.currentPage,
				field: focusedField,
			},
			type: CORE_EVENT_TYPES.FIELD.CLICK,
		});
	}, [dispatch, focusedField]);

	useEffect(() => {
		dispatch({
			payload: {defaultLanguageId, editingLanguageId},
			type: CORE_EVENT_TYPES.LANGUAGE.CHANGE,
		});
	}, [dispatch, defaultLanguageId, editingLanguageId]);

	return null;
};

/**
 * Render a new form to be used in the Sidebar so that can edit the
 * properties of a field, a new FormProvider is needed to control
 * the reducers of a Field's settingsContext structure.
 */
export const FormFieldSettings = ({children, onAction, ...otherProps}) => {
	const {config, state} = parseProps(otherProps);

	return (
		<ConfigProvider config={config} initialConfig={INITIAL_CONFIG_STATE}>
			<FormProvider
				initialState={INITIAL_STATE}
				onAction={onAction}
				reducers={[
					activePageReducer,
					fieldReducer,
					languageReducer,
					pagesStructureReducer,
					pageValidationReducer,
					paginationReducer,
					rulesReducer,
				]}
				value={state}
			>
				<StateSync {...state} />
				{children}
			</FormProvider>
		</ConfigProvider>
	);
};

FormFieldSettings.displayName = 'FormFieldSettings';

/**
 * This component is temporary and for exclusive use for Sidebar
 * in Metal.js, this creates a form for editing the properties
 * of a field in Form Builder.
 */
export const FormFieldSettingsAdapter = getConnectedReactComponentAdapter(
	React.forwardRef(({instance, spritemap, ...otherProps}, ref) => {
		const defaultRef = useRef(null);

		return (
			<ClayIconSpriteContext.Provider value={spritemap}>
				<FormFieldSettings
					{...otherProps}
					onAction={({payload, type}) => instance.emit(type, payload)}
				>
					<Form ref={ref ?? defaultRef} />
				</FormFieldSettings>
			</ClayIconSpriteContext.Provider>
		);
	})
);