import { React, PropTypes, classnames } from '@gravityforms/libraries';
import useId from '../hooks/use-id';
const { Children, useEffect, useRef, useState } = React;
/**
* @module CrossFade
* @description A component that cross fades the children.
*
* @since 4.0.3
*
* @param {object} props Component props.
* @param {number} props.activeIndex The active index.
* @param {JSX.Element} props.children The children to cross fade.
* @param {object} props.childWrapperAttributes Custom attributes for the child wrapper.
* @param {string|Array|object} props.childWrapperClasses Custom classes for the child wrapper.
* @param {object} props.customAttributes Custom attributes for the component.
* @param {string|Array|object} props.customClasses Custom classes for the component.
* @param {number} props.duration The duration of the cross fade.
* @param {string} props.id The ID of the component.
*
* @return {JSX.Element} The CrossFade component.
*/
const CrossFade = ( {
activeIndex = 0,
children,
childWrapperAttributes = {},
childWrapperClasses = [],
customAttributes = {},
customClasses = [],
duration = 250,
id = '',
} ) => {
const refs = useRef( [] );
const [ renderIndex, setRenderIndex ] = useState( activeIndex );
const [ prevRenderIndex, setPrevRenderIndex ] = useState( null );
const crossFadeId = useId( id );
// Initialize the refs array with null values
if ( ! refs.current || refs.current.length !== Children.count( children ) ) {
refs.current = Array.from( { length: Children.count( children ) }, () => null );
}
useEffect( () => {
setRenderIndex( activeIndex );
setTimeout( () => setPrevRenderIndex( activeIndex ), duration );
}, [ activeIndex, duration ] );
const componentProps = {
className: classnames( customClasses ),
id: crossFadeId,
...customAttributes,
style: {
position: 'relative',
...( customAttributes.style || {} ),
},
};
return (
<div { ...componentProps }>
{ Children.map( children, ( child, index ) => {
const shouldRender = [ renderIndex, prevRenderIndex ].includes( index );
const childId = `${ crossFadeId }-${ index }`;
const style = {
transition: `opacity ${ duration }ms`,
};
if ( index === renderIndex ) {
style.opacity = 1;
} else if ( index === prevRenderIndex ) {
style.position = 'absolute';
style.top = '0';
style.left = '0';
style.width = '100%';
style.opacity = 0;
}
const childProps = {
className: classnames( childWrapperClasses ),
id: childId,
style,
...childWrapperAttributes,
};
return (
<div
{ ...childProps }
key={ childId }
ref={ ( el ) => refs.current[ index ] = el }
>
{ shouldRender && child }
</div>
);
} ) }
</div>
);
};
CrossFade.propTypes = {
activeIndex: PropTypes.number,
children: PropTypes.node,
childWrapperAttributes: PropTypes.object,
childWrapperClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
customAttributes: PropTypes.object,
customClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
duration: PropTypes.number,
id: PropTypes.string,
};
export default CrossFade;