@gravityforms/components
Version:
UI components for use in Gravity Forms development. Both React and vanilla js flavors.
220 lines (207 loc) • 6.98 kB
JavaScript
import { classnames, React } from '@gravityforms/libraries';
import { ConditionalWrapper } from '@gravityforms/react-utils';
import { spacerClasses } from '@gravityforms/utils';
import Grid from '../../../elements/Grid';
const { forwardRef } = React;
// Helper Functions
const getWrapperProps = ( {
animated = true,
spacing = 2,
wrapperAttributes = {},
wrapperClasses = [],
} ) => ( {
className: classnames(
'gform-placeholder__wrapper',
{ 'gform-placeholder__wrapper--animated': animated },
spacerClasses( spacing ),
wrapperClasses
),
...wrapperAttributes,
} );
const getPlaceholderProps = ( {
borderRadius = '9999px',
customClasses = [],
customAttributes = {},
height = '14px',
gridCount = 1,
gridItemSpacing = 0,
gridItemWidth = 90,
width = '100%',
} ) => ( {
className: classnames(
'gform-placeholder',
{ 'gform-grid--item': gridCount > 1 },
spacerClasses( gridItemSpacing ),
customClasses
),
style: {
borderRadius,
height,
width: gridCount > 1 ? `${ gridItemWidth / gridCount }%` : width,
},
...customAttributes,
} );
const getMergedGridProps = ( gridProps = {} ) => ( {
alignItems: 'center',
container: true,
direction: 'row',
elementType: 'div',
justifyContent: 'space-between',
...gridProps,
} );
// PlaceholderStripe Component
const PlaceholderStripe = ( {
gridCount,
gridProps,
placeholderProps,
wrapperProps,
} ) => (
<div { ...wrapperProps }>
<ConditionalWrapper
condition={ gridCount > 1 }
wrapper={ ( children ) => <Grid { ...gridProps }>{ children }</Grid> }
>
{ Array( gridCount )
.fill( 0 )
.map( ( _, index ) => (
<span
key={ `placeholder-${ index }` }
{ ...placeholderProps }
/>
) ) }
</ConditionalWrapper>
</div>
);
/**
* @module Placeholder
* @description A component that renders single or grouped placeholder stripes with optional
* opacity animations and grid layout. Useful for loading states or content placeholders.
*
* @since 4.5.0
*
* @param {object} props - Component props
* @param {boolean} [props.animated] - Whether to include animation effects
* @param {string} [props.borderRadius] - Border radius of placeholder stripes
* @param {object} [props.customAttributes] - Custom HTML attributes for placeholder elements
* @param {string|string[]|object} [props.customClasses] - Additional CSS classes for placeholder elements
* @param {number} [props.gridCount] - Number of placeholder stripes in a single row
* @param {number} [props.gridItemSpacing] - Spacing between grid items when in a row using grid count
* @param {number} [props.gridItemWidth] - Width of each grid item as a percentage
* @param {object} [props.gridProps] - Props to pass to the Grid component
* @param {object[]} [props.groupData] - Array of group configurations for multiple placeholder sets
* @param {string} [props.height] - Height of placeholder stripes
* @param {number} [props.rows] - Number of rows. This overrides groupData if set.
* @param {string|number|string[]|object} [props.spacing] - Spacing between elements
* @param {string} [props.width] - Width of placeholder stripes
* @param {object} [props.wrapperAttributes] - Custom HTML attributes for wrapper element
* @param {string|string[]|object} [props.wrapperClasses] - Additional CSS classes for wrapper
* @param {object|null} props.ref - Forwarded ref to the component
*
* @return {JSX.Element} A placeholder component with configurable stripes and layout
*
* @example
* // Single placeholder
* <Placeholder height="20px" width="50%" animated={true} />
*
* // Grid of placeholders
* <Placeholder gridCount={3} spacing={4} />
*
* // Grouped placeholders
* <Placeholder
* groupData={[
* { gridCount: 2, height: '20px' },
* { gridCount: 3, height: '15px', animated: false }
* ]}
* />
*/
const Placeholder = forwardRef( ( {
animated = true,
borderRadius = '9999px',
customAttributes = {},
customClasses = [],
gridCount = 1,
gridItemSpacing = 0,
gridItemWidth = 90,
gridProps = {},
groupData = [],
height = '14px',
rows = 1,
spacing = 2,
width = '100%',
wrapperAttributes = {},
wrapperClasses = [],
}, ref ) => {
if ( ! groupData.length && rows === 1 ) {
const wrapperProps = {
...getWrapperProps( { animated, spacing, wrapperAttributes, wrapperClasses } ),
ref,
};
return (
<PlaceholderStripe
gridCount={ gridCount }
gridProps={ getMergedGridProps( gridProps ) }
placeholderProps={ getPlaceholderProps( {
borderRadius,
customClasses,
customAttributes,
height,
gridCount,
gridItemSpacing,
gridItemWidth,
width,
} ) }
wrapperProps={ wrapperProps }
/>
);
}
// if we just want to use global prop data for each row, fill out the groupData array with empty objects
if ( ! groupData.length ) {
groupData = Array( rows ).fill( {} );
}
return (
<div className="gform-placeholder__group-container">
{ groupData.map( ( group, index ) => {
const {
animated: groupAnimated = animated,
borderRadius: groupBorderRadius = borderRadius,
customAttributes: groupCustomAttributes = customAttributes,
customClasses: groupCustomClasses = customClasses,
gridCount: groupGridCount = gridCount,
gridItemSpacing: groupGridItemSpacing = gridItemSpacing,
gridItemWidth: groupGridItemWidth = gridItemWidth,
gridProps: groupGridProps = gridProps,
height: groupHeight = height,
spacing: groupSpacing = spacing,
width: groupWidth = width,
wrapperAttributes: groupWrapperAttributes = wrapperAttributes,
wrapperClasses: groupWrapperClasses = wrapperClasses,
} = group;
return (
<PlaceholderStripe
key={ `placeholder-group-${ index }` }
gridCount={ groupGridCount }
gridProps={ getMergedGridProps( groupGridProps ) }
placeholderProps={ getPlaceholderProps( {
borderRadius: groupBorderRadius,
customClasses: groupCustomClasses,
customAttributes: groupCustomAttributes,
height: groupHeight,
gridCount: groupGridCount,
gridItemSpacing: groupGridItemSpacing,
gridItemWidth: groupGridItemWidth,
width: groupWidth,
} ) }
wrapperProps={ getWrapperProps( {
animated: groupAnimated,
spacing: groupSpacing,
wrapperAttributes: groupWrapperAttributes,
wrapperClasses: groupWrapperClasses,
} ) }
/>
);
} ) }
</div>
);
} );
Placeholder.displayName = 'Loaders/Placeholder';
export default Placeholder;