import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { spacerClasses } from '@gravityforms/utils';
const { forwardRef } = React;
const possiblePositions = [
'center',
'top',
'bottom',
'left',
'right',
'top left',
'top center',
'top right',
'center left',
'center center',
'center right',
'bottom left',
'bottom center',
'bottom right',
];
/**
* @module Image
* @description An image component in react.
*
* @since 1.1.15
*
* @param {object} props Component props.
* @param {string} props.altText The alt text for the image.
* @param {boolean} props.asBg Whether the image is a background image or not.
* @param {number} props.aspectRatio The aspect ratio of the image, as width / height. This only applies to background images.
* @param {JSX.Element|string} props.caption The caption for the image. This only applies if the image is a background image.
* @param {object} props.captionAttributes Attributes for the image caption element.
* @param {object} props.customAttributes Custom attributes for the component.
* @param {string|Array|object} props.customClasses Custom classes for the component.
* @param {number} props.height The height of the image, in px. This only applies if the image is a background image.
* @param {object} props.imageAttributes Attributes for the image element.
* @param {string} props.imagePosition The position of the image, if it is a background image.
* @param {boolean} props.lazyload Whether to lazyload the image or not.
* @param {string|number|Array|object} props.spacing The spacing for the component, as a string, number, array, or object.
* @param {string} props.url The url of the image.
* @param {number} props.width The width of the image, in px.
* @param {object|null} ref Ref to the component.
*
* @return {JSX.Element} Return the function image component in React.
*
* @example
* import Image from '@gravityforms/components/react/admin/elements/Image';
*
* return (
* <Image
* asBg={ true }
* aspectRatio={ 1.5 }
* url="https://path.to.image/"
* />
* );
*
*/
const Image = forwardRef( ( {
altText = '',
asBg = false,
aspectRatio = 0,
caption = null,
captionAttributes = {},
customAttributes = {},
customClasses = [],
height = 0,
imageAttributes = {},
imagePosition = 'center',
lazyload = false,
spacing = '',
url = '',
width = 0,
}, ref ) => {
/**
* @function getBackgroundPosition
* @description Get the background position for the background image.
*
* @param {string} position The background image position.
*
* @return {string} The position of the background image.
*/
const getBackgroundPosition = ( position ) => {
if ( ! possiblePositions.includes( position ) ) {
return '';
}
return position;
};
/**
* @function getBackgroundImage
* @description Get the markup in react for a background image.
*
* @since 1.1.15
*
* @param {object} props Props for the background image.
* @param {object} props.attributes The attributes for the image wrapper.
* @param {object} props.imageAttributes The attributes for the background image.
*
* @return {JSX.Element} The background image.
*/
const getBackgroundImage = ( {
attributes,
imageAttributes: bgImageAttributes,
} ) => {
return (
<div { ...attributes }>
<div { ...bgImageAttributes } />
</div>
);
};
/**
* @function getImageCaption
* @description Get the markup in react for a caption for the image component.
*
* @since 1.1.15
*
* @param {object} props Props for the caption.
* @param {JSX.Element|string} props.children React element children.
* @param {object} props.customAttributes Custom attributes for the caption.
* @param {string|Array|object} props.customClasses Custom classes for the caption.
*
* @return {JSX.Element} The caption for the image component.
*/
const getImageCaption = ( {
children = null,
customAttributes: captionCustomAttributes = {},
customClasses: captionCustomClasses = [],
} ) => {
const captionAttrs = {
className: classnames( {
'gform-image__caption': true,
}, captionCustomClasses ),
...captionCustomAttributes,
};
return (
<figcaption { ...captionAttrs }>
{ children }
</figcaption>
);
};
/**
* @function getImageWithCaption
* @description Get the markup in react for an image with caption.
*
* @since 1.1.15
*
* @param {object} props Props for the image with caption.
* @param {object} props.attributes The attributes for the image wrapper.
* @param {JSX.Element|string} props.caption The caption for the image, can be a string or react children.
* @param {object} props.captionAttributes The attributes for the caption element.
* @param {object} props.imageAttributes The attributes for the image.
*
* @return {JSX.Element} The image with caption.
*/
const getImageWithCaption = ( {
attributes,
caption: captionElement,
captionAttributes: captionElementAttributes,
imageAttributes: imgAttributes,
} ) => {
return (
<figure { ...attributes }>
<img { ...imgAttributes } />{ /* eslint-disable-line jsx-a11y/alt-text */ }
{ captionElement && getImageCaption( {
...captionElementAttributes,
children: captionElement,
} ) }
</figure>
);
};
const attrs = {
className: classnames( {
'gform-image': true,
'gform-image--bg': asBg,
...spacerClasses( spacing ),
}, customClasses ),
ref,
...customAttributes,
};
const { customClasses: imageCustomClasses, ...restImageAttributes } = imageAttributes;
const imageAttrs = {
...restImageAttributes,
className: classnames( {
'gform-image__image': true,
}, imageCustomClasses || [] ),
};
if ( asBg ) { // Background image.
imageAttrs.style = {
backgroundImage: `url('${ url }')`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: getBackgroundPosition( imagePosition ), // Use the function to ensure it's a valid position
...( imageAttributes.style || {} ),
};
if ( aspectRatio ) {
const padding = Math.round( ( 10000 / aspectRatio ) ) / 100; // Round to nearest 2 decimals.
imageAttrs.style.paddingBottom = `${ padding }%`;
}
if ( width ) {
attrs.style = {
...( customAttributes.style || {} ),
width: `${ width }px`,
};
}
imageAttrs.role = 'img';
imageAttrs[ 'aria-label' ] = altText;
} else { // Normal image.
const imageStyles = {};
imageAttrs.src = url;
imageAttrs.alt = altText;
if ( width ) {
imageStyles.width = `${ width }px`;
}
if ( height ) {
imageStyles.height = `${ height }px`;
}
if ( 'lazy' === lazyload ) {
imageAttrs.lazyload = 'lazy';
}
imageAttrs.style = {
...( imageAttributes.style || {} ),
...imageStyles,
};
}
return asBg
? getBackgroundImage( {
attributes: attrs,
imageAttributes: imageAttrs,
} )
: ( getImageWithCaption( {
attributes: attrs,
imageAttributes: imageAttrs,
caption,
captionAttributes,
} ) );
} );
Image.propTypes = {
altText: PropTypes.string,
asBg: PropTypes.bool,
aspectRatio: PropTypes.number,
caption: PropTypes.node,
captionAttributes: PropTypes.object,
customAttributes: PropTypes.object,
customClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
height: PropTypes.number,
imageAttributes: PropTypes.object,
imagePosition: PropTypes.string,
lazyload: PropTypes.bool,
spacing: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.number,
PropTypes.array,
PropTypes.object,
] ),
url: PropTypes.string,
width: PropTypes.number,
};
Image.displayName = 'Image';
export default Image;