From 65465d813d42677d3b475daa19cf7833f1898a09 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 16 Oct 2024 10:07:17 +0200 Subject: [PATCH] Add animations for button icons --- src/components/ButtonIcon/ButtonIcon.tsx | 34 ++++++++++-- src/components/ButtonIcon/buttonIcon.css | 66 ++++++++++++++++++++++++ stories/ButtonIcon.stories.ts | 16 +++++- 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/src/components/ButtonIcon/ButtonIcon.tsx b/src/components/ButtonIcon/ButtonIcon.tsx index 047995c..04a866c 100644 --- a/src/components/ButtonIcon/ButtonIcon.tsx +++ b/src/components/ButtonIcon/ButtonIcon.tsx @@ -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 ( ); } diff --git a/src/components/ButtonIcon/buttonIcon.css b/src/components/ButtonIcon/buttonIcon.css index 46096ea..52580be 100644 --- a/src/components/ButtonIcon/buttonIcon.css +++ b/src/components/ButtonIcon/buttonIcon.css @@ -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); + } +} diff --git a/stories/ButtonIcon.stories.ts b/stories/ButtonIcon.stories.ts index 830fb88..7a6827d 100644 --- a/stories/ButtonIcon.stories.ts +++ b/stories/ButtonIcon.stories.ts @@ -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,