import styled, { css, CSSProperties, keyframes } from 'styled-components'; import { memo } from 'react'; import { icons, SessionIconSize, SessionIconType } from '.'; import { ClipRule, FillRule } from './Icons'; export type SessionIconProps = { iconType: SessionIconType; /** * iconSize is usually the height of the icon, we then have a ratio for each icons to calculate the width. * see sizeIsWidth for how to do the opposite */ iconSize: SessionIconSize | number; iconColor?: string; iconRotation?: number; iconPadding?: string; rotateDuration?: number; glowDuration?: number; borderRadius?: string; glowStartDelay?: number; noScale?: boolean; backgroundColor?: string; style?: CSSProperties; dataTestId?: string; unreadCount?: number; /** for some usecases, we want to fix the width of the icon and have the height be calculated from the ratio of the icon */ sizeIsWidth?: boolean; }; const getIconDimensionFromIconSize = (iconSize: SessionIconSize | number) => { if (typeof iconSize === 'number') { return iconSize; } switch (iconSize) { case 'tiny': return 12; case 'small': return 15; case 'medium': return 20; case 'large': return 25; case 'huge': return 30; case 'huge2': return 40; default: return 20; } }; type StyledSvgProps = { width: string | number; height: string | number; iconRotation: number; rotateDuration?: number; borderRadius?: string; iconPadding?: string; glowDuration?: number; glowStartDelay?: number; noScale?: boolean; iconColor?: string; backgroundColor?: string; fill?: string; clipRule?: ClipRule; filleRule?: FillRule; }; const rotate = keyframes` from { transform: rotate(0deg); } to { transform: rotate(360deg); } `; /** * Creates a glow animation made for multiple element sequentially */ const glow = (color: string, glowDuration: number, glowStartDelay: number) => { // increase shadow intensity by 3 const dropShadow = `drop-shadow(0px 0px 6px ${color});`; // creating keyframe for sequential animations let kf = ''; const durationWithLoop = glowDuration + 1; for (let i = 0; i <= durationWithLoop; i++) { const percent = (100 / durationWithLoop) * i; if (i === glowStartDelay + 1) { kf += `${percent}% { filter: ${dropShadow} transform: scale(1.1); }`; } else { kf += `${percent}% { filter: none; transform: scale(0.9); }`; } } return keyframes`${kf}`; }; const animation = (props: { rotateDuration?: number; glowDuration?: number; glowStartDelay?: number; iconColor?: string; noScale?: boolean; }) => { if (props.rotateDuration) { return css` animation: ${rotate} ${props.rotateDuration}s linear infinite; `; } if (props.noScale) { return css``; } if (props.glowDuration !== undefined && props.glowStartDelay !== undefined && props.iconColor) { return css` animation: ${glow(props.iconColor, props.glowDuration, props.glowStartDelay)} ${props.glowDuration}s ease infinite; `; } return undefined; }; const Svg = memo(styled.svg` width: ${props => props.width}; transform: ${props => `rotate(${props.iconRotation}deg)`}; ${props => animation(props)}; border-radius: ${props => props.borderRadius}; background-color: ${props => props.backgroundColor ? props.backgroundColor : 'var(--button-icon-background-color)'}; filter: ${props => (props.noScale ? `drop-shadow(0px 0px 4px ${props.iconColor})` : '')}; fill: ${props => (props.iconColor ? props.iconColor : 'var(--button-icon-stroke-color)')}; padding: ${props => (props.iconPadding ? props.iconPadding : '')}; transition: inherit; `); const SessionSvg = ( props: StyledSvgProps & { viewBox: string; path: string | Array; style?: CSSProperties; dataTestId?: string; } ) => { const colorSvg = props.iconColor ? props.iconColor : 'var(--button-icon-stroke-color)'; const pathArray = props.path instanceof Array ? props.path : [props.path]; const propsToPick = { width: props.width, height: props.height, rotateDuration: props.rotateDuration, iconRotation: props.iconRotation, viewBox: props.viewBox, glowDuration: props.glowDuration, glowStartDelay: props.glowStartDelay, iconColor: props.iconColor, noScale: props.noScale, backgroundColor: props.backgroundColor, borderRadius: props.borderRadius, iconPadding: props.iconPadding, fill: props.fill, clipRule: props.clipRule, fillRule: props.filleRule, style: props.style, dataTestId: props.dataTestId, }; return ( {pathArray.map((path, index) => { return ; })} ); }; export const SessionIcon = (props: SessionIconProps) => { const { iconType, iconColor, rotateDuration, glowDuration, borderRadius, glowStartDelay, noScale, backgroundColor, iconPadding, style, dataTestId, sizeIsWidth, } = props; let { iconSize, iconRotation } = props; iconSize = iconSize || 'medium'; iconRotation = iconRotation || 0; const calculatedIconSize = getIconDimensionFromIconSize(iconSize); const iconDef = icons[iconType]; const ratio = iconDef.ratio; const fill = iconDef?.fill || undefined; const clipRule = iconDef?.clipRule || 'nonzero'; const fillRule = iconDef?.fillRule || 'nonzero'; const width = sizeIsWidth ? calculatedIconSize : calculatedIconSize * ratio; const height = sizeIsWidth ? calculatedIconSize / ratio : calculatedIconSize; return ( ); };