Skip to content

Commit

Permalink
fix: add tools hover interaction icon component (#357)
Browse files Browse the repository at this point in the history
  • Loading branch information
bandhan-majumder authored Oct 17, 2024
1 parent f0323f5 commit 20b049d
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 0 deletions.
22 changes: 22 additions & 0 deletions animata/icon/hover-interaction.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import HoverInteraction from "@/animata/icon/hover-interaction";
import { Meta, StoryObj } from "@storybook/react";

const meta = {
title: "Icon/Hover Interaction",
component: HoverInteraction,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {},
} satisfies Meta<typeof HoverInteraction>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
title: "instagram",
size: "4",
},
};
133 changes: 133 additions & 0 deletions animata/icon/hover-interaction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"use client";

import React, { ElementType, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";

// default imports
import {
FigmaLogoIcon,
FramerLogoIcon,
GitHubLogoIcon,
InstagramLogoIcon,
LinkedInLogoIcon,
SquareIcon,
TwitterLogoIcon,
} from "@radix-ui/react-icons";

type IconSize = "1" | "2" | "3" | "4"; // source: https://www.radix-ui.com/themes/docs/components/icon-button

interface IconHoverProps {
title: string; // default is Square
size?: IconSize; // default is 4
}

const sizeClasses: Record<IconSize, string> = {
"1": "w-6 h-6",
"2": "w-7 h-7",
"3": "w-8 h-8",
"4": "w-10 h-10",
};

const textSizeClasses: Record<IconSize, string> = {
"1": "text-sm",
"2": "text-base",
"3": "text-lg",
"4": "text-xl",
};

const getIconForTitle = (title: string) => {
const lowercaseTitle = title.toLowerCase().trim();
const iconMap: { [key: string]: ElementType } = {
framer: FramerLogoIcon,
"twitter/x": TwitterLogoIcon,
instagram: InstagramLogoIcon,
linkedin: LinkedInLogoIcon,
github: GitHubLogoIcon,
figma: FigmaLogoIcon
};

// SquareIcon as default
return (iconMap[lowercaseTitle] as ElementType) || SquareIcon;
};

// twitter -> Twitter, Twitter -> Twitter, twitter/x -> Twitter/X, Twitter/x -> Twitter/X
const capitalizeWithSlash = (str: string) => {
return str
.split("/")
.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
.join("/");
};

export default function HoverInteraction({
// default values
title = "Square",
size = "4",
}: IconHoverProps) {
const [isHovered, setIsHovered] = useState(false);
const DynamicIcon = getIconForTitle(title);

const sizeClass = sizeClasses[size];
const textSizeClass = textSizeClasses[size];

const formattedTitle = capitalizeWithSlash(title);

const logoVariants = {
hidden: {
opacity: 0,
y: 0,
scale: 0.5,
rotate: 100,
},
visible: {
opacity: 1,
y: 13,
scale: 1,
rotate: 0,
transition: {
type: "spring",
stiffness: 100,
damping: 15,
duration: 0.3,
},
},
exit: {
opacity: 0,
y: 13,
scale: 0.5,
rotate: 100,
transition: { duration: 0.3 },
},
};

return (
<motion.div
className="storybook-fix group relative flex min-h-[120px] w-full cursor-pointer items-center justify-center"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<span
className={`relative text-center font-medium text-muted-foreground transition-colors duration-200 group-hover:text-black ${textSizeClass}`}
>
{formattedTitle}
</span>
<AnimatePresence>
{isHovered && (
<motion.div
className="absolute bottom-full left-1/2"
variants={logoVariants}
initial="hidden"
animate="visible"
exit="exit"
style={{
x: "-50%",
originX: 0.5,
originY: 1,
}}
>
<DynamicIcon className={sizeClass} />
</motion.div>
)}
</AnimatePresence>
</motion.div>
);
}
49 changes: 49 additions & 0 deletions content/docs/icon/hover-interaction.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: Hover Interaction
description: This is a component which shows icon based on text hovered by user. By default it has alreay imported Twitter/x, Framer, Figma, Instagram, GitHub, LinkedIn, and it shows Square-Box icon by default it user tries to get other icons without importing. Importing steps are provided below.
author: MEbandhan
---

<ComponentPreview name="icon-hover-interaction--docs" />

## Installation

<Steps>
<Step>Install dependencies</Step>

```bash
npm install framer-motion @radix-ui/react-icons
```

<Step>Run the following command</Step>

It will create a new file `hover-interaction.tsx` inside the `components/animata/icon` directory.

```bash
mkdir -p components/animata/icon && touch components/animata/icon/hover-interaction.tsx
```

<Step>Paste the code</Step>

Open the newly created file and paste the following code:

```jsx file=<rootDir>/animata/icon/hover-interaction.tsx

```

</Steps>

<Step>Use the component with default/non-default interacitons</Step>

defaults are mentioned in the description

**Other than defaults**

1. Go to the [radix-ui icon page](https://www.radix-ui.com/icons)
2. Search for the icon.
3. Import the icon in the copied code. If the icon name is Twitter Logo, the import will be TwitterLogoIcon
4. Add switch case for that logo in **lower case**.

## Credits

Built by [Bandhan Majumder](https://github.com/bandhan-majumder)

0 comments on commit 20b049d

Please sign in to comment.