modules_Banner_index.js

import { React, classnames } from '@gravityforms/libraries';
import { useId } from '@gravityforms/react-utils';
import { spacerClasses } from '@gravityforms/utils';
import FileUpload from '../../elements/FileUpload';
import Droplist from '../Droplist';
import { generateComplementaryGradient } from './utils';

const { forwardRef } = React;

/**
 * @module Banner
 * @description Renders a banner component with optional file upload.
 *
 * @since 5.6.0
 *
 * @param {React.ReactNode|React.ReactNode[]}          [children]                - React element children
 * @param {Record<string, any>}                        [customAttributes]        - Custom attributes for the component
 * @param {string|string[]|Record<string, boolean>}    [customClasses]           - Custom classes for the component
 * @param {Record<string, any>}                        [droplistAttributes]      - Custom attributes for the droplist component
 * @param {string|string[]|Record<string, boolean>}    [droplistClasses]         - Custom classes for the droplist component
 * @param {Record<string, any>}                        [fileUploadAttributes]    - Custom attributes for the file upload component
 * @param {boolean}                                    [hasActions=false]        - Whether to show the actions droplist
 * @param {boolean}                                    [hasUpload=false]         - Whether to show the file upload
 * @param {string}                                     [height='']               - The height of the component
 * @param {Record<string, any>}                        [i18n]                    - The i18n strings for the component
 * @param {string}                                     [id='']                   - The ID of the component
 * @param {string}                                     [imageFile='']            - The image file for the component
 * @param {string}                                     [noImageGradient='']      - The gradient for the component
 * @param {string}                                     [noImageGradientColor=''] - The gradient color for the component
 * @param {object}                                     [noImageGradientLayout]   - The optional gradient layout for the component
 * @param {Record<string, any>}                        [removeButtonAttributes]  - Custom attributes for the remove button
 * @param {Record<string, any>}                        [selectButtonAttributes]  - Custom attributes for the select button
 * @param {string|number|string[]|Record<string, any>} [spacing='']              - The spacing for the component
 * @param {Record<string, any>}                        [uploadButtonAttributes]  - Custom attributes for the upload button
 * @param {React.RefObject<HTMLElement>|null}          ref                       - Ref to the component
 *
 * @return {JSX.Element} The banner component.
 *
 * @example
 * import Banner from '@gravityforms/components/react/admin/modules/Banner';
 *
 * return (
 *     <Banner>
 *         { 'children' }
 *     </Banner>
 * );
 *
 */
const Banner = forwardRef(
	(
		{
			children = null,
			customAttributes = {},
			customClasses = [],
			droplistAttributes = {},
			droplistClasses = [],
			fileUploadAttributes = {},
			hasActions = false,
			hasUpload = false,
			height = '',
			i18n = {},
			id: idProp = '',
			imageFile = '',
			noImageGradient = '',
			noImageGradientColor = '',
			noImageGradientLayout = null,
			removeButtonAttributes = {},
			selectButtonAttributes = {},
			spacing = '',
			uploadButtonAttributes = {},
		},
		ref
	) => {
		const id = useId( idProp );

		const style = {};

		// Apply image background if provided
		if ( ! hasUpload && imageFile ) {
			style.backgroundImage = `url(${ imageFile })`;
		}

		// Apply height if provided
		if ( height ) {
			style.height = height;
		}

		// Apply gradient or background color
		if ( ! imageFile ) {
			if ( noImageGradientColor ) {
				style.backgroundColor = noImageGradientColor;
				// Generate complementary gradient if no predefined gradient is set
				if ( ! noImageGradient ) {
					style.backgroundImage = generateComplementaryGradient( noImageGradientColor, noImageGradientLayout );
					style.backgroundBlendMode = 'normal';
				}
			}
		}

		const bannerProps = {
			className: classnames( {
				'gform-banner': true,
				'gform-banner--has-image': imageFile.length > 0,
				[ `gform-gradient--${ noImageGradient }` ]: ( ( ! hasUpload && ! imageFile ) || hasUpload ) && noImageGradient.length > 0,
				...spacerClasses( spacing ),
			}, customClasses ),
			ref,
			style,
			...customAttributes,
		};

		const fileUploadProps = {
			fileURL: imageFile,
			...fileUploadAttributes,
		};

		const listItems = [
			{
				key: 'upload',
				props: {
					element: 'button',
					iconBefore: 'upload-file',
					iconPrefix: 'gravity-component-icon',
					label: i18n?.upload || '',
					...uploadButtonAttributes,
				},
			},
			{
				key: 'select',
				props: {
					element: 'button',
					iconBefore: 'photograph',
					iconPrefix: 'gravity-component-icon',
					label: i18n?.select || '',
					...selectButtonAttributes,
				},
			},
		];
		if ( imageFile ) {
			listItems.push( {
				key: 'remove',
				props: {
					element: 'button',
					iconBefore: 'trash',
					iconPrefix: 'gravity-component-icon',
					label: i18n?.remove || '',
					...removeButtonAttributes,
				},
			} );
		}

		const droplistProps = {
			align: 'right',
			closeOnClick: true,
			customClasses: classnames(
				[ 'gform-banner__droplist' ],
				droplistClasses || [],
			),
			listItems,
			width: 192,
			...droplistAttributes,
			triggerAttributes: {
				ariaId: `${ id }-droplist-aria-labelledby`,
				ariaText: '',
				iconPosition: 'leading',
				iconPrefix: 'gravity-component-icon',
				icon: 'camera-plus',
				id: `${ id }-droplist-trigger`,
				label: ( imageFile ? i18n?.triggerHasImage : i18n?.triggerNoImage ) || '',
				size: 'size-height-s',
				...( droplistAttributes?.triggerAttributes || {} ),
			},
		};

		return (
			<div { ...bannerProps }>
				{ hasUpload && <FileUpload { ...fileUploadProps } /> }
				{ hasActions && <Droplist { ...droplistProps } /> }
				{ children }
			</div>
		);
	}
);

Banner.displayName = 'Banner';

export default Banner;