robertkirsz / useripple

A React Hook for adding ripple effect
MIT License
21 stars 1 forks source link

Typescript Implementation #6

Open alligatortower opened 2 years ago

alligatortower commented 2 years ago

Thanks for the hook. I implemented it in typescript and figured other people might find use for it. Also added a classNames prop so I can use it with tailwind more easily.

I don't know if this fits as an "issue" exactly, so let me know if there's a more appropriate place.

import { CSSProperties, MouseEvent, useState } from "react";

const MINIMUM_RIPPLE_SIZE = 100;

type Ripple = {
  key: number;
  style: CSSProperties;
};

type UseRippleProps = {
  style?: {
    background?: string;
    opacity?: number;
    animationName?: string;
    animationDuration?: string;
  };
  classNames?: string;
};

export default function useRipple({ style, classNames }: UseRippleProps = {}): [
  Function,
  JSX.Element[]
] {
  const baseStyle: CSSProperties = {
    display: "block",
    position: "absolute",
    background: "currentColor",
    borderRadius: "50%",
    opacity: 0.4,
    pointerEvents: "none",
    animationName: "useRippleAnimation",
    animationDuration: ".7s",
    animationFillMode: "forwards",
    ...style,
  };
  const [ripples, setRipples] = useState<Ripple[]>([]);

  const addRipple = (event: MouseEvent<HTMLButtonElement>) => {
    const { left, top } = event.currentTarget.getBoundingClientRect();
    const x = event.clientX - left;
    const y = event.clientY - top;
    const rippleSize = Math.min(
      event.currentTarget.clientHeight,
      event.currentTarget.clientWidth,
      MINIMUM_RIPPLE_SIZE
    );

    const newRipple: Ripple = {
      key: event.timeStamp,
      style: {
        width: rippleSize,
        height: rippleSize,
        left: x - rippleSize / 2,
        top: y - rippleSize / 2,
        ...baseStyle,
      },
    };

    setRipples((state) => [...state, newRipple]);
  };

  const handleAnimationEnd = (current: number) => {
    setRipples((state) =>
      state.filter((previousRipple: Ripple) => previousRipple.key !== current)
    );
  };

  const ripplesArray: JSX.Element[] = ripples.map((currentRipple) => (
    <span
      {...currentRipple}
      onAnimationEnd={() => handleAnimationEnd(currentRipple.key)}
      className={classNames}
    />
  ));

  return [addRipple, ripplesArray];
}
robertkirsz commented 7 months ago

I'm so sorry, I completely missed the notification for this issue 😢 I'll try to find some time to convert this into TypeScript.