├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING
├── LICENSE
├── README.md
├── app
├── binary-tree
│ ├── loading.tsx
│ └── page.tsx
├── globals.css
├── graph
│ └── page.tsx
├── graphs
│ └── page.tsx
├── hash-table
│ ├── loading.tsx
│ └── page.tsx
├── icon.ico
├── layout.tsx
├── linked-list
│ └── page.tsx
├── page.tsx
├── preload.js
├── queue
│ └── page.tsx
├── queues
│ └── page.tsx
├── stack
│ └── page.tsx
├── stacks
│ └── page.tsx
├── trees
│ └── page.tsx
└── tutorials
│ ├── applications
│ └── page.tsx
│ ├── binary-tree
│ ├── applications
│ │ └── page.tsx
│ ├── delete-operation
│ │ └── page.tsx
│ ├── insert-operation
│ │ └── page.tsx
│ ├── introduction
│ │ └── page.tsx
│ ├── layout.tsx
│ ├── search-operation
│ │ └── page.tsx
│ └── traversal-operation
│ │ └── page.tsx
│ ├── creating-nodes
│ └── page.tsx
│ ├── deletion
│ └── page.tsx
│ ├── hash-table
│ ├── applications
│ │ └── page.tsx
│ ├── delete-operation
│ │ └── page.tsx
│ ├── insert-operation
│ │ └── page.tsx
│ ├── introduction
│ │ └── page.tsx
│ └── search-operation
│ │ └── page.tsx
│ ├── insertion
│ └── page.tsx
│ ├── introduction
│ └── page.tsx
│ ├── queue
│ ├── applications
│ │ └── page.tsx
│ ├── dequeue-operation
│ │ └── page.tsx
│ ├── enqueue-operation
│ │ └── page.tsx
│ ├── introduction
│ │ └── page.tsx
│ └── peek-operation
│ │ └── page.tsx
│ ├── searching
│ └── page.tsx
│ ├── stack
│ ├── applications
│ │ └── page.tsx
│ ├── introduction
│ │ └── page.tsx
│ ├── operations
│ │ └── page.tsx
│ ├── peek-operation
│ │ └── page.tsx
│ ├── pop-operation
│ │ └── page.tsx
│ └── push-operation
│ │ └── page.tsx
│ └── traversal
│ └── page.tsx
├── components.json
├── components
├── binary-tree-tutorial-layout.tsx
├── binary-tree
│ ├── binary-tree-operations.tsx
│ ├── binary-tree-properties.tsx
│ └── binary-tree-visualizer.tsx
├── code-block.tsx
├── dynamic-tutorial-content.tsx
├── graph
│ ├── graph-operations.tsx
│ ├── graph-properties.tsx
│ └── graph-visualizer.tsx
├── hash-table-tutorial-layout.tsx
├── hash-table
│ ├── hash-table-operations.tsx
│ ├── hash-table-properties.tsx
│ └── hash-table-visualizer.tsx
├── interactive-exercise.tsx
├── linked-list-visualizer.tsx
├── optimized-image.tsx
├── queue-tutorial-layout.tsx
├── queue-visualizer.tsx
├── queue
│ ├── queue-operations.tsx
│ ├── queue-properties.tsx
│ ├── queue-visualizer.tsx
│ └── simple-queue-visualizer.tsx
├── stack-tutorial-layout.tsx
├── stack-visualizer.tsx
├── stack
│ ├── stack-operations.tsx
│ ├── stack-properties.tsx
│ └── stack-visualizer.tsx
├── theme-provider.tsx
├── tutorial-layout.tsx
└── ui
│ ├── accordion.tsx
│ ├── alert-dialog.tsx
│ ├── alert.tsx
│ ├── aspect-ratio.tsx
│ ├── avatar.tsx
│ ├── badge.tsx
│ ├── breadcrumb.tsx
│ ├── button.tsx
│ ├── calendar.tsx
│ ├── card.tsx
│ ├── carousel.tsx
│ ├── chart.tsx
│ ├── checkbox.tsx
│ ├── code-block.tsx
│ ├── collapsible.tsx
│ ├── command.tsx
│ ├── context-menu.tsx
│ ├── dialog.tsx
│ ├── drawer.tsx
│ ├── dropdown-menu.tsx
│ ├── form.tsx
│ ├── hover-card.tsx
│ ├── input-otp.tsx
│ ├── input.tsx
│ ├── label.tsx
│ ├── menubar.tsx
│ ├── navigation-menu.tsx
│ ├── pagination.tsx
│ ├── popover.tsx
│ ├── progress.tsx
│ ├── radio-group.tsx
│ ├── resizable.tsx
│ ├── scroll-area.tsx
│ ├── select.tsx
│ ├── separator.tsx
│ ├── sheet.tsx
│ ├── sidebar.tsx
│ ├── skeleton.tsx
│ ├── slider.tsx
│ ├── sonner.tsx
│ ├── switch.tsx
│ ├── table.tsx
│ ├── tabs.tsx
│ ├── textarea.tsx
│ ├── toast.tsx
│ ├── toaster.tsx
│ ├── toggle-group.tsx
│ ├── toggle.tsx
│ ├── tooltip.tsx
│ ├── use-mobile.tsx
│ └── use-toast.ts
├── hooks
├── use-mobile.tsx
└── use-toast.ts
├── lib
├── data-structures
│ ├── binary-tree.ts
│ ├── graph.ts
│ ├── hash-table.ts
│ ├── queue.ts
│ └── stack.ts
└── utils.ts
├── next-env.d.ts
├── next.config.mjs
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
├── styles
└── globals.css
├── tailwind.config.js
├── tasks.txt
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # next.js
7 | /.next/
8 | /out/
9 |
10 | # production
11 | /build
12 |
13 | # debug
14 | npm-debug.log*
15 | yarn-debug.log*
16 | yarn-error.log*
17 | .pnpm-debug.log*
18 |
19 | # env files
20 | .env*
21 |
22 | # vercel
23 | .vercel
24 |
25 | # typescript
26 | *.tsbuildinfo
27 | next-env.d.ts
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | pauaranega20@gmail.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for your interest in contributing! Here’s how you can help:
4 |
5 | 1. **Fork the repo** and create a new branch.
6 | 2. **Make your changes** and test them.
7 | 3. **Submit a pull request (PR)** with a clear description.
8 |
9 | Issues and feature requests are welcome!
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Pau Aranega Bellido
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📊 Data Structures Visualizer
2 |
3 | An interactive web app to visualize data structures in real-time. Perfect for students, educators, and developers learning how algorithms work under the hood.
4 |
5 | ---
6 |
7 | ## 🚀 Latest Update: Interactive Tutorials & UI Revamp
8 |
9 | We’ve just launched a major update focused on **learning, usability, and design**!
10 |
11 | ### What's New
12 |
13 | - **📚 Interactive Tutorials**
14 | Learn core data structures—arrays, linked lists, stacks, queues, trees, graphs—with step-by-step tutorials, visual walkthroughs, and live coding environments.
15 |
16 | - **🎨 UI Overhaul**
17 | Redesigned all pages with modern aesthetics, smoother navigation, and full responsiveness across devices.
18 |
19 | - **♿ Accessibility & UX Enhancements**
20 | Improved visual consistency, readability, and user interactions for a better, more inclusive experience.
21 |
22 | ---
23 |
24 | ✨ Features
25 |
26 | - Visualize trees, graphs, linked lists, stacks, and queues
27 | - Perform key operations: insert, delete, search, and traverse
28 | - Real-time animations to show how each structure changes
29 | - Basic complexity analysis to understand performance
30 |
31 | ---
32 | ## 🤝 Contributing
33 | Thanks for your interest in contributing! Here's how to get started:
34 |
35 |
36 | 1. Fork the repo and create a new branch.
37 |
38 | 2. Make your changes and test them.
39 |
40 | 3. Submit a pull request with a clear description.
41 |
42 | Got ideas or found a bug? Open an issue or request a feature—contributions are always welcome!
43 |
44 | ## 🛠️ Installation
45 |
46 | Clone the repository and run the app locally:
47 |
48 | ```
49 | git clone https://github.com/paudefclasspy/data-structures.git
50 | ```
51 | ```
52 | cd data-structures
53 | ```
54 | ```
55 | npm install
56 | npm run dev
57 | ```
58 |
--------------------------------------------------------------------------------
/app/binary-tree/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function Loading() {
2 | return null
3 | }
4 |
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 260 40% 12%;
8 | --foreground: 0 0% 100%;
9 | --card: 261 40% 16%;
10 | --card-foreground: 0 0% 100%;
11 | --popover: 261 40% 16%;
12 | --popover-foreground: 0 0% 100%;
13 | --primary: 252 100% 69%;
14 | --primary-foreground: 0 0% 100%;
15 | --secondary: 260 40% 20%;
16 | --secondary-foreground: 0 0% 100%;
17 | --muted: 260 40% 16%;
18 | --muted-foreground: 0 0% 80%;
19 | --accent: 252 100% 69%;
20 | --accent-foreground: 0 0% 100%;
21 | --destructive: 0 84% 60%;
22 | --destructive-foreground: 0 0% 100%;
23 | --border: 260 40% 20%;
24 | --input: 260 40% 20%;
25 | --ring: 252 100% 69%;
26 | --radius: 0.75rem;
27 | }
28 | }
29 |
30 | @layer base {
31 | * {
32 | @apply border-border;
33 | }
34 | body {
35 | @apply bg-gradient-purple text-foreground antialiased;
36 | }
37 | }
38 |
39 | @keyframes fade-up {
40 | 0% {
41 | opacity: 1;
42 | transform: translateY(0);
43 | }
44 | 100% {
45 | opacity: 0;
46 | transform: translateY(-20px);
47 | }
48 | }
49 |
50 | @keyframes fade-left {
51 | 0% {
52 | opacity: 1;
53 | transform: translateX(0);
54 | }
55 | 100% {
56 | opacity: 0;
57 | transform: translateX(-20px);
58 | }
59 | }
60 |
61 | .animate-fade-up {
62 | animation: fade-up 1s forwards;
63 | }
64 |
65 | .animate-fade-left {
66 | animation: fade-left 1s forwards;
67 | }
68 |
69 | /* Custom styles for cards and buttons */
70 | .card-gradient {
71 | background: linear-gradient(145deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
72 | backdrop-filter: blur(10px);
73 | border: 1px solid rgba(255, 255, 255, 0.1);
74 | }
75 |
76 | .button-gradient {
77 | background: linear-gradient(90deg, #6d28d9 0%, #7c3aed 100%);
78 | transition: all 0.3s ease;
79 | }
80 |
81 | .button-gradient:hover {
82 | background: linear-gradient(90deg, #5b21b6 0%, #6d28d9 100%);
83 | transform: translateY(-1px);
84 | }
85 |
86 | .button-outline {
87 | background: transparent;
88 | border: 1px solid rgba(255, 255, 255, 0.2);
89 | transition: all 0.3s ease;
90 | }
91 |
92 | .button-outline:hover {
93 | border-color: rgba(255, 255, 255, 0.4);
94 | background: rgba(255, 255, 255, 0.05);
95 | }
96 |
97 | /* Custom node styles */
98 | .node {
99 | background: linear-gradient(145deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
100 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
101 | transition: all 0.3s ease;
102 | }
103 |
104 | .node:hover {
105 | transform: translateY(-2px);
106 | box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
107 | }
108 |
109 | /* Custom animations */
110 | @keyframes float {
111 | 0% {
112 | transform: translateY(0px);
113 | }
114 | 50% {
115 | transform: translateY(-10px);
116 | }
117 | 100% {
118 | transform: translateY(0px);
119 | }
120 | }
121 |
122 | .animate-float {
123 | animation: float 3s ease-in-out infinite;
124 | }
125 |
--------------------------------------------------------------------------------
/app/hash-table/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function Loading() {
2 | return null
3 | }
4 |
--------------------------------------------------------------------------------
/app/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paudefclasspy/data-structures/HEAD/app/icon.ico
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type React from "react"
2 | import "./globals.css"
3 | import type { Metadata } from "next"
4 | import { Inter } from "next/font/google"
5 |
6 | const inter = Inter({ subsets: ["latin"] })
7 |
8 | export const metadata: Metadata = {
9 | title: "Data Structures Visualizer",
10 | description: "Interactive visualizations of common data structures",
11 | }
12 |
13 | export default function RootLayout({
14 | children,
15 | }: {
16 | children: React.ReactNode
17 | }) {
18 | return (
19 |
20 |
{children}
21 |
22 | )
23 | }
24 |
25 |
26 | import './globals.css'
--------------------------------------------------------------------------------
/app/preload.js:
--------------------------------------------------------------------------------
1 | // This file is used to preload critical resources
2 | export function preload() {
3 | // Preload critical CSS
4 | const linkPreload = document.createElement('link')
5 | linkPreload.rel = 'preload'
6 | linkPreload.as = 'style'
7 | linkPreload.href = '/styles.css'
8 | document.head.appendChild(linkPreload)
9 |
10 | // Preconnect to external domains
11 | const domains = [
12 | 'fonts.googleapis.com',
13 | 'fonts.gstatic.com'
14 | ]
15 |
16 | domains.forEach(domain => {
17 | const link = document.createElement('link')
18 | link.rel = 'preconnect'
19 | link.href = `https://${domain}`
20 | link.crossOrigin = 'anonymous'
21 | document.head.appendChild(link)
22 | })
23 |
24 | // Preload critical fonts
25 | const fontPreload = document.createElement('link')
26 | fontPreload.rel = 'preload'
27 | fontPreload.as = 'font'
28 | fontPreload.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'
29 | fontPreload.crossOrigin = 'anonymous'
30 | document.head.appendChild(fontPreload)
31 | }
32 |
--------------------------------------------------------------------------------
/app/queues/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
5 | import { Button } from "@/components/ui/button"
6 | import { CodeBlock } from "@/components/code-block"
7 | import { QueueVisualizer } from "@/components/queue/simple-queue-visualizer"
8 |
9 | export default function QueuesPage() {
10 | const [queue, setQueue] = useState([1, 2, 3, 4, 5])
11 | const [value, setValue] = useState(0)
12 | const [selectedOperation, setSelectedOperation] = useState(null)
13 |
14 | // Base code structure for Queue operations
15 | const baseCode = `class Queue {
16 | constructor() {
17 | this.items = [];
18 | this.front = 0;
19 | this.rear = -1;
20 | }
21 | }`
22 |
23 | // Operation-specific code snippets
24 | const operationCodes: Record = {
25 | enqueue: {
26 | code: ` // Add an element to the end of the queue
27 | enqueue(value) {
28 | this.rear++;
29 | this.items[this.rear] = value;
30 | return this.items; // O(1) constant time operation
31 | }`,
32 | description: "Enqueue operation adds an element to the end of the queue with O(1) time complexity.",
33 | },
34 | dequeue: {
35 | code: ` // Remove the front element from the queue
36 | dequeue() {
37 | if (this.isEmpty()) {
38 | return "Queue underflow";
39 | }
40 |
41 | const value = this.items[this.front];
42 | this.front++;
43 |
44 | // Reset the queue when it becomes empty
45 | if (this.front > this.rear) {
46 | this.front = 0;
47 | this.rear = -1;
48 | this.items = [];
49 | }
50 |
51 | return value; // O(1) constant time operation
52 | }`,
53 | description: "Dequeue operation removes the front element from the queue with O(1) time complexity.",
54 | },
55 | peek: {
56 | code: ` // View the front element without removing it
57 | peek() {
58 | if (this.isEmpty()) {
59 | return "Queue is empty";
60 | }
61 |
62 | return this.items[this.front]; // O(1) constant time operation
63 | }`,
64 | description: "Peek operation returns the front element without removing it with O(1) time complexity.",
65 | },
66 | isEmpty: {
67 | code: ` // Check if the queue is empty
68 | isEmpty() {
69 | return this.front > this.rear; // O(1) constant time operation
70 | }`,
71 | description: "isEmpty operation checks if the queue is empty with O(1) time complexity.",
72 | },
73 | }
74 |
75 | // Combine base code with selected operation code
76 | const getDisplayCode = () => {
77 | if (!selectedOperation) return baseCode
78 | return `${baseCode}\n\n${operationCodes[selectedOperation].code}`
79 | }
80 |
81 | // Get description based on selected operation
82 | const getDescription = () => {
83 | if (!selectedOperation) return "Select an operation to see its implementation and time complexity."
84 | return operationCodes[selectedOperation].description
85 | }
86 |
87 | const handleEnqueue = () => {
88 | setSelectedOperation("enqueue")
89 | const newQueue = [...queue]
90 | newQueue.push(value)
91 | setQueue(newQueue)
92 | }
93 |
94 | const handleDequeue = () => {
95 | setSelectedOperation("dequeue")
96 | if (queue.length > 0) {
97 | const newQueue = [...queue]
98 | newQueue.shift()
99 | setQueue(newQueue)
100 | }
101 | }
102 |
103 | const handlePeek = () => {
104 | setSelectedOperation("peek")
105 | // Just visual indication, not changing the queue
106 | }
107 |
108 | const handleIsEmpty = () => {
109 | setSelectedOperation("isEmpty")
110 | // Just visual indication, not changing the queue
111 | }
112 |
113 | const resetView = () => {
114 | setSelectedOperation(null)
115 | }
116 |
117 | return (
118 |
119 |
Queues
120 |
121 |
122 |
123 | Queue Operations
124 | {getDescription()}
125 |
126 |
127 |
128 |
129 | Enqueue
130 | Dequeue
131 | Peek
132 | Is Empty
133 | {selectedOperation && (
134 |
135 | Reset View
136 |
137 | )}
138 |
139 |
140 | Value
141 | setValue(Number.parseInt(e.target.value) || 0)}
145 | className="w-full p-2 border rounded"
146 | />
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | Queue Visualization
155 | Visual representation of the queue and its operations
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | {selectedOperation
166 | ? `${selectedOperation.charAt(0).toUpperCase() + selectedOperation.slice(1)} Operation Code`
167 | : "Queue Implementation"}
168 |
169 |
170 | {selectedOperation
171 | ? `Highlighting the ${selectedOperation} operation implementation`
172 | : "Select an operation to see its implementation"}
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | )
182 | }
183 |
--------------------------------------------------------------------------------
/app/stacks/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
5 | import { Button } from "@/components/ui/button"
6 | import { CodeBlock } from "@/components/code-block"
7 | import { StackVisualizer } from "@/components/stack-visualizer"
8 |
9 | export default function StacksPage() {
10 | const [stack, setStack] = useState([5, 4, 3, 2, 1])
11 | const [value, setValue] = useState(0)
12 | const [selectedOperation, setSelectedOperation] = useState(null)
13 |
14 | // Base code structure for Stack operations
15 | const baseCode = `class Stack {
16 | constructor() {
17 | this.items = [];
18 | this.top = -1;
19 | }
20 | }`
21 |
22 | // Operation-specific code snippets
23 | const operationCodes: Record = {
24 | push: {
25 | code: ` // Add an element to the top of the stack
26 | push(value) {
27 | this.top++;
28 | this.items[this.top] = value;
29 | return this.items; // O(1) constant time operation
30 | }`,
31 | description: "Push operation adds an element to the top of the stack with O(1) time complexity.",
32 | },
33 | pop: {
34 | code: ` // Remove the top element from the stack
35 | pop() {
36 | if (this.isEmpty()) {
37 | return "Stack underflow";
38 | }
39 |
40 | const value = this.items[this.top];
41 | this.top--;
42 | this.items.length = this.top + 1;
43 | return value; // O(1) constant time operation
44 | }`,
45 | description: "Pop operation removes the top element from the stack with O(1) time complexity.",
46 | },
47 | peek: {
48 | code: ` // View the top element without removing it
49 | peek() {
50 | if (this.isEmpty()) {
51 | return "Stack is empty";
52 | }
53 |
54 | return this.items[this.top]; // O(1) constant time operation
55 | }`,
56 | description: "Peek operation returns the top element without removing it with O(1) time complexity.",
57 | },
58 | isEmpty: {
59 | code: ` // Check if the stack is empty
60 | isEmpty() {
61 | return this.top === -1; // O(1) constant time operation
62 | }`,
63 | description: "isEmpty operation checks if the stack is empty with O(1) time complexity.",
64 | },
65 | }
66 |
67 | // Combine base code with selected operation code
68 | const getDisplayCode = () => {
69 | if (!selectedOperation) return baseCode
70 | return `${baseCode}\n\n${operationCodes[selectedOperation].code}`
71 | }
72 |
73 | // Get description based on selected operation
74 | const getDescription = () => {
75 | if (!selectedOperation) return "Select an operation to see its implementation and time complexity."
76 | return operationCodes[selectedOperation].description
77 | }
78 |
79 | const handlePush = () => {
80 | setSelectedOperation("push")
81 | const newStack = [...stack]
82 | newStack.push(value)
83 | setStack(newStack)
84 | }
85 |
86 | const handlePop = () => {
87 | setSelectedOperation("pop")
88 | if (stack.length > 0) {
89 | const newStack = [...stack]
90 | newStack.pop()
91 | setStack(newStack)
92 | }
93 | }
94 |
95 | const handlePeek = () => {
96 | setSelectedOperation("peek")
97 | // Just visual indication, not changing the stack
98 | }
99 |
100 | const handleIsEmpty = () => {
101 | setSelectedOperation("isEmpty")
102 | // Just visual indication, not changing the stack
103 | }
104 |
105 | const resetView = () => {
106 | setSelectedOperation(null)
107 | }
108 |
109 | return (
110 |
111 |
Stacks
112 |
113 |
114 |
115 | Stack Operations
116 | {getDescription()}
117 |
118 |
119 |
120 |
121 | Push
122 | Pop
123 | Peek
124 | Is Empty
125 | {selectedOperation && (
126 |
127 | Reset View
128 |
129 | )}
130 |
131 |
132 | Value
133 | setValue(Number.parseInt(e.target.value) || 0)}
137 | className="w-full p-2 border rounded"
138 | />
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | Stack Visualization
147 | Visual representation of the stack and its operations
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | {selectedOperation
158 | ? `${selectedOperation.charAt(0).toUpperCase() + selectedOperation.slice(1)} Operation Code`
159 | : "Stack Implementation"}
160 |
161 |
162 | {selectedOperation
163 | ? `Highlighting the ${selectedOperation} operation implementation`
164 | : "Select an operation to see its implementation"}
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | )
174 | }
175 |
--------------------------------------------------------------------------------
/app/tutorials/binary-tree/introduction/page.tsx:
--------------------------------------------------------------------------------
1 | import BinaryTreeTutorialLayout from "@/components/binary-tree-tutorial-layout"
2 | import { CodeBlock } from "@/components/code-block"
3 |
4 | export default function IntroductionToBinaryTrees() {
5 | return (
6 |
13 |
14 |
15 | What is a Binary Tree?
16 |
17 | A binary tree is a hierarchical data structure in which each node has at most two children, referred to as
18 | the left child and the right child. Each node in a binary tree contains:
19 |
20 |
21 | A data element (or value)
22 | A reference to the left child
23 | A reference to the right child
24 |
25 |
26 |
27 |
28 | Types of Binary Trees
29 |
30 |
31 |
Full Binary Tree
32 |
A binary tree in which every node has either 0 or 2 children.
33 |
34 |
35 |
Complete Binary Tree
36 |
37 | A binary tree in which all levels are completely filled except possibly the last level, which is filled
38 | from left to right.
39 |
40 |
41 |
42 |
Perfect Binary Tree
43 |
44 | A binary tree in which all internal nodes have exactly two children and all leaf nodes are at the same
45 | level.
46 |
47 |
48 |
49 |
Binary Search Tree (BST)
50 |
51 | A binary tree with the property that for each node, all elements in its left subtree are less than the
52 | node's value, and all elements in its right subtree are greater than the node's value.
53 |
54 |
55 |
56 |
57 |
58 |
59 | Basic Structure of a Binary Tree Node
60 | Here's how we define a basic binary tree node in TypeScript:
61 |
75 |
76 |
77 |
78 | Why Use Binary Trees?
79 | Binary trees are useful for many reasons:
80 |
81 |
82 | Efficient Searching: Binary search trees provide O(log n) search
83 | time on average.
84 |
85 |
86 | Hierarchical Structure: They naturally represent hierarchical
87 | relationships between data.
88 |
89 |
90 | Efficient Insertions/Deletions: BSTs allow for efficient insertions
91 | and deletions.
92 |
93 |
94 | Ordered Traversal: In-order traversal of a BST gives elements in
95 | sorted order.
96 |
97 |
98 | Foundation for Advanced Data Structures: They form the basis for
99 | more complex structures like AVL trees, Red-Black trees, and heaps.
100 |
101 |
102 |
103 |
104 |
105 | Common Operations
106 | The main operations performed on binary trees include:
107 |
108 |
109 |
Insertion
110 |
Adding a new node to the tree
111 |
112 |
113 |
Deletion
114 |
Removing a node from the tree
115 |
116 |
117 |
Searching
118 |
Finding a specific value in the tree
119 |
120 |
121 |
Traversal
122 |
Visiting all nodes in the tree in a specific order (in-order, pre-order, post-order, level-order)
123 |
124 |
125 |
126 |
127 |
128 |
Next Steps
129 |
130 | In the following sections, we'll explore each of these operations in detail, focusing on binary search trees
131 | (BSTs) as they are one of the most commonly used types of binary trees.
132 |
133 |
134 |
135 |
136 | )
137 | }
138 |
--------------------------------------------------------------------------------
/app/tutorials/binary-tree/layout.tsx:
--------------------------------------------------------------------------------
1 | import type React from "react"
2 |
3 | export default function Layout({
4 | children,
5 | }: {
6 | children: React.ReactNode
7 | }) {
8 | return <>{children}>
9 | }
10 |
--------------------------------------------------------------------------------
/app/tutorials/introduction/page.tsx:
--------------------------------------------------------------------------------
1 | import { TutorialLayout } from "@/components/tutorial-layout"
2 | import { LinkedListVisualizer } from "@/components/linked-list-visualizer"
3 | import { CodeBlock } from "@/components/code-block"
4 | import { InteractiveExercise } from "@/components/interactive-exercise"
5 |
6 | export default function IntroductionPage() {
7 | return (
8 |
15 |
16 |
17 | What is a Linked List?
18 |
19 | A linked list is a linear data structure where elements are stored in nodes. Unlike arrays, linked list
20 | elements are not stored in contiguous memory locations. Instead, each node contains data and a reference (or
21 | link) to the next node in the sequence.
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | In the visualization above, you can see a simple linked list with four nodes. Each node contains a value,
30 | and an arrow pointing to the next node in the sequence. The last node points to null, indicating the end of
31 | the list.
32 |
33 |
34 |
35 |
36 | Key Characteristics of Linked Lists
37 |
38 |
39 |
40 | Dynamic Size: Linked lists can grow or shrink during execution as needed.
41 |
42 |
43 | Efficient Insertions/Deletions: Adding or removing elements doesn't require shifting
44 | other elements.
45 |
46 |
47 | Non-Contiguous Memory: Nodes can be stored anywhere in memory, unlike arrays.
48 |
49 |
50 | Sequential Access: To access a specific element, you must traverse from the head node.
51 |
52 |
53 |
54 |
55 |
56 | Basic Structure in Code
57 |
58 |
77 |
78 |
79 | The code above shows the basic structure of a linked list in JavaScript. Each node contains a value and a
80 | reference to the next node. The linked list itself has a head property that points to the first node in the
81 | list.
82 |
83 |
84 |
85 |
86 | Advantages and Disadvantages
87 |
88 |
89 |
90 |
Advantages
91 |
92 | Dynamic size allocation
93 | Efficient insertions and deletions
94 | No need to pre-allocate memory
95 | Can easily grow or shrink during execution
96 |
97 |
98 |
99 |
100 |
Disadvantages
101 |
102 | No random access (must traverse from head)
103 | Extra memory for storing references
104 | Not cache-friendly due to non-contiguous memory
105 | Reverse traversal is difficult in singly linked lists
106 |
107 |
108 |
109 |
110 |
111 |
112 | Check Your Understanding
113 |
114 |
125 |
126 |
127 |
128 | Next Steps
129 |
130 | Now that you understand what linked lists are and their basic structure, let's move on to creating nodes and
131 | building a linked list from scratch in the next tutorial.
132 |
133 |
134 |
135 |
136 | )
137 | }
138 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
--------------------------------------------------------------------------------
/components/binary-tree/binary-tree-properties.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { memo } from "react"
4 | import Link from "next/link"
5 | import { Button } from "@/components/ui/button"
6 | import { Info, BookOpen } from "lucide-react"
7 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
8 |
9 | const BinaryTreeProperties = memo(() => {
10 | return (
11 |
12 |
13 |
14 |
15 | Binary Search Tree Properties
16 |
17 |
18 |
19 |
20 |
21 |
Ordered Structure
22 |
23 | For each node, all values in the left subtree are less than the node's value, and all values in the right subtree are greater.
24 |
25 |
26 |
27 |
28 |
Time Complexity
29 |
30 | Insert:
31 | O(log n) average
32 |
33 |
34 | Find:
35 | O(log n) average
36 |
37 |
38 | Remove:
39 | O(log n) average
40 |
41 |
42 |
43 |
44 |
Applications
45 |
46 | Hierarchical data storage
47 | Priority queues
48 | Sorting algorithms
49 | Database indexing
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Learn More in Tutorial Mode
58 |
59 |
60 |
61 |
62 |
63 |
64 | )
65 | })
66 |
67 | BinaryTreeProperties.displayName = "BinaryTreeProperties"
68 |
69 | export { BinaryTreeProperties }
70 |
--------------------------------------------------------------------------------
/components/binary-tree/binary-tree-visualizer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useRef, useEffect, memo } from "react"
4 | import { BinarySearchTree, TreeNode } from "@/lib/data-structures/binary-tree"
5 | import { CodeBlock } from "@/components/code-block"
6 |
7 | interface BinaryTreeVisualizerProps {
8 | initialItems?: number[]
9 | }
10 |
11 | interface TreeNodeProps {
12 | node: TreeNode
13 | isHighlighted?: boolean
14 | level: number
15 | }
16 |
17 | const TreeNodeComponent = memo(({ node, isHighlighted, level }: TreeNodeProps) => {
18 | return (
19 |
20 |
30 | {node.value}
31 |
32 |
33 | {(node.left || node.right) && (
34 |
35 | {node.left ? (
36 |
40 | ) : (
41 |
42 | )}
43 |
44 | {node.right ? (
45 |
49 | ) : (
50 |
51 | )}
52 |
53 | )}
54 |
55 | )
56 | })
57 |
58 | TreeNodeComponent.displayName = "TreeNodeComponent"
59 |
60 | const BinaryTreeVisualizer = memo(({ initialItems = [50, 30, 70, 20, 40, 60, 80] }: BinaryTreeVisualizerProps) => {
61 | const [tree] = useState(new BinarySearchTree())
62 | const [treeRoot, setTreeRoot] = useState(null)
63 | const [animationStep, setAnimationStep] = useState(0)
64 | const [animationValue, setAnimationValue] = useState(null)
65 | const [isAnimating, setIsAnimating] = useState(false)
66 | const [operationDescription, setOperationDescription] = useState("")
67 | const [currentCodeSnippet, setCurrentCodeSnippet] = useState("")
68 | const [currentOperation, setCurrentOperation] = useState("empty")
69 | const [highlightedPath, setHighlightedPath] = useState([])
70 | const animationTimeoutRef = useRef(null)
71 |
72 | // Update the tree visualization whenever the tree changes
73 | const updateTree = () => {
74 | setTreeRoot(tree.getTree())
75 | }
76 |
77 | // Initialize the tree with example items
78 | useEffect(() => {
79 | if (!treeRoot && initialItems.length > 0) {
80 | initialItems.forEach(item => tree.insert(item))
81 | updateTree()
82 | }
83 | }, [tree, treeRoot, initialItems])
84 |
85 | // Clean up animations on unmount
86 | useEffect(() => {
87 | return () => {
88 | if (animationTimeoutRef.current) {
89 | clearTimeout(animationTimeoutRef.current)
90 | }
91 | }
92 | }, [])
93 |
94 | return (
95 |
96 |
97 |
98 | {treeRoot ? (
99 |
100 | ) : (
101 |
102 |
The tree is empty
103 |
Use the controls to add nodes
104 |
105 | )}
106 |
107 |
108 |
109 | {/* Operation description */}
110 | {isAnimating && (
111 |
112 |
{operationDescription}
113 |
114 | {animationStep === 1 && "Traversing the tree..."}
115 | {animationStep === 2 && "Performing operation..."}
116 | {animationStep === 3 && "Operation completed successfully"}
117 |
118 |
119 | )}
120 |
121 | {/* Code snippet for current operation */}
122 | {currentCodeSnippet && (
123 |
124 |
138 |
139 | )}
140 |
141 | )
142 | })
143 |
144 | BinaryTreeVisualizer.displayName = "BinaryTreeVisualizer"
145 |
146 | export { BinaryTreeVisualizer }
147 |
--------------------------------------------------------------------------------
/components/code-block.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { Check, Clipboard, Code } from "lucide-react"
5 | import { cn } from "@/lib/utils"
6 |
7 | interface CodeBlockProps {
8 | code: string
9 | language?: string
10 | title?: string
11 | highlightLines?: number[]
12 | className?: string
13 | }
14 |
15 | export function CodeBlock({ code, language = "javascript", title, highlightLines = [], className }: CodeBlockProps) {
16 | const [copied, setCopied] = useState(false)
17 |
18 | const handleCopy = () => {
19 | navigator.clipboard.writeText(code)
20 | setCopied(true)
21 | setTimeout(() => setCopied(false), 2000)
22 | }
23 |
24 | return (
25 |
26 | {title && (
27 |
28 |
29 |
30 | {title}
31 |
32 |
33 |
37 | {copied ? (
38 | <>
39 |
40 | Copied!
41 | >
42 | ) : (
43 | <>
44 |
45 | Copy
46 | >
47 | )}
48 |
49 |
50 |
51 | )}
52 |
53 | {code.split("\n").map((line, i) => (
54 |
61 | {line || " "}
62 |
63 | ))}
64 |
65 |
66 | )
67 | }
68 |
--------------------------------------------------------------------------------
/components/dynamic-tutorial-content.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import dynamic from 'next/dynamic'
4 | import { useState, useEffect } from 'react'
5 | import { Skeleton } from '@/components/ui/skeleton'
6 |
7 | interface DynamicTutorialContentProps {
8 | tutorialPath: string
9 | fallback?: React.ReactNode
10 | }
11 |
12 | export function DynamicTutorialContent({
13 | tutorialPath,
14 | fallback =
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | }: DynamicTutorialContentProps) {
24 | const [Component, setComponent] = useState | null>(null)
25 | const [isLoading, setIsLoading] = useState(true)
26 |
27 | useEffect(() => {
28 | setIsLoading(true)
29 |
30 | // Dynamically import the tutorial content
31 | const importComponent = async () => {
32 | try {
33 | const module = await import(`@/app/tutorials/${tutorialPath}/page`)
34 | setComponent(() => module.default)
35 | } catch (error) {
36 | console.error(`Error loading tutorial content: ${tutorialPath}`, error)
37 | } finally {
38 | setIsLoading(false)
39 | }
40 | }
41 |
42 | importComponent()
43 | }, [tutorialPath])
44 |
45 | if (isLoading || !Component) {
46 | return <>{fallback}>
47 | }
48 |
49 | return
50 | }
51 |
--------------------------------------------------------------------------------
/components/graph/graph-properties.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { memo } from "react"
4 | import Link from "next/link"
5 | import { Button } from "@/components/ui/button"
6 | import { Info, BookOpen } from "lucide-react"
7 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
8 |
9 | const GraphProperties = memo(() => {
10 | return (
11 |
12 |
13 |
14 |
15 | Graph Properties
16 |
17 |
18 |
19 |
20 |
21 |
Vertices and Edges
22 |
23 | Graphs consist of vertices (nodes) connected by edges, representing relationships between entities.
24 |
25 |
26 |
27 |
28 |
Time Complexity
29 |
30 | Add Vertex:
31 | O(1)
32 |
33 |
34 | Add Edge:
35 | O(1)
36 |
37 |
38 | Remove Vertex:
39 | O(V + E)
40 |
41 |
42 | BFS/DFS:
43 | O(V + E)
44 |
45 |
46 |
47 |
48 |
Applications
49 |
50 | Social networks
51 | Route planning
52 | Web page ranking
53 | Network topology
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Learn More in Tutorial Mode
62 |
63 |
64 |
65 |
66 |
67 |
68 | )
69 | })
70 |
71 | GraphProperties.displayName = "GraphProperties"
72 |
73 | export { GraphProperties }
74 |
--------------------------------------------------------------------------------
/components/hash-table/hash-table-properties.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { memo } from "react"
4 | import Link from "next/link"
5 | import { Button } from "@/components/ui/button"
6 | import { Info, BookOpen } from "lucide-react"
7 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
8 |
9 | const HashTableProperties = memo(() => {
10 | return (
11 |
12 |
13 |
14 |
15 | Hash Table Properties
16 |
17 |
18 |
19 |
20 |
21 |
Key-Value Storage
22 |
23 | Hash tables store data as key-value pairs with fast lookups.
24 |
25 |
26 |
27 |
28 |
Time Complexity
29 |
30 | Set:
31 | O(1) average
32 |
33 |
34 | Get:
35 | O(1) average
36 |
37 |
38 | Delete:
39 | O(1) average
40 |
41 |
42 |
43 |
44 |
Applications
45 |
46 | Database indexing
47 | Caching
48 | Symbol tables
49 | Associative arrays
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Learn More in Tutorial Mode
58 |
59 |
60 |
61 |
62 |
63 |
64 | )
65 | })
66 |
67 | HashTableProperties.displayName = "HashTableProperties"
68 |
69 | export { HashTableProperties }
70 |
--------------------------------------------------------------------------------
/components/hash-table/hash-table-visualizer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useRef, useEffect, memo } from "react"
4 | import { HashTable } from "@/lib/data-structures/hash-table"
5 | import { CodeBlock } from "@/components/code-block"
6 |
7 | interface HashTableVisualizerProps {
8 | initialItems?: { key: string, value: any }[]
9 | }
10 |
11 | const HashTableVisualizer = memo(({ initialItems = [
12 | { key: "name", value: "John" },
13 | { key: "age", value: 30 },
14 | { key: "city", value: "New York" }
15 | ] }: HashTableVisualizerProps) => {
16 | const [hashTable] = useState(new HashTable(10))
17 | const [buckets, setBuckets] = useState([])
18 | const [animationStep, setAnimationStep] = useState(0)
19 | const [animationItem, setAnimationItem] = useState<{ key: string, value: any, index: number } | null>(null)
20 | const [isAnimating, setIsAnimating] = useState(false)
21 | const [operationDescription, setOperationDescription] = useState("")
22 | const [currentCodeSnippet, setCurrentCodeSnippet] = useState("")
23 | const [currentOperation, setCurrentOperation] = useState("empty")
24 | const animationTimeoutRef = useRef(null)
25 |
26 | // Update the buckets whenever the hash table changes
27 | const updateBuckets = () => {
28 | setBuckets(hashTable.getBuckets())
29 | }
30 |
31 | // Initialize the hash table with example items
32 | useEffect(() => {
33 | if (buckets.length === 0 && initialItems.length > 0) {
34 | initialItems.forEach(item => hashTable.set(item.key, item.value))
35 | updateBuckets()
36 | }
37 | }, [hashTable, buckets.length, initialItems])
38 |
39 | // Clean up animations on unmount
40 | useEffect(() => {
41 | return () => {
42 | if (animationTimeoutRef.current) {
43 | clearTimeout(animationTimeoutRef.current)
44 | }
45 | }
46 | }, [])
47 |
48 | return (
49 |
50 |
51 | {buckets.map((bucket, index) => (
52 |
53 |
54 | {index}
55 |
56 |
57 | {bucket.length === 0 ? (
58 |
Empty bucket
59 | ) : (
60 |
61 | {bucket.map((item, itemIndex) => (
62 |
75 | {item.key}: {item.value}
76 |
77 | ))}
78 |
79 | )}
80 |
81 |
82 | ))}
83 |
84 |
85 | {/* Operation description */}
86 | {isAnimating && (
87 |
88 |
{operationDescription}
89 |
90 | {animationStep === 1 && "Calculating hash..."}
91 | {animationStep === 2 && "Placing item in bucket..."}
92 | {animationStep === 3 && "Operation completed successfully"}
93 |
94 |
95 | )}
96 |
97 | {/* Code snippet for current operation */}
98 | {currentCodeSnippet && (
99 |
100 |
116 |
117 | )}
118 |
119 | )
120 | })
121 |
122 | HashTableVisualizer.displayName = "HashTableVisualizer"
123 |
124 | export { HashTableVisualizer }
125 |
--------------------------------------------------------------------------------
/components/interactive-exercise.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { AlertCircle, CheckCircle2 } from "lucide-react"
5 | import { Button } from "@/components/ui/button"
6 | import { cn } from "@/lib/utils"
7 |
8 | interface Question {
9 | question: string
10 | options: string[]
11 | correctAnswer: number
12 | explanation: string
13 | }
14 |
15 | interface InteractiveExerciseProps {
16 | questions: Question | Question[]
17 | className?: string
18 | }
19 |
20 | export function InteractiveExercise({ questions, className }: InteractiveExerciseProps) {
21 | const questionArray = Array.isArray(questions) ? questions : [questions]
22 | const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)
23 | const [selectedOptions, setSelectedOptions] = useState<(number | null)[]>(questionArray.map(() => null))
24 | const [isSubmitted, setIsSubmitted] = useState(false)
25 |
26 | const currentQuestion = questionArray[currentQuestionIndex]
27 |
28 | // If there's no current question, render nothing
29 | if (!currentQuestion) {
30 | return null
31 | }
32 |
33 | const isCorrect = selectedOptions[currentQuestionIndex] === currentQuestion.correctAnswer
34 |
35 | const handleOptionSelect = (index: number) => {
36 | if (!isSubmitted) {
37 | const newSelectedOptions = [...selectedOptions]
38 | newSelectedOptions[currentQuestionIndex] = index
39 | setSelectedOptions(newSelectedOptions)
40 | }
41 | }
42 |
43 | const handleSubmit = () => {
44 | if (selectedOptions[currentQuestionIndex] !== null) {
45 | setIsSubmitted(true)
46 | }
47 | }
48 |
49 | const handleNext = () => {
50 | if (currentQuestionIndex < questionArray.length - 1) {
51 | setCurrentQuestionIndex(currentQuestionIndex + 1)
52 | setIsSubmitted(false)
53 | }
54 | }
55 |
56 | const handleReset = () => {
57 | const newSelectedOptions = [...selectedOptions]
58 | newSelectedOptions[currentQuestionIndex] = null
59 | setSelectedOptions(newSelectedOptions)
60 | setIsSubmitted(false)
61 | }
62 |
63 | return (
64 |
65 |
{currentQuestion.question}
66 |
67 |
68 | {currentQuestion.options.map((option, index) => (
69 | handleOptionSelect(index)}
84 | disabled={isSubmitted}
85 | >
86 | {option}
87 |
88 | ))}
89 |
90 |
91 | {isSubmitted && (
92 |
98 | {isCorrect ? (
99 |
100 | ) : (
101 |
102 | )}
103 |
104 |
{isCorrect ? "Correct!" : "Incorrect"}
105 |
{currentQuestion.explanation}
106 |
107 |
108 | )}
109 |
110 |
111 | {isSubmitted ? (
112 | <>
113 | Try Again
114 | {currentQuestionIndex < questionArray.length - 1 && Next Question }
115 | >
116 | ) : (
117 |
118 | Submit Answer
119 |
120 | )}
121 |
122 |
123 | )
124 | }
125 |
--------------------------------------------------------------------------------
/components/optimized-image.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useEffect } from 'react'
4 | import Image from 'next/image'
5 | import { cn } from '@/lib/utils'
6 |
7 | interface OptimizedImageProps {
8 | src: string
9 | alt: string
10 | width: number
11 | height: number
12 | className?: string
13 | priority?: boolean
14 | }
15 |
16 | export function OptimizedImage({
17 | src,
18 | alt,
19 | width,
20 | height,
21 | className,
22 | priority = false,
23 | }: OptimizedImageProps) {
24 | const [isLoaded, setIsLoaded] = useState(false)
25 |
26 | return (
27 |
28 |
setIsLoaded(true)}
38 | priority={priority}
39 | loading={priority ? "eager" : "lazy"}
40 | />
41 | {!isLoaded && (
42 |
43 | )}
44 |
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/components/queue-visualizer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useEffect } from "react"
4 | import { cn } from "@/lib/utils"
5 |
6 | interface QueueVisualizerProps {
7 | initialValues?: number[]
8 | className?: string
9 | }
10 |
11 | export default function QueueVisualizer({ initialValues = [], className }: QueueVisualizerProps) {
12 | const [queue, setQueue] = useState([])
13 |
14 | useEffect(() => {
15 | setQueue(initialValues)
16 | }, [initialValues])
17 |
18 | return (
19 |
20 | {/* Queue visualization */}
21 |
22 | {queue.length === 0 ? (
23 |
24 |
The queue is empty
25 |
26 | ) : (
27 |
28 | {queue.map((value, index) => (
29 |
30 |
31 | {value}
32 |
33 | {index < queue.length - 1 && (
34 |
35 | >
36 |
37 | )}
38 |
39 | ))}
40 |
41 | )}
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/components/queue/queue-properties.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { memo } from "react"
4 | import Link from "next/link"
5 | import { Button } from "@/components/ui/button"
6 | import { Info, BookOpen } from "lucide-react"
7 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
8 |
9 | const QueueProperties = memo(() => {
10 | return (
11 |
12 |
13 |
14 |
15 | Queue Properties
16 |
17 |
18 |
19 |
20 |
21 |
First-In-First-Out (FIFO)
22 |
23 | The first item added to the queue is the first one to be removed.
24 |
25 |
26 |
27 |
28 |
Time Complexity
29 |
30 | Enqueue:
31 | O(1)
32 |
33 |
34 | Dequeue:
35 | O(n)
36 |
37 |
38 | Peek:
39 | O(1)
40 |
41 |
42 |
43 |
44 |
Applications
45 |
46 | Task scheduling
47 | Print job management
48 | Breadth-first search
49 | Message queues
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Learn More in Tutorial Mode
58 |
59 |
60 |
61 |
62 |
63 |
64 | )
65 | })
66 |
67 | QueueProperties.displayName = "QueueProperties"
68 |
69 | export { QueueProperties }
70 |
--------------------------------------------------------------------------------
/components/queue/queue-visualizer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useRef, useEffect, memo } from "react"
4 | import { Queue } from "@/lib/data-structures/queue"
5 | import { CodeBlock } from "@/components/code-block"
6 |
7 | interface QueueVisualizerProps {
8 | initialItems?: number[]
9 | }
10 |
11 | const QueueVisualizer = memo(({ initialItems = [10, 20, 30] }: QueueVisualizerProps) => {
12 | const [queue] = useState(new Queue())
13 | const [queueItems, setQueueItems] = useState([])
14 | const [animationStep, setAnimationStep] = useState(0)
15 | const [animationItem, setAnimationItem] = useState(null)
16 | const [isAnimating, setIsAnimating] = useState(false)
17 | const [operationDescription, setOperationDescription] = useState("")
18 | const [dequeuedItem, setDequeuedItem] = useState(null)
19 | const [currentCodeSnippet, setCurrentCodeSnippet] = useState("")
20 | const [currentOperation, setCurrentOperation] = useState("empty")
21 | const animationTimeoutRef = useRef(null)
22 |
23 | // Update the queue items whenever the queue changes
24 | const updateQueueItems = () => {
25 | setQueueItems(queue.getItems())
26 | }
27 |
28 | // Initialize the queue with example items
29 | useEffect(() => {
30 | if (queueItems.length === 0 && initialItems.length > 0) {
31 | initialItems.forEach(item => queue.enqueue(item))
32 | updateQueueItems()
33 | }
34 | }, [queue, queueItems.length, initialItems])
35 |
36 | // Clean up animations on unmount
37 | useEffect(() => {
38 | return () => {
39 | if (animationTimeoutRef.current) {
40 | clearTimeout(animationTimeoutRef.current)
41 | }
42 | }
43 | }, [])
44 |
45 | return (
46 |
47 | {queueItems.length === 0 ? (
48 |
49 |
The queue is empty
50 |
Use the controls to add items
51 |
52 | ) : (
53 |
54 |
55 |
Front
56 |
Back
57 |
58 |
59 | {queueItems.map((value, index) => (
60 |
74 | {value}
75 |
76 | ))}
77 |
78 |
79 | )}
80 |
81 | {/* Animation for enqueuing */}
82 | {isAnimating && animationStep === 1 && animationItem !== null && (
83 |
84 | {animationItem}
85 |
86 | )}
87 |
88 | {/* Animation for dequeuing */}
89 | {isAnimating && animationStep === 2 && dequeuedItem !== null && (
90 |
91 | {dequeuedItem}
92 |
93 | )}
94 |
95 | {/* Operation description */}
96 | {isAnimating && (
97 |
98 |
{operationDescription}
99 |
100 | {animationStep === 1 && "Preparing operation..."}
101 | {animationStep === 2 && "Operation completed successfully"}
102 | {animationStep === 3 && "Peeking at the front element without removing it"}
103 |
104 |
105 | )}
106 |
107 | {/* Code snippet for current operation */}
108 | {currentCodeSnippet && (
109 |
110 |
124 |
125 | )}
126 |
127 | )
128 | })
129 |
130 | QueueVisualizer.displayName = "QueueVisualizer"
131 |
132 | export { QueueVisualizer }
133 |
--------------------------------------------------------------------------------
/components/queue/simple-queue-visualizer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { memo } from "react"
4 | import { cn } from "@/lib/utils"
5 |
6 | interface QueueVisualizerProps {
7 | queue?: number[]
8 | highlightIndex?: number
9 | highlightedIndices?: number[]
10 | highlightedValues?: number[]
11 | }
12 |
13 | const SimpleQueueVisualizer = memo(({
14 | queue = [],
15 | highlightIndex = -1,
16 | highlightedIndices = [],
17 | highlightedValues = []
18 | }: QueueVisualizerProps) => {
19 | return (
20 |
21 | {queue.length === 0 ? (
22 |
23 |
The queue is empty
24 |
25 | ) : (
26 |
27 |
28 |
Front
29 |
Back
30 |
31 |
32 | {queue.map((value, index) => (
33 |
42 | {value}
43 |
44 | ))}
45 |
46 |
47 | )}
48 |
49 | )
50 | })
51 |
52 | SimpleQueueVisualizer.displayName = "SimpleQueueVisualizer"
53 |
54 | export { SimpleQueueVisualizer as QueueVisualizer }
55 |
--------------------------------------------------------------------------------
/components/stack/stack-properties.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { memo } from "react"
4 | import Link from "next/link"
5 | import { Button } from "@/components/ui/button"
6 | import { Info, BookOpen } from "lucide-react"
7 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
8 |
9 | const StackProperties = memo(() => {
10 | return (
11 |
12 |
13 |
14 |
15 | Stack Properties
16 |
17 |
18 |
19 |
20 |
21 |
Last-In-First-Out (LIFO)
22 |
23 | The last item added to the stack is the first one to be removed.
24 |
25 |
26 |
27 |
28 |
Time Complexity
29 |
30 | Push:
31 | O(1)
32 |
33 |
34 | Pop:
35 | O(1)
36 |
37 |
38 | Peek:
39 | O(1)
40 |
41 |
42 |
43 |
44 |
Applications
45 |
46 | Function call management
47 | Expression evaluation
48 | Undo mechanisms
49 | Backtracking algorithms
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Learn More in Tutorial Mode
58 |
59 |
60 |
61 |
62 |
63 |
64 | )
65 | })
66 |
67 | StackProperties.displayName = "StackProperties"
68 |
69 | export { StackProperties }
70 |
--------------------------------------------------------------------------------
/components/stack/stack-visualizer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useRef, useEffect, memo } from "react"
4 | import { Stack } from "@/lib/data-structures/stack"
5 | import { CodeBlock } from "@/components/code-block"
6 |
7 | interface StackVisualizerProps {
8 | initialItems?: number[]
9 | }
10 |
11 | const StackVisualizer = memo(({ initialItems = [10, 20, 30] }: StackVisualizerProps) => {
12 | const [stack] = useState(new Stack())
13 | const [stackItems, setStackItems] = useState([])
14 | const [animationStep, setAnimationStep] = useState(0)
15 | const [animationItem, setAnimationItem] = useState(null)
16 | const [isAnimating, setIsAnimating] = useState(false)
17 | const [operationDescription, setOperationDescription] = useState("")
18 | const [poppedItem, setPoppedItem] = useState(null)
19 | const [currentCodeSnippet, setCurrentCodeSnippet] = useState("")
20 | const [currentOperation, setCurrentOperation] = useState("empty")
21 | const animationTimeoutRef = useRef(null)
22 |
23 | // Update the stack items whenever the stack changes
24 | const updateStackItems = () => {
25 | setStackItems(stack.getItems())
26 | }
27 |
28 | // Clear any ongoing animations
29 | const clearAnimation = () => {
30 | if (animationTimeoutRef.current) {
31 | clearTimeout(animationTimeoutRef.current)
32 | animationTimeoutRef.current = null
33 | }
34 | setAnimationStep(0)
35 | setAnimationItem(null)
36 | setIsAnimating(false)
37 | setPoppedItem(null)
38 | }
39 |
40 | // Initialize the stack with example items
41 | useEffect(() => {
42 | if (stackItems.length === 0 && initialItems.length > 0) {
43 | initialItems.forEach(item => stack.push(item))
44 | updateStackItems()
45 | }
46 | }, [stack, stackItems.length, initialItems])
47 |
48 | // Clean up animations on unmount
49 | useEffect(() => {
50 | return () => {
51 | if (animationTimeoutRef.current) {
52 | clearTimeout(animationTimeoutRef.current)
53 | }
54 | }
55 | }, [])
56 |
57 | return (
58 |
59 | {stackItems.length === 0 ? (
60 |
61 |
The stack is empty
62 |
Use the controls to add items
63 |
64 | ) : (
65 |
66 |
Bottom
67 | {stackItems.map((value, index) => (
68 |
82 | {value}
83 |
84 | ))}
85 |
Top
86 |
87 | )}
88 |
89 | {/* Animation for pushing */}
90 | {isAnimating && animationStep === 1 && animationItem !== null && (
91 |
92 | {animationItem}
93 |
94 | )}
95 |
96 | {/* Animation for popping */}
97 | {isAnimating && animationStep === 2 && poppedItem !== null && (
98 |
99 | {poppedItem}
100 |
101 | )}
102 |
103 | {/* Operation description */}
104 | {isAnimating && (
105 |
106 |
{operationDescription}
107 |
108 | {animationStep === 1 && "Preparing operation..."}
109 | {animationStep === 2 && "Operation completed successfully"}
110 | {animationStep === 3 && "Peeking at the top element without removing it"}
111 |
112 |
113 | )}
114 |
115 | {/* Code snippet for current operation */}
116 | {currentCodeSnippet && (
117 |
118 |
132 |
133 | )}
134 |
135 | )
136 | })
137 |
138 | StackVisualizer.displayName = "StackVisualizer"
139 |
140 | export { StackVisualizer }
141 |
--------------------------------------------------------------------------------
/components/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 | import {
5 | ThemeProvider as NextThemesProvider,
6 | type ThemeProviderProps,
7 | } from 'next-themes'
8 |
9 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
10 | return {children}
11 | }
12 |
--------------------------------------------------------------------------------
/components/ui/accordion.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AccordionPrimitive from "@radix-ui/react-accordion"
5 | import { ChevronDown } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Accordion = AccordionPrimitive.Root
10 |
11 | const AccordionItem = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
20 | ))
21 | AccordionItem.displayName = "AccordionItem"
22 |
23 | const AccordionTrigger = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, children, ...props }, ref) => (
27 |
28 | svg]:rotate-180",
32 | className
33 | )}
34 | {...props}
35 | >
36 | {children}
37 |
38 |
39 |
40 | ))
41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42 |
43 | const AccordionContent = React.forwardRef<
44 | React.ElementRef,
45 | React.ComponentPropsWithoutRef
46 | >(({ className, children, ...props }, ref) => (
47 |
52 | {children}
53 |
54 | ))
55 |
56 | AccordionContent.displayName = AccordionPrimitive.Content.displayName
57 |
58 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
59 |
--------------------------------------------------------------------------------
/components/ui/alert-dialog.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { buttonVariants } from "@/components/ui/button"
8 |
9 | const AlertDialog = AlertDialogPrimitive.Root
10 |
11 | const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12 |
13 | const AlertDialogPortal = AlertDialogPrimitive.Portal
14 |
15 | const AlertDialogOverlay = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, ...props }, ref) => (
19 |
27 | ))
28 | AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
29 |
30 | const AlertDialogContent = React.forwardRef<
31 | React.ElementRef,
32 | React.ComponentPropsWithoutRef
33 | >(({ className, ...props }, ref) => (
34 |
35 |
36 |
44 |
45 | ))
46 | AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
47 |
48 | const AlertDialogHeader = ({
49 | className,
50 | ...props
51 | }: React.HTMLAttributes) => (
52 |
59 | )
60 | AlertDialogHeader.displayName = "AlertDialogHeader"
61 |
62 | const AlertDialogFooter = ({
63 | className,
64 | ...props
65 | }: React.HTMLAttributes) => (
66 |
73 | )
74 | AlertDialogFooter.displayName = "AlertDialogFooter"
75 |
76 | const AlertDialogTitle = React.forwardRef<
77 | React.ElementRef,
78 | React.ComponentPropsWithoutRef
79 | >(({ className, ...props }, ref) => (
80 |
85 | ))
86 | AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
87 |
88 | const AlertDialogDescription = React.forwardRef<
89 | React.ElementRef,
90 | React.ComponentPropsWithoutRef
91 | >(({ className, ...props }, ref) => (
92 |
97 | ))
98 | AlertDialogDescription.displayName =
99 | AlertDialogPrimitive.Description.displayName
100 |
101 | const AlertDialogAction = React.forwardRef<
102 | React.ElementRef,
103 | React.ComponentPropsWithoutRef
104 | >(({ className, ...props }, ref) => (
105 |
110 | ))
111 | AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
112 |
113 | const AlertDialogCancel = React.forwardRef<
114 | React.ElementRef,
115 | React.ComponentPropsWithoutRef
116 | >(({ className, ...props }, ref) => (
117 |
126 | ))
127 | AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
128 |
129 | export {
130 | AlertDialog,
131 | AlertDialogPortal,
132 | AlertDialogOverlay,
133 | AlertDialogTrigger,
134 | AlertDialogContent,
135 | AlertDialogHeader,
136 | AlertDialogFooter,
137 | AlertDialogTitle,
138 | AlertDialogDescription,
139 | AlertDialogAction,
140 | AlertDialogCancel,
141 | }
142 |
--------------------------------------------------------------------------------
/components/ui/alert.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const alertVariants = cva(
7 | "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
8 | {
9 | variants: {
10 | variant: {
11 | default: "bg-background text-foreground",
12 | destructive:
13 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
14 | },
15 | },
16 | defaultVariants: {
17 | variant: "default",
18 | },
19 | }
20 | )
21 |
22 | const Alert = React.forwardRef<
23 | HTMLDivElement,
24 | React.HTMLAttributes & VariantProps
25 | >(({ className, variant, ...props }, ref) => (
26 |
32 | ))
33 | Alert.displayName = "Alert"
34 |
35 | const AlertTitle = React.forwardRef<
36 | HTMLParagraphElement,
37 | React.HTMLAttributes
38 | >(({ className, ...props }, ref) => (
39 |
44 | ))
45 | AlertTitle.displayName = "AlertTitle"
46 |
47 | const AlertDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | AlertDescription.displayName = "AlertDescription"
58 |
59 | export { Alert, AlertTitle, AlertDescription }
60 |
--------------------------------------------------------------------------------
/components/ui/aspect-ratio.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
4 |
5 | const AspectRatio = AspectRatioPrimitive.Root
6 |
7 | export { AspectRatio }
8 |
--------------------------------------------------------------------------------
/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AvatarPrimitive from "@radix-ui/react-avatar"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Avatar = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 | ))
21 | Avatar.displayName = AvatarPrimitive.Root.displayName
22 |
23 | const AvatarImage = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => (
27 |
32 | ))
33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName
34 |
35 | const AvatarFallback = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 | ))
48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
49 |
50 | export { Avatar, AvatarImage, AvatarFallback }
51 |
--------------------------------------------------------------------------------
/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13 | secondary:
14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15 | destructive:
16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17 | outline: "text-foreground",
18 | },
19 | },
20 | defaultVariants: {
21 | variant: "default",
22 | },
23 | }
24 | )
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | )
34 | }
35 |
36 | export { Badge, badgeVariants }
37 |
--------------------------------------------------------------------------------
/components/ui/breadcrumb.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { ChevronRight, MoreHorizontal } from "lucide-react"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const Breadcrumb = React.forwardRef<
8 | HTMLElement,
9 | React.ComponentPropsWithoutRef<"nav"> & {
10 | separator?: React.ReactNode
11 | }
12 | >(({ ...props }, ref) => )
13 | Breadcrumb.displayName = "Breadcrumb"
14 |
15 | const BreadcrumbList = React.forwardRef<
16 | HTMLOListElement,
17 | React.ComponentPropsWithoutRef<"ol">
18 | >(({ className, ...props }, ref) => (
19 |
27 | ))
28 | BreadcrumbList.displayName = "BreadcrumbList"
29 |
30 | const BreadcrumbItem = React.forwardRef<
31 | HTMLLIElement,
32 | React.ComponentPropsWithoutRef<"li">
33 | >(({ className, ...props }, ref) => (
34 |
39 | ))
40 | BreadcrumbItem.displayName = "BreadcrumbItem"
41 |
42 | const BreadcrumbLink = React.forwardRef<
43 | HTMLAnchorElement,
44 | React.ComponentPropsWithoutRef<"a"> & {
45 | asChild?: boolean
46 | }
47 | >(({ asChild, className, ...props }, ref) => {
48 | const Comp = asChild ? Slot : "a"
49 |
50 | return (
51 |
56 | )
57 | })
58 | BreadcrumbLink.displayName = "BreadcrumbLink"
59 |
60 | const BreadcrumbPage = React.forwardRef<
61 | HTMLSpanElement,
62 | React.ComponentPropsWithoutRef<"span">
63 | >(({ className, ...props }, ref) => (
64 |
72 | ))
73 | BreadcrumbPage.displayName = "BreadcrumbPage"
74 |
75 | const BreadcrumbSeparator = ({
76 | children,
77 | className,
78 | ...props
79 | }: React.ComponentProps<"li">) => (
80 | svg]:w-3.5 [&>svg]:h-3.5", className)}
84 | {...props}
85 | >
86 | {children ?? }
87 |
88 | )
89 | BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
90 |
91 | const BreadcrumbEllipsis = ({
92 | className,
93 | ...props
94 | }: React.ComponentProps<"span">) => (
95 |
101 |
102 | More
103 |
104 | )
105 | BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
106 |
107 | export {
108 | Breadcrumb,
109 | BreadcrumbList,
110 | BreadcrumbItem,
111 | BreadcrumbLink,
112 | BreadcrumbPage,
113 | BreadcrumbSeparator,
114 | BreadcrumbEllipsis,
115 | }
116 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default: "button-gradient text-white",
13 | destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14 | outline: "button-outline text-white hover:bg-accent hover:text-accent-foreground",
15 | secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
16 | ghost: "hover:bg-accent hover:text-accent-foreground",
17 | link: "text-primary underline-offset-4 hover:underline",
18 | },
19 | size: {
20 | default: "h-10 px-4 py-2",
21 | sm: "h-9 rounded-md px-3",
22 | lg: "h-11 rounded-md px-8",
23 | icon: "h-10 w-10",
24 | },
25 | },
26 | defaultVariants: {
27 | variant: "default",
28 | size: "default",
29 | },
30 | },
31 | )
32 |
33 | export interface ButtonProps
34 | extends React.ButtonHTMLAttributes,
35 | VariantProps {
36 | asChild?: boolean
37 | }
38 |
39 | const Button = React.forwardRef(
40 | ({ className, variant, size, asChild = false, ...props }, ref) => {
41 | const Comp = asChild ? Slot : "button"
42 | return
43 | },
44 | )
45 | Button.displayName = "Button"
46 |
47 | export { Button, buttonVariants }
48 |
--------------------------------------------------------------------------------
/components/ui/calendar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { ChevronLeft, ChevronRight } from "lucide-react"
5 | import { DayPicker } from "react-day-picker"
6 |
7 | import { cn } from "@/lib/utils"
8 | import { buttonVariants } from "@/components/ui/button"
9 |
10 | export type CalendarProps = React.ComponentProps
11 |
12 | function Calendar({
13 | className,
14 | classNames,
15 | showOutsideDays = true,
16 | ...props
17 | }: CalendarProps) {
18 | return (
19 | ,
58 | IconRight: ({ ...props }) => ,
59 | }}
60 | {...props}
61 | />
62 | )
63 | }
64 | Calendar.displayName = "Calendar"
65 |
66 | export { Calendar }
67 |
--------------------------------------------------------------------------------
/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Card = React.forwardRef<
6 | HTMLDivElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
17 | ))
18 | Card.displayName = "Card"
19 |
20 | const CardHeader = React.forwardRef<
21 | HTMLDivElement,
22 | React.HTMLAttributes
23 | >(({ className, ...props }, ref) => (
24 |
29 | ))
30 | CardHeader.displayName = "CardHeader"
31 |
32 | const CardTitle = React.forwardRef<
33 | HTMLDivElement,
34 | React.HTMLAttributes
35 | >(({ className, ...props }, ref) => (
36 |
44 | ))
45 | CardTitle.displayName = "CardTitle"
46 |
47 | const CardDescription = React.forwardRef<
48 | HTMLDivElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | CardDescription.displayName = "CardDescription"
58 |
59 | const CardContent = React.forwardRef<
60 | HTMLDivElement,
61 | React.HTMLAttributes
62 | >(({ className, ...props }, ref) => (
63 |
64 | ))
65 | CardContent.displayName = "CardContent"
66 |
67 | const CardFooter = React.forwardRef<
68 | HTMLDivElement,
69 | React.HTMLAttributes
70 | >(({ className, ...props }, ref) => (
71 |
76 | ))
77 | CardFooter.displayName = "CardFooter"
78 |
79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
80 |
--------------------------------------------------------------------------------
/components/ui/checkbox.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5 | import { Check } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Checkbox = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => (
13 |
21 |
24 |
25 |
26 |
27 | ))
28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName
29 |
30 | export { Checkbox }
31 |
--------------------------------------------------------------------------------
/components/ui/code-block.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { Check, Clipboard, Code } from "lucide-react"
5 | import { cn } from "@/lib/utils"
6 |
7 | interface CodeBlockProps {
8 | code: string
9 | language?: string
10 | title?: string
11 | highlightLines?: number[]
12 | className?: string
13 | }
14 |
15 | export function CodeBlock({ code, language = "javascript", title, highlightLines = [], className }: CodeBlockProps) {
16 | const [copied, setCopied] = useState(false)
17 |
18 | const handleCopy = () => {
19 | navigator.clipboard.writeText(code)
20 | setCopied(true)
21 | setTimeout(() => setCopied(false), 2000)
22 | }
23 |
24 | return (
25 |
26 | {title && (
27 |
28 |
29 |
30 | {title}
31 |
32 |
33 |
37 | {copied ? (
38 | <>
39 |
40 | Copied!
41 | >
42 | ) : (
43 | <>
44 |
45 | Copy
46 | >
47 | )}
48 |
49 |
50 |
51 | )}
52 |
53 |
54 | {code.split("\n").map((line, i) => (
55 |
62 | {line || " "}
63 |
64 | ))}
65 |
66 |
67 |
68 | )
69 | }
70 |
--------------------------------------------------------------------------------
/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/components/ui/command.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { type DialogProps } from "@radix-ui/react-dialog"
5 | import { Command as CommandPrimitive } from "cmdk"
6 | import { Search } from "lucide-react"
7 |
8 | import { cn } from "@/lib/utils"
9 | import { Dialog, DialogContent } from "@/components/ui/dialog"
10 |
11 | const Command = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
23 | ))
24 | Command.displayName = CommandPrimitive.displayName
25 |
26 | const CommandDialog = ({ children, ...props }: DialogProps) => {
27 | return (
28 |
29 |
30 |
31 | {children}
32 |
33 |
34 |
35 | )
36 | }
37 |
38 | const CommandInput = React.forwardRef<
39 | React.ElementRef,
40 | React.ComponentPropsWithoutRef
41 | >(({ className, ...props }, ref) => (
42 |
43 |
44 |
52 |
53 | ))
54 |
55 | CommandInput.displayName = CommandPrimitive.Input.displayName
56 |
57 | const CommandList = React.forwardRef<
58 | React.ElementRef,
59 | React.ComponentPropsWithoutRef
60 | >(({ className, ...props }, ref) => (
61 |
66 | ))
67 |
68 | CommandList.displayName = CommandPrimitive.List.displayName
69 |
70 | const CommandEmpty = React.forwardRef<
71 | React.ElementRef,
72 | React.ComponentPropsWithoutRef
73 | >((props, ref) => (
74 |
79 | ))
80 |
81 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName
82 |
83 | const CommandGroup = React.forwardRef<
84 | React.ElementRef,
85 | React.ComponentPropsWithoutRef
86 | >(({ className, ...props }, ref) => (
87 |
95 | ))
96 |
97 | CommandGroup.displayName = CommandPrimitive.Group.displayName
98 |
99 | const CommandSeparator = React.forwardRef<
100 | React.ElementRef,
101 | React.ComponentPropsWithoutRef
102 | >(({ className, ...props }, ref) => (
103 |
108 | ))
109 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName
110 |
111 | const CommandItem = React.forwardRef<
112 | React.ElementRef,
113 | React.ComponentPropsWithoutRef
114 | >(({ className, ...props }, ref) => (
115 |
123 | ))
124 |
125 | CommandItem.displayName = CommandPrimitive.Item.displayName
126 |
127 | const CommandShortcut = ({
128 | className,
129 | ...props
130 | }: React.HTMLAttributes) => {
131 | return (
132 |
139 | )
140 | }
141 | CommandShortcut.displayName = "CommandShortcut"
142 |
143 | export {
144 | Command,
145 | CommandDialog,
146 | CommandInput,
147 | CommandList,
148 | CommandEmpty,
149 | CommandGroup,
150 | CommandItem,
151 | CommandShortcut,
152 | CommandSeparator,
153 | }
154 |
--------------------------------------------------------------------------------
/components/ui/dialog.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as DialogPrimitive from "@radix-ui/react-dialog"
5 | import { X } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Dialog = DialogPrimitive.Root
10 |
11 | const DialogTrigger = DialogPrimitive.Trigger
12 |
13 | const DialogPortal = DialogPrimitive.Portal
14 |
15 | const DialogClose = DialogPrimitive.Close
16 |
17 | const DialogOverlay = React.forwardRef<
18 | React.ElementRef,
19 | React.ComponentPropsWithoutRef
20 | >(({ className, ...props }, ref) => (
21 |
29 | ))
30 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
31 |
32 | const DialogContent = React.forwardRef<
33 | React.ElementRef,
34 | React.ComponentPropsWithoutRef
35 | >(({ className, children, ...props }, ref) => (
36 |
37 |
38 |
46 | {children}
47 |
48 |
49 | Close
50 |
51 |
52 |
53 | ))
54 | DialogContent.displayName = DialogPrimitive.Content.displayName
55 |
56 | const DialogHeader = ({
57 | className,
58 | ...props
59 | }: React.HTMLAttributes) => (
60 |
67 | )
68 | DialogHeader.displayName = "DialogHeader"
69 |
70 | const DialogFooter = ({
71 | className,
72 | ...props
73 | }: React.HTMLAttributes) => (
74 |
81 | )
82 | DialogFooter.displayName = "DialogFooter"
83 |
84 | const DialogTitle = React.forwardRef<
85 | React.ElementRef,
86 | React.ComponentPropsWithoutRef
87 | >(({ className, ...props }, ref) => (
88 |
96 | ))
97 | DialogTitle.displayName = DialogPrimitive.Title.displayName
98 |
99 | const DialogDescription = React.forwardRef<
100 | React.ElementRef,
101 | React.ComponentPropsWithoutRef
102 | >(({ className, ...props }, ref) => (
103 |
108 | ))
109 | DialogDescription.displayName = DialogPrimitive.Description.displayName
110 |
111 | export {
112 | Dialog,
113 | DialogPortal,
114 | DialogOverlay,
115 | DialogClose,
116 | DialogTrigger,
117 | DialogContent,
118 | DialogHeader,
119 | DialogFooter,
120 | DialogTitle,
121 | DialogDescription,
122 | }
123 |
--------------------------------------------------------------------------------
/components/ui/drawer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { Drawer as DrawerPrimitive } from "vaul"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Drawer = ({
9 | shouldScaleBackground = true,
10 | ...props
11 | }: React.ComponentProps) => (
12 |
16 | )
17 | Drawer.displayName = "Drawer"
18 |
19 | const DrawerTrigger = DrawerPrimitive.Trigger
20 |
21 | const DrawerPortal = DrawerPrimitive.Portal
22 |
23 | const DrawerClose = DrawerPrimitive.Close
24 |
25 | const DrawerOverlay = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef
28 | >(({ className, ...props }, ref) => (
29 |
34 | ))
35 | DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
36 |
37 | const DrawerContent = React.forwardRef<
38 | React.ElementRef,
39 | React.ComponentPropsWithoutRef
40 | >(({ className, children, ...props }, ref) => (
41 |
42 |
43 |
51 |
52 | {children}
53 |
54 |
55 | ))
56 | DrawerContent.displayName = "DrawerContent"
57 |
58 | const DrawerHeader = ({
59 | className,
60 | ...props
61 | }: React.HTMLAttributes) => (
62 |
66 | )
67 | DrawerHeader.displayName = "DrawerHeader"
68 |
69 | const DrawerFooter = ({
70 | className,
71 | ...props
72 | }: React.HTMLAttributes) => (
73 |
77 | )
78 | DrawerFooter.displayName = "DrawerFooter"
79 |
80 | const DrawerTitle = React.forwardRef<
81 | React.ElementRef,
82 | React.ComponentPropsWithoutRef
83 | >(({ className, ...props }, ref) => (
84 |
92 | ))
93 | DrawerTitle.displayName = DrawerPrimitive.Title.displayName
94 |
95 | const DrawerDescription = React.forwardRef<
96 | React.ElementRef,
97 | React.ComponentPropsWithoutRef
98 | >(({ className, ...props }, ref) => (
99 |
104 | ))
105 | DrawerDescription.displayName = DrawerPrimitive.Description.displayName
106 |
107 | export {
108 | Drawer,
109 | DrawerPortal,
110 | DrawerOverlay,
111 | DrawerTrigger,
112 | DrawerClose,
113 | DrawerContent,
114 | DrawerHeader,
115 | DrawerFooter,
116 | DrawerTitle,
117 | DrawerDescription,
118 | }
119 |
--------------------------------------------------------------------------------
/components/ui/form.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as LabelPrimitive from "@radix-ui/react-label"
5 | import { Slot } from "@radix-ui/react-slot"
6 | import {
7 | Controller,
8 | ControllerProps,
9 | FieldPath,
10 | FieldValues,
11 | FormProvider,
12 | useFormContext,
13 | } from "react-hook-form"
14 |
15 | import { cn } from "@/lib/utils"
16 | import { Label } from "@/components/ui/label"
17 |
18 | const Form = FormProvider
19 |
20 | type FormFieldContextValue<
21 | TFieldValues extends FieldValues = FieldValues,
22 | TName extends FieldPath = FieldPath
23 | > = {
24 | name: TName
25 | }
26 |
27 | const FormFieldContext = React.createContext(
28 | {} as FormFieldContextValue
29 | )
30 |
31 | const FormField = <
32 | TFieldValues extends FieldValues = FieldValues,
33 | TName extends FieldPath = FieldPath
34 | >({
35 | ...props
36 | }: ControllerProps) => {
37 | return (
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | const useFormField = () => {
45 | const fieldContext = React.useContext(FormFieldContext)
46 | const itemContext = React.useContext(FormItemContext)
47 | const { getFieldState, formState } = useFormContext()
48 |
49 | const fieldState = getFieldState(fieldContext.name, formState)
50 |
51 | if (!fieldContext) {
52 | throw new Error("useFormField should be used within ")
53 | }
54 |
55 | const { id } = itemContext
56 |
57 | return {
58 | id,
59 | name: fieldContext.name,
60 | formItemId: `${id}-form-item`,
61 | formDescriptionId: `${id}-form-item-description`,
62 | formMessageId: `${id}-form-item-message`,
63 | ...fieldState,
64 | }
65 | }
66 |
67 | type FormItemContextValue = {
68 | id: string
69 | }
70 |
71 | const FormItemContext = React.createContext(
72 | {} as FormItemContextValue
73 | )
74 |
75 | const FormItem = React.forwardRef<
76 | HTMLDivElement,
77 | React.HTMLAttributes
78 | >(({ className, ...props }, ref) => {
79 | const id = React.useId()
80 |
81 | return (
82 |
83 |
84 |
85 | )
86 | })
87 | FormItem.displayName = "FormItem"
88 |
89 | const FormLabel = React.forwardRef<
90 | React.ElementRef,
91 | React.ComponentPropsWithoutRef
92 | >(({ className, ...props }, ref) => {
93 | const { error, formItemId } = useFormField()
94 |
95 | return (
96 |
102 | )
103 | })
104 | FormLabel.displayName = "FormLabel"
105 |
106 | const FormControl = React.forwardRef<
107 | React.ElementRef,
108 | React.ComponentPropsWithoutRef
109 | >(({ ...props }, ref) => {
110 | const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
111 |
112 | return (
113 |
124 | )
125 | })
126 | FormControl.displayName = "FormControl"
127 |
128 | const FormDescription = React.forwardRef<
129 | HTMLParagraphElement,
130 | React.HTMLAttributes
131 | >(({ className, ...props }, ref) => {
132 | const { formDescriptionId } = useFormField()
133 |
134 | return (
135 |
141 | )
142 | })
143 | FormDescription.displayName = "FormDescription"
144 |
145 | const FormMessage = React.forwardRef<
146 | HTMLParagraphElement,
147 | React.HTMLAttributes
148 | >(({ className, children, ...props }, ref) => {
149 | const { error, formMessageId } = useFormField()
150 | const body = error ? String(error?.message) : children
151 |
152 | if (!body) {
153 | return null
154 | }
155 |
156 | return (
157 |
163 | {body}
164 |
165 | )
166 | })
167 | FormMessage.displayName = "FormMessage"
168 |
169 | export {
170 | useFormField,
171 | Form,
172 | FormItem,
173 | FormLabel,
174 | FormControl,
175 | FormDescription,
176 | FormMessage,
177 | FormField,
178 | }
179 |
--------------------------------------------------------------------------------
/components/ui/hover-card.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const HoverCard = HoverCardPrimitive.Root
9 |
10 | const HoverCardTrigger = HoverCardPrimitive.Trigger
11 |
12 | const HoverCardContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16 |
26 | ))
27 | HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
28 |
29 | export { HoverCard, HoverCardTrigger, HoverCardContent }
30 |
--------------------------------------------------------------------------------
/components/ui/input-otp.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { OTPInput, OTPInputContext } from "input-otp"
5 | import { Dot } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const InputOTP = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, containerClassName, ...props }, ref) => (
13 |
22 | ))
23 | InputOTP.displayName = "InputOTP"
24 |
25 | const InputOTPGroup = React.forwardRef<
26 | React.ElementRef<"div">,
27 | React.ComponentPropsWithoutRef<"div">
28 | >(({ className, ...props }, ref) => (
29 |
30 | ))
31 | InputOTPGroup.displayName = "InputOTPGroup"
32 |
33 | const InputOTPSlot = React.forwardRef<
34 | React.ElementRef<"div">,
35 | React.ComponentPropsWithoutRef<"div"> & { index: number }
36 | >(({ index, className, ...props }, ref) => {
37 | const inputOTPContext = React.useContext(OTPInputContext)
38 | const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
39 |
40 | return (
41 |
50 | {char}
51 | {hasFakeCaret && (
52 |
55 | )}
56 |
57 | )
58 | })
59 | InputOTPSlot.displayName = "InputOTPSlot"
60 |
61 | const InputOTPSeparator = React.forwardRef<
62 | React.ElementRef<"div">,
63 | React.ComponentPropsWithoutRef<"div">
64 | >(({ ...props }, ref) => (
65 |
66 |
67 |
68 | ))
69 | InputOTPSeparator.displayName = "InputOTPSeparator"
70 |
71 | export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
72 |
--------------------------------------------------------------------------------
/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | export interface InputProps extends React.InputHTMLAttributes {}
6 |
7 | const Input = React.forwardRef(({ className, type, ...props }, ref) => {
8 | return (
9 |
18 | )
19 | })
20 | Input.displayName = "Input"
21 |
22 | export { Input }
23 |
--------------------------------------------------------------------------------
/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as LabelPrimitive from "@radix-ui/react-label"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const labelVariants = cva(
10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11 | )
12 |
13 | const Label = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef &
16 | VariantProps
17 | >(({ className, ...props }, ref) => (
18 |
23 | ))
24 | Label.displayName = LabelPrimitive.Root.displayName
25 |
26 | export { Label }
27 |
--------------------------------------------------------------------------------
/components/ui/navigation-menu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
3 | import { cva } from "class-variance-authority"
4 | import { ChevronDown } from "lucide-react"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const NavigationMenu = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, children, ...props }, ref) => (
12 |
20 | {children}
21 |
22 |
23 | ))
24 | NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
25 |
26 | const NavigationMenuList = React.forwardRef<
27 | React.ElementRef,
28 | React.ComponentPropsWithoutRef
29 | >(({ className, ...props }, ref) => (
30 |
38 | ))
39 | NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
40 |
41 | const NavigationMenuItem = NavigationMenuPrimitive.Item
42 |
43 | const navigationMenuTriggerStyle = cva(
44 | "group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
45 | )
46 |
47 | const NavigationMenuTrigger = React.forwardRef<
48 | React.ElementRef,
49 | React.ComponentPropsWithoutRef
50 | >(({ className, children, ...props }, ref) => (
51 |
56 | {children}{" "}
57 |
61 |
62 | ))
63 | NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
64 |
65 | const NavigationMenuContent = React.forwardRef<
66 | React.ElementRef,
67 | React.ComponentPropsWithoutRef
68 | >(({ className, ...props }, ref) => (
69 |
77 | ))
78 | NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
79 |
80 | const NavigationMenuLink = NavigationMenuPrimitive.Link
81 |
82 | const NavigationMenuViewport = React.forwardRef<
83 | React.ElementRef,
84 | React.ComponentPropsWithoutRef
85 | >(({ className, ...props }, ref) => (
86 |
87 |
95 |
96 | ))
97 | NavigationMenuViewport.displayName =
98 | NavigationMenuPrimitive.Viewport.displayName
99 |
100 | const NavigationMenuIndicator = React.forwardRef<
101 | React.ElementRef,
102 | React.ComponentPropsWithoutRef
103 | >(({ className, ...props }, ref) => (
104 |
112 |
113 |
114 | ))
115 | NavigationMenuIndicator.displayName =
116 | NavigationMenuPrimitive.Indicator.displayName
117 |
118 | export {
119 | navigationMenuTriggerStyle,
120 | NavigationMenu,
121 | NavigationMenuList,
122 | NavigationMenuItem,
123 | NavigationMenuContent,
124 | NavigationMenuTrigger,
125 | NavigationMenuLink,
126 | NavigationMenuIndicator,
127 | NavigationMenuViewport,
128 | }
129 |
--------------------------------------------------------------------------------
/components/ui/pagination.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
3 |
4 | import { cn } from "@/lib/utils"
5 | import { ButtonProps, buttonVariants } from "@/components/ui/button"
6 |
7 | const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
8 |
14 | )
15 | Pagination.displayName = "Pagination"
16 |
17 | const PaginationContent = React.forwardRef<
18 | HTMLUListElement,
19 | React.ComponentProps<"ul">
20 | >(({ className, ...props }, ref) => (
21 |
26 | ))
27 | PaginationContent.displayName = "PaginationContent"
28 |
29 | const PaginationItem = React.forwardRef<
30 | HTMLLIElement,
31 | React.ComponentProps<"li">
32 | >(({ className, ...props }, ref) => (
33 |
34 | ))
35 | PaginationItem.displayName = "PaginationItem"
36 |
37 | type PaginationLinkProps = {
38 | isActive?: boolean
39 | } & Pick &
40 | React.ComponentProps<"a">
41 |
42 | const PaginationLink = ({
43 | className,
44 | isActive,
45 | size = "icon",
46 | ...props
47 | }: PaginationLinkProps) => (
48 |
59 | )
60 | PaginationLink.displayName = "PaginationLink"
61 |
62 | const PaginationPrevious = ({
63 | className,
64 | ...props
65 | }: React.ComponentProps) => (
66 |
72 |
73 | Previous
74 |
75 | )
76 | PaginationPrevious.displayName = "PaginationPrevious"
77 |
78 | const PaginationNext = ({
79 | className,
80 | ...props
81 | }: React.ComponentProps) => (
82 |
88 | Next
89 |
90 |
91 | )
92 | PaginationNext.displayName = "PaginationNext"
93 |
94 | const PaginationEllipsis = ({
95 | className,
96 | ...props
97 | }: React.ComponentProps<"span">) => (
98 |
103 |
104 | More pages
105 |
106 | )
107 | PaginationEllipsis.displayName = "PaginationEllipsis"
108 |
109 | export {
110 | Pagination,
111 | PaginationContent,
112 | PaginationEllipsis,
113 | PaginationItem,
114 | PaginationLink,
115 | PaginationNext,
116 | PaginationPrevious,
117 | }
118 |
--------------------------------------------------------------------------------
/components/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as PopoverPrimitive from "@radix-ui/react-popover"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Popover = PopoverPrimitive.Root
9 |
10 | const PopoverTrigger = PopoverPrimitive.Trigger
11 |
12 | const PopoverContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16 |
17 |
27 |
28 | ))
29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName
30 |
31 | export { Popover, PopoverTrigger, PopoverContent }
32 |
--------------------------------------------------------------------------------
/components/ui/progress.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as ProgressPrimitive from "@radix-ui/react-progress"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const Progress = React.forwardRef<
7 | React.ElementRef,
8 | React.ComponentPropsWithoutRef
9 | >(({ className, value, ...props }, ref) => (
10 |
15 |
19 |
20 | ))
21 | Progress.displayName = ProgressPrimitive.Root.displayName
22 |
23 | export { Progress }
24 |
--------------------------------------------------------------------------------
/components/ui/radio-group.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
5 | import { Circle } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const RadioGroup = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => {
13 | return (
14 |
19 | )
20 | })
21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
22 |
23 | const RadioGroupItem = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => {
27 | return (
28 |
36 |
37 |
38 |
39 |
40 | )
41 | })
42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
43 |
44 | export { RadioGroup, RadioGroupItem }
45 |
--------------------------------------------------------------------------------
/components/ui/resizable.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { GripVertical } from "lucide-react"
4 | import * as ResizablePrimitive from "react-resizable-panels"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const ResizablePanelGroup = ({
9 | className,
10 | ...props
11 | }: React.ComponentProps) => (
12 |
19 | )
20 |
21 | const ResizablePanel = ResizablePrimitive.Panel
22 |
23 | const ResizableHandle = ({
24 | withHandle,
25 | className,
26 | ...props
27 | }: React.ComponentProps & {
28 | withHandle?: boolean
29 | }) => (
30 | div]:rotate-90",
33 | className
34 | )}
35 | {...props}
36 | >
37 | {withHandle && (
38 |
39 |
40 |
41 | )}
42 |
43 | )
44 |
45 | export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
46 |
--------------------------------------------------------------------------------
/components/ui/scroll-area.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const ScrollArea = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, children, ...props }, ref) => (
12 |
17 |
18 | {children}
19 |
20 |
21 |
22 |
23 | ))
24 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
25 |
26 | const ScrollBar = React.forwardRef<
27 | React.ElementRef,
28 | React.ComponentPropsWithoutRef
29 | >(({ className, orientation = "vertical", ...props }, ref) => (
30 |
43 |
44 |
45 | ))
46 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
47 |
48 | export { ScrollArea, ScrollBar }
49 |
--------------------------------------------------------------------------------
/components/ui/select.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as SelectPrimitive from "@radix-ui/react-select"
3 | import { Check, ChevronDown, ChevronUp } from "lucide-react"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const Select = SelectPrimitive.Root
8 |
9 | const SelectGroup = SelectPrimitive.Group
10 |
11 | const SelectValue = SelectPrimitive.Value
12 |
13 | const SelectTrigger = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef
16 | >(({ className, children, ...props }, ref) => (
17 | span]:line-clamp-1",
21 | className,
22 | )}
23 | {...props}
24 | >
25 | {children}
26 |
27 |
28 |
29 |
30 | ))
31 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
32 |
33 | const SelectScrollUpButton = React.forwardRef<
34 | React.ElementRef,
35 | React.ComponentPropsWithoutRef
36 | >(({ className, ...props }, ref) => (
37 |
42 |
43 |
44 | ))
45 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
46 |
47 | const SelectScrollDownButton = React.forwardRef<
48 | React.ElementRef,
49 | React.ComponentPropsWithoutRef
50 | >(({ className, ...props }, ref) => (
51 |
56 |
57 |
58 | ))
59 | SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
60 |
61 | const SelectContent = React.forwardRef<
62 | React.ElementRef,
63 | React.ComponentPropsWithoutRef
64 | >(({ className, children, position = "popper", ...props }, ref) => (
65 |
66 |
77 |
78 |
85 | {children}
86 |
87 |
88 |
89 |
90 | ))
91 | SelectContent.displayName = SelectPrimitive.Content.displayName
92 |
93 | const SelectLabel = React.forwardRef<
94 | React.ElementRef,
95 | React.ComponentPropsWithoutRef
96 | >(({ className, ...props }, ref) => (
97 |
98 | ))
99 | SelectLabel.displayName = SelectPrimitive.Label.displayName
100 |
101 | const SelectItem = React.forwardRef<
102 | React.ElementRef,
103 | React.ComponentPropsWithoutRef
104 | >(({ className, children, ...props }, ref) => (
105 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | {children}
120 |
121 | ))
122 | SelectItem.displayName = SelectPrimitive.Item.displayName
123 |
124 | const SelectSeparator = React.forwardRef<
125 | React.ElementRef,
126 | React.ComponentPropsWithoutRef
127 | >(({ className, ...props }, ref) => (
128 |
129 | ))
130 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName
131 |
132 | export {
133 | Select,
134 | SelectGroup,
135 | SelectValue,
136 | SelectTrigger,
137 | SelectContent,
138 | SelectLabel,
139 | SelectItem,
140 | SelectSeparator,
141 | SelectScrollUpButton,
142 | SelectScrollDownButton,
143 | }
144 |
--------------------------------------------------------------------------------
/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref
15 | ) => (
16 |
27 | )
28 | )
29 | Separator.displayName = SeparatorPrimitive.Root.displayName
30 |
31 | export { Separator }
32 |
--------------------------------------------------------------------------------
/components/ui/sheet.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SheetPrimitive from "@radix-ui/react-dialog"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 | import { X } from "lucide-react"
7 |
8 | import { cn } from "@/lib/utils"
9 |
10 | const Sheet = SheetPrimitive.Root
11 |
12 | const SheetTrigger = SheetPrimitive.Trigger
13 |
14 | const SheetClose = SheetPrimitive.Close
15 |
16 | const SheetPortal = SheetPrimitive.Portal
17 |
18 | const SheetOverlay = React.forwardRef<
19 | React.ElementRef,
20 | React.ComponentPropsWithoutRef
21 | >(({ className, ...props }, ref) => (
22 |
30 | ))
31 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32 |
33 | const sheetVariants = cva(
34 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
35 | {
36 | variants: {
37 | side: {
38 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
39 | bottom:
40 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
41 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
42 | right:
43 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
44 | },
45 | },
46 | defaultVariants: {
47 | side: "right",
48 | },
49 | }
50 | )
51 |
52 | interface SheetContentProps
53 | extends React.ComponentPropsWithoutRef,
54 | VariantProps {}
55 |
56 | const SheetContent = React.forwardRef<
57 | React.ElementRef,
58 | SheetContentProps
59 | >(({ side = "right", className, children, ...props }, ref) => (
60 |
61 |
62 |
67 | {children}
68 |
69 |
70 | Close
71 |
72 |
73 |
74 | ))
75 | SheetContent.displayName = SheetPrimitive.Content.displayName
76 |
77 | const SheetHeader = ({
78 | className,
79 | ...props
80 | }: React.HTMLAttributes) => (
81 |
88 | )
89 | SheetHeader.displayName = "SheetHeader"
90 |
91 | const SheetFooter = ({
92 | className,
93 | ...props
94 | }: React.HTMLAttributes) => (
95 |
102 | )
103 | SheetFooter.displayName = "SheetFooter"
104 |
105 | const SheetTitle = React.forwardRef<
106 | React.ElementRef,
107 | React.ComponentPropsWithoutRef
108 | >(({ className, ...props }, ref) => (
109 |
114 | ))
115 | SheetTitle.displayName = SheetPrimitive.Title.displayName
116 |
117 | const SheetDescription = React.forwardRef<
118 | React.ElementRef,
119 | React.ComponentPropsWithoutRef
120 | >(({ className, ...props }, ref) => (
121 |
126 | ))
127 | SheetDescription.displayName = SheetPrimitive.Description.displayName
128 |
129 | export {
130 | Sheet,
131 | SheetPortal,
132 | SheetOverlay,
133 | SheetTrigger,
134 | SheetClose,
135 | SheetContent,
136 | SheetHeader,
137 | SheetFooter,
138 | SheetTitle,
139 | SheetDescription,
140 | }
141 |
--------------------------------------------------------------------------------
/components/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils"
2 |
3 | function Skeleton({
4 | className,
5 | ...props
6 | }: React.HTMLAttributes) {
7 | return (
8 |
12 | )
13 | }
14 |
15 | export { Skeleton }
16 |
--------------------------------------------------------------------------------
/components/ui/slider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SliderPrimitive from "@radix-ui/react-slider"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Slider = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
21 |
22 |
23 |
24 |
25 | ))
26 | Slider.displayName = SliderPrimitive.Root.displayName
27 |
28 | export { Slider }
29 |
--------------------------------------------------------------------------------
/components/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useTheme } from "next-themes"
4 | import { Toaster as Sonner } from "sonner"
5 |
6 | type ToasterProps = React.ComponentProps
7 |
8 | const Toaster = ({ ...props }: ToasterProps) => {
9 | const { theme = "system" } = useTheme()
10 |
11 | return (
12 |
28 | )
29 | }
30 |
31 | export { Toaster }
32 |
--------------------------------------------------------------------------------
/components/ui/switch.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SwitchPrimitives from "@radix-ui/react-switch"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Switch = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
25 |
26 | ))
27 | Switch.displayName = SwitchPrimitives.Root.displayName
28 |
29 | export { Switch }
30 |
--------------------------------------------------------------------------------
/components/ui/table.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Table = React.forwardRef<
6 | HTMLTableElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
16 | ))
17 | Table.displayName = "Table"
18 |
19 | const TableHeader = React.forwardRef<
20 | HTMLTableSectionElement,
21 | React.HTMLAttributes
22 | >(({ className, ...props }, ref) => (
23 |
24 | ))
25 | TableHeader.displayName = "TableHeader"
26 |
27 | const TableBody = React.forwardRef<
28 | HTMLTableSectionElement,
29 | React.HTMLAttributes
30 | >(({ className, ...props }, ref) => (
31 |
36 | ))
37 | TableBody.displayName = "TableBody"
38 |
39 | const TableFooter = React.forwardRef<
40 | HTMLTableSectionElement,
41 | React.HTMLAttributes
42 | >(({ className, ...props }, ref) => (
43 | tr]:last:border-b-0",
47 | className
48 | )}
49 | {...props}
50 | />
51 | ))
52 | TableFooter.displayName = "TableFooter"
53 |
54 | const TableRow = React.forwardRef<
55 | HTMLTableRowElement,
56 | React.HTMLAttributes
57 | >(({ className, ...props }, ref) => (
58 |
66 | ))
67 | TableRow.displayName = "TableRow"
68 |
69 | const TableHead = React.forwardRef<
70 | HTMLTableCellElement,
71 | React.ThHTMLAttributes
72 | >(({ className, ...props }, ref) => (
73 |
81 | ))
82 | TableHead.displayName = "TableHead"
83 |
84 | const TableCell = React.forwardRef<
85 | HTMLTableCellElement,
86 | React.TdHTMLAttributes
87 | >(({ className, ...props }, ref) => (
88 |
93 | ))
94 | TableCell.displayName = "TableCell"
95 |
96 | const TableCaption = React.forwardRef<
97 | HTMLTableCaptionElement,
98 | React.HTMLAttributes
99 | >(({ className, ...props }, ref) => (
100 |
105 | ))
106 | TableCaption.displayName = "TableCaption"
107 |
108 | export {
109 | Table,
110 | TableHeader,
111 | TableBody,
112 | TableFooter,
113 | TableHead,
114 | TableRow,
115 | TableCell,
116 | TableCaption,
117 | }
118 |
--------------------------------------------------------------------------------
/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as TabsPrimitive from "@radix-ui/react-tabs"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const Tabs = TabsPrimitive.Root
7 |
8 | const TabsList = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
17 | ))
18 | TabsList.displayName = TabsPrimitive.List.displayName
19 |
20 | const TabsTrigger = React.forwardRef<
21 | React.ElementRef,
22 | React.ComponentPropsWithoutRef
23 | >(({ className, ...props }, ref) => (
24 |
32 | ))
33 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
34 |
35 | const TabsContent = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 | ))
48 | TabsContent.displayName = TabsPrimitive.Content.displayName
49 |
50 | export { Tabs, TabsList, TabsTrigger, TabsContent }
51 |
--------------------------------------------------------------------------------
/components/ui/textarea.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Textarea = React.forwardRef<
6 | HTMLTextAreaElement,
7 | React.ComponentProps<"textarea">
8 | >(({ className, ...props }, ref) => {
9 | return (
10 |
18 | )
19 | })
20 | Textarea.displayName = "Textarea"
21 |
22 | export { Textarea }
23 |
--------------------------------------------------------------------------------
/components/ui/toast.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ToastPrimitives from "@radix-ui/react-toast"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 | import { X } from "lucide-react"
7 |
8 | import { cn } from "@/lib/utils"
9 |
10 | const ToastProvider = ToastPrimitives.Provider
11 |
12 | const ToastViewport = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, ...props }, ref) => (
16 |
24 | ))
25 | ToastViewport.displayName = ToastPrimitives.Viewport.displayName
26 |
27 | const toastVariants = cva(
28 | "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
29 | {
30 | variants: {
31 | variant: {
32 | default: "border bg-background text-foreground",
33 | destructive:
34 | "destructive group border-destructive bg-destructive text-destructive-foreground",
35 | },
36 | },
37 | defaultVariants: {
38 | variant: "default",
39 | },
40 | }
41 | )
42 |
43 | const Toast = React.forwardRef<
44 | React.ElementRef,
45 | React.ComponentPropsWithoutRef &
46 | VariantProps
47 | >(({ className, variant, ...props }, ref) => {
48 | return (
49 |
54 | )
55 | })
56 | Toast.displayName = ToastPrimitives.Root.displayName
57 |
58 | const ToastAction = React.forwardRef<
59 | React.ElementRef,
60 | React.ComponentPropsWithoutRef
61 | >(({ className, ...props }, ref) => (
62 |
70 | ))
71 | ToastAction.displayName = ToastPrimitives.Action.displayName
72 |
73 | const ToastClose = React.forwardRef<
74 | React.ElementRef,
75 | React.ComponentPropsWithoutRef
76 | >(({ className, ...props }, ref) => (
77 |
86 |
87 |
88 | ))
89 | ToastClose.displayName = ToastPrimitives.Close.displayName
90 |
91 | const ToastTitle = React.forwardRef<
92 | React.ElementRef,
93 | React.ComponentPropsWithoutRef
94 | >(({ className, ...props }, ref) => (
95 |
100 | ))
101 | ToastTitle.displayName = ToastPrimitives.Title.displayName
102 |
103 | const ToastDescription = React.forwardRef<
104 | React.ElementRef,
105 | React.ComponentPropsWithoutRef
106 | >(({ className, ...props }, ref) => (
107 |
112 | ))
113 | ToastDescription.displayName = ToastPrimitives.Description.displayName
114 |
115 | type ToastProps = React.ComponentPropsWithoutRef
116 |
117 | type ToastActionElement = React.ReactElement
118 |
119 | export {
120 | type ToastProps,
121 | type ToastActionElement,
122 | ToastProvider,
123 | ToastViewport,
124 | Toast,
125 | ToastTitle,
126 | ToastDescription,
127 | ToastClose,
128 | ToastAction,
129 | }
130 |
--------------------------------------------------------------------------------
/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useToast } from "@/hooks/use-toast"
4 | import {
5 | Toast,
6 | ToastClose,
7 | ToastDescription,
8 | ToastProvider,
9 | ToastTitle,
10 | ToastViewport,
11 | } from "@/components/ui/toast"
12 |
13 | export function Toaster() {
14 | const { toasts } = useToast()
15 |
16 | return (
17 |
18 | {toasts.map(function ({ id, title, description, action, ...props }) {
19 | return (
20 |
21 |
22 | {title && {title} }
23 | {description && (
24 | {description}
25 | )}
26 |
27 | {action}
28 |
29 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/components/ui/toggle-group.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
5 | import { type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 | import { toggleVariants } from "@/components/ui/toggle"
9 |
10 | const ToggleGroupContext = React.createContext<
11 | VariantProps
12 | >({
13 | size: "default",
14 | variant: "default",
15 | })
16 |
17 | const ToggleGroup = React.forwardRef<
18 | React.ElementRef,
19 | React.ComponentPropsWithoutRef &
20 | VariantProps
21 | >(({ className, variant, size, children, ...props }, ref) => (
22 |
27 |
28 | {children}
29 |
30 |
31 | ))
32 |
33 | ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
34 |
35 | const ToggleGroupItem = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef &
38 | VariantProps
39 | >(({ className, children, variant, size, ...props }, ref) => {
40 | const context = React.useContext(ToggleGroupContext)
41 |
42 | return (
43 |
54 | {children}
55 |
56 | )
57 | })
58 |
59 | ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
60 |
61 | export { ToggleGroup, ToggleGroupItem }
62 |
--------------------------------------------------------------------------------
/components/ui/toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TogglePrimitive from "@radix-ui/react-toggle"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const toggleVariants = cva(
10 | "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2",
11 | {
12 | variants: {
13 | variant: {
14 | default: "bg-transparent",
15 | outline:
16 | "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
17 | },
18 | size: {
19 | default: "h-10 px-3 min-w-10",
20 | sm: "h-9 px-2.5 min-w-9",
21 | lg: "h-11 px-5 min-w-11",
22 | },
23 | },
24 | defaultVariants: {
25 | variant: "default",
26 | size: "default",
27 | },
28 | }
29 | )
30 |
31 | const Toggle = React.forwardRef<
32 | React.ElementRef,
33 | React.ComponentPropsWithoutRef &
34 | VariantProps
35 | >(({ className, variant, size, ...props }, ref) => (
36 |
41 | ))
42 |
43 | Toggle.displayName = TogglePrimitive.Root.displayName
44 |
45 | export { Toggle, toggleVariants }
46 |
--------------------------------------------------------------------------------
/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const TooltipProvider = TooltipPrimitive.Provider
9 |
10 | const Tooltip = TooltipPrimitive.Root
11 |
12 | const TooltipTrigger = TooltipPrimitive.Trigger
13 |
14 | const TooltipContent = React.forwardRef<
15 | React.ElementRef,
16 | React.ComponentPropsWithoutRef
17 | >(({ className, sideOffset = 4, ...props }, ref) => (
18 |
27 | ))
28 | TooltipContent.displayName = TooltipPrimitive.Content.displayName
29 |
30 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
31 |
--------------------------------------------------------------------------------
/components/ui/use-mobile.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | const MOBILE_BREAKPOINT = 768
4 |
5 | export function useIsMobile() {
6 | const [isMobile, setIsMobile] = React.useState(undefined)
7 |
8 | React.useEffect(() => {
9 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10 | const onChange = () => {
11 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12 | }
13 | mql.addEventListener("change", onChange)
14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15 | return () => mql.removeEventListener("change", onChange)
16 | }, [])
17 |
18 | return !!isMobile
19 | }
20 |
--------------------------------------------------------------------------------
/components/ui/use-toast.ts:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | // Inspired by react-hot-toast library
4 | import * as React from "react"
5 |
6 | import type {
7 | ToastActionElement,
8 | ToastProps,
9 | } from "@/components/ui/toast"
10 |
11 | const TOAST_LIMIT = 1
12 | const TOAST_REMOVE_DELAY = 1000000
13 |
14 | type ToasterToast = ToastProps & {
15 | id: string
16 | title?: React.ReactNode
17 | description?: React.ReactNode
18 | action?: ToastActionElement
19 | }
20 |
21 | const actionTypes = {
22 | ADD_TOAST: "ADD_TOAST",
23 | UPDATE_TOAST: "UPDATE_TOAST",
24 | DISMISS_TOAST: "DISMISS_TOAST",
25 | REMOVE_TOAST: "REMOVE_TOAST",
26 | } as const
27 |
28 | let count = 0
29 |
30 | function genId() {
31 | count = (count + 1) % Number.MAX_SAFE_INTEGER
32 | return count.toString()
33 | }
34 |
35 | type ActionType = typeof actionTypes
36 |
37 | type Action =
38 | | {
39 | type: ActionType["ADD_TOAST"]
40 | toast: ToasterToast
41 | }
42 | | {
43 | type: ActionType["UPDATE_TOAST"]
44 | toast: Partial
45 | }
46 | | {
47 | type: ActionType["DISMISS_TOAST"]
48 | toastId?: ToasterToast["id"]
49 | }
50 | | {
51 | type: ActionType["REMOVE_TOAST"]
52 | toastId?: ToasterToast["id"]
53 | }
54 |
55 | interface State {
56 | toasts: ToasterToast[]
57 | }
58 |
59 | const toastTimeouts = new Map>()
60 |
61 | const addToRemoveQueue = (toastId: string) => {
62 | if (toastTimeouts.has(toastId)) {
63 | return
64 | }
65 |
66 | const timeout = setTimeout(() => {
67 | toastTimeouts.delete(toastId)
68 | dispatch({
69 | type: "REMOVE_TOAST",
70 | toastId: toastId,
71 | })
72 | }, TOAST_REMOVE_DELAY)
73 |
74 | toastTimeouts.set(toastId, timeout)
75 | }
76 |
77 | export const reducer = (state: State, action: Action): State => {
78 | switch (action.type) {
79 | case "ADD_TOAST":
80 | return {
81 | ...state,
82 | toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
83 | }
84 |
85 | case "UPDATE_TOAST":
86 | return {
87 | ...state,
88 | toasts: state.toasts.map((t) =>
89 | t.id === action.toast.id ? { ...t, ...action.toast } : t
90 | ),
91 | }
92 |
93 | case "DISMISS_TOAST": {
94 | const { toastId } = action
95 |
96 | // ! Side effects ! - This could be extracted into a dismissToast() action,
97 | // but I'll keep it here for simplicity
98 | if (toastId) {
99 | addToRemoveQueue(toastId)
100 | } else {
101 | state.toasts.forEach((toast) => {
102 | addToRemoveQueue(toast.id)
103 | })
104 | }
105 |
106 | return {
107 | ...state,
108 | toasts: state.toasts.map((t) =>
109 | t.id === toastId || toastId === undefined
110 | ? {
111 | ...t,
112 | open: false,
113 | }
114 | : t
115 | ),
116 | }
117 | }
118 | case "REMOVE_TOAST":
119 | if (action.toastId === undefined) {
120 | return {
121 | ...state,
122 | toasts: [],
123 | }
124 | }
125 | return {
126 | ...state,
127 | toasts: state.toasts.filter((t) => t.id !== action.toastId),
128 | }
129 | }
130 | }
131 |
132 | const listeners: Array<(state: State) => void> = []
133 |
134 | let memoryState: State = { toasts: [] }
135 |
136 | function dispatch(action: Action) {
137 | memoryState = reducer(memoryState, action)
138 | listeners.forEach((listener) => {
139 | listener(memoryState)
140 | })
141 | }
142 |
143 | type Toast = Omit
144 |
145 | function toast({ ...props }: Toast) {
146 | const id = genId()
147 |
148 | const update = (props: ToasterToast) =>
149 | dispatch({
150 | type: "UPDATE_TOAST",
151 | toast: { ...props, id },
152 | })
153 | const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
154 |
155 | dispatch({
156 | type: "ADD_TOAST",
157 | toast: {
158 | ...props,
159 | id,
160 | open: true,
161 | onOpenChange: (open) => {
162 | if (!open) dismiss()
163 | },
164 | },
165 | })
166 |
167 | return {
168 | id: id,
169 | dismiss,
170 | update,
171 | }
172 | }
173 |
174 | function useToast() {
175 | const [state, setState] = React.useState(memoryState)
176 |
177 | React.useEffect(() => {
178 | listeners.push(setState)
179 | return () => {
180 | const index = listeners.indexOf(setState)
181 | if (index > -1) {
182 | listeners.splice(index, 1)
183 | }
184 | }
185 | }, [state])
186 |
187 | return {
188 | ...state,
189 | toast,
190 | dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
191 | }
192 | }
193 |
194 | export { useToast, toast }
195 |
--------------------------------------------------------------------------------
/hooks/use-mobile.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | const MOBILE_BREAKPOINT = 768
4 |
5 | export function useIsMobile() {
6 | const [isMobile, setIsMobile] = React.useState(undefined)
7 |
8 | React.useEffect(() => {
9 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10 | const onChange = () => {
11 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12 | }
13 | mql.addEventListener("change", onChange)
14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15 | return () => mql.removeEventListener("change", onChange)
16 | }, [])
17 |
18 | return !!isMobile
19 | }
20 |
--------------------------------------------------------------------------------
/hooks/use-toast.ts:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | // Inspired by react-hot-toast library
4 | import * as React from "react"
5 |
6 | import type {
7 | ToastActionElement,
8 | ToastProps,
9 | } from "@/components/ui/toast"
10 |
11 | const TOAST_LIMIT = 1
12 | const TOAST_REMOVE_DELAY = 1000000
13 |
14 | type ToasterToast = ToastProps & {
15 | id: string
16 | title?: React.ReactNode
17 | description?: React.ReactNode
18 | action?: ToastActionElement
19 | }
20 |
21 | const actionTypes = {
22 | ADD_TOAST: "ADD_TOAST",
23 | UPDATE_TOAST: "UPDATE_TOAST",
24 | DISMISS_TOAST: "DISMISS_TOAST",
25 | REMOVE_TOAST: "REMOVE_TOAST",
26 | } as const
27 |
28 | let count = 0
29 |
30 | function genId() {
31 | count = (count + 1) % Number.MAX_SAFE_INTEGER
32 | return count.toString()
33 | }
34 |
35 | type ActionType = typeof actionTypes
36 |
37 | type Action =
38 | | {
39 | type: ActionType["ADD_TOAST"]
40 | toast: ToasterToast
41 | }
42 | | {
43 | type: ActionType["UPDATE_TOAST"]
44 | toast: Partial
45 | }
46 | | {
47 | type: ActionType["DISMISS_TOAST"]
48 | toastId?: ToasterToast["id"]
49 | }
50 | | {
51 | type: ActionType["REMOVE_TOAST"]
52 | toastId?: ToasterToast["id"]
53 | }
54 |
55 | interface State {
56 | toasts: ToasterToast[]
57 | }
58 |
59 | const toastTimeouts = new Map>()
60 |
61 | const addToRemoveQueue = (toastId: string) => {
62 | if (toastTimeouts.has(toastId)) {
63 | return
64 | }
65 |
66 | const timeout = setTimeout(() => {
67 | toastTimeouts.delete(toastId)
68 | dispatch({
69 | type: "REMOVE_TOAST",
70 | toastId: toastId,
71 | })
72 | }, TOAST_REMOVE_DELAY)
73 |
74 | toastTimeouts.set(toastId, timeout)
75 | }
76 |
77 | export const reducer = (state: State, action: Action): State => {
78 | switch (action.type) {
79 | case "ADD_TOAST":
80 | return {
81 | ...state,
82 | toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
83 | }
84 |
85 | case "UPDATE_TOAST":
86 | return {
87 | ...state,
88 | toasts: state.toasts.map((t) =>
89 | t.id === action.toast.id ? { ...t, ...action.toast } : t
90 | ),
91 | }
92 |
93 | case "DISMISS_TOAST": {
94 | const { toastId } = action
95 |
96 | // ! Side effects ! - This could be extracted into a dismissToast() action,
97 | // but I'll keep it here for simplicity
98 | if (toastId) {
99 | addToRemoveQueue(toastId)
100 | } else {
101 | state.toasts.forEach((toast) => {
102 | addToRemoveQueue(toast.id)
103 | })
104 | }
105 |
106 | return {
107 | ...state,
108 | toasts: state.toasts.map((t) =>
109 | t.id === toastId || toastId === undefined
110 | ? {
111 | ...t,
112 | open: false,
113 | }
114 | : t
115 | ),
116 | }
117 | }
118 | case "REMOVE_TOAST":
119 | if (action.toastId === undefined) {
120 | return {
121 | ...state,
122 | toasts: [],
123 | }
124 | }
125 | return {
126 | ...state,
127 | toasts: state.toasts.filter((t) => t.id !== action.toastId),
128 | }
129 | }
130 | }
131 |
132 | const listeners: Array<(state: State) => void> = []
133 |
134 | let memoryState: State = { toasts: [] }
135 |
136 | function dispatch(action: Action) {
137 | memoryState = reducer(memoryState, action)
138 | listeners.forEach((listener) => {
139 | listener(memoryState)
140 | })
141 | }
142 |
143 | type Toast = Omit
144 |
145 | function toast({ ...props }: Toast) {
146 | const id = genId()
147 |
148 | const update = (props: ToasterToast) =>
149 | dispatch({
150 | type: "UPDATE_TOAST",
151 | toast: { ...props, id },
152 | })
153 | const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
154 |
155 | dispatch({
156 | type: "ADD_TOAST",
157 | toast: {
158 | ...props,
159 | id,
160 | open: true,
161 | onOpenChange: (open) => {
162 | if (!open) dismiss()
163 | },
164 | },
165 | })
166 |
167 | return {
168 | id: id,
169 | dismiss,
170 | update,
171 | }
172 | }
173 |
174 | function useToast() {
175 | const [state, setState] = React.useState(memoryState)
176 |
177 | React.useEffect(() => {
178 | listeners.push(setState)
179 | return () => {
180 | const index = listeners.indexOf(setState)
181 | if (index > -1) {
182 | listeners.splice(index, 1)
183 | }
184 | }
185 | }, [state])
186 |
187 | return {
188 | ...state,
189 | toast,
190 | dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
191 | }
192 | }
193 |
194 | export { useToast, toast }
195 |
--------------------------------------------------------------------------------
/lib/data-structures/hash-table.ts:
--------------------------------------------------------------------------------
1 | // Hash Table implementation
2 | interface HashItem {
3 | key: string
4 | value: any
5 | }
6 |
7 | export class HashTable {
8 | buckets: HashItem[][]
9 | size: number
10 |
11 | constructor(size = 10) {
12 | this.buckets = Array(size).fill(null).map(() => [])
13 | this.size = size
14 | }
15 |
16 | // Hash function to convert key to index
17 | hash(key: string): number {
18 | let hash = 0
19 | for (let i = 0; i < key.length; i++) {
20 | hash += key.charCodeAt(i)
21 | }
22 | return hash % this.size
23 | }
24 |
25 | // Set a key-value pair
26 | set(key: string, value: any): void {
27 | const index = this.hash(key)
28 | const bucket = this.buckets[index]
29 |
30 | // Check if key already exists
31 | const item = bucket.find(item => item.key === key)
32 | if (item) {
33 | item.value = value
34 | } else {
35 | bucket.push({ key, value })
36 | }
37 | }
38 |
39 | // Get a value by key
40 | get(key: string): any {
41 | const index = this.hash(key)
42 | const bucket = this.buckets[index]
43 | const item = bucket.find(item => item.key === key)
44 | return item ? item.value : null
45 | }
46 |
47 | // Delete a key-value pair
48 | delete(key: string): boolean {
49 | const index = this.hash(key)
50 | const bucket = this.buckets[index]
51 | const itemIndex = bucket.findIndex(item => item.key === key)
52 |
53 | if (itemIndex !== -1) {
54 | bucket.splice(itemIndex, 1)
55 | return true
56 | }
57 | return false
58 | }
59 |
60 | // Check if a key exists
61 | has(key: string): boolean {
62 | const index = this.hash(key)
63 | const bucket = this.buckets[index]
64 | return bucket.some(item => item.key === key)
65 | }
66 |
67 | // Get all keys
68 | keys(): string[] {
69 | return this.buckets.reduce((keys, bucket) => {
70 | return keys.concat(bucket.map(item => item.key))
71 | }, [] as string[])
72 | }
73 |
74 | // Get all entries
75 | entries(): { key: string, value: any }[] {
76 | return this.buckets.reduce((entries, bucket) => {
77 | return entries.concat(bucket.map(item => ({ key: item.key, value: item.value })))
78 | }, [] as { key: string, value: any }[])
79 | }
80 |
81 | // Get all buckets for visualization
82 | getBuckets(): HashItem[][] {
83 | return this.buckets
84 | }
85 | }
86 |
87 | // Code snippets for operations
88 | export const HASH_TABLE_CODE_SNIPPETS = {
89 | hash: `// Hash function - O(k) where k is key length
90 | hash(key: string): number {
91 | let hash = 0
92 | for (let i = 0; i < key.length; i++) {
93 | hash += key.charCodeAt(i)
94 | }
95 | return hash % this.size
96 | }`,
97 | set: `// Set operation - O(1) average, O(n) worst case
98 | set(key: string, value: any): void {
99 | const index = this.hash(key)
100 | const bucket = this.buckets[index]
101 |
102 | // Check if key already exists
103 | const item = bucket.find(item => item.key === key)
104 | if (item) {
105 | item.value = value
106 | } else {
107 | bucket.push({ key, value })
108 | }
109 | }`,
110 | get: `// Get operation - O(1) average, O(n) worst case
111 | get(key: string): any {
112 | const index = this.hash(key)
113 | const bucket = this.buckets[index]
114 | const item = bucket.find(item => item.key === key)
115 | return item ? item.value : null
116 | }`,
117 | delete: `// Delete operation - O(1) average, O(n) worst case
118 | delete(key: string): boolean {
119 | const index = this.hash(key)
120 | const bucket = this.buckets[index]
121 | const itemIndex = bucket.findIndex(item => item.key === key)
122 |
123 | if (itemIndex !== -1) {
124 | bucket.splice(itemIndex, 1)
125 | return true
126 | }
127 | return false
128 | }`,
129 | empty: `// Hash table with empty buckets
130 | // Use the set operation to add key-value pairs
131 | set(key: string, value: any): void {
132 | const index = this.hash(key)
133 | this.buckets[index].push({ key, value })
134 | }`,
135 | }
136 |
--------------------------------------------------------------------------------
/lib/data-structures/queue.ts:
--------------------------------------------------------------------------------
1 | // Queue class implementation
2 | export class Queue {
3 | items: number[]
4 |
5 | constructor() {
6 | this.items = []
7 | }
8 |
9 | // Add an item to the queue (at the end)
10 | enqueue(item: number) {
11 | this.items.push(item)
12 | return this
13 | }
14 |
15 | // Remove an item from the queue (from the front)
16 | dequeue() {
17 | if (this.isEmpty()) {
18 | return null
19 | }
20 | return this.items.shift()
21 | }
22 |
23 | // Look at the front item without removing it
24 | peek() {
25 | if (this.isEmpty()) {
26 | return null
27 | }
28 | return this.items[0]
29 | }
30 |
31 | // Check if the queue is empty
32 | isEmpty() {
33 | return this.items.length === 0
34 | }
35 |
36 | // Get the size of the queue
37 | size() {
38 | return this.items.length
39 | }
40 |
41 | // Get all items in the queue
42 | getItems() {
43 | return [...this.items]
44 | }
45 | }
46 |
47 | // Code snippets for operations
48 | export const QUEUE_CODE_SNIPPETS = {
49 | enqueue: `// Enqueue operation - O(1)
50 | enqueue(item: number) {
51 | // Add the item to the end of the array
52 | this.items.push(item)
53 | return this
54 | }`,
55 | dequeue: `// Dequeue operation - O(n)
56 | dequeue() {
57 | // Check if the queue is empty
58 | if (this.isEmpty()) {
59 | return null
60 | }
61 | // Remove and return the first item
62 | return this.items.shift()
63 | }`,
64 | peek: `// Peek operation - O(1)
65 | peek() {
66 | // Check if the queue is empty
67 | if (this.isEmpty()) {
68 | return null
69 | }
70 | // Return the first item without removing it
71 | return this.items[0]
72 | }`,
73 | empty: `// Queue is empty
74 | // Use the enqueue operation to add items to the queue
75 | enqueue(item: number) {
76 | this.items.push(item)
77 | return this
78 | }`,
79 | }
80 |
--------------------------------------------------------------------------------
/lib/data-structures/stack.ts:
--------------------------------------------------------------------------------
1 | // Stack class implementation
2 | export class Stack {
3 | items: number[]
4 |
5 | constructor() {
6 | this.items = []
7 | }
8 |
9 | // Push an item onto the stack
10 | push(item: number) {
11 | this.items.push(item)
12 | return this
13 | }
14 |
15 | // Pop an item from the stack
16 | pop() {
17 | if (this.isEmpty()) {
18 | return null
19 | }
20 | return this.items.pop()
21 | }
22 |
23 | // Peek at the top item without removing it
24 | peek() {
25 | if (this.isEmpty()) {
26 | return null
27 | }
28 | return this.items[this.items.length - 1]
29 | }
30 |
31 | // Check if the stack is empty
32 | isEmpty() {
33 | return this.items.length === 0
34 | }
35 |
36 | // Get the size of the stack
37 | size() {
38 | return this.items.length
39 | }
40 |
41 | // Get all items in the stack
42 | getItems() {
43 | return [...this.items]
44 | }
45 | }
46 |
47 | // Code snippets for operations
48 | export const STACK_CODE_SNIPPETS = {
49 | push: `// Push operation - O(1)
50 | push(item: number) {
51 | // Add the item to the end of the array
52 | this.items.push(item)
53 | return this
54 | }`,
55 | pop: `// Pop operation - O(1)
56 | pop() {
57 | // Check if the stack is empty
58 | if (this.isEmpty()) {
59 | return null
60 | }
61 | // Remove and return the last item
62 | return this.items.pop()
63 | }`,
64 | peek: `// Peek operation - O(1)
65 | peek() {
66 | // Check if the stack is empty
67 | if (this.isEmpty()) {
68 | return null
69 | }
70 | // Return the last item without removing it
71 | return this.items[this.items.length - 1]
72 | }`,
73 | empty: `// Stack is empty
74 | // Use the push operation to add items to the stack
75 | push(item: number) {
76 | this.items.push(item)
77 | return this
78 | }`,
79 | }
80 |
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
6 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | eslint: {
4 | ignoreDuringBuilds: true,
5 | },
6 | typescript: {
7 | ignoreBuildErrors: true,
8 | },
9 | images: {
10 | unoptimized: false, // Enable image optimization
11 | },
12 | experimental: {
13 | webpackBuildWorker: true,
14 | // Removed unnecessary experimental features
15 | },
16 | }
17 |
18 | export default nextConfig
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "data-structures",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@hookform/resolvers": "^3.9.1",
13 | "@radix-ui/react-accordion": "^1.2.2",
14 | "@radix-ui/react-alert-dialog": "^1.1.4",
15 | "@radix-ui/react-aspect-ratio": "^1.1.1",
16 | "@radix-ui/react-avatar": "^1.1.2",
17 | "@radix-ui/react-checkbox": "^1.1.3",
18 | "@radix-ui/react-collapsible": "^1.1.2",
19 | "@radix-ui/react-context-menu": "^2.2.4",
20 | "@radix-ui/react-dialog": "^1.1.4",
21 | "@radix-ui/react-dropdown-menu": "^2.1.4",
22 | "@radix-ui/react-hover-card": "^1.1.4",
23 | "@radix-ui/react-label": "^2.1.1",
24 | "@radix-ui/react-menubar": "^1.1.4",
25 | "@radix-ui/react-navigation-menu": "^1.2.3",
26 | "@radix-ui/react-popover": "^1.1.4",
27 | "@radix-ui/react-progress": "latest",
28 | "@radix-ui/react-radio-group": "^1.2.2",
29 | "@radix-ui/react-scroll-area": "^1.2.2",
30 | "@radix-ui/react-select": "latest",
31 | "@radix-ui/react-separator": "^1.1.1",
32 | "@radix-ui/react-slider": "^1.2.2",
33 | "@radix-ui/react-slot": "latest",
34 | "@radix-ui/react-switch": "^1.1.2",
35 | "@radix-ui/react-tabs": "latest",
36 | "@radix-ui/react-toast": "^1.2.4",
37 | "@radix-ui/react-toggle": "^1.1.1",
38 | "@radix-ui/react-toggle-group": "^1.1.1",
39 | "@radix-ui/react-tooltip": "^1.1.6",
40 | "autoprefixer": "^10.4.20",
41 | "class-variance-authority": "^0.7.1",
42 | "clsx": "^2.1.1",
43 | "cmdk": "1.0.4",
44 | "date-fns": "4.1.0",
45 | "embla-carousel-react": "8.5.1",
46 | "input-otp": "1.4.1",
47 | "lucide-react": "^0.454.0",
48 | "next": "15.2.4",
49 | "next-themes": "^0.4.4",
50 | "react": "^19",
51 | "react-day-picker": "8.10.1",
52 | "react-dom": "^19",
53 | "react-hook-form": "^7.54.1",
54 | "react-resizable-panels": "^2.1.7",
55 | "recharts": "2.15.0",
56 | "sonner": "^1.7.1",
57 | "tailwind-merge": "^2.5.5",
58 | "tailwindcss-animate": "^1.0.7",
59 | "vaul": "^0.9.6",
60 | "zod": "^3.24.1"
61 | },
62 | "devDependencies": {
63 | "@types/node": "^22",
64 | "@types/react": "^19",
65 | "@types/react-dom": "^19",
66 | "postcss": "^8",
67 | "tailwindcss": "^3.4.17",
68 | "typescript": "^5"
69 | }
70 | }
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | font-family: Arial, Helvetica, sans-serif;
7 | }
8 |
9 | @layer utilities {
10 | .text-balance {
11 | text-wrap: balance;
12 | }
13 | }
14 |
15 | @layer base {
16 | :root {
17 | --background: 0 0% 100%;
18 | --foreground: 0 0% 3.9%;
19 | --card: 0 0% 100%;
20 | --card-foreground: 0 0% 3.9%;
21 | --popover: 0 0% 100%;
22 | --popover-foreground: 0 0% 3.9%;
23 | --primary: 0 0% 9%;
24 | --primary-foreground: 0 0% 98%;
25 | --secondary: 0 0% 96.1%;
26 | --secondary-foreground: 0 0% 9%;
27 | --muted: 0 0% 96.1%;
28 | --muted-foreground: 0 0% 45.1%;
29 | --accent: 0 0% 96.1%;
30 | --accent-foreground: 0 0% 9%;
31 | --destructive: 0 84.2% 60.2%;
32 | --destructive-foreground: 0 0% 98%;
33 | --border: 0 0% 89.8%;
34 | --input: 0 0% 89.8%;
35 | --ring: 0 0% 3.9%;
36 | --chart-1: 12 76% 61%;
37 | --chart-2: 173 58% 39%;
38 | --chart-3: 197 37% 24%;
39 | --chart-4: 43 74% 66%;
40 | --chart-5: 27 87% 67%;
41 | --radius: 0.5rem;
42 | --sidebar-background: 0 0% 98%;
43 | --sidebar-foreground: 240 5.3% 26.1%;
44 | --sidebar-primary: 240 5.9% 10%;
45 | --sidebar-primary-foreground: 0 0% 98%;
46 | --sidebar-accent: 240 4.8% 95.9%;
47 | --sidebar-accent-foreground: 240 5.9% 10%;
48 | --sidebar-border: 220 13% 91%;
49 | --sidebar-ring: 217.2 91.2% 59.8%;
50 | }
51 | .dark {
52 | --background: 0 0% 3.9%;
53 | --foreground: 0 0% 98%;
54 | --card: 0 0% 3.9%;
55 | --card-foreground: 0 0% 98%;
56 | --popover: 0 0% 3.9%;
57 | --popover-foreground: 0 0% 98%;
58 | --primary: 0 0% 98%;
59 | --primary-foreground: 0 0% 9%;
60 | --secondary: 0 0% 14.9%;
61 | --secondary-foreground: 0 0% 98%;
62 | --muted: 0 0% 14.9%;
63 | --muted-foreground: 0 0% 63.9%;
64 | --accent: 0 0% 14.9%;
65 | --accent-foreground: 0 0% 98%;
66 | --destructive: 0 62.8% 30.6%;
67 | --destructive-foreground: 0 0% 98%;
68 | --border: 0 0% 14.9%;
69 | --input: 0 0% 14.9%;
70 | --ring: 0 0% 83.1%;
71 | --chart-1: 220 70% 50%;
72 | --chart-2: 160 60% 45%;
73 | --chart-3: 30 80% 55%;
74 | --chart-4: 280 65% 60%;
75 | --chart-5: 340 75% 55%;
76 | --sidebar-background: 240 5.9% 10%;
77 | --sidebar-foreground: 240 4.8% 95.9%;
78 | --sidebar-primary: 224.3 76.3% 48%;
79 | --sidebar-primary-foreground: 0 0% 100%;
80 | --sidebar-accent: 240 3.7% 15.9%;
81 | --sidebar-accent-foreground: 240 4.8% 95.9%;
82 | --sidebar-border: 240 3.7% 15.9%;
83 | --sidebar-ring: 217.2 91.2% 59.8%;
84 | }
85 | }
86 |
87 | @layer base {
88 | * {
89 | @apply border-border;
90 | }
91 | body {
92 | @apply bg-background text-foreground;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | "./pages/**/*.{ts,tsx}",
6 | "./components/**/*.{ts,tsx}",
7 | "./app/**/*.{ts,tsx}",
8 | "./src/**/*.{ts,tsx}",
9 | "*.{js,ts,jsx,tsx,mdx}",
10 | ],
11 | theme: {
12 | container: {
13 | center: true,
14 | padding: "2rem",
15 | screens: {
16 | "2xl": "1400px",
17 | },
18 | },
19 | extend: {
20 | colors: {
21 | border: "hsl(var(--border))",
22 | input: "hsl(var(--input))",
23 | ring: "hsl(var(--ring))",
24 | background: "hsl(var(--background))",
25 | foreground: "hsl(var(--foreground))",
26 | primary: {
27 | DEFAULT: "hsl(var(--primary))",
28 | foreground: "hsl(var(--primary-foreground))",
29 | },
30 | secondary: {
31 | DEFAULT: "hsl(var(--secondary))",
32 | foreground: "hsl(var(--secondary-foreground))",
33 | },
34 | destructive: {
35 | DEFAULT: "hsl(var(--destructive))",
36 | foreground: "hsl(var(--destructive-foreground))",
37 | },
38 | muted: {
39 | DEFAULT: "hsl(var(--muted))",
40 | foreground: "hsl(var(--muted-foreground))",
41 | },
42 | accent: {
43 | DEFAULT: "hsl(var(--accent))",
44 | foreground: "hsl(var(--accent-foreground))",
45 | },
46 | popover: {
47 | DEFAULT: "hsl(var(--popover))",
48 | foreground: "hsl(var(--popover-foreground))",
49 | },
50 | card: {
51 | DEFAULT: "hsl(var(--card))",
52 | foreground: "hsl(var(--card-foreground))",
53 | },
54 | },
55 | borderRadius: {
56 | lg: "var(--radius)",
57 | md: "calc(var(--radius) - 2px)",
58 | sm: "calc(var(--radius) - 4px)",
59 | },
60 | keyframes: {
61 | "accordion-down": {
62 | from: { height: 0 },
63 | to: { height: "var(--radix-accordion-content-height)" },
64 | },
65 | "accordion-up": {
66 | from: { height: "var(--radix-accordion-content-height)" },
67 | to: { height: 0 },
68 | },
69 | "fade-up": {
70 | "0%": { opacity: 1, transform: "translateY(0)" },
71 | "100%": { opacity: 0, transform: "translateY(-20px)" },
72 | },
73 | "fade-left": {
74 | "0%": { opacity: 1, transform: "translateX(0)" },
75 | "100%": { opacity: 0, transform: "translateX(-20px)" },
76 | },
77 | },
78 | animation: {
79 | "accordion-down": "accordion-down 0.2s ease-out",
80 | "accordion-up": "accordion-up 0.2s ease-out",
81 | "fade-up": "fade-up 1s forwards",
82 | "fade-left": "fade-left 1s forwards",
83 | },
84 | backgroundImage: {
85 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
86 | "gradient-purple": "linear-gradient(to bottom right, #2D1F3D, #1F1635)",
87 | },
88 | },
89 | },
90 | plugins: [require("tailwindcss-animate")],
91 | }
92 |
--------------------------------------------------------------------------------
/tasks.txt:
--------------------------------------------------------------------------------
1 | ## Educational Enhancements
2 |
3 | 1. **Step-by-step Tutorials**: Add guided tutorials that walk users through each data structure with explanations of core concepts.
4 | 2. **Code View**: Show the actual implementation code alongside visualizations so users can connect visual operations with code.
5 | 3. **Complexity Visualizer**: Add visual indicators showing how many operations/comparisons are performed during each action to illustrate time complexity.
6 | 4. **Algorithm Comparisons**: Allow users to compare different algorithms (e.g., different tree balancing techniques) side by side.
7 | 5. **Quiz Mode**: Implement interactive quizzes to test understanding of data structure operations.
8 |
9 |
10 | ## User Experience Improvements
11 |
12 | 1. **Dark/Light Theme Toggle**: Add theme switching for different user preferences and environments.
13 | 2. **Responsive Controls**: Make the visualization controls more accessible on mobile devices.
14 | 3. **Keyboard Shortcuts**: Add keyboard shortcuts for common operations.
15 | 4. **Save/Load Functionality**: Allow users to save their current data structure state and load it later.
16 | 5. **Customizable Animation Speed**: Let users control how fast or slow the animations run.
17 | 6. **Fullscreen Mode**: Add the ability to expand visualizations to fullscreen for presentations.
18 |
19 |
20 | ## Additional Data Structures
21 |
22 | 1. **AVL Trees**: Implement self-balancing AVL trees to demonstrate tree balancing.
23 | 2. **Red-Black Trees**: Add another type of self-balancing tree with different properties.
24 | 3. **Heaps**: Implement min and max heaps with visualization of heapify operations.
25 | 4. **Trie**: Add prefix tree visualization for string operations.
26 | 5. **B-Trees**: Implement B-trees to show how they're used in databases and file systems.
27 | 6. **Disjoint Set/Union Find**: Visualize operations like union and find with path compression.
28 |
29 |
30 | ## Technical Improvements
31 |
32 | 1. **Performance Optimization**: Optimize rendering for larger data structures.
33 | 2. **Accessibility Improvements**: Ensure all visualizations are accessible with proper ARIA attributes and keyboard navigation.
34 | 3. **Localization**: Add support for multiple languages.
35 | 4. **Progressive Web App**: Make the app installable as a PWA for offline use.
36 | 5. **URL State Sharing**: Allow users to share specific data structure states via URL.
37 |
38 |
39 | ## Interactive Features
40 |
41 | 1. **Custom Input Data Sets**: Let users upload or input their own data sets.
42 | 2. **Real-world Examples**: Show how each data structure is used in real applications.
43 | 3. **Collaborative Mode**: Allow multiple users to interact with the same visualization simultaneously.
44 | 4. **Challenge Mode**: Create challenges where users need to build efficient data structures to solve problems.
45 | 5. **Performance Benchmarks**: Show performance metrics for different operations across data structures.
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "target": "ES6",
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------