diff --git a/README.md b/README.md index 9459fafe..288f63d0 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@
### Table of Contents + 1. [Introduction](#introduction) - [What is Animata?](#what-is-animata) - [What is not Animata?](#what-is-not-animata) @@ -49,24 +50,29 @@ 3. [Contributing](#contributing) 4. [Authors](#authors) 5. [License](#license) - + ## Introduction ### What is Animata? + Welcome to Animata, a free and open-source collection of hand-crafted animations, effects, and interactions that you can seamlessly integrate into your project with a simple copy and paste. The animations are built using TailwindCSS and React.js, so they can be easily customized to fit your project's design. ### What is not Animata? + Animata is not a full-fledged UI library like Material-UI or Chakra-UI. It is a collection of animations and effects that you can use to enhance your project's design. You can also use Animata alongside other UI libraries or design systems (you will need to set up TailwindCSS for this). ## Getting Started + You don't need to install it as a dependency instead you can simply copy and paste the code, as shadcn/ui, into your project. However, you still need to install the other dependency that the code needs. ### Requirements + - [TailwindCSS](https://tailwindcss.com/docs/installation): For styling. - [Framer Motion](https://www.framer.com/motion/) (Optional): For complex animations. - [Lucide Icons](https://lucide.dev/) or [Radix Icons](https://www.radix-ui.com/icons) (Optional): Use for icons, or replace with any other icon library or SVGs. ### Setup Instructions + #### Folder Structure (Recommended) ```bash @@ -87,7 +93,9 @@ where `/` is the root of your project, `/components` is where you keep your comp } } ``` + #### Install Dependencies + Install the required dependencies, if you haven't already: ```sh @@ -103,18 +111,20 @@ module.exports = { ``` ### Create Utility Functions + Create utils.ts file in the libs folder and paste the following code: ```ts import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; - + export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` #### NOTE + 1. If you see something that has been imported but not mentioned in the documentation, then it is a dependency you need to install. If it starts with @/ then it is Animata's component else it is an external dependency. In such a case, you can submit a PR to update the documentation. 2. If something is not working, the docs probably miss the tailwind.config.js updates. You can look for the entries that have been added to the tailwind.config.js in Animata's source code. You can create an issue or submit a PR to update the documentation. @@ -127,6 +137,7 @@ Contributions to Animata are always welcome! or find us on [Discord](https://discord.gg/STYEh3UW), we will take the time to guide you. ## Authors + Heartfelt gratitude goes to each of you for your amazing contributions to this project. Your hard work, creativity, and dedication have been nothing short of incredible. Whether it was coding, debugging, testing, or sharing ideas, every effort made a significant difference.
; - -export default meta; -type Story = StoryObj; - - -// Example contents for AnimatedDock -const dockItems = [ - { title: "Home", icon: , href: "/" }, - { title: "Search", icon: , href: "/search" }, - { title: "Notifications", icon: , href: "/notifications" }, - { title: "Profile", icon: , href: "/profile" }, -]; - - -// Primary story for AnimatedDock (default layout) -export const Primary: Story = { - args: { - items: dockItems, - largeClassName: "max-w-lg", - smallClassName: "w-full", - }, - render: (args) => ( -
- -
- ), -}; - - -// Story focused on the Small layout (for mobile view) -export const Small: Story = { - args: { - items: dockItems, - smallClassName: "w-full", - }, - render: (args) => ( -
- -
- ), -}; - - -// Story focused on the Large layout (for desktop view) -export const Large: Story = { - args: { - items: dockItems, - largeClassName: "max-w-lg", - }, - render: (args) => ( -
- -
- ), -}; - - -// Story showing both layouts at the same time (for comparison) -export const Multiple: Story = { - args: { - items: dockItems, - largeClassName: "max-w-lg", - smallClassName: "w-full", - }, - render: (args) => ( -
- - -
- ), -}; diff --git a/animata/container/animated-dock.tsx b/animata/container/animated-dock.tsx deleted file mode 100644 index 190815cc..00000000 --- a/animata/container/animated-dock.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { cn } from "@/lib/utils"; // Import utility for conditional class names -import { - AnimatePresence, // Enables animation presence detection - MotionValue, // Type for motion values - motion, // Main component for animations - useMotionValue, // Hook to create a motion value - useSpring, // Hook to create smooth spring animations - useTransform, // Hook to transform motion values -} from "framer-motion"; -import Link from "next/link"; // Next.js Link component for navigation -import React, { useRef, useState } from "react"; // Importing React hooks -import { Menu, X } from "lucide-react"; // Importing icons from lucide-react - -// Interface for props accepted by the AnimatedDock component -interface AnimatedDockProps { - items: { title: string; icon: React.ReactNode; href: string }[]; // Array of menu items - largeClassName?: string; // Optional class name for large dock - smallClassName?: string; // Optional class name for small dock -} - -// Main AnimatedDock component that renders both LargeDock and SmallDock -export default function AnimatedDock({ items, largeClassName, smallClassName }: AnimatedDockProps) { - return ( - <> - {/* Render LargeDock for larger screens */} - - {/* Render SmallDock for smaller screens */} - - - ); -} - -// Component for the large dock, visible on larger screens -const LargeDock = ({ - items, - className, -}: { - items: { title: string; icon: React.ReactNode; href: string }[]; // Items to display - className?: string; // Optional class name -}) => { - const mouseXPosition = useMotionValue(Infinity); // Create a motion value for mouse X position - return ( - mouseXPosition.set(e.pageX)} // Update mouse X position on mouse move - onMouseLeave={() => mouseXPosition.set(Infinity)} // Reset on mouse leave - className={cn( - "mx-auto hidden h-16 items-end gap-4 rounded-2xl bg-white/10 px-4 pb-3 dark:bg-black/10 md:flex", // Large dock styles - className, - "border border-gray-200/30 backdrop-blur-sm dark:border-gray-800/30", - )} - > - {/* Render each dock icon */} - {items.map((item) => ( - - ))} - - ); -}; - -// Component for individual icons in the dock -function DockIcon({ - mouseX, - title, - icon, - href, -}: { - mouseX: MotionValue; // Motion value for mouse position - title: string; // Title of the icon - icon: React.ReactNode; // Icon component - href: string; // Link destination -}) { - const ref = useRef(null); // Ref for measuring distance from mouse - - // Calculate the distance from the mouse to the icon - const distanceFromMouse = useTransform(mouseX, (val) => { - const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 }; // Get icon bounds - return val - bounds.x - bounds.width / 2; // Calculate distance from center - }); - - // Transform properties for width and height based on mouse distance - const widthTransform = useTransform(distanceFromMouse, [-150, 0, 150], [40, 80, 40]); - const heightTransform = useTransform(distanceFromMouse, [-150, 0, 150], [40, 80, 40]); - - // Transform properties for icon size based on mouse distance - const iconWidthTransform = useTransform(distanceFromMouse, [-150, 0, 150], [20, 40, 20]); - const iconHeightTransform = useTransform(distanceFromMouse, [-150, 0, 150], [20, 40, 20]); - - // Spring animations for smooth transitions - const width = useSpring(widthTransform, { mass: 0.1, stiffness: 150, damping: 12 }); - const height = useSpring(heightTransform, { mass: 0.1, stiffness: 150, damping: 12 }); - const iconWidth = useSpring(iconWidthTransform, { mass: 0.1, stiffness: 150, damping: 12 }); - const iconHeight = useSpring(iconHeightTransform, { mass: 0.1, stiffness: 150, damping: 12 }); - - const [isHovered, setIsHovered] = useState(false); // State for hover effect - - return ( - - setIsHovered(true)} // Handle mouse enter - onMouseLeave={() => setIsHovered(false)} // Handle mouse leave - className="relative flex aspect-square items-center justify-center rounded-full bg-white/20 text-black shadow-lg backdrop-blur-md dark:bg-black/20 dark:text-white" - > - - {/* Tooltip that appears on hover */} - {isHovered && ( - - {title} {/* Tooltip text */} - - )} - - - {icon} {/* Render the icon */} - - - - ); -} - -// Component for the small dock, visible on smaller screens -const SmallDock = ({ - items, - className, -}: { - items: { title: string; icon: React.ReactNode; href: string }[]; // Items to display - className?: string; // Optional class name -}) => { - const [isOpen, setIsOpen] = useState(false); // State to manage open/close of the small dock - - return ( -
- - {/* Render menu items when open */} - {isOpen && ( - - {items.map((item, index) => ( - - -
{item.icon}
{/* Render the icon */} - -
- ))} -
- )} -
- {/* Button to toggle the small dock open/close */} - -
- ); -}; diff --git a/animata/progress/checklist-completion.stories.tsx b/animata/progress/checklist-completion.stories.tsx new file mode 100644 index 00000000..cda72bed --- /dev/null +++ b/animata/progress/checklist-completion.stories.tsx @@ -0,0 +1,19 @@ +import ChecklistCompletion from "@/animata/progress/checklist-completion"; +import { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Progress/Checklist Completion", + component: ChecklistCompletion, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: {}, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Primary: Story = { + args: {}, +}; diff --git a/animata/progress/checklist-completion.tsx b/animata/progress/checklist-completion.tsx new file mode 100644 index 00000000..22ab3131 --- /dev/null +++ b/animata/progress/checklist-completion.tsx @@ -0,0 +1,143 @@ +"use client"; +import React, { useRef, useState } from "react"; + +type Task = { + id: number; + name: string; + completed: boolean; +}; + +interface ChecklistCompletionProps { + tasks?: Task[]; + onTaskToggle?: (taskId: number) => void; +} + +const initialTasks: Task[] = [ + { id: 1, name: "How it all started", completed: true }, + { id: 2, name: "Our values and approach", completed: true }, + { id: 3, name: "Working together", completed: false }, + { id: 4, name: "Union rules and integration", completed: false }, + { id: 5, name: "Security policy", completed: false }, + { id: 6, name: "Performance reviews", completed: false }, + { id: 7, name: "Employee benefits", completed: false }, + { id: 8, name: "Final assessment", completed: false }, +]; + +// ProgressBar Component +const ProgressBar: React.FC<{ completedTasks: number; totalTasks: number }> = ({ + completedTasks, + totalTasks, +}) => ( +
+
+
+
+
+ + {completedTasks} / {totalTasks} Completed + +
+
+); + +// TaskList Component +const TaskList: React.FC<{ + tasks: Task[]; + onTaskToggle: (taskId: number) => void; + taskRefs: React.MutableRefObject<(HTMLLIElement | null)[]>; + scrollableContainerRef: React.RefObject; +}> = ({ tasks, onTaskToggle, taskRefs, scrollableContainerRef }) => ( +
    + {tasks.map((task, index) => ( +
  • onTaskToggle(task.id)} + ref={(el) => { + taskRefs.current[index] = el; + }} + > + {task.completed ? ( +
    + + + + {task.name} +
    + ) : ( +
    + + {task.name} +
    + )} +
  • + ))} + +
+); + +const ChecklistCompletion: React.FC = ({ + tasks = initialTasks, + onTaskToggle, +}) => { + const [taskState, setTaskState] = useState(tasks); + const taskRefs = useRef<(HTMLLIElement | null)[]>([]); + const scrollableContainerRef = useRef(null); + + const toggleTaskCompletion = (taskId: number): void => { + const updatedTasks = taskState.map((task) => + task.id === taskId ? { ...task, completed: !task.completed } : task, + ); + setTaskState(updatedTasks); + onTaskToggle?.(taskId); + + const taskIndex = taskState.findIndex((task) => task.id === taskId); + if (taskRefs.current[taskIndex]) { + taskRefs.current[taskIndex]?.scrollIntoView({ + behavior: "smooth", + block: "nearest", + }); + } + + if (scrollableContainerRef.current) { + const currentScroll = scrollableContainerRef.current.scrollTop; + const scrollHeight = scrollableContainerRef.current.scrollHeight; + const containerHeight = scrollableContainerRef.current.clientHeight; + + if (currentScroll + containerHeight < scrollHeight) { + scrollableContainerRef.current.scrollTo({ + top: currentScroll + 50, + behavior: "smooth", + }); + } + } + }; + + const completedTasks = taskState.filter((task) => task.completed).length; + const totalTasks = taskState.length; + + return ( +
+ + +
+ ); +}; + +export default ChecklistCompletion; diff --git a/content/docs/container/animated-dock.mdx b/content/docs/container/animated-dock.mdx deleted file mode 100644 index a88eff89..00000000 --- a/content/docs/container/animated-dock.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Animated Dock -description: A sleek dock-style navigation bar, inspired by macOS, that combines glassmorphic design with functionality. With smooth animations and responsive icons, it enhances navigation for a modern web application. -author: R0X4R ---- - - - -## Installation - - -Install dependencies - -```bash -npm install framer-motion lucide-react -``` - -Run the following command - -It will create a new file `animated-dock.tsx` inside the `components/animata/container` directory. - -```bash -mkdir -p components/animata/container && touch components/animata/container/animated-dock.tsx -``` - -Paste the code - -Open the newly created file and paste the following code: - -```jsx file=/animata/container/animated-dock.tsx - -``` - - - -## Credits - - Built by [Eshan Singh](https://github.com/R0X4R) with the help of [Framer Motion](https://www.framer.com/motion/). Inspired by [Build UI](https://buildui.com/recipes/magnified-dock) diff --git a/content/docs/progress/checklist-completion.mdx b/content/docs/progress/checklist-completion.mdx new file mode 100644 index 00000000..f928da12 --- /dev/null +++ b/content/docs/progress/checklist-completion.mdx @@ -0,0 +1,35 @@ +--- +title: Checklist Completion +description: A progress tracker component with a progress bar, checkmarks for completed steps, and a counter. +author: shoomankhatri +--- + + + +## Installation + + + +Run the following command + +It will create a new file `checklist-completion.tsx` inside the `components/animata/progress` directory. + +```bash +mkdir -p components/animata/progress && touch components/animata/progress/checklist-completion.tsx +``` + +Paste the code{" "} + +Open the newly created file and paste the following code: + +```jsx file=/animata/progress/checklist-completion.tsx + +``` + + + +## Credits + +Built by [Suman Khatri](https://github.com/shoomankhatri) + +...Add appropriate credits here.