├── .eslintrc.json
├── .gitignore
├── .prettierrc
├── README.md
├── app
├── (mdx)
│ ├── layout.tsx
│ └── start
│ │ └── page.mdx
├── api
│ └── subscribe
│ │ └── route.ts
├── cta
│ └── page.tsx
├── faq
│ └── page.tsx
├── favicon.ico
├── feature
│ └── page.tsx
├── footer
│ └── page.tsx
├── globals.css
├── header
│ └── page.tsx
├── hero
│ └── page.tsx
├── layout.tsx
├── opengraph-image.jpg
├── page.tsx
├── play
│ └── page.tsx
├── pricing
│ └── page.tsx
├── robots.txt
├── sitemap.ts
└── twitter-image.jpg
├── components.json
├── components
├── backgrounds.tsx
├── copy-link.tsx
├── copy.tsx
├── craft.tsx
├── ctas
│ ├── cta-four.tsx
│ ├── cta-one.tsx
│ ├── cta-three.tsx
│ └── cta-two.tsx
├── email-form.tsx
├── end.tsx
├── faqs
│ ├── faqs-one.tsx
│ └── faqs-two.tsx
├── features
│ ├── feature-eight.tsx
│ ├── feature-five.tsx
│ ├── feature-four.tsx
│ ├── feature-nine.tsx
│ ├── feature-one.tsx
│ ├── feature-seven.tsx
│ ├── feature-six.tsx
│ ├── feature-three.tsx
│ └── feature-two.tsx
├── footers
│ ├── footer-five.tsx
│ ├── footer-four.tsx
│ ├── footer-one.tsx
│ ├── footer-three.tsx
│ └── footer-two.tsx
├── headers
│ ├── header-one.tsx
│ └── header-two.tsx
├── heros
│ ├── hero-five.tsx
│ ├── hero-four.tsx
│ ├── hero-one.tsx
│ ├── hero-six.tsx
│ ├── hero-three.tsx
│ └── hero-two.tsx
├── info.tsx
├── nav-links.tsx
├── nav.tsx
├── pricing
│ ├── pricing-four.tsx
│ ├── pricing-one.tsx
│ ├── pricing-three.tsx
│ └── pricing-two.tsx
├── site
│ └── theme
│ │ ├── theme-provider.tsx
│ │ └── theme-toggle.tsx
├── type-button.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
│ ├── 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-toast.ts
├── view-code.tsx
└── wrapper.tsx
├── hooks
└── use-mobile.tsx
├── lib
├── types.d.ts
└── utils.ts
├── mdx-components.tsx
├── next.config.js
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── logo.svg
└── placeholder.jpg
├── registry.ts
├── tailwind.config.ts
├── tsconfig.json
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "prettier-plugin-tailwindcss"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # brijr/components
2 |
3 | [components.work](https://components.work)
4 |
5 | > A collection of [Next.js](https://nextjs.org) components for building websites at rapid speed using [Tailwind](https://tailwindcss.com), [React](https://react.dev), [shadcn/ui](https://ui.shadcn.com), [brijr/craft](https://github.com/brijr/craft), and [Typescript](https://www.typescriptlang.org/).
6 |
7 | ## What is it?
8 |
9 | 
10 |
11 | Open source collection of type-safe React components styled with Tailwind for building Next.js websites. Created by [Bridger Tower](https://bridger.to).
12 |
13 | ## Setup
14 |
15 | > Want to use a starter? Go here 🔗 [brijr/starter](https://github.com/brijr/starter)
16 |
17 | ### Step 1: Create a Next.js application
18 |
19 | ```
20 | npx create-next-app@latest my-app --typescript --tailwind --eslint
21 | ```
22 |
23 | ### Step 2: Install [craft-ds](https://github.com/brijr/craft) (this will also install shadcn/ui)
24 |
25 | ```
26 | npx craft-ds@latest init
27 | ```
28 |
29 | - Add a `ThemeProvider` and `ModeToggle` (if you want dark mode) by following these steps: [Adding dark mode to your next app](https://ui.shadcn.com/docs/dark-mode/next)
30 |
31 | ### Step 3: Copy and Paste components from the [website](htpps://components.work) into your component folder to get started!
32 |
33 | ## brijr/components in use
34 |
35 | [](https://www.youtube.com/watch?v=s-cb_7Kyupg)
36 |
37 | [](https://www.youtube.com/watch?v=PjSfcq3p2jY)
38 |
--------------------------------------------------------------------------------
/app/(mdx)/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Main, Section, Container } from "@/components/craft";
2 |
3 | export default function Layout({
4 | children,
5 | }: Readonly<{
6 | children: React.ReactNode;
7 | }>) {
8 | return (
9 |
10 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/app/(mdx)/start/page.mdx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button";
2 | import Link from "next/link";
3 |
4 |
5 |
6 | Back to components
7 |
8 |
9 |
10 | ### How to set up brijr/components
11 |
12 | Want to use a starter? Check out the [brijr/starter](https://github.com/brijr/starter)
13 |
14 | ---
15 |
16 | #### Step 1: Create a Next.js application
17 |
18 | ```
19 | npx create-next-app@latest my-app --typescript --tailwind --eslint
20 | ```
21 |
22 | #### Step 2: Install [craft-ds](https://github.com/brijr/craft) (this will also install shadcn/ui)
23 |
24 | ```
25 | npx craft-ds@latest init
26 | ```
27 |
28 | - (if you want dark mode) Add a `ThemeProvider` and `ModeToggle` by following these steps: [Adding dark mode to your next app](https://ui.shadcn.com/docs/dark-mode/next)
29 |
30 | #### Step 3: Copy and Paste components from the [homepage](/) into your component folder to get started!
31 |
--------------------------------------------------------------------------------
/app/api/subscribe/route.ts:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 |
3 | const LOOPS_API_KEY = process.env.LOOPS_API_KEY;
4 |
5 | export async function POST(request: Request) {
6 | try {
7 | const { email, source, userGroup } = await request.json();
8 |
9 | if (!email) {
10 | return NextResponse.json({ error: "Email is required" }, { status: 400 });
11 | }
12 |
13 | if (!LOOPS_API_KEY) {
14 | console.error("LOOPS_API_KEY environment variable is not set");
15 | return NextResponse.json(
16 | { error: "API configuration error" },
17 | { status: 500 },
18 | );
19 | }
20 |
21 | const response = await fetch(
22 | "https://app.loops.so/api/v1/contacts/create",
23 | {
24 | method: "POST",
25 | headers: {
26 | Authorization: `Bearer ${LOOPS_API_KEY}`,
27 | "Content-Type": "application/json",
28 | },
29 | body: JSON.stringify({ email, source, userGroup }),
30 | },
31 | );
32 |
33 | if (!response.ok) {
34 | const errorData = await response.text();
35 | console.error("Loops API error:", errorData);
36 | throw new Error(`Failed to create contact in Loops: ${errorData}`);
37 | }
38 |
39 | const data = await response.json();
40 |
41 | return NextResponse.json({ success: true, id: data.id }, { status: 201 });
42 | } catch (error) {
43 | console.error("Error in subscribe API:", error);
44 | return NextResponse.json(
45 | { error: "Internal Server Error" },
46 | { status: 500 },
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/cta/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components, types } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components
17 | .filter((component) => component.type === "cta")
18 | .map((component) => (
19 |
25 |
26 |
27 | ))}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/faq/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components, types } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components
17 | .filter((component) => component.type === "faq")
18 | .map((component) => (
19 |
25 |
26 |
27 | ))}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brijr/components/df5867169b8d746b622b20aa50f0a64bfc7df826/app/favicon.ico
--------------------------------------------------------------------------------
/app/feature/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components, types } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components
17 | .filter((component) => component.type === "feature")
18 | .map((component) => (
19 |
25 |
26 |
27 | ))}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/footer/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components, types } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components
17 | .filter((component) => component.type === "footer")
18 | .map((component) => (
19 |
25 |
26 |
27 | ))}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 0 0% 3.9%;
9 |
10 | --card: 0 0% 100%;
11 | --card-foreground: 0 0% 3.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 0 0% 3.9%;
15 |
16 | --primary: 0 0% 9%;
17 | --primary-foreground: 0 0% 98%;
18 |
19 | --secondary: 0 0% 96.1%;
20 | --secondary-foreground: 0 0% 9%;
21 |
22 | --muted: 0 0% 96.1%;
23 | --muted-foreground: 0 0% 45.1%;
24 |
25 | --accent: 0 0% 96.1%;
26 | --accent-foreground: 0 0% 9%;
27 |
28 | --destructive: 0 84.2% 60.2%;
29 | --destructive-foreground: 0 0% 98%;
30 |
31 | --border: 0 0% 89.8%;
32 | --input: 0 0% 89.8%;
33 | --ring: 0 0% 3.9%;
34 |
35 | --radius: 0.5rem;
36 |
37 | --sidebar-background: 0 0% 98%;
38 |
39 | --sidebar-foreground: 240 5.3% 26.1%;
40 |
41 | --sidebar-primary: 240 5.9% 10%;
42 |
43 | --sidebar-primary-foreground: 0 0% 98%;
44 |
45 | --sidebar-accent: 240 4.8% 95.9%;
46 |
47 | --sidebar-accent-foreground: 240 5.9% 10%;
48 |
49 | --sidebar-border: 220 13% 91%;
50 |
51 | --sidebar-ring: 217.2 91.2% 59.8%;
52 | }
53 |
54 | .dark {
55 | --background: 0 0% 3.9%;
56 | --foreground: 0 0% 98%;
57 |
58 | --card: 0 0% 3.9%;
59 | --card-foreground: 0 0% 98%;
60 |
61 | --popover: 0 0% 3.9%;
62 | --popover-foreground: 0 0% 98%;
63 |
64 | --primary: 0 0% 98%;
65 | --primary-foreground: 0 0% 9%;
66 |
67 | --secondary: 0 0% 14.9%;
68 | --secondary-foreground: 0 0% 98%;
69 |
70 | --muted: 0 0% 14.9%;
71 | --muted-foreground: 0 0% 63.9%;
72 |
73 | --accent: 0 0% 14.9%;
74 | --accent-foreground: 0 0% 98%;
75 |
76 | --destructive: 0 62.8% 30.6%;
77 | --destructive-foreground: 0 0% 98%;
78 |
79 | --border: 0 0% 14.9%;
80 | --input: 0 0% 14.9%;
81 | --ring: 0 0% 83.1%;
82 | --sidebar-background: 240 5.9% 10%;
83 | --sidebar-foreground: 240 4.8% 95.9%;
84 | --sidebar-primary: 224.3 76.3% 48%;
85 | --sidebar-primary-foreground: 0 0% 100%;
86 | --sidebar-accent: 240 3.7% 15.9%;
87 | --sidebar-accent-foreground: 240 4.8% 95.9%;
88 | --sidebar-border: 240 3.7% 15.9%;
89 | --sidebar-ring: 217.2 91.2% 59.8%;
90 | }
91 | }
92 |
93 | @layer base {
94 | * {
95 | @apply border-border;
96 | }
97 |
98 | body {
99 | @apply bg-background text-foreground;
100 | }
101 | }
102 |
103 | @layer utilities {
104 |
105 | /* Hide scrollbar for Chrome, Safari and Opera */
106 | .no-scrollbar::-webkit-scrollbar {
107 | display: none;
108 | }
109 |
110 | /* Hide scrollbar for IE, Edge and Firefox */
111 | .no-scrollbar {
112 | -ms-overflow-style: none;
113 | /* IE and Edge */
114 | scrollbar-width: none;
115 | /* Firefox */
116 | }
117 | }
118 |
119 | .fade-in {
120 | -webkit-animation: fade-in-up 1000ms cubic-bezier(0.39, 0.575, 0.565, 1) 200ms both;
121 | animation: fade-in-up 1000ms cubic-bezier(0.39, 0.575, 0.565, 1) 200ms both;
122 | }
123 |
124 | @-webkit-keyframes fade-in-up {
125 | 0% {
126 | opacity: 0;
127 | -webkit-transform: translateY(20px);
128 | transform: translateY(20px);
129 | }
130 |
131 | 100% {
132 | opacity: 1;
133 | -webkit-transform: translateY(0);
134 | transform: translateY(0);
135 | }
136 | }
137 |
138 | @keyframes fade-in-up {
139 | 0% {
140 | opacity: 0;
141 | -webkit-transform: translateY(20px);
142 | transform: translateY(20px);
143 | }
144 |
145 | 100% {
146 | opacity: 1;
147 | -webkit-transform: translateY(0);
148 | transform: translateY(0);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/app/header/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components, types } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components
17 | .filter((component) => component.type === "header")
18 | .map((component) => (
19 |
25 |
26 |
27 | ))}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/hero/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components, types } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components
17 | .filter((component) => component.type === "hero")
18 | .map((component) => (
19 |
25 |
26 |
27 | ))}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./globals.css";
2 | import type { Metadata } from "next";
3 | import { Manrope } from "next/font/google";
4 | import { Layout } from "@/components/craft";
5 | import { Toaster } from "@/components/ui/sonner";
6 | import { Background } from "@/components/backgrounds";
7 | import { Analytics } from "@vercel/analytics/react";
8 | import { ThemeProvider } from "@/components/site/theme/theme-provider";
9 | import { Nav } from "@/components/nav";
10 |
11 | const font = Manrope({ subsets: ["latin"] });
12 |
13 | export const metadata: Metadata = {
14 | metadataBase: new URL("https://components.work"),
15 | title: "Next JS Components for building websites created by Bridger Tower",
16 | description:
17 | "Collection of Typescript React components for Next JS built using Tailwind and shadcn/ui. Created by Bridger Tower.",
18 | };
19 |
20 | export default function RootLayout({
21 | children,
22 | }: Readonly<{
23 | children: React.ReactNode;
24 | }>) {
25 | return (
26 |
27 |
28 |
34 |
35 | {children}
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/app/opengraph-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brijr/components/df5867169b8d746b622b20aa50f0a64bfc7df826/app/opengraph-image.jpg
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components.map((component) => (
17 |
23 |
24 |
25 | ))}
26 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/app/play/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { Wrapper } from "@/components/wrapper";
4 | import Link from "next/link";
5 |
6 | // Component Imports
7 | import Component from "@/components/features/feature-eight";
8 |
9 | export default function Home() {
10 | return (
11 |
12 | Go home
13 |
14 | {/* Component in Dev */}
15 |
16 | {/* Component in Dev */}
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/app/pricing/page.tsx:
--------------------------------------------------------------------------------
1 | // Layout Imports
2 | import { Main } from "@/components/craft";
3 | import { End } from "@/components/end";
4 | import { Wrapper } from "@/components/wrapper";
5 | import { Info } from "@/components/info";
6 | import * as Craft from "@/components/craft";
7 |
8 | // Component Config
9 | import { components, types } from "@/registry";
10 |
11 | export default function Home() {
12 | return (
13 |
14 |
15 |
16 | {components
17 | .filter((component) => component.type === "pricing")
18 | .map((component) => (
19 |
25 |
26 |
27 | ))}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/app/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 | Disallow: /play
4 |
5 | Sitemap: https://components.work/sitemap.xml
6 |
7 | Github: https://github.com/brijr/components
8 |
--------------------------------------------------------------------------------
/app/sitemap.ts:
--------------------------------------------------------------------------------
1 | import type { MetadataRoute } from "next";
2 |
3 | export default function sitemap(): MetadataRoute.Sitemap {
4 | return [
5 | {
6 | url: "https://components.work",
7 | lastModified: new Date(),
8 | changeFrequency: "monthly",
9 | priority: 1,
10 | },
11 | {
12 | url: "https://components.work/start",
13 | lastModified: new Date(),
14 | changeFrequency: "monthly",
15 | priority: 0.9,
16 | },
17 | {
18 | url: "https://components.work/pricing",
19 | lastModified: new Date(),
20 | changeFrequency: "monthly",
21 | priority: 0.8,
22 | },
23 | {
24 | url: "https://components.work/faq",
25 | lastModified: new Date(),
26 | changeFrequency: "monthly",
27 | priority: 0.8,
28 | },
29 | {
30 | url: "https://components.work/hero",
31 | lastModified: new Date(),
32 | changeFrequency: "monthly",
33 | priority: 0.8,
34 | },
35 | {
36 | url: "https://components.work/header",
37 | lastModified: new Date(),
38 | changeFrequency: "monthly",
39 | priority: 0.8,
40 | },
41 | {
42 | url: "https://components.work/footer",
43 | lastModified: new Date(),
44 | changeFrequency: "monthly",
45 | priority: 0.8,
46 | },
47 | {
48 | url: "https://components.work/feature",
49 | lastModified: new Date(),
50 | changeFrequency: "monthly",
51 | priority: 0.8,
52 | },
53 | {
54 | url: "https://components.work/cta",
55 | lastModified: new Date(),
56 | changeFrequency: "monthly",
57 | priority: 0.8,
58 | },
59 | ];
60 | }
61 |
--------------------------------------------------------------------------------
/app/twitter-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brijr/components/df5867169b8d746b622b20aa50f0a64bfc7df826/app/twitter-image.jpg
--------------------------------------------------------------------------------
/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 | }
17 | }
18 |
--------------------------------------------------------------------------------
/components/backgrounds.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export function Background() {
4 | return (
5 |
6 | {/* Radial gradient for the container to give a faded look */}
7 |
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/components/copy-link.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useClipboard } from "use-clipboard-copy";
4 | import { toast } from "sonner";
5 | import { Link } from "lucide-react";
6 |
7 | import {
8 | Tooltip,
9 | TooltipContent,
10 | TooltipProvider,
11 | TooltipTrigger,
12 | } from "@/components/ui/tooltip";
13 |
14 | export const CopyLink = ({ path }: { path: string }) => {
15 | const clipboard = useClipboard({ copiedTimeout: 1500 });
16 |
17 | const handleCopyLink = () => {
18 | const link = `https://components.work#${path}`;
19 | clipboard.copy(link);
20 | toast.success("Link Copied to Clipboard");
21 | };
22 |
23 | return (
24 |
25 |
26 |
27 |
28 | components/
29 | {path}
30 |
31 |
32 |
33 |
34 | Copy Link to Component
35 |
36 |
37 |
38 |
39 | );
40 | };
41 |
--------------------------------------------------------------------------------
/components/copy.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import { Copy } from "lucide-react";
5 | import { toast } from "sonner";
6 | import { Button } from "./ui/button";
7 |
8 | type CopyButtonProps = {
9 | textToCopy: string;
10 | className?: string;
11 | };
12 |
13 | const CopyButton: React.FC = ({ textToCopy, className }) => {
14 | const [copyStatus, setCopyStatus] = React.useState(
15 | <>
16 | Copy Code
17 |
18 | >,
19 | );
20 |
21 | const handleCopy = async () => {
22 | await navigator.clipboard.writeText(textToCopy);
23 | toast.success("Code Copied to Clipboard");
24 | };
25 |
26 | return (
27 | {
33 | handleCopy();
34 | e.stopPropagation();
35 | }}
36 | >
37 | {copyStatus}
38 |
39 | );
40 | };
41 |
42 | export default CopyButton;
43 |
--------------------------------------------------------------------------------
/components/ctas/cta-four.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as z from "zod";
4 | import { useForm } from "react-hook-form";
5 | import { zodResolver } from "@hookform/resolvers/zod";
6 | import Balancer from "react-wrap-balancer";
7 |
8 | import { Section, Container } from "@/components/craft";
9 | import { Button } from "@/components/ui/button";
10 | import {
11 | Form,
12 | FormControl,
13 | FormField,
14 | FormItem,
15 | FormLabel,
16 | FormMessage,
17 | } from "@/components/ui/form";
18 | import { Input } from "@/components/ui/input";
19 |
20 | const formSchema = z.object({
21 | email: z.string().email({
22 | message: "Please enter a valid email address.",
23 | }),
24 | });
25 |
26 | export function CTA() {
27 | // 1. Define your form.
28 | const form = useForm>({
29 | resolver: zodResolver(formSchema),
30 | defaultValues: {
31 | email: "",
32 | },
33 | });
34 |
35 | // 2. Define a submit handler.
36 | function onSubmit(values: z.infer) {
37 | // Do something with the form values.
38 | // ✅ This will be type-safe and validated.
39 | console.log(values);
40 | }
41 |
42 | return (
43 |
44 |
45 | Lorem ipsum dolor sit amet!
46 |
47 |
48 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
49 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 |
51 |
52 |
76 |
77 |
78 |
79 | );
80 | }
81 |
82 | export default CTA;
83 |
--------------------------------------------------------------------------------
/components/ctas/cta-one.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Link from "next/link";
3 |
4 | // Third-party library imports
5 | import Balancer from "react-wrap-balancer";
6 |
7 | // UI component imports
8 | import { Button } from "@/components/ui/button";
9 |
10 | // Custom components
11 | import { Container, Section } from "@/components/craft";
12 |
13 | const CTA = () => {
14 | return (
15 |
16 |
17 | Lorem ipsum dolor sit amet
18 |
19 |
20 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
21 | labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 | Get Started
27 |
28 |
29 | Learn More {"->"}
30 |
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default CTA;
38 |
--------------------------------------------------------------------------------
/components/ctas/cta-three.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Link from "next/link";
3 |
4 | // Third-party library imports
5 | import Balancer from "react-wrap-balancer";
6 |
7 | // UI component imports
8 | import { Button } from "@/components/ui/button";
9 |
10 | // Custom components
11 | import { Section, Container } from "@/components/craft";
12 |
13 | const CTA = () => {
14 | return (
15 |
16 |
17 | Lorem ipsum dolor sit amet
18 |
19 |
20 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
21 | labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 | Get Started
27 |
28 |
29 | Learn More {"->"}
30 |
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default CTA;
38 |
--------------------------------------------------------------------------------
/components/ctas/cta-two.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as z from "zod";
4 | import { useForm } from "react-hook-form";
5 | import { zodResolver } from "@hookform/resolvers/zod";
6 | import Balancer from "react-wrap-balancer";
7 |
8 | import { Section, Container } from "@/components/craft";
9 | import { Button } from "@/components/ui/button";
10 | import {
11 | Form,
12 | FormControl,
13 | FormDescription,
14 | FormField,
15 | FormItem,
16 | FormLabel,
17 | FormMessage,
18 | } from "@/components/ui/form";
19 | import { Input } from "@/components/ui/input";
20 |
21 | const formSchema = z.object({
22 | email: z.string().email({
23 | message: "Please enter a valid email address.",
24 | }),
25 | });
26 |
27 | export function CTA() {
28 | // 1. Define your form.
29 | const form = useForm>({
30 | resolver: zodResolver(formSchema),
31 | defaultValues: {
32 | email: "",
33 | },
34 | });
35 |
36 | // 2. Define a submit handler.
37 | function onSubmit(values: z.infer) {
38 | // Do something with the form values.
39 | // ✅ This will be type-safe and validated.
40 | console.log(values);
41 | }
42 |
43 | return (
44 |
45 |
46 | Lorem ipsum dolor sit amet!
47 |
48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
50 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
51 |
52 |
53 |
75 |
76 |
77 |
78 | );
79 | }
80 |
81 | export default CTA;
82 |
--------------------------------------------------------------------------------
/components/end.tsx:
--------------------------------------------------------------------------------
1 | import * as Craft from "@/components/craft";
2 | import { ModeToggle } from "./site/theme/theme-toggle";
3 | import { Button } from "./ui/button";
4 | import { Github, Twitter } from "lucide-react";
5 | import Link from "next/link";
6 |
7 | export const End = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Visit Github
17 |
18 |
19 |
20 |
21 |
22 | Visit Twitter
23 |
24 |
25 |
26 |
27 | source code can be found on{" "}
28 |
32 | github
33 |
34 | . follow me on{" "}
35 |
39 | x (twitter)
40 |
41 | .
42 |
43 |
44 | components.work by bridger © 2024
45 |
46 |
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/components/faqs/faqs-one.tsx:
--------------------------------------------------------------------------------
1 | import { Section, Container } from "@/components/craft";
2 |
3 | import { ArrowUpRight } from "lucide-react";
4 |
5 | import {
6 | Accordion,
7 | AccordionContent,
8 | AccordionItem,
9 | AccordionTrigger,
10 | } from "@/components/ui/accordion";
11 |
12 | type FAQItem = {
13 | question: string;
14 | answer: string;
15 | link?: string;
16 | };
17 |
18 | const content: FAQItem[] = [
19 | {
20 | question: "Lorem ipsum dolor sit amet?",
21 | answer:
22 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
23 | link: "https://google.com",
24 | },
25 | {
26 | question: "Ut enim ad minim veniam?",
27 | answer:
28 | "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
29 | },
30 | {
31 | question: "Duis aute irure dolor in reprehenderit?",
32 | answer:
33 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
34 | },
35 | {
36 | question: "Excepteur sint occaecat cupidatat non proident?",
37 | answer:
38 | "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
39 | },
40 | ];
41 |
42 | const FAQ = () => {
43 | return (
44 |
45 |
46 | Frequently Asked Questions
47 |
48 | Can't find the answer you're looking for? Reach out to our
49 | customer support team.
50 |
51 |
52 | {content.map((item, index) => (
53 |
54 |
55 |
56 | {item.question}
57 |
58 |
59 | {item.answer}
60 | {item.link && (
61 |
65 | Learn more
66 |
67 | )}
68 |
69 |
70 |
71 | ))}
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export default FAQ;
79 |
--------------------------------------------------------------------------------
/components/faqs/faqs-two.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import React from "react";
3 |
4 | // Third-party library imports
5 | import { ArrowUpRight } from "lucide-react";
6 |
7 | // UI component imports
8 | import {
9 | Accordion,
10 | AccordionContent,
11 | AccordionItem,
12 | AccordionTrigger,
13 | } from "@/components/ui/accordion";
14 |
15 | // Custom components
16 | import { Section, Container } from "@/components/craft";
17 |
18 | type FAQItem = {
19 | question: string;
20 | answer: string;
21 | link?: string;
22 | };
23 |
24 | const content: FAQItem[] = [
25 | {
26 | question: "Lorem ipsum dolor sit amet?",
27 | answer:
28 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
29 | link: "https://google.com",
30 | },
31 | {
32 | question: "Ut enim ad minim veniam?",
33 | answer:
34 | "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
35 | },
36 | {
37 | question: "Duis aute irure dolor in reprehenderit?",
38 | answer:
39 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
40 | },
41 | {
42 | question: "Excepteur sint occaecat cupidatat non proident?",
43 | answer:
44 | "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
45 | },
46 | ];
47 |
48 | const FAQ = () => {
49 | return (
50 |
51 |
52 | Frequently Asked Questions
53 |
54 | Can't find the answer you're looking for? Reach out to our
55 | customer support team.
56 |
57 |
58 | {content.map((item, index) => (
59 |
60 |
64 |
65 | {item.question}
66 |
67 |
68 | {item.answer}
69 | {item.link && (
70 |
74 | Learn more
75 |
76 | )}
77 |
78 |
79 |
80 | ))}
81 |
82 |
83 |
84 | );
85 | };
86 |
87 | export default FAQ;
88 |
--------------------------------------------------------------------------------
/components/features/feature-eight.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import Balancer from "react-wrap-balancer";
4 |
5 | import { Button } from "@/components/ui/button";
6 | import { Section, Container } from "@/components/craft";
7 | import Placeholder from "@/public/placeholder.jpg";
8 |
9 | const Feature = () => {
10 | return (
11 |
12 |
13 | We have this amazing feature in our app.
14 |
15 |
16 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
17 | labore et dolore magna aliqua.
18 |
19 |
20 |
21 |
22 | Get Started
23 |
24 |
25 | Learn More {"->"}
26 |
27 |
28 |
29 |
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | export default Feature;
41 |
--------------------------------------------------------------------------------
/components/features/feature-five.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js
2 | import Link from "next/link";
3 |
4 | // Third-party libraries
5 | import Balancer from "react-wrap-balancer";
6 |
7 | // Custom components
8 | import { Section, Container } from "@/components/craft";
9 |
10 | // Icons
11 | import { Coins, ArrowRight } from "lucide-react";
12 |
13 | type FeatureText = {
14 | icon: JSX.Element;
15 | title: string;
16 | description: string;
17 | href?: string;
18 | cta?: string;
19 | };
20 |
21 | const featureText: FeatureText[] = [
22 | {
23 | icon: ,
24 | title: "Lorem Ipsum",
25 | href: "/",
26 | description:
27 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
28 | cta: "Learn More",
29 | },
30 | {
31 | icon: ,
32 | title: "Lorem Ipsum",
33 | href: "/",
34 | description:
35 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
36 | cta: "Learn More",
37 | },
38 | ];
39 |
40 | const Feature = () => {
41 | return (
42 |
43 |
44 |
45 |
46 |
47 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
48 |
49 |
50 |
51 |
52 | Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
53 |
54 |
55 |
56 |
57 | {featureText.map(
58 | ({ icon, title, description, href, cta }, index) => (
59 |
64 |
65 | {icon}
66 |
{title}
67 |
{description}
68 |
69 | {cta && (
70 |
73 | )}
74 |
75 | ),
76 | )}
77 |
78 |
79 |
80 |
81 | );
82 | };
83 |
84 | export default Feature;
85 |
--------------------------------------------------------------------------------
/components/features/feature-four.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Link from "next/link";
3 | import Image from "next/image";
4 |
5 | // UI component imports
6 | import { Section, Container } from "@/components/craft";
7 | import { Button } from "@/components/ui/button";
8 |
9 | // Asset imports
10 | import Placeholder from "@/public/placeholder.jpg";
11 |
12 | const Feature = () => {
13 | return (
14 |
15 |
16 |
17 |
Lorem ipsum dolor sit
18 |
19 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
20 | labore et dolore magna aliqua.
21 |
22 |
23 |
24 | Get Started
25 |
26 |
27 | Learn More {"->"}
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default Feature;
44 |
--------------------------------------------------------------------------------
/components/features/feature-nine.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import Image from "next/image";
3 |
4 | import { Section, Container } from "@/components/craft";
5 | import { Card, CardContent } from "@/components/ui/card";
6 |
7 | import {
8 | Carousel,
9 | CarouselContent,
10 | CarouselItem,
11 | CarouselNext,
12 | CarouselPrevious,
13 | } from "@/components/ui/carousel";
14 |
15 | const photos = [
16 | {
17 | src: "https://images.unsplash.com/photo-1721137287642-43b251bd6f00?q=80&w=2072&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
18 | },
19 | {
20 | src: "https://images.unsplash.com/photo-1507730690594-f21182eee8b1?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
21 | },
22 | {
23 | src: "https://images.unsplash.com/photo-1721041879224-ff011603ada5?q=80&w=2232&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
24 | },
25 | {
26 | src: "https://images.unsplash.com/photo-1720983627245-ca4a6913016f?q=80&w=2080&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
27 | },
28 | {
29 | src: "https://images.unsplash.com/photo-1720887236665-43caad593cdf?q=80&w=1836&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
30 | },
31 | ];
32 |
33 | const FeatureNine = () => {
34 | return (
35 |
36 |
37 | This is a Feature with an Image Carousel
38 |
39 | Use it to showcase your latest images. Get started at
40 | components.bridgher.to and make sure to install brijr/craft for
41 | styling.
42 |
43 |
44 |
45 | {photos.map((photo, index) => (
46 |
50 |
51 |
52 |
53 |
60 |
61 |
62 |
63 |
64 | ))}
65 |
66 |
67 |
68 |
69 |
70 |
71 | );
72 | };
73 |
74 | export default FeatureNine;
75 |
--------------------------------------------------------------------------------
/components/features/feature-one.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js
2 | import React from "react";
3 |
4 | // Layout Components
5 | import { Section, Container } from "@/components/craft";
6 | import Balancer from "react-wrap-balancer";
7 |
8 | // Icons
9 | import { Coins } from "lucide-react";
10 |
11 | type FeatureText = {
12 | icon: JSX.Element;
13 | title: string;
14 | description: string;
15 | };
16 |
17 | const featureText: FeatureText[] = [
18 | {
19 | icon: ,
20 | title: "Lorem Ipsum",
21 | description:
22 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
23 | },
24 | {
25 | icon: ,
26 | title: "Lorem Ipsum",
27 | description:
28 | "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
29 | },
30 | {
31 | icon: ,
32 | title: "Lorem Ipsum",
33 | description:
34 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
35 | },
36 | ];
37 |
38 | const Feature = () => {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
46 |
47 |
48 |
49 |
50 | Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
51 |
52 |
53 |
54 |
55 | {featureText.map(({ icon, title, description }, index) => (
56 |
57 | {icon}
58 |
{title}
59 |
{description}
60 |
61 | ))}
62 |
63 |
64 |
65 |
66 | );
67 | };
68 |
69 | export default Feature;
70 |
--------------------------------------------------------------------------------
/components/features/feature-seven.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Link from "next/link";
3 | import Image from "next/image";
4 |
5 | // Third-party library imports
6 | import Balancer from "react-wrap-balancer";
7 |
8 | // UI component imports
9 | import { Section, Container } from "@/components/craft";
10 | import { Button } from "@/components/ui/button";
11 |
12 | // Asset imports
13 | import Placeholder from "@/public/placeholder.jpg";
14 |
15 | const Feature = () => {
16 | return (
17 |
18 |
19 |
20 |
25 |
26 | We have this amazing feature in our app.
27 |
28 |
29 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
30 | labore et dolore magna aliqua.
31 |
32 |
33 |
34 |
35 | Get Started
36 |
37 |
38 | Learn More {"->"}
39 |
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default Feature;
47 |
--------------------------------------------------------------------------------
/components/features/feature-six.tsx:
--------------------------------------------------------------------------------
1 | // Layout
2 | import { Section, Container } from "@/components/craft";
3 | import Balancer from "react-wrap-balancer";
4 | import Link from "next/link";
5 |
6 | // Icons
7 | import { Coins, ArrowRight } from "lucide-react";
8 |
9 | type FeatureText = {
10 | icon: JSX.Element;
11 | title: string;
12 | description: string;
13 | href?: string;
14 | cta?: string;
15 | };
16 |
17 | const featureText: FeatureText[] = [
18 | {
19 | icon: ,
20 | title: "Lorem Ipsum",
21 | href: "/",
22 | description:
23 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
24 | cta: "Learn More",
25 | },
26 | {
27 | icon: ,
28 | title: "Lorem Ipsum",
29 | href: "/",
30 | description:
31 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
32 | cta: "Learn More",
33 | },
34 | ];
35 |
36 | const singleFeatureText: FeatureText[] = [
37 | {
38 | icon: ,
39 | title: "Lorem Ipsum",
40 | href: "/",
41 | description:
42 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
43 | cta: "Learn More",
44 | },
45 | ];
46 |
47 | const Feature = () => {
48 | return (
49 |
50 |
51 |
52 |
53 |
54 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
55 |
56 |
57 |
58 |
59 | Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
60 |
61 |
62 |
63 |
64 | {featureText.map(
65 | ({ icon, title, description, href, cta }, index) => (
66 |
71 |
72 | {icon}
73 |
{title}
74 |
{description}
75 |
76 | {cta && (
77 |
80 | )}
81 |
82 | ),
83 | )}
84 |
85 |
86 | {singleFeatureText.map(
87 | ({ icon, title, description, href, cta }, index) => (
88 |
93 |
94 | {icon}
95 |
{title}
96 |
{description}
97 |
98 | {cta && (
99 |
102 | )}
103 |
104 | ),
105 | )}
106 |
107 |
108 |
109 |
110 | );
111 | };
112 |
113 | export default Feature;
114 |
--------------------------------------------------------------------------------
/components/features/feature-three.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Image from "next/image";
3 | import Link from "next/link";
4 |
5 | // UI component imports
6 | import * as Craft from "@/components/craft";
7 | import { Button } from "@/components/ui/button";
8 |
9 | // Asset imports
10 | import Placeholder from "@/public/placeholder.jpg";
11 |
12 | const Feature = () => {
13 | return (
14 |
15 |
16 |
17 |
22 |
23 |
24 |
Lorem ipsum dolor sit
25 |
26 | Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
27 | labore et dolore magna aliqua.
28 |
29 |
30 |
31 | Get Started
32 |
33 |
34 | Learn More {"->"}
35 |
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default Feature;
44 |
--------------------------------------------------------------------------------
/components/features/feature-two.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Link from "next/link";
3 |
4 | // Third-party library imports
5 | import Balancer from "react-wrap-balancer";
6 |
7 | // UI component imports
8 | import { Section, Container } from "@/components/craft";
9 |
10 | // Icon imports
11 | import { Coins, ArrowRight } from "lucide-react";
12 |
13 | type FeatureText = {
14 | icon: JSX.Element;
15 | title: string;
16 | description: string;
17 | href?: string;
18 | cta?: string;
19 | };
20 |
21 | const featureText: FeatureText[] = [
22 | {
23 | icon: ,
24 | title: "Lorem Ipsum",
25 | href: "/",
26 | description:
27 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
28 | cta: "Learn More",
29 | },
30 | {
31 | icon: ,
32 | title: "Lorem Ipsum",
33 | href: "/",
34 | description:
35 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
36 | cta: "Learn More",
37 | },
38 | {
39 | icon: ,
40 | title: "Lorem Ipsum",
41 | href: "/",
42 | description:
43 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
44 | cta: "Learn More",
45 | },
46 | {
47 | icon: ,
48 | title: "Lorem Ipsum",
49 | href: "/",
50 | description:
51 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
52 | cta: "Learn More",
53 | },
54 | ];
55 |
56 | const Feature = () => {
57 | return (
58 |
59 |
60 |
61 |
62 |
63 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
64 |
65 |
66 |
67 |
68 | Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
69 |
70 |
71 |
72 |
73 | {featureText.map(
74 | ({ icon, title, description, href, cta }, index) => (
75 |
80 |
81 | {icon}
82 |
{title}
83 |
{description}
84 |
85 | {cta && (
86 |
89 | )}
90 |
91 | ),
92 | )}
93 |
94 |
95 |
96 |
97 | );
98 | };
99 |
100 | export default Feature;
101 |
--------------------------------------------------------------------------------
/components/footers/footer-five.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Image from "next/image";
3 | import Link from "next/link";
4 |
5 | // Third-party library imports
6 | import Balancer from "react-wrap-balancer";
7 |
8 | // UI component imports
9 | import { Button } from "../ui/button";
10 |
11 | // Icon imports
12 | import { Github, Twitter, Facebook } from "lucide-react";
13 |
14 | // Local component imports
15 | import { Section, Container } from "../craft";
16 |
17 | // Asset imports
18 | import Logo from "@/public/logo.svg";
19 |
20 | export default function Footer() {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
brijr/components
28 |
35 |
36 |
37 |
38 | brijr/components is a collection of Next.js, React, Typescript
39 | components for building landing pages and websites.
40 |
41 |
42 |
43 |
44 | Privacy Policy
45 | Terms of Service
46 | Cookie Policy
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | ©{" "}
63 | brijr/components .
64 | All rights reserved. 2024-present.
65 |
66 |
67 |
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/components/footers/footer-four.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Image from "next/image";
3 | import Link from "next/link";
4 |
5 | // Third-party library imports
6 | import Balancer from "react-wrap-balancer";
7 |
8 | // UI component imports
9 | import { Button } from "../ui/button";
10 |
11 | // Icon imports
12 | import { Github, Twitter, Facebook } from "lucide-react";
13 |
14 | // Local component imports
15 | import { Section, Container } from "../craft";
16 |
17 | // Asset imports
18 | import Logo from "@/public/logo.svg";
19 |
20 | export default function Footer() {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
brijr/components
28 |
35 |
36 |
37 |
38 | brijr/components is a collection of Next.js, React, Typescript
39 | components for building landing pages and websites.
40 |
41 |
42 |
43 |
44 |
Website
45 | Blog
46 | Authors
47 | Categories
48 |
49 |
50 |
Legal
51 | Privacy Policy
52 | Terms of Service
53 | Cookie Policy
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | ©{" "}
70 | brijr/components .
71 | All rights reserved. 2024-present.
72 |
73 |
74 |
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/components/footers/footer-one.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as z from "zod";
4 | import Image from "next/image";
5 | import Link from "next/link";
6 | import Balancer from "react-wrap-balancer";
7 | import { useForm } from "react-hook-form";
8 | import { zodResolver } from "@hookform/resolvers/zod";
9 | import { Github, Twitter, Facebook } from "lucide-react";
10 |
11 | import Logo from "@/public/logo.svg";
12 | import { Section, Container } from "../craft";
13 | import { Button } from "../ui/button";
14 | import { Input } from "@/components/ui/input";
15 | import {
16 | Form,
17 | FormControl,
18 | FormDescription,
19 | FormField,
20 | FormItem,
21 | FormLabel,
22 | FormMessage,
23 | } from "@/components/ui/form";
24 |
25 | const formSchema = z.object({
26 | email: z.string().email({
27 | message: "Please enter a valid email address.",
28 | }),
29 | });
30 |
31 | export default function Footer() {
32 | // 1. Define your form.
33 | const form = useForm>({
34 | resolver: zodResolver(formSchema),
35 | defaultValues: {
36 | email: "",
37 | },
38 | });
39 |
40 | // 2. Define a submit handler.
41 | function onSubmit(values: z.infer) {
42 | // Do something with the form values.
43 | // ✅ This will be type-safe and validated.
44 | console.log(values);
45 | }
46 | return (
47 |
48 |
49 |
50 |
51 |
52 |
brijr/components
53 |
60 |
61 |
62 |
63 | brijr/components is a collection of Next.js, React, Typescript
64 | components for building landing pages and websites.
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
107 |
108 |
109 |
110 |
111 | Privacy Policy
112 | Terms of Service
113 | Cookie Policy
114 |
115 |
116 | ©{" "}
117 | brijr/components .
118 | All rights reserved. 2024-present.
119 |
120 |
121 |
122 |
123 | );
124 | }
125 |
--------------------------------------------------------------------------------
/components/footers/footer-three.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Image from "next/image";
3 | import Link from "next/link";
4 |
5 | // Third-party library imports
6 | import Balancer from "react-wrap-balancer";
7 |
8 | // Local component imports
9 | import { Section, Container } from "../craft";
10 |
11 | // Asset imports
12 | import Logo from "@/public/logo.svg";
13 |
14 | export default function Footer() {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
brijr/components
22 |
29 |
30 |
31 |
32 | brijr/components is a collection of Next.js, React, Typescript
33 | components for building landing pages and websites.
34 |
35 |
36 |
37 | ©{" "}
38 | brijr/components
39 | . All rights reserved. 2024-present.
40 |
41 |
42 |
43 |
Website
44 | Blog
45 | Authors
46 | Categories
47 |
48 |
49 |
Legal
50 | Privacy Policy
51 | Terms of Service
52 | Cookie Policy
53 |
54 |
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/components/footers/footer-two.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import Balancer from "react-wrap-balancer";
4 |
5 | import { Section, Container } from "../craft";
6 | import Logo from "@/public/logo.svg";
7 |
8 | export default function Footer() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
brijr/components
16 |
23 |
24 |
25 |
26 | brijr/components is a collection of Next.js, React, Typescript
27 | components for building landing pages and websites.
28 |
29 |
30 |
31 | Privacy Policy
32 | Terms of Service
33 | Cookie Policy
34 |
35 |
36 | ©{" "}
37 | brijr/components
38 | . All rights reserved. 2024-present.
39 |
40 |
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/components/headers/header-one.tsx:
--------------------------------------------------------------------------------
1 | import Balancer from "react-wrap-balancer";
2 |
3 | import { Button } from "../ui/button";
4 | import { Container, Section } from "@/components/craft";
5 |
6 | const Header = () => {
7 | return (
8 |
9 |
10 | Lorem ipsum dolor sit amet consectetur
11 |
12 |
13 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
14 | voluptate quidem natus.
15 |
16 |
17 |
18 | Get Started
19 | Learn More
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default Header;
27 |
--------------------------------------------------------------------------------
/components/headers/header-two.tsx:
--------------------------------------------------------------------------------
1 | // Third-party library imports
2 | import Balancer from "react-wrap-balancer";
3 |
4 | // Local component imports
5 | import { Container, Section } from "@/components/craft";
6 | import { Button } from "../ui/button";
7 |
8 | const Header = () => {
9 | return (
10 |
11 |
12 | Lorem ipsum dolor sit amet consectetur
13 |
14 |
15 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
16 | voluptate quidem natus.
17 |
18 |
19 |
20 | Get Started
21 | Learn More
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default Header;
29 |
--------------------------------------------------------------------------------
/components/heros/hero-five.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import Balancer from "react-wrap-balancer";
4 | import { ArrowRight } from "lucide-react";
5 |
6 | // UI Components
7 | import { Button } from "@/components/ui/button";
8 |
9 | // Custom Components
10 | import { Section, Container } from "@/components/craft";
11 |
12 | // Assets
13 | import Placeholder from "@/public/placeholder.jpg";
14 |
15 | export default function Hero() {
16 | return (
17 |
18 |
19 |
20 |
26 |
27 | Lorem ipsum dolor sit amet
28 |
29 |
30 |
31 |
32 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
33 |
34 |
35 |
36 |
37 | Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
38 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
39 |
40 |
41 |
42 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 |
--------------------------------------------------------------------------------
/components/heros/hero-four.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Link from "next/link";
3 | import Image from "next/image";
4 |
5 | // Local component imports
6 | import { Section, Container } from "@/components/craft";
7 |
8 | // Asset imports
9 | import Placeholder from "@/public/placeholder.jpg";
10 |
11 | const Hero = () => {
12 | return (
13 |
14 |
15 | {/* Name and Nav */}
16 |
17 |
18 |
19 |
20 | Lorem Ipsum
21 |
22 |
23 |
24 | Another component from Craft UI.
25 |
26 |
27 |
28 |
29 | Book a component
30 |
31 |
32 | Heros
33 |
34 |
35 | Shop
36 |
37 |
38 | Learn More
39 |
40 |
41 |
42 |
43 | {/* Images */}
44 |
45 | {/* Image 1 */}
46 |
47 |
52 |
53 | {/* Image 2 */}
54 |
55 |
60 |
61 |
62 |
63 | {/* Circle CTA */}
64 |
65 |
66 | Get Started
67 |
68 |
69 |
70 |
71 | );
72 | };
73 |
74 | export default Hero;
75 |
--------------------------------------------------------------------------------
/components/heros/hero-one.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Image from "next/image";
3 | import Link from "next/link";
4 |
5 | // Third-party library imports
6 | import Balancer from "react-wrap-balancer";
7 | import { ArrowRight } from "lucide-react";
8 |
9 | // Local component imports
10 | import { Section, Container } from "@/components/craft";
11 | import { Button } from "../ui/button";
12 |
13 | // Asset imports
14 | import Placeholder from "@/public/placeholder.jpg";
15 |
16 | const Hero = () => {
17 | return (
18 |
19 |
20 |
21 |
27 |
28 | Lorem ipsum dolor sit amet
29 |
30 |
31 |
32 |
33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
34 |
35 |
36 |
37 |
38 | Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
39 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
40 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
41 |
42 |
43 |
44 |
52 |
53 |
54 |
55 |
56 | );
57 | };
58 |
59 | export default Hero;
60 |
--------------------------------------------------------------------------------
/components/heros/hero-six.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | import { ArrowRight } from "lucide-react";
4 |
5 | import { Section, Container } from "@/components/craft";
6 | import { Button } from "../ui/button";
7 | import { Badge } from "../ui/badge";
8 |
9 | const Hero = () => {
10 | return (
11 |
12 |
13 |
14 |
18 | Lorem ipsum dolor sit amet
19 |
20 |
21 |
22 |
23 | What if building landing pages was as easy as copy and paste?
24 |
25 |
26 | Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
27 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
28 | eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 |
30 |
31 |
32 | Get Started
33 | Learn More
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | export default Hero;
41 |
--------------------------------------------------------------------------------
/components/heros/hero-three.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 |
4 | import { Section, Container } from "@/components/craft";
5 | import Logo from "@/public/logo.svg";
6 |
7 | const Hero = () => {
8 | return (
9 |
10 |
11 |
12 | {/* Large Text */}
13 |
14 | This is a component {/* eslint-disable-next-line */}
15 | {" "}
22 | with special {/* eslint-disable-next-line */}
23 | {" "}
30 | image capabilities. {/* eslint-disable-next-line */}
31 |
32 | {/* logo features */}
33 |
34 |
As seen in:
35 |
36 |
37 |
38 |
39 | {/* End Text */}
40 |
41 |
42 | We make components for marketing sites.
43 |
44 |
45 |
46 | Visit craftui.org to use our components for free.{" "}
47 |
48 |
49 | Check it Out -{`>`}
50 |
51 |
52 |
53 | Available now online. Tell us
54 | what you build next.
55 |
56 |
57 |
58 |
59 |
60 | );
61 | };
62 |
63 | export default Hero;
64 |
--------------------------------------------------------------------------------
/components/heros/hero-two.tsx:
--------------------------------------------------------------------------------
1 | // React and Next.js imports
2 | import Link from "next/link";
3 | import Image from "next/image";
4 |
5 | // Third-party library imports
6 | import Balancer from "react-wrap-balancer";
7 | import { Camera } from "lucide-react";
8 |
9 | // Local component imports
10 | import { Section, Container } from "@/components/craft";
11 | import { Button } from "@/components/ui/button";
12 |
13 | // Asset imports
14 | import Logo from "@/public/logo.svg";
15 |
16 | const Hero = () => {
17 | return (
18 |
19 |
20 |
27 |
28 |
29 | Lorem ipsum dolor sit amet, consectetur adipiscing elit.
30 |
31 |
32 |
33 |
34 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
35 | nisi ut aliquip ex ea commodo consequat.
36 |
37 |
38 |
39 |
40 |
41 |
42 | Lorem Ipsum
43 |
44 |
45 |
46 | Dolor Sit Amet -{">"}
47 |
48 |
49 |
50 |
51 | );
52 | };
53 |
54 | export default Hero;
55 |
--------------------------------------------------------------------------------
/components/info.tsx:
--------------------------------------------------------------------------------
1 | import { Section, Container } from "@/components/craft";
2 |
3 | import { EmailForm } from "./email-form";
4 |
5 | export const Info = ({ title }: { title: string }) => {
6 | return (
7 |
8 |
9 | {title}
10 |
11 | Read the{" "}
12 |
17 | documentation on GitHub
18 |
19 | . Copy a component below and paste it into your application.
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | };
28 |
--------------------------------------------------------------------------------
/components/nav-links.tsx:
--------------------------------------------------------------------------------
1 | import { Container } from "./craft";
2 | import { Button } from "./ui/button";
3 | import Link from "next/link";
4 |
5 | // Types of components
6 | const types: ComponentTypes[] = [
7 | "all",
8 | "hero",
9 | "feature",
10 | "cta",
11 | "header",
12 | "faq",
13 | ];
14 |
15 | export function NavLinks() {
16 | return (
17 |
18 | Sort by Type:
19 | {types.map((type) => (
20 |
28 | {type}
29 |
30 | ))}
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/components/nav.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Link from "next/link";
4 | import { usePathname } from "next/navigation";
5 | import { ModeToggle } from "./site/theme/theme-toggle";
6 | import { Button } from "./ui/button";
7 | import { motion, useScroll, useMotionValueEvent } from "framer-motion";
8 | import { useState } from "react";
9 | import { Github, Menu } from "lucide-react";
10 | import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
11 |
12 | export const Nav = () => {
13 | const { scrollY } = useScroll();
14 | const [hidden, setHidden] = useState(false);
15 |
16 | useMotionValueEvent(scrollY, "change", (latest) => {
17 | const previous = scrollY.getPrevious() ?? 0;
18 | if (latest > previous && latest > 150) {
19 | setHidden(true);
20 | } else {
21 | setHidden(false);
22 | }
23 | });
24 |
25 | return (
26 |
35 |
36 |
37 | components .work
38 |
39 |
40 |
41 |
42 | );
43 | };
44 |
45 | const links = [
46 | { name: "Heros", href: "/hero" },
47 | { name: "Features", href: "/feature" },
48 | { name: "CTAs", href: "/cta" },
49 | { name: "FAQs", href: "/faq" },
50 | { name: "Pricing", href: "/pricing" },
51 | { name: "Headers", href: "/header" },
52 | { name: "Footers", href: "/footer" },
53 | ];
54 |
55 | const NavList = () => {
56 | const pathname = usePathname();
57 |
58 | return (
59 |
60 |
61 | {links.map((link) => (
62 |
71 | {link.name}
72 |
73 | ))}
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | {links.map((link) => (
92 |
101 | {link.name}
102 |
103 | ))}
104 |
105 |
106 |
107 |
108 |
109 | );
110 | };
111 |
--------------------------------------------------------------------------------
/components/pricing/pricing-four.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Balancer from "react-wrap-balancer";
4 | import { useState } from "react";
5 | import { Button } from "@/components/ui/button";
6 | import { CircleCheck } from "lucide-react";
7 | import { Badge } from "@/components/ui/badge";
8 | import Link from "next/link";
9 | import { Slider } from "@/components/ui/slider";
10 | import { Container, Section } from "../craft";
11 |
12 | interface PricingCardProps {
13 | title: "Basic" | "Standard" | "Pro";
14 | basePrice: number;
15 | pricePerUser: number;
16 | description?: string;
17 | features: string[];
18 | cta: string;
19 | href: string;
20 | isPopular?: boolean;
21 | }
22 |
23 | // Dummy pricing data
24 | const pricingData: PricingCardProps[] = [
25 | {
26 | title: "Basic",
27 | basePrice: 29,
28 | pricePerUser: 5,
29 | description: "Perfect for small businesses and individuals.",
30 | features: ["3 Pages", "Basic SEO", "Email Support", "Responsive Design"],
31 | cta: "Choose Basic",
32 | href: "https://stripe.com/",
33 | },
34 | {
35 | title: "Standard",
36 | basePrice: 59,
37 | pricePerUser: 8,
38 | description: "Best for growing businesses with more needs.",
39 | features: [
40 | "10 Pages",
41 | "Advanced SEO",
42 | "CMS Integration",
43 | "24/7 Chat Support",
44 | ],
45 | cta: "Choose Standard",
46 | href: "https://stripe.com/",
47 | isPopular: true,
48 | },
49 | {
50 | title: "Pro",
51 | basePrice: 99,
52 | pricePerUser: 12,
53 | description: "Ideal for larger businesses that need scalability.",
54 | features: [
55 | "Unlimited Pages",
56 | "E-commerce Integration",
57 | "Priority Support",
58 | "Custom API Integration",
59 | ],
60 | cta: "Choose Pro",
61 | href: "https://stripe.com/",
62 | },
63 | ];
64 |
65 | const Pricing = () => {
66 | const [userCount, setUserCount] = useState(1);
67 |
68 | return (
69 |
70 |
71 | Pricing
72 |
73 | Customize your plan to fit your exact needs. Only pay for what you use.
74 |
75 |
76 |
77 |
78 | Number of Users
79 |
80 | {userCount} {userCount === 1 ? "user" : "users"}
81 |
82 |
83 |
setUserCount(value[0])}
89 | className="mb-6"
90 | />
91 |
92 |
93 |
94 | {pricingData.map((plan, index) => (
95 |
96 | ))}
97 |
98 |
99 |
100 | );
101 | };
102 |
103 | const PricingCard = ({
104 | plan,
105 | userCount,
106 | }: {
107 | plan: PricingCardProps;
108 | userCount: number;
109 | }) => {
110 | const totalPrice = plan.basePrice + plan.pricePerUser * (userCount - 1);
111 |
112 | return (
113 |
120 |
121 |
122 | {plan.title}
123 | {plan.isPopular && (
124 | ★ Popular
125 | )}
126 |
127 |
128 | ${totalPrice}/month
129 |
130 |
{plan.description}
131 |
132 | ${plan.basePrice} base + ${plan.pricePerUser}/additional user
133 |
134 |
135 |
136 |
137 |
138 |
139 | {plan.features.map((feature, i) => (
140 |
141 |
142 | {feature}
143 |
144 | ))}
145 |
146 |
147 |
148 |
149 |
154 | {plan.cta}
155 |
156 |
157 |
158 |
159 | );
160 | };
161 |
162 | export default Pricing;
163 |
--------------------------------------------------------------------------------
/components/pricing/pricing-one.tsx:
--------------------------------------------------------------------------------
1 | import Balancer from "react-wrap-balancer";
2 | import { Article, Container, Section } from "../craft";
3 | import { ReactNode } from "react";
4 | import { Button } from "../ui/button";
5 | import { CircleCheck } from "lucide-react";
6 | import { Badge } from "../ui/badge";
7 | import Link from "next/link";
8 |
9 | interface PricingCardProps {
10 | title: "Basic" | "Standard" | "Pro";
11 | price: string;
12 | description?: string;
13 | features: string[];
14 | cta: string;
15 | href: string;
16 | }
17 |
18 | // Dummy pricing data
19 | const pricingData: PricingCardProps[] = [
20 | {
21 | title: "Basic",
22 | price: "$29/month",
23 | description: "Perfect for small businesses and individuals.",
24 | features: ["3 Pages", "Basic SEO", "Email Support", "Responsive Design"],
25 | cta: "Choose Basic",
26 | href: "https://stripe.com/",
27 | },
28 | {
29 | title: "Standard",
30 | price: "$59/month",
31 | description: "Best for growing businesses with more needs.",
32 | features: [
33 | "10 Pages",
34 | "Advanced SEO",
35 | "CMS Integration",
36 | "24/7 Chat Support",
37 | ],
38 | cta: "Choose Standard",
39 | href: "https://stripe.com/",
40 | },
41 | {
42 | title: "Pro",
43 | price: "$99/month",
44 | description: "Ideal for larger businesses that need scalability.",
45 | features: [
46 | "Unlimited Pages",
47 | "E-commerce Integration",
48 | "Priority Support",
49 | "Custom API Integration",
50 | ],
51 | cta: "Choose Pro",
52 | href: "https://stripe.com/",
53 | },
54 | ];
55 |
56 | const Pricing = () => {
57 | return (
58 |
59 |
60 | Pricing
61 |
62 | Select the plan that best suits your needs.
63 |
64 |
65 |
66 | {pricingData.map((plan, index) => (
67 |
68 | ))}
69 |
70 |
71 |
72 | );
73 | };
74 |
75 | const PricingCard = ({ plan }: { plan: PricingCardProps }) => {
76 | return (
77 |
78 |
79 |
{plan.title}
80 |
{plan.price}
81 |
{plan.description}
82 |
83 |
84 |
85 |
86 |
87 | {plan.features.map((feature, i) => (
88 |
89 |
90 | {feature}
91 |
92 | ))}
93 |
94 |
95 |
96 |
97 |
98 | {plan.cta}
99 |
100 |
101 |
102 |
103 | );
104 | };
105 |
106 | export default Pricing;
107 |
--------------------------------------------------------------------------------
/components/pricing/pricing-three.tsx:
--------------------------------------------------------------------------------
1 | import Balancer from "react-wrap-balancer";
2 | import { Button } from "@/components/ui/button";
3 | import { CircleCheck } from "lucide-react";
4 | import { Badge } from "@/components/ui/badge";
5 | import Link from "next/link";
6 | import { Container, Section } from "../craft";
7 |
8 | interface PricingCardProps {
9 | title: "Basic" | "Standard" | "Pro";
10 | price: string;
11 | description?: string;
12 | features: string[];
13 | cta: string;
14 | href: string;
15 | isPopular?: boolean;
16 | }
17 |
18 | // Dummy pricing data
19 | const pricingData: PricingCardProps[] = [
20 | {
21 | title: "Basic",
22 | price: "$29/month",
23 | description: "Perfect for small businesses and individuals.",
24 | features: ["3 Pages", "Basic SEO", "Email Support", "Responsive Design"],
25 | cta: "Choose Basic",
26 | href: "https://stripe.com/",
27 | },
28 | {
29 | title: "Standard",
30 | price: "$59/month",
31 | description: "Best for growing businesses with more needs.",
32 | features: [
33 | "10 Pages",
34 | "Advanced SEO",
35 | "CMS Integration",
36 | "24/7 Chat Support",
37 | ],
38 | cta: "Choose Standard",
39 | href: "https://stripe.com/",
40 | isPopular: true,
41 | },
42 | {
43 | title: "Pro",
44 | price: "$99/month",
45 | description: "Ideal for larger businesses that need scalability.",
46 | features: [
47 | "Unlimited Pages",
48 | "E-commerce Integration",
49 | "Priority Support",
50 | "Custom API Integration",
51 | ],
52 | cta: "Choose Pro",
53 | href: "https://stripe.com/",
54 | },
55 | ];
56 |
57 | const Pricing = () => {
58 | return (
59 |
60 |
61 | Pricing
62 |
63 | Select the plan that best suits your needs.
64 |
65 |
66 |
67 | {pricingData.map((plan, index) => (
68 |
69 | ))}
70 |
71 |
72 |
73 | );
74 | };
75 | const PricingCard = ({ plan }: { plan: PricingCardProps }) => {
76 | return (
77 |
84 |
85 |
86 | {plan.title}
87 | {plan.isPopular && (
88 | ★ Popular
89 | )}
90 |
91 |
92 | {plan.price}
93 |
94 |
{plan.description}
95 |
96 |
97 |
98 |
99 |
100 | {plan.features.map((feature, i) => (
101 |
102 |
103 | {feature}
104 |
105 | ))}
106 |
107 |
108 |
109 |
110 |
115 | {plan.cta}
116 |
117 |
118 |
119 |
120 | );
121 | };
122 |
123 | export default Pricing;
124 |
--------------------------------------------------------------------------------
/components/pricing/pricing-two.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import Balancer from "react-wrap-balancer"
4 | import { useState } from "react"
5 | import { Button } from "@/components/ui/button"
6 | import { CircleCheck } from "lucide-react"
7 | import { Badge } from "@/components/ui/badge"
8 | import Link from "next/link"
9 | import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
10 | import { Section,Container } from "../craft"
11 |
12 | interface PricingCardProps {
13 | title: "Basic" | "Standard" | "Pro"
14 | monthlyPrice: string
15 | yearlyPrice: string
16 | description?: string
17 | features: string[]
18 | cta: string
19 | href: string
20 | }
21 |
22 | // Dummy pricing data
23 | const pricingData: PricingCardProps[] = [
24 | {
25 | title: "Basic",
26 | monthlyPrice: "$29",
27 | yearlyPrice: "$290",
28 | description: "Perfect for small businesses and individuals.",
29 | features: ["3 Pages", "Basic SEO", "Email Support", "Responsive Design"],
30 | cta: "Choose Basic",
31 | href: "https://stripe.com/",
32 | },
33 | {
34 | title: "Standard",
35 | monthlyPrice: "$59",
36 | yearlyPrice: "$590",
37 | description: "Best for growing businesses with more needs.",
38 | features: ["10 Pages", "Advanced SEO", "CMS Integration", "24/7 Chat Support"],
39 | cta: "Choose Standard",
40 | href: "https://stripe.com/",
41 | },
42 | {
43 | title: "Pro",
44 | monthlyPrice: "$99",
45 | yearlyPrice: "$990",
46 | description: "Ideal for larger businesses that need scalability.",
47 | features: ["Unlimited Pages", "E-commerce Integration", "Priority Support", "Custom API Integration"],
48 | cta: "Choose Pro",
49 | href: "https://stripe.com/",
50 | },
51 | ]
52 |
53 | const Pricing = () => {
54 | const [billingPeriod, setBillingPeriod] = useState<"monthly" | "yearly">("monthly")
55 |
56 | return (
57 |
58 |
59 | Pricing
60 |
61 | Select the plan that best suits your needs.
62 |
63 |
64 |
65 | setBillingPeriod(value as "monthly" | "yearly")}
68 | className="w-[300px]"
69 | >
70 |
71 | Monthly
72 |
73 | Yearly
74 |
75 | Save 17%
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | {pricingData.map((plan, index) => (
84 |
85 | ))}
86 |
87 |
88 |
89 | )
90 | }
91 |
92 | const PricingCard = ({
93 | plan,
94 | billingPeriod,
95 | }: {
96 | plan: PricingCardProps
97 | billingPeriod: "monthly" | "yearly"
98 | }) => {
99 | const price = billingPeriod === "monthly" ? `${plan.monthlyPrice}/month` : `${plan.yearlyPrice}/year`
100 |
101 | return (
102 |
103 |
104 |
{plan.title}
105 |
{price}
106 |
{plan.description}
107 |
108 |
109 |
110 |
111 |
112 | {plan.features.map((feature, i) => (
113 |
114 |
115 | {feature}
116 |
117 | ))}
118 |
119 |
120 |
121 |
122 |
123 | {plan.cta}
124 |
125 |
126 |
127 |
128 | )
129 | }
130 |
131 | export default Pricing
132 |
133 |
--------------------------------------------------------------------------------
/components/site/theme/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { ThemeProvider as NextThemesProvider } from "next-themes";
5 | import { type ThemeProviderProps } from "next-themes/dist/types";
6 |
7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
8 | return {children} ;
9 | }
10 |
--------------------------------------------------------------------------------
/components/site/theme/theme-toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { Moon, Sun } from "lucide-react";
5 | import { useTheme } from "next-themes";
6 | import { Button } from "@/components/ui/button";
7 |
8 | export function ModeToggle() {
9 | const { theme, setTheme } = useTheme();
10 |
11 | const toggleTheme = () => {
12 | setTheme(theme === "dark" ? "light" : "dark");
13 | };
14 |
15 | return (
16 |
17 |
18 |
19 | Toggle theme
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/components/type-button.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "./ui/button";
2 | import Link from "next/link";
3 |
4 | type TypeButtonProps = {
5 | type: string;
6 | className?: string;
7 | };
8 |
9 | export const TypeButton = ({ type, className }: TypeButtonProps) => {
10 | return (
11 |
12 | {type}
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/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 gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15 | outline:
16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost: "hover:bg-accent hover:text-accent-foreground",
20 | link: "text-primary underline-offset-4 hover:underline",
21 | },
22 | size: {
23 | default: "h-10 px-4 py-2",
24 | sm: "h-9 rounded-md px-3",
25 | lg: "h-11 rounded-md px-8",
26 | icon: "h-10 w-10",
27 | },
28 | },
29 | defaultVariants: {
30 | variant: "default",
31 | size: "default",
32 | },
33 | }
34 | )
35 |
36 | export interface ButtonProps
37 | extends React.ButtonHTMLAttributes,
38 | VariantProps {
39 | asChild?: boolean
40 | }
41 |
42 | const Button = React.forwardRef(
43 | ({ className, variant, size, asChild = false, ...props }, ref) => {
44 | const Comp = asChild ? Slot : "button"
45 | return (
46 |
51 | )
52 | }
53 | )
54 | Button.displayName = "Button"
55 |
56 | export { Button, buttonVariants }
57 |
--------------------------------------------------------------------------------
/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 |
59 | ),
60 | IconRight: ({ className, ...props }) => (
61 |
62 | ),
63 | }}
64 | {...props}
65 | />
66 | )
67 | }
68 | Calendar.displayName = "Calendar"
69 |
70 | export { Calendar }
71 |
--------------------------------------------------------------------------------
/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/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/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 | FormProvider,
9 | useFormContext,
10 | type ControllerProps,
11 | type FieldPath,
12 | type FieldValues,
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 | const Input = React.forwardRef>(
6 | ({ className, type, ...props }, ref) => {
7 | return (
8 |
17 | )
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/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 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ProgressPrimitive from "@radix-ui/react-progress"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Progress = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, value, ...props }, ref) => (
12 |
20 |
24 |
25 | ))
26 | Progress.displayName = ProgressPrimitive.Root.displayName
27 |
28 | export { Progress }
29 |
--------------------------------------------------------------------------------
/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/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 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TabsPrimitive from "@radix-ui/react-tabs"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Tabs = TabsPrimitive.Root
9 |
10 | const TabsList = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ))
23 | TabsList.displayName = TabsPrimitive.List.displayName
24 |
25 | const TabsTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef
28 | >(({ className, ...props }, ref) => (
29 |
37 | ))
38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
39 |
40 | const TabsContent = React.forwardRef<
41 | React.ElementRef,
42 | React.ComponentPropsWithoutRef
43 | >(({ className, ...props }, ref) => (
44 |
52 | ))
53 | TabsContent.displayName = TabsPrimitive.Content.displayName
54 |
55 | export { Tabs, TabsList, TabsTrigger, TabsContent }
56 |
--------------------------------------------------------------------------------
/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 | import * as React from "react"
2 | import * as ToastPrimitives from "@radix-ui/react-toast"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 | import { X } from "lucide-react"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const ToastProvider = ToastPrimitives.Provider
9 |
10 | const ToastViewport = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ))
23 | ToastViewport.displayName = ToastPrimitives.Viewport.displayName
24 |
25 | const toastVariants = cva(
26 | "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",
27 | {
28 | variants: {
29 | variant: {
30 | default: "border bg-background text-foreground",
31 | destructive:
32 | "destructive group border-destructive bg-destructive text-destructive-foreground",
33 | },
34 | },
35 | defaultVariants: {
36 | variant: "default",
37 | },
38 | }
39 | )
40 |
41 | const Toast = React.forwardRef<
42 | React.ElementRef,
43 | React.ComponentPropsWithoutRef &
44 | VariantProps
45 | >(({ className, variant, ...props }, ref) => {
46 | return (
47 |
52 | )
53 | })
54 | Toast.displayName = ToastPrimitives.Root.displayName
55 |
56 | const ToastAction = React.forwardRef<
57 | React.ElementRef,
58 | React.ComponentPropsWithoutRef
59 | >(({ className, ...props }, ref) => (
60 |
68 | ))
69 | ToastAction.displayName = ToastPrimitives.Action.displayName
70 |
71 | const ToastClose = React.forwardRef<
72 | React.ElementRef,
73 | React.ComponentPropsWithoutRef
74 | >(({ className, ...props }, ref) => (
75 |
84 |
85 |
86 | ))
87 | ToastClose.displayName = ToastPrimitives.Close.displayName
88 |
89 | const ToastTitle = React.forwardRef<
90 | React.ElementRef,
91 | React.ComponentPropsWithoutRef
92 | >(({ className, ...props }, ref) => (
93 |
98 | ))
99 | ToastTitle.displayName = ToastPrimitives.Title.displayName
100 |
101 | const ToastDescription = React.forwardRef<
102 | React.ElementRef,
103 | React.ComponentPropsWithoutRef
104 | >(({ className, ...props }, ref) => (
105 |
110 | ))
111 | ToastDescription.displayName = ToastPrimitives.Description.displayName
112 |
113 | type ToastProps = React.ComponentPropsWithoutRef
114 |
115 | type ToastActionElement = React.ReactElement
116 |
117 | export {
118 | type ToastProps,
119 | type ToastActionElement,
120 | ToastProvider,
121 | ToastViewport,
122 | Toast,
123 | ToastTitle,
124 | ToastDescription,
125 | ToastClose,
126 | ToastAction,
127 | }
128 |
--------------------------------------------------------------------------------
/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import {
4 | Toast,
5 | ToastClose,
6 | ToastDescription,
7 | ToastProvider,
8 | ToastTitle,
9 | ToastViewport,
10 | } from "@/components/ui/toast"
11 | import { useToast } from "@/components/ui/use-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-toast.ts:
--------------------------------------------------------------------------------
1 | // Inspired by react-hot-toast library
2 | import * as React from "react"
3 |
4 | import type {
5 | ToastActionElement,
6 | ToastProps,
7 | } from "@/components/ui/toast"
8 |
9 | const TOAST_LIMIT = 1
10 | const TOAST_REMOVE_DELAY = 1000000
11 |
12 | type ToasterToast = ToastProps & {
13 | id: string
14 | title?: React.ReactNode
15 | description?: React.ReactNode
16 | action?: ToastActionElement
17 | }
18 |
19 | const actionTypes = {
20 | ADD_TOAST: "ADD_TOAST",
21 | UPDATE_TOAST: "UPDATE_TOAST",
22 | DISMISS_TOAST: "DISMISS_TOAST",
23 | REMOVE_TOAST: "REMOVE_TOAST",
24 | } as const
25 |
26 | let count = 0
27 |
28 | function genId() {
29 | count = (count + 1) % Number.MAX_SAFE_INTEGER
30 | return count.toString()
31 | }
32 |
33 | type ActionType = typeof actionTypes
34 |
35 | type Action =
36 | | {
37 | type: ActionType["ADD_TOAST"]
38 | toast: ToasterToast
39 | }
40 | | {
41 | type: ActionType["UPDATE_TOAST"]
42 | toast: Partial
43 | }
44 | | {
45 | type: ActionType["DISMISS_TOAST"]
46 | toastId?: ToasterToast["id"]
47 | }
48 | | {
49 | type: ActionType["REMOVE_TOAST"]
50 | toastId?: ToasterToast["id"]
51 | }
52 |
53 | interface State {
54 | toasts: ToasterToast[]
55 | }
56 |
57 | const toastTimeouts = new Map>()
58 |
59 | const addToRemoveQueue = (toastId: string) => {
60 | if (toastTimeouts.has(toastId)) {
61 | return
62 | }
63 |
64 | const timeout = setTimeout(() => {
65 | toastTimeouts.delete(toastId)
66 | dispatch({
67 | type: "REMOVE_TOAST",
68 | toastId: toastId,
69 | })
70 | }, TOAST_REMOVE_DELAY)
71 |
72 | toastTimeouts.set(toastId, timeout)
73 | }
74 |
75 | export const reducer = (state: State, action: Action): State => {
76 | switch (action.type) {
77 | case "ADD_TOAST":
78 | return {
79 | ...state,
80 | toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
81 | }
82 |
83 | case "UPDATE_TOAST":
84 | return {
85 | ...state,
86 | toasts: state.toasts.map((t) =>
87 | t.id === action.toast.id ? { ...t, ...action.toast } : t
88 | ),
89 | }
90 |
91 | case "DISMISS_TOAST": {
92 | const { toastId } = action
93 |
94 | // ! Side effects ! - This could be extracted into a dismissToast() action,
95 | // but I'll keep it here for simplicity
96 | if (toastId) {
97 | addToRemoveQueue(toastId)
98 | } else {
99 | state.toasts.forEach((toast) => {
100 | addToRemoveQueue(toast.id)
101 | })
102 | }
103 |
104 | return {
105 | ...state,
106 | toasts: state.toasts.map((t) =>
107 | t.id === toastId || toastId === undefined
108 | ? {
109 | ...t,
110 | open: false,
111 | }
112 | : t
113 | ),
114 | }
115 | }
116 | case "REMOVE_TOAST":
117 | if (action.toastId === undefined) {
118 | return {
119 | ...state,
120 | toasts: [],
121 | }
122 | }
123 | return {
124 | ...state,
125 | toasts: state.toasts.filter((t) => t.id !== action.toastId),
126 | }
127 | }
128 | }
129 |
130 | const listeners: Array<(state: State) => void> = []
131 |
132 | let memoryState: State = { toasts: [] }
133 |
134 | function dispatch(action: Action) {
135 | memoryState = reducer(memoryState, action)
136 | listeners.forEach((listener) => {
137 | listener(memoryState)
138 | })
139 | }
140 |
141 | type Toast = Omit
142 |
143 | function toast({ ...props }: Toast) {
144 | const id = genId()
145 |
146 | const update = (props: ToasterToast) =>
147 | dispatch({
148 | type: "UPDATE_TOAST",
149 | toast: { ...props, id },
150 | })
151 | const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
152 |
153 | dispatch({
154 | type: "ADD_TOAST",
155 | toast: {
156 | ...props,
157 | id,
158 | open: true,
159 | onOpenChange: (open) => {
160 | if (!open) dismiss()
161 | },
162 | },
163 | })
164 |
165 | return {
166 | id: id,
167 | dismiss,
168 | update,
169 | }
170 | }
171 |
172 | function useToast() {
173 | const [state, setState] = React.useState(memoryState)
174 |
175 | React.useEffect(() => {
176 | listeners.push(setState)
177 | return () => {
178 | const index = listeners.indexOf(setState)
179 | if (index > -1) {
180 | listeners.splice(index, 1)
181 | }
182 | }
183 | }, [state])
184 |
185 | return {
186 | ...state,
187 | toast,
188 | dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
189 | }
190 | }
191 |
192 | export { useToast, toast }
193 |
--------------------------------------------------------------------------------
/components/view-code.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { Code2, X } from "lucide-react";
5 | import { Button } from "@/components/ui/button";
6 | import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
7 | import { atomDark } from "react-syntax-highlighter/dist/esm/styles/prism";
8 |
9 | import CopyButton from "./copy";
10 |
11 | import {
12 | Dialog,
13 | DialogContent,
14 | DialogTitle,
15 | DialogTrigger,
16 | } from "@/components/ui/dialog";
17 |
18 | interface CodeDialogProps {
19 | code: string;
20 | }
21 |
22 | export const ViewCode: React.FC = ({ code }) => {
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Source Code
34 |
42 | {code}
43 |
44 |
45 |
46 |
47 | );
48 | };
49 |
--------------------------------------------------------------------------------
/components/wrapper.tsx:
--------------------------------------------------------------------------------
1 | import { Circle, ArrowUpRight } from "lucide-react";
2 | import { Button } from "./ui/button";
3 | import { CopyLink } from "./copy-link";
4 | import { ViewCode } from "./view-code";
5 |
6 | import Link from "next/link";
7 | import CopyButton from "./copy";
8 |
9 | export const Wrapper = ({
10 | children,
11 | path,
12 | code,
13 | }: {
14 | children: React.ReactNode;
15 | path: string;
16 | code: string;
17 | type?: string;
18 | }) => {
19 | return (
20 |
24 |
25 |
26 | {children}
27 |
28 |
29 | );
30 | };
31 |
32 | const TopBar = ({ path, code }: { path: string; code: string }) => {
33 | return (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
56 | view on github
57 |
58 |
59 |
60 | );
61 | };
62 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module "!!raw-loader!*" {
2 | const content: string;
3 | export default content;
4 | }
5 |
6 | type ComponentTypes =
7 | | "all"
8 | | "hero"
9 | | "cta"
10 | | "faq"
11 | | "feature"
12 | | "header"
13 | | "footer"
14 | | "pricing";
15 |
16 | type Components = {
17 | component: any;
18 | path: string;
19 | code: string;
20 | type: ComponentTypes;
21 | };
22 |
23 | // Types of components
24 | const types: ComponentTypes[] = [
25 | "all",
26 | "hero",
27 | "feature",
28 | "cta",
29 | "header",
30 | "faq",
31 | "footer",
32 | "pricing",
33 | ];
34 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/mdx-components.tsx:
--------------------------------------------------------------------------------
1 | import type { MDXComponents } from 'mdx/types'
2 |
3 | export function useMDXComponents(components: MDXComponents): MDXComponents {
4 | return {
5 | ...components,
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | const withMDX = require("@next/mdx")();
2 |
3 | /** @type {import('next').NextConfig} */
4 | const nextConfig = {
5 | async redirects() {
6 | return [
7 | {
8 | source: "/all",
9 | destination: "/",
10 | permanent: true,
11 | },
12 | ];
13 | },
14 |
15 | pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
16 |
17 | images: {
18 | remotePatterns: [
19 | {
20 | protocol: "https",
21 | hostname: "images.unsplash.com",
22 | port: "",
23 | pathname: "/**",
24 | },
25 | ],
26 | },
27 | };
28 |
29 | module.exports = withMDX(nextConfig);
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "components",
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.10.0",
13 | "@mdx-js/loader": "^3.1.0",
14 | "@mdx-js/react": "^3.1.0",
15 | "@next/mdx": "^14.2.29",
16 | "@radix-ui/react-accordion": "^1.2.11",
17 | "@radix-ui/react-alert-dialog": "^1.1.14",
18 | "@radix-ui/react-aspect-ratio": "^1.1.7",
19 | "@radix-ui/react-avatar": "^1.1.10",
20 | "@radix-ui/react-checkbox": "^1.3.2",
21 | "@radix-ui/react-collapsible": "^1.1.11",
22 | "@radix-ui/react-context-menu": "^2.2.15",
23 | "@radix-ui/react-dialog": "^1.1.14",
24 | "@radix-ui/react-dropdown-menu": "^2.1.15",
25 | "@radix-ui/react-hover-card": "^1.1.14",
26 | "@radix-ui/react-label": "^2.1.7",
27 | "@radix-ui/react-menubar": "^1.1.15",
28 | "@radix-ui/react-navigation-menu": "^1.2.13",
29 | "@radix-ui/react-popover": "^1.1.14",
30 | "@radix-ui/react-progress": "^1.1.7",
31 | "@radix-ui/react-radio-group": "^1.3.7",
32 | "@radix-ui/react-scroll-area": "^1.2.9",
33 | "@radix-ui/react-select": "^2.2.5",
34 | "@radix-ui/react-separator": "^1.1.7",
35 | "@radix-ui/react-slider": "^1.3.5",
36 | "@radix-ui/react-slot": "^1.2.3",
37 | "@radix-ui/react-switch": "^1.2.5",
38 | "@radix-ui/react-tabs": "^1.1.12",
39 | "@radix-ui/react-toast": "^1.2.14",
40 | "@radix-ui/react-toggle": "^1.1.9",
41 | "@radix-ui/react-toggle-group": "^1.1.10",
42 | "@radix-ui/react-tooltip": "^1.2.7",
43 | "@types/mdx": "^2.0.13",
44 | "@vercel/analytics": "^1.5.0",
45 | "class-variance-authority": "^0.7.1",
46 | "clsx": "^2.1.1",
47 | "cmdk": "^1.1.1",
48 | "date-fns": "^3.6.0",
49 | "embla-carousel-react": "^8.6.0",
50 | "framer-motion": "^11.18.2",
51 | "input-otp": "^1.4.2",
52 | "lucide-react": "^0.483.0",
53 | "mini-svg-data-uri": "^1.4.4",
54 | "next": "^15.3.3",
55 | "react": "^19.1.0",
56 | "react-day-picker": "8.10.1",
57 | "react-dom": "^19.1.0",
58 | "react-hook-form": "^7.56.4",
59 | "react-resizable-panels": "^2.1.9",
60 | "react-syntax-highlighter": "^15.6.1",
61 | "react-use-measure": "^2.1.7",
62 | "recharts": "^2.15.3",
63 | "sharp": "^0.33.5",
64 | "sonner": "^1.7.4",
65 | "tailwind-merge": "^2.6.0",
66 | "tailwindcss-animate": "^1.0.7",
67 | "use-clipboard-copy": "^0.2.0",
68 | "vaul": "^1.1.2",
69 | "zod": "^3.25.42"
70 | },
71 | "devDependencies": {
72 | "@tailwindcss/typography": "^0.5.16",
73 | "@types/node": "^20.17.56",
74 | "@types/react": "^18.3.23",
75 | "@types/react-dom": "^18.3.7",
76 | "@types/react-syntax-highlighter": "^15.5.13",
77 | "autoprefixer": "^10.4.21",
78 | "eslint": "^9.27.0",
79 | "eslint-config-next": "^15.3.3",
80 | "next-themes": "^0.2.1",
81 | "postcss": "^8.5.4",
82 | "prettier": "^3.5.3",
83 | "prettier-plugin-tailwindcss": "^0.6.12",
84 | "raw-loader": "^4.0.2",
85 | "react-wrap-balancer": "^1.1.1",
86 | "tailwindcss": "^3.4.17",
87 | "typescript": "^5.8.3"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brijr/components/df5867169b8d746b622b20aa50f0a64bfc7df826/public/placeholder.jpg
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const svgToDataUri = require("mini-svg-data-uri");
4 |
5 | const colors = require("tailwindcss/colors");
6 | const {
7 | default: flattenColorPalette,
8 | } = require("tailwindcss/lib/util/flattenColorPalette");
9 |
10 | const config = {
11 | darkMode: ["class"],
12 | content: [
13 | "./pages/**/*.{ts,tsx}",
14 | "./components/**/*.{ts,tsx}",
15 | "./app/**/*.{ts,tsx}",
16 | "./src/**/*.{ts,tsx}",
17 | ],
18 | prefix: "",
19 | theme: {
20 | container: {
21 | center: true,
22 | padding: '2rem',
23 | screens: {
24 | '2xl': '1400px'
25 | }
26 | },
27 | extend: {
28 | colors: {
29 | border: 'hsl(var(--border))',
30 | input: 'hsl(var(--input))',
31 | ring: 'hsl(var(--ring))',
32 | background: 'hsl(var(--background))',
33 | foreground: 'hsl(var(--foreground))',
34 | primary: {
35 | DEFAULT: 'hsl(var(--primary))',
36 | foreground: 'hsl(var(--primary-foreground))'
37 | },
38 | secondary: {
39 | DEFAULT: 'hsl(var(--secondary))',
40 | foreground: 'hsl(var(--secondary-foreground))'
41 | },
42 | destructive: {
43 | DEFAULT: 'hsl(var(--destructive))',
44 | foreground: 'hsl(var(--destructive-foreground))'
45 | },
46 | muted: {
47 | DEFAULT: 'hsl(var(--muted))',
48 | foreground: 'hsl(var(--muted-foreground))'
49 | },
50 | accent: {
51 | DEFAULT: 'hsl(var(--accent))',
52 | foreground: 'hsl(var(--accent-foreground))'
53 | },
54 | popover: {
55 | DEFAULT: 'hsl(var(--popover))',
56 | foreground: 'hsl(var(--popover-foreground))'
57 | },
58 | card: {
59 | DEFAULT: 'hsl(var(--card))',
60 | foreground: 'hsl(var(--card-foreground))'
61 | },
62 | sidebar: {
63 | DEFAULT: 'hsl(var(--sidebar-background))',
64 | foreground: 'hsl(var(--sidebar-foreground))',
65 | primary: 'hsl(var(--sidebar-primary))',
66 | 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
67 | accent: 'hsl(var(--sidebar-accent))',
68 | 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
69 | border: 'hsl(var(--sidebar-border))',
70 | ring: 'hsl(var(--sidebar-ring))'
71 | }
72 | },
73 | borderRadius: {
74 | lg: 'var(--radius)',
75 | md: 'calc(var(--radius) - 2px)',
76 | sm: 'calc(var(--radius) - 4px)'
77 | },
78 | keyframes: {
79 | 'accordion-down': {
80 | from: {
81 | height: '0'
82 | },
83 | to: {
84 | height: 'var(--radix-accordion-content-height)'
85 | }
86 | },
87 | 'accordion-up': {
88 | from: {
89 | height: 'var(--radix-accordion-content-height)'
90 | },
91 | to: {
92 | height: '0'
93 | }
94 | }
95 | },
96 | animation: {
97 | 'accordion-down': 'accordion-down 0.2s ease-out',
98 | 'accordion-up': 'accordion-up 0.2s ease-out'
99 | }
100 | }
101 | },
102 | plugins: [
103 | require("tailwindcss-animate"),
104 | require("@tailwindcss/typography"),
105 | function ({ matchUtilities, theme }: any) {
106 | matchUtilities(
107 | {
108 | "bg-grid": (value: any) => ({
109 | backgroundImage: `url("${svgToDataUri(
110 | ` `
111 | )}")`,
112 | }),
113 | "bg-grid-small": (value: any) => ({
114 | backgroundImage: `url("${svgToDataUri(
115 | ` `
116 | )}")`,
117 | }),
118 | "bg-dot": (value: any) => ({
119 | backgroundImage: `url("${svgToDataUri(
120 | ` `
121 | )}")`,
122 | }),
123 | },
124 | { values: flattenColorPalette(theme("backgroundColor")), type: "color" }
125 | );
126 | },
127 | ],
128 | } satisfies Config;
129 |
130 | export default config;
131 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": true,
11 | "noEmit": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "bundler",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "incremental": true,
19 | "plugins": [
20 | {
21 | "name": "next"
22 | }
23 | ],
24 | "paths": {
25 | "@/*": [
26 | "./*"
27 | ]
28 | },
29 | "target": "ES2017"
30 | },
31 | "include": [
32 | "next-env.d.ts",
33 | "**/*.ts",
34 | "**/*.tsx",
35 | ".next/types/**/*.ts",
36 | "app/(mdx)/start/page.mdx"
37 | ],
38 | "exclude": [
39 | "node_modules"
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brijr/components/df5867169b8d746b622b20aa50f0a64bfc7df826/webpack.config.js
--------------------------------------------------------------------------------