UNPKG

@spaced-out/ui-design-system

Version:
142 lines (123 loc) 3.99 kB
// @flow strict import * as React from 'react'; import { borderRadiusCircle, borderRadiusMedium, borderRadiusNone, borderRadiusXSmall, } from '../../styles/variables/_border'; import {size40, sizeFluid} from '../../styles/variables/_size'; import classify from '../../utils/classify'; import {appendPx} from '../../utils/string'; import css from './Shimmer.module.css'; type ClassNames = $ReadOnly<{wrapper?: string}>; export const SHIMMER_TYPES = Object.freeze({ text: 'text', rounded: 'rounded', circular: 'circular', rectangular: 'rectangular', }); export const SHIMMER_TYPE_TO_BORDER_RADIUS_MAP = Object.freeze({ text: borderRadiusXSmall, rounded: borderRadiusMedium, circular: borderRadiusCircle, rectangular: borderRadiusNone, }); export type ShimmerType = $Values<typeof SHIMMER_TYPES>; export type ShimmerProps = { classNames?: ClassNames, show?: boolean, type?: ShimmerType, width?: number | string, height?: number | string, borderRadius?: number | string, children?: React.Node, }; export type ShimmerWrapperProps = { children?: React.Node, }; /** * Note(Nishant): ShimmerWrapper is a wrapper component for Shimmer component. This should only be used for Text based Shimmers * This solves a very annoying problem with out text components where the display prop is set to flex. * Genesis assumes that every element is flexible for simplicity and for text text shimmers to work in use cases * where text wraps across multiple lines, we need to wrap the shimmer in a span element. * to avoid the misuse where consumers use there own / other block level components, we have this wrapper. * This would ensure the layout remains same even when you toggle the shimmer to show your actual content * @param {React.Node} children - The children to be rendered */ export const ShimmerWrapper = ({ children, }: ShimmerWrapperProps): React$Element<'span'> => <span>{children}</span>; export const Shimmer: React$AbstractComponent<ShimmerProps, HTMLSpanElement> = React.forwardRef<ShimmerProps, HTMLSpanElement>( ( { classNames, show = true, type = SHIMMER_TYPES.text, children, width = size40, height = sizeFluid, borderRadius, }: ShimmerProps, ref, ) => { if (!show) { return <>{children}</>; } const borderRadiusValue = borderRadius ?? SHIMMER_TYPE_TO_BORDER_RADIUS_MAP[type]; return ( <span ref={ref} data-testid="Shimmer" className={classify(css.wrapper, css[type], classNames?.wrapper)} style={{ '--width': appendPx(width), '--height': appendPx(height), '--border-radius': appendPx(borderRadiusValue), }} ></span> ); }, ); type KPIShimmerClassNames = $ReadOnly<{ wrapper?: string, icon?: string, text?: string, }>; export type KPIShimmerProps = { textWidth?: number | string, hasTopContent?: boolean, hasMiddleContent?: boolean, hasBottomContent?: boolean, hasIcon?: boolean, classNames?: KPIShimmerClassNames, }; export const KPIShimmer = ({ textWidth = 150, hasBottomContent = true, hasIcon = true, hasTopContent = true, hasMiddleContent = true, classNames, }: KPIShimmerProps): React.Node => ( <div className={classify(css.kpiBox, classNames?.wrapper)}> {hasIcon && ( <div className={classify(css.section, css.iconSection, classNames?.icon)}> <Shimmer type="rounded" width={60} height={60}></Shimmer> </div> )} <div className={classify(css.section, classNames?.text)}> {hasTopContent && ( <Shimmer type="text" width={textWidth} height={15}></Shimmer> )} {hasMiddleContent && ( <Shimmer type="text" width={textWidth} height={25}></Shimmer> )} {hasBottomContent && ( <Shimmer type="text" width={textWidth} height={15}></Shimmer> )} </div> </div> );