import * as React from 'react';
import cx from 'classnames';
import {useAnimation} from './animation';
import {useTimeout} from './timeout';
import {shapeAnimationMap, shapeColorMap, variants} from './presets';
import useReducedMotion from '../utils/useReducedMotion';
import Particle from './Particle';
export interface SparksPropsType {
/**
* Optional string. Additional class names.
*/
className?: string;
/**
* Optional object. Style object to be applied to the component.
* @example
**/
style?: React.CSSProperties;
/**
* Optional ReacNode. Component to be warped with sparks.
*/
children?: React.ReactNode;
/**
* Optional string. Shape of the sparks.
* @default 'spark'
* @example
**/
shape?: 'spark' | 'heart';
/**
* Optional string. Variant of the sparks which determines the size and the number of sparks.
* @default 'l'
* @example
**/
variant?: 's' | 'm' | 'l';
/**
* Optional boolean. Whether the sparks are active.
* @default false
* @example
*
*
**/
active?: boolean;
/**
* Optional number. Duration of the animation.
* @default 6000
* @example
*
*
*
**/
duration?: number;
/**
* Optional number. Delay of the animation.
* This value is used at the beginning of the whole animation
* @default 0
* @example
**/
delay?: number;
/**
* Optional number. Delay of the animation.
* This value is used between each iteration of the animation
* @default 500
* @example
**/
iterationDelay?: number;
/**
* Optional number. Number of iterations to play the full sequence: entry, exit, iterationDelay.
* @default 3
* @example
*
*
**/
iterationCount?: number;
/**
* Optional array of strings. Colors of the particles. If not provided, the colors will be chosen based on the shape.
* @example
**/
colors?: [string, string, string, string];
/**
* Optional string. The placement property is a shorthand property for setting the top, right, bottom, and left properties (inset) of an internal canvas.
* Thanks to that you can adjust the position of the canvas thus the position of the sparks to always keep them in line with the edge of the container
* no matter what the size of the container and the variant of the sparks is. It is useful when specific variant of the sparks is used in a container
* of the size that is not covered perfectly by the default positioning.
* @remarks
* @example
* // This will move the sparks 8px outside the container top and bottom edge and 0px outside the container left and right edge.
*
* @example
* // this will move the sparks 16px outside the container top and bottom endge and 0px outside the container left and right edge.
*
**/
placement?: React.CSSProperties['inset'];
/**
* Optional string. The display property specifies the display behavior (the type of rendering box) of spark container.
* It's exposed directly from the CSS to conveniently change the display behavior of the spark container.
*/
display?: React.CSSProperties['display'];
/**
* Optional string. The width property specifies the width of the spark container.
* It's exposed directly from the CSS to conveniently change the width of the spark container.
* @example
*/
width?: React.CSSProperties['width'];
/**
* Optional string. The height property specifies the height of the spark container.
* It's exposed directly from the CSS to conveniently change the height of the spark container.
* @example
*/
height?: React.CSSProperties['height'];
}
const Sparks = React.forwardRef(
(
{
children,
className,
style,
shape = 'spark',
variant = 'l',
active = false,
duration = 6000,
delay = 0,
iterationDelay = 500,
iterationCount = 3,
colors,
placement,
display,
width,
height,
}: SparksPropsType,
ref
) => {
const iteration = React.useRef(0);
const hasReduceMotion = useReducedMotion();
const animationConfig =
shapeAnimationMap[shape][hasReduceMotion ? 'reduced' : 'default'];
const {register, phase, setPhase} = useAnimation(animationConfig);
const [timeoutDelay, setTimeoutDelay] = React.useState(null);
const restartTimeout = useTimeout(() => {
if (!active) {
return;
}
if (phase === 'entry') {
setPhase('exit');
} else if (iteration.current < iterationCount) {
setPhase('entry');
}
}, timeoutDelay);
React.useEffect(() => {
if (active) {
setPhase('initial');
} else {
setPhase('exit');
}
}, [active, setPhase]);
React.useEffect(() => {
if (phase === 'initial' && active) {
iteration.current = 0;
restartTimeout();
setTimeoutDelay(delay);
}
if (phase === 'entry' && active) {
iteration.current++;
if (duration !== Infinity) {
restartTimeout();
setTimeoutDelay(duration);
}
}
if (phase === 'finished') {
restartTimeout();
if (iterationDelay !== Infinity) {
setTimeoutDelay(iterationDelay);
}
}
}, [
phase,
delay,
iterationDelay,
active,
duration,
iterationCount,
restartTimeout,
]);
const shapeColor = shapeColorMap[shape];
const componnetCssVariables = {
'--sparks-display': display,
'--sparks-width': width,
'--sparks-height': height,
};
const canvasCssVariables = {
'--sparks-inset': placement,
} as React.CSSProperties;
return (