modules_Avatar_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 Icon from '../../elements/Icon';
import Droplist from '../Droplist';

const { forwardRef } = React;

/**
 * @module Avatar
 * @description Renders an avatar component with optional file upload.
 *
 * @since 5.4.6
 *
 * @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 {boolean}                                    [hasActions=false]                           - Whether to show the actions droplist
 * @param {boolean}                                    [hasBorder=false]                            - Whether the avatar has a border
 * @param {boolean}                                    [hasUpload=false]                            - Whether to show the file upload
 * @param {boolean}                                    [hideInitials=false]                         - Whether to hide the initials
 * @param {boolean}                                    [hideLogo=false]                             - Whether to hide the logo
 * @param {boolean}                                    [hideNoImageIcon=false]                      - Whether to show the no image icon in the background.
 * @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 avatar
 * @param {string}                                     [initials='']                                - The initials for the avatar
 * @param {React.ComponentType}                        [Logo=null]                                  - The logo component
 * @param {string}                                     [noImageIcon='user']                         - The icon to show when no image is present
 * @param {string}                                     [noImageIconPrefix='gravity-component-icon'] - The icon prefix
 * @param {Record<string, any>}                        [selectButtonAttributes]                     - Custom attributes for the select button
 * @param {'2xs'|'xs'|'sm'|'md'|'lg'|'xl'|'2xl'}       [size='lg']                                  - The size of the avatar
 * @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 avatar component.
 *
 * @example
 * import Avatar from '@gravityforms/components/react/admin/modules/Avatar';
 *
 * return (
 *     <Avatar>
 *         { 'children' }
 *     </Avatar>
 * );
 *
 */
const Avatar = forwardRef( ( {
	children = null,
	customAttributes = {},
	customClasses = [],
	droplistAttributes = {},
	droplistClasses = [],
	fileUploadAttributes = {},
	hasActions = false,
	hasBorder = false,
	hasUpload = false,
	hideInitials = false,
	hideLogo = false,
	hideNoImageIcon = false,
	i18n = {},
	id: idProp = '',
	imageFile = '',
	initials = '',
	Logo = null,
	noImageIcon = 'user',
	noImageIconPrefix = 'gravity-component-icon',
	selectButtonAttributes = {},
	size = 'lg',
	spacing = '',
	uploadButtonAttributes = {},
}, ref ) => {
	const id = useId( idProp );

	const style = {};
	if ( ! hasUpload && imageFile ) {
		style.backgroundImage = `url(${ imageFile })`;
	}

	const avatarProps = {
		className: classnames( {
			'gform-avatar': true,
			[ `gform-avatar--size-${ size }` ]: true,
			'gform-avatar--has-image': imageFile.length > 0,
			'gform-avatar--initials-hidden': hideInitials,
			'gform-avatar--no-image-icon-hidden': hideNoImageIcon,
			'gform-avatar--has-border': hasBorder,
			'gform-avatar--has-upload': hasUpload,
			'gform-avatar--has-actions': hasActions,
			...spacerClasses( spacing ),
		}, customClasses ),
		ref,
		...customAttributes,
	};

	const avatarInnerProps = {
		className: 'gform-avatar__inner',
		style,
	};

	const fileUploadProps = {
		fileURL: imageFile,
		uploadIcon: 'plus-regular',
		...fileUploadAttributes,
	};

	const noImageIconProps = {
		customClasses: [ 'gform-avatar__no-image-icon' ],
		icon: noImageIcon,
		iconPrefix: noImageIconPrefix,
	};

	const initialsProps = {
		className: 'gform-avatar__initials',
	};

	const renderLogo = () => {
		if ( Logo && ! hideLogo ) {
			return (
				<div className="gform-avatar__logo">
					<Logo />
				</div>
			);
		}
	};

	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,
			},
		},
	];

	const droplistProps = {
		align: 'left',
		closeOnClick: true,
		customClasses: classnames(
			[ 'gform-avatar__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',
			type: 'icon-grey',
			...( droplistAttributes?.triggerAttributes || {} ),
		},
	};

	return (
		<div { ...avatarProps }>
			<div { ...avatarInnerProps }>
				{ hasUpload && <FileUpload { ...fileUploadProps } /> }
				{ ! imageFile && ! hideInitials && <span { ...initialsProps }>{ initials }</span> }
				{ ! imageFile && ! hideNoImageIcon && <Icon { ...noImageIconProps } /> }
				{ children }
				{ renderLogo() }
				{ hasActions && <Droplist { ...droplistProps } /> }
			</div>
		</div>
	);
} );

Avatar.displayName = 'Avatar';

export default Avatar;