Add animations for button icons
This commit is contained in:
parent
379fd1fc27
commit
65465d813d
|
@ -1,4 +1,9 @@
|
|||
import { ComponentType, CSSProperties } from "react";
|
||||
import {
|
||||
AnimationEventHandler,
|
||||
ComponentType,
|
||||
CSSProperties,
|
||||
useState,
|
||||
} from "react";
|
||||
import "./buttonIcon.css";
|
||||
import { attributes } from "../utils/attributes";
|
||||
|
||||
|
@ -9,7 +14,10 @@ interface CustomStyleCSS extends CSSProperties {
|
|||
}
|
||||
|
||||
type Props = {
|
||||
Icon: ComponentType;
|
||||
Icon: ComponentType<{
|
||||
className?: string;
|
||||
onAnimationEnd?: AnimationEventHandler | undefined;
|
||||
}>;
|
||||
|
||||
variant?: "big" | "small";
|
||||
|
||||
|
@ -33,6 +41,11 @@ type Props = {
|
|||
* Apply custom classname.
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Apply an animation on click
|
||||
*/
|
||||
animation?: "buzz" | "bounce";
|
||||
};
|
||||
|
||||
export function ButtonIcon({
|
||||
|
@ -41,19 +54,30 @@ export function ButtonIcon({
|
|||
style,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
className = "",
|
||||
animation,
|
||||
disabled = false,
|
||||
variant = "big",
|
||||
}: Props) {
|
||||
const [animationClassName, setAnimationClassName] = useState("");
|
||||
|
||||
const onInternalClick = () => {
|
||||
setAnimationClassName("buttonIcon--" + animation);
|
||||
onClick?.();
|
||||
};
|
||||
|
||||
const onAnimationEnd = () => setAnimationClassName("");
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`buttonIcon buttonIcon--${variant}`}
|
||||
className={`buttonIcon buttonIcon--${variant} ${className}`}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={onClick}
|
||||
onClick={onInternalClick}
|
||||
style={style}
|
||||
{...attributes({ disabled: disabled, "aria-disabled": disabled })}
|
||||
>
|
||||
<Icon />
|
||||
<Icon className={animationClassName} onAnimationEnd={onAnimationEnd} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,3 +35,69 @@
|
|||
color: var(--codex-color-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.buttonIcon--buzz {
|
||||
-webkit-animation-name: buzz;
|
||||
animation-name: buzz;
|
||||
-webkit-animation-duration: 0.45s;
|
||||
animation-duration: 0.45s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
animation-timing-function: linear;
|
||||
-webkit-animation-iteration-count: 5;
|
||||
animation-iteration-count: 5;
|
||||
}
|
||||
|
||||
@keyframes buzz {
|
||||
0% {
|
||||
-webkit-transform: translateX(0) rotate(0deg);
|
||||
transform: translateX(0px) rotate(0deg);
|
||||
}
|
||||
|
||||
25% {
|
||||
-webkit-transform: translateX(-3px) rotate(-15deg);
|
||||
transform: translateX(-3px) rotate(-15deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateX(0) rotate(0deg);
|
||||
transform: translateX(0px) rotate(0deg);
|
||||
}
|
||||
|
||||
75% {
|
||||
-webkit-transform: translateX(3px) rotate(15deg);
|
||||
transform: translateX(3px) rotate(15deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateX(0) rotate(0deg);
|
||||
transform: translateX(0px) rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
.buttonIcon--bounce {
|
||||
-webkit-animation-name: bounce;
|
||||
animation-name: bounce;
|
||||
-webkit-animation-duration: 0.6s;
|
||||
animation-duration: 0.6s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
animation-timing-function: linear;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0% {
|
||||
-webkit-transform: translateY(0) scale(1);
|
||||
transform: translateY(0px) scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateY(-10px) scale(1);
|
||||
transform: translateY(-10px) scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateY(0) scale(1);
|
||||
transform: translateY(0px) scale(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { fn } from "@storybook/test";
|
||||
import { Plus } from "lucide-react";
|
||||
import { Copy, Download, Plus } from "lucide-react";
|
||||
import { ButtonIcon } from "../src/components/ButtonIcon/ButtonIcon";
|
||||
|
||||
const meta = {
|
||||
|
@ -42,6 +42,20 @@ export const Disabled: Story = {
|
|||
},
|
||||
};
|
||||
|
||||
export const BuzzAnimation: Story = {
|
||||
args: {
|
||||
Icon: Copy,
|
||||
animation: "buzz"
|
||||
},
|
||||
};
|
||||
|
||||
export const BounceAnimation: Story = {
|
||||
args: {
|
||||
Icon: Download,
|
||||
animation: "bounce"
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomStyle: Story = {
|
||||
args: {
|
||||
Icon: Plus,
|
||||
|
|
Loading…
Reference in New Issue