Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checklist-completion.tsx #354

Closed
wants to merge 10 commits into from
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<br>

### Table of Contents

1. [Introduction](#introduction)
- [What is Animata?](#what-is-animata)
- [What is not Animata?](#what-is-not-animata)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.

Expand All @@ -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.

<section id="#Authors"
Expand Down
11 changes: 3 additions & 8 deletions animata/container/animated-dock.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Meta, StoryObj } from "@storybook/react";
import { Home, Search, Bell, User } from "lucide-react";
import AnimatedDock from "@/animata/container/animated-dock";
import { Bell, Home, Search, User } from "lucide-react";

import AnimatedDock from "@/animata/container/animated-dock";
import { Meta, StoryObj } from "@storybook/react";

const meta = {
title: "Container/Animated Dock",
Expand All @@ -19,7 +19,6 @@ const meta = {
export default meta;
type Story = StoryObj<typeof meta>;


// Example contents for AnimatedDock
const dockItems = [
{ title: "Home", icon: <Home />, href: "/" },
Expand All @@ -28,7 +27,6 @@ const dockItems = [
{ title: "Profile", icon: <User />, href: "/profile" },
];


// Primary story for AnimatedDock (default layout)
export const Primary: Story = {
args: {
Expand All @@ -43,7 +41,6 @@ export const Primary: Story = {
),
};


// Story focused on the Small layout (for mobile view)
export const Small: Story = {
args: {
Expand All @@ -57,7 +54,6 @@ export const Small: Story = {
),
};


// Story focused on the Large layout (for desktop view)
export const Large: Story = {
args: {
Expand All @@ -71,7 +67,6 @@ export const Large: Story = {
),
};


// Story showing both layouts at the same time (for comparison)
export const Multiple: Story = {
args: {
Expand Down
9 changes: 5 additions & 4 deletions animata/container/animated-dock.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { cn } from "@/lib/utils"; // Import utility for conditional class names
import React, { useRef, useState } from "react"; // Importing React hooks
import Link from "next/link"; // Next.js Link component for navigation
import {
AnimatePresence, // Enables animation presence detection
MotionValue, // Type for motion values
motion, // Main component for animations
MotionValue, // Type for motion values
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

import { cn } from "@/lib/utils"; // Import utility for conditional class names

// Interface for props accepted by the AnimatedDock component
interface AnimatedDockProps {
items: { title: string; icon: React.ReactNode; href: string }[]; // Array of menu items
Expand Down
19 changes: 19 additions & 0 deletions animata/progress/checklist-completion.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof ChecklistCompletion>;

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

export const Primary: Story = {
args: {},
};
115 changes: 115 additions & 0 deletions animata/progress/checklist-completion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"use client";
import React, { useRef, useState } from "react";

type Task = {
id: number;
name: string;
completed: boolean;
};

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 },
];

export default function ChecklistCompletion() {
const [tasks, setTasks] = useState<Task[]>(initialTasks);
const taskRefs = useRef<(HTMLLIElement | null)[]>([]);
const scrollableContainerRef = useRef<HTMLUListElement>(null); // Reference to the scrollable list container

const toggleTaskCompletion = (taskId: number): void => {
const updatedTasks = tasks.map((task) =>
task.id === taskId ? { ...task, completed: !task.completed } : task,
);
setTasks(updatedTasks);

// Scroll the clicked task into view
const taskIndex = tasks.findIndex((task) => task.id === taskId);
if (taskRefs.current[taskIndex]) {
taskRefs.current[taskIndex]?.scrollIntoView({
behavior: "smooth",
block: "nearest",
});
}

// Optionally scroll down a bit after completing a task
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, // Scroll down by 50px
behavior: "smooth",
});
}
}
};

const completedTasks = tasks.filter((task) => task.completed).length;
const totalTasks = tasks.length;

return (
<div className="mx-auto flex h-auto min-h-screen w-full max-w-lg flex-col overflow-hidden bg-gray-50 p-16">
{/* Progress Bar */}
<div className="sticky z-10 mb-12 rounded-lg bg-white p-4 shadow-lg">
<div className="mb-4 flex items-center justify-between">
<div className="relative mr-4 h-3 w-full rounded-full bg-gray-300">
<div
className="h-3 rounded-full bg-black transition-all duration-300"
style={{ width: `${(completedTasks / totalTasks) * 100}%` }}
></div>
</div>
<span className="whitespace-nowrap text-gray-600">
{completedTasks} / {totalTasks} Completed
</span>
</div>
</div>

{/* Scrollable Task List */}
<ul
ref={scrollableContainerRef} // Attach the ref to the scrollable list
className="space-y-6 overflow-y-auto"
style={{ height: "160px", scrollbarWidth: "none", msOverflowStyle: "none" }}
>
{tasks.map((task, index) => (
<li
key={task.id}
className="flex cursor-pointer items-center space-x-2 text-lg"
onClick={() => toggleTaskCompletion(task.id)}
ref={(el) => {
taskRefs.current[index] = el;
}}
>
{task.completed ? (
<div className="flex items-center">
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-green-500">
<span className="text-black">&#10003;</span>
</span>
<span className="ml-2">{task.name}</span>
</div>
) : (
<div className="flex items-center">
<span className="inline-block h-4 w-4 rounded-full border border-gray-400"></span>
<span className="ml-2 text-gray-400">{task.name}</span>
</div>
)}
</li>
))}
</ul>

<style>{`
ul::-webkit-scrollbar {
display: none;
}
`}</style>
</div>
);
}
4 changes: 2 additions & 2 deletions content/docs/container/animated-dock.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ Open the newly created file and paste the following code:
```jsx file=<rootDir>/animata/container/animated-dock.tsx

```
</Steps>

</Steps>

## 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)
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)
35 changes: 35 additions & 0 deletions content/docs/progress/checklist-completion.mdx
Original file line number Diff line number Diff line change
@@ -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
---

<ComponentPreview name="progress-checklist-completion--docs" />

## Installation

<Steps>

<Step>Run the following command</Step>

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
```

<Step>Paste the code</Step>{" "}

Open the newly created file and paste the following code:

```jsx file=<rootDir>/animata/progress/checklist-completion.tsx

```

</Steps>

## Credits

Built by [Suman Khatri](https://github.com/shoomankhatri)

...Add appropriate credits here.
Loading